diff --git a/include/net/socket.h b/include/net/socket.h index 8ef4eeea7e7..0663b19e84b 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -139,6 +139,12 @@ struct zsock_pollfd { #define TLS_DTLS_HANDSHAKE_TIMEOUT_MIN 8 #define TLS_DTLS_HANDSHAKE_TIMEOUT_MAX 9 +/** Socket option for preventing certificates from being copied to the mbedTLS + * heap if possible. The option is only effective for DER certificates and is + * ignored for PEM certificates. + */ +#define TLS_CERT_NOCOPY 10 + /** @} */ /* Valid values for TLS_PEER_VERIFY option */ @@ -150,6 +156,10 @@ struct zsock_pollfd { #define TLS_DTLS_ROLE_CLIENT 0 /**< Client role in a DTLS session. */ #define TLS_DTLS_ROLE_SERVER 1 /**< Server role in a DTLS session. */ +/* Valid values for TLS_CERT_NOCOPY option */ +#define TLS_CERT_NOCOPY_NONE 0 /**< Cert duplicated in heap */ +#define TLS_CERT_NOCOPY_OPTIONAL 1 /**< Cert not copied in heap if DER */ + struct zsock_addrinfo { struct zsock_addrinfo *ai_next; int ai_flags; diff --git a/subsys/net/lib/sockets/sockets_tls.c b/subsys/net/lib/sockets/sockets_tls.c index 9e443874cd6..78490392c70 100644 --- a/subsys/net/lib/sockets/sockets_tls.c +++ b/subsys/net/lib/sockets/sockets_tls.c @@ -132,6 +132,11 @@ __net_socket struct tls_context { /** Peer verification level. */ int8_t verify_level; + /** Indicating on whether DER certificates should not be copied + * to the heap. + */ + int8_t cert_nocopy; + /** DTLS role, client by default. */ int8_t role; @@ -654,12 +659,30 @@ static int tls_rx(void *ctx, unsigned char *buf, size_t len) return received; } +#if defined(MBEDTLS_X509_CRT_PARSE_C) +static bool crt_is_pem(const unsigned char *buf, size_t buflen) +{ + return (buflen != 0 && buf[buflen - 1] == '\0' && + strstr((const char *)buf, "-----BEGIN CERTIFICATE-----") != NULL); +} +#endif + static int tls_add_ca_certificate(struct tls_context *tls, struct tls_credential *ca_cert) { #if defined(MBEDTLS_X509_CRT_PARSE_C) - int err = mbedtls_x509_crt_parse(&tls->ca_chain, - ca_cert->buf, ca_cert->len); + int err; + + if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE || + crt_is_pem(ca_cert->buf, ca_cert->len)) { + err = mbedtls_x509_crt_parse(&tls->ca_chain, ca_cert->buf, + ca_cert->len); + } else { + err = mbedtls_x509_crt_parse_der_nocopy(&tls->ca_chain, + ca_cert->buf, + ca_cert->len); + } + if (err != 0) { return -EINVAL; } @@ -679,17 +702,53 @@ static void tls_set_ca_chain(struct tls_context *tls) #endif /* MBEDTLS_X509_CRT_PARSE_C */ } -static int tls_set_own_cert(struct tls_context *tls, - struct tls_credential *own_cert, - struct tls_credential *priv_key) +static int tls_add_own_cert(struct tls_context *tls, + struct tls_credential *own_cert) { #if defined(MBEDTLS_X509_CRT_PARSE_C) - int err = mbedtls_x509_crt_parse(&tls->own_cert, - own_cert->buf, own_cert->len); + int err; + + if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE || + crt_is_pem(own_cert->buf, own_cert->len)) { + err = mbedtls_x509_crt_parse(&tls->own_cert, + own_cert->buf, own_cert->len); + } else { + err = mbedtls_x509_crt_parse_der_nocopy(&tls->own_cert, + own_cert->buf, + own_cert->len); + } + if (err != 0) { return -EINVAL; } + return 0; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + return -ENOTSUP; +} + +static int tls_set_own_cert(struct tls_context *tls) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + int err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert, + &tls->priv_key); + if (err != 0) { + err = -ENOMEM; + } + + return err; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + return -ENOTSUP; +} + +static int tls_set_private_key(struct tls_context *tls, + struct tls_credential *priv_key) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + int err; + err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf, priv_key->len, NULL, 0, tls_ctr_drbg_random, NULL); @@ -697,12 +756,6 @@ static int tls_set_own_cert(struct tls_context *tls, return -EINVAL; } - err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert, - &tls->priv_key); - if (err != 0) { - err = -ENOMEM; - } - return 0; #endif /* MBEDTLS_X509_CRT_PARSE_C */ @@ -736,21 +789,11 @@ static int tls_set_credential(struct tls_context *tls, return tls_add_ca_certificate(tls, cred); case TLS_CREDENTIAL_SERVER_CERTIFICATE: - { - struct tls_credential *priv_key = - credential_get(cred->tag, TLS_CREDENTIAL_PRIVATE_KEY); - if (!priv_key) { - return -ENOENT; - } - - return tls_set_own_cert(tls, cred, priv_key); - } + return tls_add_own_cert(tls, cred); case TLS_CREDENTIAL_PRIVATE_KEY: - /* Ignore private key - it will be used together - * with public certificate - */ - break; + return tls_set_private_key(tls, cred); + break; case TLS_CREDENTIAL_PSK: { @@ -781,7 +824,7 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls) struct tls_credential *cred; sec_tag_t tag; int i, err = 0; - bool tag_found, ca_cert_present = false; + bool tag_found, ca_cert_present = false, own_cert_present = false; credentials_lock(); @@ -800,6 +843,8 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls) if (cred->type == TLS_CREDENTIAL_CA_CERTIFICATE) { ca_cert_present = true; + } else if (cred->type == TLS_CREDENTIAL_SERVER_CERTIFICATE) { + own_cert_present = true; } } @@ -812,8 +857,13 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls) exit: credentials_unlock(); - if (err == 0 && ca_cert_present) { - tls_set_ca_chain(tls); + if (err == 0) { + if (ca_cert_present) { + tls_set_ca_chain(tls); + } + if (own_cert_present) { + err = tls_set_own_cert(tls); + } } return err; @@ -1327,6 +1377,31 @@ static int tls_opt_peer_verify_set(struct tls_context *context, return 0; } +static int tls_opt_cert_nocopy_set(struct tls_context *context, + const void *optval, socklen_t optlen) +{ + int *cert_nocopy; + + if (!optval) { + return -EINVAL; + } + + if (optlen != sizeof(int)) { + return -EINVAL; + } + + cert_nocopy = (int *)optval; + + if (*cert_nocopy != TLS_CERT_NOCOPY_NONE && + *cert_nocopy != TLS_CERT_NOCOPY_OPTIONAL) { + return -EINVAL; + } + + context->options.cert_nocopy = *cert_nocopy; + + return 0; +} + static int tls_opt_dtls_role_set(struct tls_context *context, const void *optval, socklen_t optlen) { @@ -2464,6 +2539,10 @@ int ztls_setsockopt_ctx(struct tls_context *ctx, int level, int optname, err = tls_opt_peer_verify_set(ctx, optval, optlen); break; + case TLS_CERT_NOCOPY: + err = tls_opt_cert_nocopy_set(ctx, optval, optlen); + break; + case TLS_DTLS_ROLE: err = tls_opt_dtls_role_set(ctx, optval, optlen); break;