WIP: Adds MbedTLS support to Cytoplasm #54

Draft
lda wants to merge 9 commits from lda/Cytoplasm:add-mbed into master
2 changed files with 31 additions and 19 deletions
Showing only changes of commit 4ce2b136a5 - Show all commits

View file

@ -85,6 +85,7 @@ static bool
RegisterPEMs(mbedtls_x509_crt *certs) RegisterPEMs(mbedtls_x509_crt *certs)
{ {
char *cafile; char *cafile;
int loaded = 0;
if (!certs) if (!certs)
{ {
return false; return false;
@ -94,28 +95,26 @@ RegisterPEMs(mbedtls_x509_crt *certs)
cafile = getenv("CYTO_TLS_CA"); cafile = getenv("CYTO_TLS_CA");
if (AddPEM(certs, cafile)) if (AddPEM(certs, cafile))
{ {
return true; loaded++;
} }

Forgive me for not understanding MbedTLS that well, but why does this need to be sourced from the environment? Can't we randomly generate a seed? What's the use case for using the same seed more than once?

Forgive me for not understanding MbedTLS that well, but why does this need to be sourced from the environment? Can't we randomly generate a seed? What's the use case for using the same seed more than once?
Outdated
Review

It doesn't have to in most cases(MbedTLS will try to use things like /dev/random if possible) but considering how POSIX doesn't really mandate such sources to exist in the first place (and that I really wouldn't use something like the Rand API for cryptographic tasks), and I think having an optional source of extra randomness, if possible, may be worth it.

What's the use case for using the same seed more than once?
MbdeTLS tends to change the seed anyways with whatever other sources it can grab too.

It *doesn't* have to in most cases(MbedTLS will try to use things like `/dev/random` if possible) but considering how POSIX doesn't really mandate such sources to exist in the first place (and that I really *wouldn't* use something like the `Rand` API for cryptographic tasks), and I think having an optional source of extra randomness, if possible, may be worth it. > What's the use case for using the same seed more than once? MbdeTLS tends to change the seed anyways with whatever other sources it can grab too.

(and that I really wouldn't use something like the Rand API for cryptographic tasks)

I agree, and that's the real problem here. Ideally, we should use the Rand API to generate the MbedTLS seed. The fact that we can't because Rand isn't cryptographically secure is a problem of its own that, in my opinion, should be fixed first before we start relying on environment variables.

I'd like Cytoplasm to very much be link-and-forget. It shouldn't have any special runtime dependencies like this. I don't even know how we'd trust the user to set the seed properly. If the system doesn't have some source of entropy that we can draw from, then I don't know how the user could generate a seed. Because if they can do it on the machine we're running on, then so can we, and so should we. Otherwise, they'd have to generate it on a different machine and then copy it over to the machine executing this Cytoplasm code? That seems pretty impractical, particularly because every time Cytoplasm is started, (or every time this function is called?) the seed needs to be freshly generated.

> (and that I really wouldn't use something like the Rand API for cryptographic tasks) I agree, and that's the real problem here. Ideally, we *should* use the Rand API to generate the MbedTLS seed. The fact that we can't because Rand isn't cryptographically secure is a problem of its own that, in my opinion, should be fixed first before we start relying on environment variables. I'd like Cytoplasm to very much be link-and-forget. It shouldn't have any special runtime dependencies like this. I don't even know how we'd trust the user to set the seed properly. If the system doesn't have some source of entropy that we can draw from, then I don't know how the user *could* generate a seed. Because if they can do it on the machine we're running on, then so can we, and so should we. Otherwise, they'd have to generate it on a different machine and then copy it over to the machine executing this Cytoplasm code? That seems pretty impractical, particularly because every time Cytoplasm is started, (or every time this function is called?) the seed needs to be freshly generated.
/* Step 1: Try /etc/ssl/certs */ /* Step 1: Try /etc/ssl/certs */
if (AddPEM(certs, "/etc/ssl/certs")) if (AddPEM(certs, "/etc/ssl/certs"))
{ {
return true; loaded++;
} }
/* Step 2: Try loading off Mozilla's certificates */ /* Step 2: Try loading off Mozilla's certificates */
if (AddPEM(certs, "/usr/share/ca-certificates/mozilla")) if (AddPEM(certs, "/usr/share/ca-certificates/mozilla"))
{ {
return true; loaded++;
} }
/* Step 3: Try loading from its root directly */ /* Step 3: Try loading from its root directly */
if (AddPEM(certs, "/usr/share/ca-certificates")) if (AddPEM(certs, "/usr/share/ca-certificates"))
{ {
return true; loaded++;
} }
return loaded != 0;
/* Step 4: Give up. */
return false;
} }
void * void *

Again, why should this come from the environment? Is there a way we can somehow just use the system's CA file?

Again, why should this come from the environment? Is there a way we can somehow just use the system's CA file?
Outdated
Review

Just made it check for known certificate paths(though I've kept the environment, in case any users may still want to use a custom directory/are really unlucky in their OS setups)

Just made it check for known certificate paths(though I've kept the environment, in case any users may still want to use a custom directory/are really unlucky in their OS setups)

Okay, I think I'm good with what you've done here. I do have a question though: Can you load multiple PEMs? Like, why bother with return true on success, when you could just keep going and load all the CA stores you can find? If the answer is no, you can only load one CA store, then that makes sense. I'm just curious if we could be even more flexible and load multiple.

However, I don't think this behavior should be specific to Mbed. If we're going to use CYTO_TLS_CA to modify Cytoplasm's runtime behavior in loading certificate stores, shouldn't all of the other TLS implementations honor this environment variable too?

For example, say I'm a sysadmin at a large corporation with tons of self-signed certificates for internal services. Instead of installing the certificate authority to the system, I want to just set CYTO_TLS_CA to my CA store. I'm on a Linux system so I can use OpenSSL, but the TLS implementation really shouldn't matter. Though I guess it might determine what format the CA store must be in. But the point is, the behavior should be consistent across all implementations.

Okay, I think I'm good with what you've done here. I do have a question though: Can you load *multiple* PEMs? Like, why bother with `return true` on success, when you could just keep going and load all the CA stores you can find? If the answer is no, you can only load one CA store, then that makes sense. I'm just curious if we could be *even more* flexible and load multiple. **However**, I don't think this behavior should be specific to Mbed. If we're going to use `CYTO_TLS_CA` to modify Cytoplasm's runtime behavior in loading certificate stores, shouldn't all of the *other* TLS implementations honor this environment variable too? For example, say I'm a sysadmin at a large corporation with tons of self-signed certificates for internal services. Instead of installing the certificate authority to the system, I want to just set `CYTO_TLS_CA` to my CA store. I'm on a Linux system so I can use OpenSSL, but the TLS implementation really shouldn't matter. Though I guess it might determine what format the CA store must be in. But the point is, the behavior should be consistent across all implementations.
Outdated
Review

Can you load multiple PEMs?

You actually can(if you look under the MbedTLS hood, loading a directory of PEM files is essentially just loading every PEM directly), so I just added that.

As for the CYTO_TLS_CA, I'm not sure I can exactly replicate that behavior around LibreSSL, as the functions used to replace certs with it seem to overwrite the current option, rather than just adding it alongside. We could just also do that on the MbedTLS side, but I don't know if that'd be a good thing...

> Can you load *multiple* PEMs? You actually *can*(if you look under the MbedTLS hood, loading a directory of PEM files is essentially just loading every PEM directly), so I just added that. As for the `CYTO_TLS_CA`, I'm not sure I can exactly replicate that behavior around LibreSSL, as the functions used to replace certs with it seem to *overwrite* the current option, rather than just adding it alongside. We *could* just also do that on the MbedTLS side, but I don't know if that'd be a good thing...

Ah, that's a bummer. I suppose maybe it would suffice to have CYTO_TLS_CA replace the system's CA store for all implementations. In other words, have Libre and Open check CYTO_TLS_CA and load the store given by that variable if it is set, otherwise do nothing and assume that they load the system store. For Mbed, only check and load the other certificate stores if CYTO_TLS_CA is unset, so that the behavior is consistent across all three implementations: if CYTO_TLS_CA is set, use only that store, otherwise use the system stores, and in the case of Mbed, try to determine what those stores are.

Doe that make sense? Does that sound like a good idea?

Ah, that's a bummer. I suppose maybe it would suffice to have `CYTO_TLS_CA` replace the system's CA store for all implementations. In other words, have Libre and Open check `CYTO_TLS_CA` and load the store given by that variable if it is set, otherwise do nothing and assume that they load the system store. For Mbed, only check and load the other certificate stores if `CYTO_TLS_CA` is unset, so that the behavior is consistent across all three implementations: if `CYTO_TLS_CA` is set, use *only* that store, otherwise use the system stores, and in the case of Mbed, try to determine what those stores are. Doe that make sense? Does that sound like a good idea?
@ -140,8 +139,8 @@ TlsInitClient(int fd, const char *serverName)
mbedtls_x509_crt_init(&cookie->cert); mbedtls_x509_crt_init(&cookie->cert);
mbedtls_ctr_drbg_init(&cookie->ctrDrbg); mbedtls_ctr_drbg_init(&cookie->ctrDrbg);
mbedtls_pk_init(&cookie->serverkey); mbedtls_pk_init(&cookie->serverkey);
mbedtls_entropy_init(&cookie->entropy); mbedtls_entropy_init(&cookie->entropy);
err = mbedtls_ctr_drbg_seed( err = mbedtls_ctr_drbg_seed(
&cookie->ctrDrbg, &cookie->ctrDrbg,
mbedtls_entropy_func, mbedtls_entropy_func,
@ -154,8 +153,6 @@ TlsInitClient(int fd, const char *serverName)
goto error; goto error;
} }
/* TODO: Reconsider a source of additional entropy. */
cookie->serverFD.fd = fd; cookie->serverFD.fd = fd;
err = mbedtls_ssl_config_defaults( err = mbedtls_ssl_config_defaults(
@ -168,7 +165,7 @@ TlsInitClient(int fd, const char *serverName)
{ {
char message[256]; char message[256];
mbedtls_strerror(err, message, 255); mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on client certs: %s", message); Log(LOG_ERR, "MbedTLS failure on client config: %s", message);
goto error; goto error;
} }
@ -180,6 +177,7 @@ TlsInitClient(int fd, const char *serverName)
Log(LOG_ERR, "MbedTLS failure on client certs: %s", message); Log(LOG_ERR, "MbedTLS failure on client certs: %s", message);
goto error; goto error;
} }
mbedtls_ssl_conf_authmode(&cookie->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&cookie->conf, &cookie->cert, NULL); mbedtls_ssl_conf_ca_chain(&cookie->conf, &cookie->cert, NULL);
/* Setup some callbacks */ /* Setup some callbacks */
@ -196,6 +194,12 @@ TlsInitClient(int fd, const char *serverName)
goto error; goto error;
} }
/* Setup some functions */
mbedtls_ssl_set_bio(
&cookie->ssl, &cookie->serverFD,
mbedtls_net_send, mbedtls_net_recv, NULL
);
/* Setup the servername */ /* Setup the servername */
if ((err = mbedtls_ssl_set_hostname(&cookie->ssl, serverName)) != 0) if ((err = mbedtls_ssl_set_hostname(&cookie->ssl, serverName)) != 0)
{ {
@ -204,12 +208,20 @@ TlsInitClient(int fd, const char *serverName)
Log(LOG_ERR, "MbedTLS failure on client hostname: %s", message); Log(LOG_ERR, "MbedTLS failure on client hostname: %s", message);
goto error; goto error;
} }
while ((err = mbedtls_ssl_handshake(&cookie->ssl)) != 0)
/* Setup some functions */ {
mbedtls_ssl_set_bio( char message[256];
&cookie->ssl, &cookie->serverFD, switch (err)
mbedtls_net_send, mbedtls_net_recv, NULL {
); case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_WANT_READ:
break;
default:
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on handshake: %s", message);
goto error;
}
}
return cookie; return cookie;
error: error:

View file

@ -399,7 +399,7 @@ end_loop:
} }
else if (StrEquals(op, "def?")) else if (StrEquals(op, "def?"))
{ {
Definition *def; Definition *def = NULL;
size_t i; size_t i;
char *directive = Eval(&argv, stack); char *directive = Eval(&argv, stack);