diff --git a/doc/dox_comments/header_files/asn_public.h b/doc/dox_comments/header_files/asn_public.h index a28acb88c6e..ecd8ef8fdcf 100644 --- a/doc/dox_comments/header_files/asn_public.h +++ b/doc/dox_comments/header_files/asn_public.h @@ -1740,6 +1740,9 @@ int wc_AllocDer(DerBuffer** pDer, word32 length, int type, \ingroup ASN \brief Frees DER buffer allocated by wc_AllocDer or wc_PemToDer. + Releases one reference and frees the buffer when no references remain. + A manually constructed DerBuffer must be zero-initialized before use. + \param pDer Pointer to DerBuffer pointer to free _Example_ diff --git a/src/internal.c b/src/internal.c index 60f13ef08f8..68c70f58de4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -379,6 +379,7 @@ DerBuffer *wolfssl_priv_der_unblind(const DerBuffer* key, const DerBuffer* mask) return NULL; if (AllocDer(&ret, key->length, key->type, key->heap) != 0) return NULL; + ret->zeroOnFree = key->zeroOnFree; xorbufout(ret->buffer, key->buffer, mask->buffer, mask->length); return ret; } @@ -3563,17 +3564,11 @@ void SSL_CtxResourceFree(WOLFSSL_CTX* ctx) #endif /* SINGLE_THREADED */ #ifndef NO_CERTS - if (ctx->privateKey != NULL && ctx->privateKey->buffer != NULL) { - ForceZero(ctx->privateKey->buffer, ctx->privateKey->length); - } FreeDer(&ctx->privateKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ctx->privateKeyMask); #endif #ifdef WOLFSSL_DUAL_ALG_CERTS - if (ctx->altPrivateKey != NULL && ctx->altPrivateKey->buffer != NULL) { - ForceZero(ctx->altPrivateKey->buffer, ctx->altPrivateKey->length); - } FreeDer(&ctx->altPrivateKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ctx->altPrivateKeyMask); @@ -8282,40 +8277,135 @@ static int SetSSL_CTX_CheckVersion(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #endif /* OPENSSL_EXTRA */ #ifndef NO_CERTS -/* Copy the certificate, certificate chain and private key buffers from the - * CTX into the SSL object. Returns 0 on success. */ -static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) +/* Every SSL DER pointer holds a reference, regardless of ownership. */ +void FreeSslDer(DerBuffer** der, byte* weOwn) { - int ret = 0; + FreeDer(der); + *weOwn = 0; +} + +/* Acquire the new reference before releasing the previous one. Callers must + * synchronize a CTX key replacement with acquisition from that CTX. */ +int AliasSslDer(DerBuffer** der, byte* weOwn, DerBuffer* src) +{ + int ret = RefDer(src); + if (ret == 0) { + FreeSslDer(der, weOwn); + *der = src; + } + return ret; +} + +/* Replace one CTX-held buffer and drop the CTX's reference to the old one. */ +int wolfSSL_CtxDerReplace(WOLFSSL_CTX* ctx, int chain, DerBuffer* replacement, + int chainCount) +{ + DerBuffer** der = chain ? &ctx->certChain : &ctx->certificate; + int ret = wolfSSL_RefWithMutexLock(&ctx->ref); + + if (ret != 0) + return ret; + if (*der != NULL) { + if (!chain) + ctx->certificateChanged = 1; + else + ctx->certChainChanged = 1; + } + FreeDer(der); + *der = replacement; + if (chain) + ctx->certChainCnt = chainCount; + if (wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); + return ret; +} +/* A borrowed SSL certificate can differ from the current CTX after reload. */ +int wolfSSL_CertMatchesCtx(WOLFSSL* ssl, int allowCache) +{ + int matches = 0; + WOLFSSL_CTX* ctx = ssl->ctx; + + if (ctx != NULL && wolfSSL_RefWithMutexLock(&ctx->ref) == 0) { + matches = ssl->buffers.certificate == ctx->certificate && + (!allowCache || !ctx->certificateChanged); + if (wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); + } + return matches; +} + +int wolfSSL_ChainMatchesCtx(WOLFSSL* ssl) +{ + int matches = 0; + WOLFSSL_CTX* ctx = ssl->ctx; + + if (ctx != NULL && wolfSSL_RefWithMutexLock(&ctx->ref) == 0) { + matches = ssl->buffers.certChain == ctx->certChain && + !ctx->certChainChanged; + if (wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); + } + return matches; +} + +/* Attach a consistent certificate and chain snapshot to a connection. */ +int wolfSSL_SetCertBuffersFromCtx(WOLFSSL* ssl, WOLFSSL_CTX* ctx) +{ + DerBuffer* cert = NULL; + DerBuffer* chain = NULL; + int ret = wolfSSL_RefWithMutexLock(&ctx->ref); + + if (ret != 0) + return ret; #ifdef WOLFSSL_COPY_CERT - /* If WOLFSSL_COPY_CERT is defined, always copy the cert */ - if (ctx->certificate != NULL) { - ret = AllocCopyDer(&ssl->buffers.certificate, ctx->certificate->buffer, + if (ctx->certificate != NULL) + ret = AllocCopyDer(&cert, ctx->certificate->buffer, ctx->certificate->length, ctx->certificate->type, ctx->certificate->heap); - if (ret != 0) { - return ret; - } - - ssl->buffers.weOwnCert = 1; - } - if (ctx->certChain != NULL) { - ret = AllocCopyDer(&ssl->buffers.certChain, ctx->certChain->buffer, + if (ret == 0 && ctx->certChain != NULL) + ret = AllocCopyDer(&chain, ctx->certChain->buffer, ctx->certChain->length, ctx->certChain->type, ctx->certChain->heap); - if (ret != 0) { - return ret; - } - - ssl->buffers.weOwnCertChain = 1; +#else + ret = RefDer(ctx->certificate); + if (ret == 0) { + cert = ctx->certificate; + ret = RefDer(ctx->certChain); + if (ret == 0) + chain = ctx->certChain; } +#endif + if (ret == 0) { + FreeDer(&ssl->buffers.certificate); + FreeDer(&ssl->buffers.certChain); + ssl->buffers.certificate = cert; + ssl->buffers.certChain = chain; +#ifdef WOLFSSL_COPY_CERT + ssl->buffers.weOwnCert = cert != NULL; + ssl->buffers.weOwnCertChain = chain != NULL; #else - /* ctx still owns certificate, certChain, key, dh, and cm */ - ssl->buffers.certificate = ctx->certificate; - ssl->buffers.certChain = ctx->certChain; + ssl->buffers.weOwnCert = 0; + ssl->buffers.weOwnCertChain = 0; #endif - ssl->buffers.certChainCnt = ctx->certChainCnt; + ssl->buffers.certChainCnt = ctx->certChainCnt; + } + else { + FreeDer(&cert); + FreeDer(&chain); + } + if (wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); + return ret; +} + +/* Attach CTX certificate and key buffers to the SSL object. */ +static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) +{ + int ret = wolfSSL_SetCertBuffersFromCtx(ssl, ctx); + + if (ret != 0) + return ret; #ifndef WOLFSSL_BLIND_PRIVATE_KEY #ifdef WOLFSSL_COPY_KEY if (ctx->privateKey != NULL) { @@ -8328,13 +8418,17 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return ret; } + ssl->buffers.key->zeroOnFree = 1; ssl->buffers.weOwnKey = 1; } else { - ssl->buffers.key = ctx->privateKey; + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); } #else - ssl->buffers.key = ctx->privateKey; + ret = AliasSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey, + ctx->privateKey); + if (ret != 0) + return ret; #endif #else if (ctx->privateKey != NULL) { @@ -8347,6 +8441,7 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return ret; } + ssl->buffers.key->zeroOnFree = 1; ssl->buffers.weOwnKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ctx->privateKeyMask); @@ -8364,7 +8459,10 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->buffers.keyDevId = ctx->privateKeyDevId; #ifdef WOLFSSL_DUAL_ALG_CERTS #ifndef WOLFSSL_BLIND_PRIVATE_KEY - ssl->buffers.altKey = ctx->altPrivateKey; + ret = AliasSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey, + ctx->altPrivateKey); + if (ret != 0) + return ret; #else if (ctx->altPrivateKey != NULL) { ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer, @@ -8373,6 +8471,7 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return ret; } + ssl->buffers.altKey->zeroOnFree = 1; ssl->buffers.weOwnAltKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.altKey, @@ -10668,6 +10767,18 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) #ifndef NO_CERTS ssl->keepCert = 0; /* make sure certificate is free'd */ wolfSSL_UnloadCertsKeys(ssl); +#ifdef KEEP_OUR_CERT + if (!ssl->buffers.weOwnCert && ssl->buffers.certificate != NULL) { + wolfSSL_X509_free(ssl->ourCert); + ssl->ourCert = NULL; + } +#endif + FreeDer(&ssl->buffers.certificate); + FreeDer(&ssl->buffers.certChain); + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); +#ifdef WOLFSSL_DUAL_ALG_CERTS + FreeSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey); +#endif #endif #ifndef NO_RSA FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); @@ -29641,8 +29752,8 @@ int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request, /* Creates OCSP response and places it in variable "response". Memory * management for "buffer* response" is up to the caller. * - * Also creates an OcspRequest in the case that ocspRequest is null or that - * ssl->buffers.weOwnCert is set. A newly created request may be cached on the + * Also creates an OcspRequest when no compatible CTX request is available. + * A newly created request may be cached on the * CTX, which then owns it and frees it in wolfSSL_CTX_free(). "ctxOwnsRequest" * carries that ownership: it tells this function whether the CTX already owns * the request handed in, and on success it reports whether the CTX owns the @@ -29664,6 +29775,7 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest, OcspRequest* request = NULL; byte createdRequest = 0; byte ctxOwns = 0; + int canCache; /* Zero the output before any exit so that a caller freeing response->buffer * without checking the return code never acts on an indeterminate value. */ @@ -29677,6 +29789,7 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest, request = *ocspRequest; ctxOwns = *ctxOwnsRequest; + canCache = !ssl->buffers.weOwnCert && wolfSSL_CertMatchesCtx(ssl, 0); /* unable to fetch status. skip. The stapling object is checked here so that * every use of it below needs no further guarding. */ @@ -29685,7 +29798,7 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest, goto exit_cor; } - if (request == NULL || ssl->buffers.weOwnCert) { + if (request == NULL || !canCache) { DerBuffer* der = ssl->buffers.certificate; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); @@ -29707,33 +29820,30 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest, der->length); } - /* Only a request built from a CTX-held certificate may be cached, since - * the cache outlives this connection. - * - * Not being able to cache is not an error. ctxOwns stays 0, so this - * connection keeps ownership of the request and frees it as it would - * any other one it built; all that is lost is the reuse by later - * connections on this CTX. Failing the handshake over a missed - * optimization would be the worse outcome. */ - if (ret == 0 && !ssl->buffers.weOwnCert) { + /* Publish only for the current CTX certificate. A prior replacement + * is harmless when no request was cached; otherwise retain the old + * cache for existing users and keep this request local to the SSL. */ + if (ret == 0 && canCache) { wolfSSL_Mutex* ocspLock = GetCtxOcspLock(ssl); - /* SSL_CM(ssl) can resolve through ssl->x509_store_pt, so a stapling - * object on the CTX manager is not implied by the one checked - * above. */ if (ocspLock == NULL) { WOLFSSL_MSG("No CTX OCSP lock, not caching the request"); } - else if (wc_LockMutex(ocspLock) != 0) { - WOLFSSL_MSG("Couldn't lock CTX OCSP mutex, not caching the " - "request"); + else if (wolfSSL_RefWithMutexLock(&ssl->ctx->ref) != 0) { + WOLFSSL_MSG("Couldn't lock CTX certificate mutex"); } else { - if (ssl->ctx->certOcspRequest == NULL) { - ssl->ctx->certOcspRequest = request; - ctxOwns = 1; + if (ssl->buffers.certificate == ssl->ctx->certificate && + wc_LockMutex(ocspLock) == 0) { + if (ssl->ctx->certOcspRequest == NULL) { + ssl->ctx->certOcspRequest = request; + ssl->ctx->certificateChanged = 0; + ctxOwns = 1; + } + wc_UnLockMutex(ocspLock); } - wc_UnLockMutex(ocspLock); + if (wolfSSL_RefWithMutexUnlock(&ssl->ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); } } @@ -30598,6 +30708,9 @@ static OcspRequest* GetCtxOcspRequest(WOLFSSL* ssl, byte* ctxOwnsRequest) *ctxOwnsRequest = 0; + if (ssl->buffers.weOwnCert || !wolfSSL_CertMatchesCtx(ssl, 1)) + return NULL; + if (ocspLock == NULL) { WOLFSSL_MSG("No CTX OCSP lock, skipping the request cache"); } @@ -30763,8 +30876,9 @@ int SendCertificateStatus(WOLFSSL* ssl) } /* leafRequest is done; the chain below has its own. */ - if (ret == 0 && (!ssl->ctx->chainOcspRequest[0] - || ssl->buffers.weOwnCertChain)) { + if (ret == 0 && (!ssl->ctx->chainOcspRequest[0] || + ssl->buffers.weOwnCertChain || + !wolfSSL_ChainMatchesCtx(ssl))) { buffer der; word32 idx = 0; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); diff --git a/src/ssl.c b/src/ssl.c index 3773badb1d9..f5fdc44d939 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8617,7 +8617,7 @@ void wolfSSL_certs_clear(WOLFSSL* ssl) if (ssl == NULL) return; - /* ctx still owns certificate, certChain, key, dh, and cm */ + /* Release the certificate, chain and key when owned by this SSL. */ if (ssl->buffers.weOwnCert) { FreeDer(&ssl->buffers.certificate); #ifdef KEEP_OUR_CERT @@ -8629,11 +8629,19 @@ void wolfSSL_certs_clear(WOLFSSL* ssl) #endif ssl->buffers.weOwnCert = 0; } +#ifdef KEEP_OUR_CERT + if (!ssl->buffers.weOwnCert && ssl->buffers.certificate != NULL) { + wolfSSL_X509_free(ssl->ourCert); + ssl->ourCert = NULL; + } +#endif + FreeDer(&ssl->buffers.certificate); ssl->buffers.certificate = NULL; if (ssl->buffers.weOwnCertChain) { FreeDer(&ssl->buffers.certChain); ssl->buffers.weOwnCertChain = 0; } + FreeDer(&ssl->buffers.certChain); ssl->buffers.certChain = NULL; ssl->buffers.certChainCnt = 0; #ifdef KEEP_OUR_CERT @@ -8649,7 +8657,7 @@ void wolfSSL_certs_clear(WOLFSSL* ssl) #endif ssl->buffers.weOwnKey = 0; } - ssl->buffers.key = NULL; + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY ssl->buffers.keyMask = NULL; #endif @@ -8666,7 +8674,7 @@ void wolfSSL_certs_clear(WOLFSSL* ssl) #endif ssl->buffers.weOwnAltKey = 0; } - ssl->buffers.altKey = NULL; + FreeSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY ssl->buffers.altKeyMask = NULL; #endif @@ -8732,7 +8740,10 @@ long wolfSSL_CTX_ctrl(WOLFSSL_CTX* ctx, int cmd, long opt, void* pt) break; } /* Clear certificate chain */ - FreeDer(&ctx->certChain); + if (wolfSSL_CtxDerReplace(ctx, 1, NULL, 0) != 0) { + ret = WOLFSSL_FAILURE; + break; + } if (sk) { for (i = 0; i < wolfSSL_sk_X509_num(sk); i++) { x509 = wolfSSL_sk_X509_value(sk, i); @@ -9062,42 +9073,17 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) (void)ret; #endif + /* Release old shared buffers before dropping the old CTX reference. */ +#ifndef NO_CERTS + wolfSSL_certs_clear(ssl); +#endif if (ssl->ctx != NULL) wolfSSL_CTX_free(ssl->ctx); ssl->ctx = ctx; #ifndef NO_CERTS - /* Release the certificate, chain and keys this SSL owns so that a ctx - * without them leaves none of the old ones behind. */ - wolfSSL_certs_clear(ssl); -#ifdef WOLFSSL_COPY_CERT - /* If WOLFSSL_COPY_CERT defined, always make new copy of cert from ctx */ - if (ctx->certificate != NULL) { - ret = AllocCopyDer(&ssl->buffers.certificate, ctx->certificate->buffer, - ctx->certificate->length, ctx->certificate->type, - ctx->certificate->heap); - if (ret != 0) { - return NULL; - } - - ssl->buffers.weOwnCert = 1; - } - if (ctx->certChain != NULL) { - ret = AllocCopyDer(&ssl->buffers.certChain, ctx->certChain->buffer, - ctx->certChain->length, ctx->certChain->type, - ctx->certChain->heap); - if (ret != 0) { - return NULL; - } - - ssl->buffers.weOwnCertChain = 1; - } -#else - /* ctx owns certificate, certChain and key */ - ssl->buffers.certificate = ctx->certificate; - ssl->buffers.certChain = ctx->certChain; -#endif - ssl->buffers.certChainCnt = ctx->certChainCnt; + if (wolfSSL_SetCertBuffersFromCtx(ssl, ctx) != 0) + return NULL; #ifndef WOLFSSL_BLIND_PRIVATE_KEY #ifdef WOLFSSL_COPY_KEY if (ctx->privateKey != NULL) { @@ -9107,13 +9093,16 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return NULL; } + ssl->buffers.key->zeroOnFree = 1; ssl->buffers.weOwnKey = 1; } else { - ssl->buffers.key = ctx->privateKey; + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); } #else - ssl->buffers.key = ctx->privateKey; + if (AliasSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey, + ctx->privateKey) != 0) + return NULL; #endif #else if (ctx->privateKey != NULL) { @@ -9123,6 +9112,7 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return NULL; } + ssl->buffers.key->zeroOnFree = 1; ssl->buffers.weOwnKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ctx->privateKeyMask); @@ -9149,7 +9139,9 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->options.haveSlhDsaSig = ctx->haveSlhDsaSig; #ifdef WOLFSSL_DUAL_ALG_CERTS #ifndef WOLFSSL_BLIND_PRIVATE_KEY - ssl->buffers.altKey = ctx->altPrivateKey; + if (AliasSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey, + ctx->altPrivateKey) != 0) + return NULL; #else if (ctx->altPrivateKey != NULL) { ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer, @@ -9158,6 +9150,7 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ret != 0) { return NULL; } + ssl->buffers.altKey->zeroOnFree = 1; ssl->buffers.weOwnAltKey = 1; /* Blind the private key for the SSL with new random mask. */ wolfssl_priv_der_blind_toggle(ssl->buffers.altKey, diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 15781db1732..a4c8af24f22 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -1128,10 +1128,6 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) if (ssl->buffers.weOwnKey) { WOLFSSL_MSG("Unloading key"); - if ((ssl->buffers.key != NULL) && - (ssl->buffers.key->buffer != NULL)) { - ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length); - } FreeDer(&ssl->buffers.key); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); @@ -1142,11 +1138,6 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->buffers.weOwnAltKey) { WOLFSSL_MSG("Unloading alt key"); - if ((ssl->buffers.altKey != NULL) && - (ssl->buffers.altKey->buffer != NULL)) { - ForceZero(ssl->buffers.altKey->buffer, - ssl->buffers.altKey->length); - } FreeDer(&ssl->buffers.altKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); @@ -2106,14 +2097,17 @@ WOLFSSL_X509* wolfSSL_get_certificate(WOLFSSL* ssl) WOLFSSL_MSG("Invalid parameter"); } /* Use certificate in SSL/TLS object if we own it. */ - else if (ssl->buffers.weOwnCert) { + else if (ssl->buffers.weOwnCert || + (ssl->buffers.certificate != NULL && + !wolfSSL_CertMatchesCtx(ssl, 0))) { /* Check if we already have a certificate allocated. */ if (ssl->ourCert == NULL) { /* Check if ctx has ourCert set - if so, use it instead of creating * a new X509. This maintains pointer compatibility with * applications (like nginx OCSP stapling) that use the X509 pointer * from SSL_CTX_use_certificate as a lookup key. */ - if ((ssl->ctx != NULL) && (ssl->ctx->ourCert != NULL)) { + if ((ssl->ctx != NULL) && (ssl->ctx->ourCert != NULL) && + (ssl->ctx->certificate != NULL)) { /* Compare cert buffers to make sure they are the same */ if ((ssl->buffers.certificate == NULL) || (ssl->buffers.certificate->buffer == NULL) || diff --git a/src/ssl_load.c b/src/ssl_load.c index e9e0c9ec0d2..e87bc3cd488 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -246,6 +246,8 @@ static int ProcessUserChainRetain(WOLFSSL_CTX* ctx, WOLFSSL* ssl, if (ssl->buffers.weOwnCertChain) { FreeDer(&ssl->buffers.certChain); } + FreeDer(&ssl->buffers.certChain); + ssl->buffers.certChain = NULL; /* Allocate and copy the buffer into SSL object. */ ret = AllocCopyDer(&ssl->buffers.certChain, chainBuffer, len, type, heap); @@ -255,12 +257,13 @@ static int ProcessUserChainRetain(WOLFSSL_CTX* ctx, WOLFSSL* ssl, } /* Store in SSL context object if available. */ else if (ctx != NULL) { - /* Dispose of old chain and allocate and copy in new chain. */ - FreeDer(&ctx->certChain); - /* Allocate and copy the buffer into SSL context object. */ - ret = AllocCopyDer(&ctx->certChain, chainBuffer, len, type, heap); - /* Update count of certificates in chain. */ - ctx->certChainCnt = cnt; + DerBuffer* newChain = NULL; + ret = AllocCopyDer(&newChain, chainBuffer, len, type, heap); + if (ret == 0) { + ret = wolfSSL_CtxDerReplace(ctx, 1, newChain, cnt); + if (ret != 0) + FreeDer(&newChain); + } } return ret; @@ -1419,18 +1422,18 @@ static int ProcessBufferPrivKeyHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, int ret = 0; (void)type; + /* The key slot can contain a public key for an external private key. */ + (*der)->zeroOnFree = 1; #ifdef WOLFSSL_DUAL_ALG_CERTS if (type == ALT_PRIVATEKEY_TYPE) { /* Put in alternate private key fields of objects. */ if (ssl != NULL) { - /* Dispose of previous key if not context's. */ - if (ssl->buffers.weOwnAltKey) { - FreeDer(&ssl->buffers.altKey); + /* Release the SSL reference to the previous key. */ + FreeSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); #endif - } ssl->buffers.altKeyId = 0; ssl->buffers.altKeyLabel = 0; ssl->buffers.altKeyDevId = INVALID_DEVID; @@ -1457,13 +1460,11 @@ static int ProcessBufferPrivKeyHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, else #endif /* WOLFSSL_DUAL_ALG_CERTS */ if (ssl != NULL) { - /* Dispose of previous key if not context's. */ - if (ssl->buffers.weOwnKey) { - FreeDer(&ssl->buffers.key); + /* Release the SSL reference to the previous key. */ + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); #endif - } ssl->buffers.keyId = 0; ssl->buffers.keyLabel = 0; ssl->buffers.keyDevId = INVALID_DEVID; @@ -2513,6 +2514,13 @@ static int ProcessBufferCertHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, ssl->ourCert = NULL; #endif } + #ifdef KEEP_OUR_CERT + if (!ssl->buffers.weOwnCert && ssl->buffers.certificate != NULL) { + wolfSSL_X509_free(ssl->ourCert); + ssl->ourCert = NULL; + } + #endif + FreeDer(&ssl->buffers.certificate); /* Store certificate as ours. */ ssl->buffers.certificate = der; #ifdef KEEP_OUR_CERT @@ -2524,7 +2532,11 @@ static int ProcessBufferCertHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, } else if (ctx != NULL) { /* Free previous certificate. */ - FreeDer(&ctx->certificate); /* Make sure previous is free'd */ + ret = wolfSSL_CtxDerReplace(ctx, 0, der, 0); + if (ret != 0) { + FreeDer(&der); + return ret; + } #ifdef KEEP_OUR_CERT /* Dispose of X509 version of certificate if we own it. */ if (ctx->ownOurCert) { @@ -2533,7 +2545,6 @@ static int ProcessBufferCertHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, ctx->ourCert = NULL; #endif /* Store certificate as ours. */ - ctx->certificate = der; /* ourCert is created on demand. */ } } @@ -4987,13 +4998,11 @@ int wolfSSL_use_PrivateKey_Id(WOLFSSL* ssl, const unsigned char* id, return 0; } - /* Dispose of old private key if owned and allocate and copy in id. */ - if (ssl->buffers.weOwnKey) { - FreeDer(&ssl->buffers.key); + /* Release the old key and allocate and copy in the id. */ + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); #endif - } if (AllocCopyDer(&ssl->buffers.key, id, (word32)sz, PRIVATEKEY_TYPE, ssl->heap) != 0) { ret = 0; @@ -5063,13 +5072,11 @@ int wolfSSL_use_PrivateKey_Label(WOLFSSL* ssl, const char* label, int devId) sz = (word32)XSTRLEN(label) + 1; - /* Dispose of old private key if owned and allocate and copy in label. */ - if (ssl->buffers.weOwnKey) { - FreeDer(&ssl->buffers.key); + /* Release the old key and allocate and copy in the label. */ + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); #endif - } if (AllocCopyDer(&ssl->buffers.key, (const byte*)label, (word32)sz, PRIVATEKEY_TYPE, ssl->heap) != 0) { ret = 0; @@ -5109,12 +5116,10 @@ int wolfSSL_use_AltPrivateKey_Id(WOLFSSL* ssl, const unsigned char* id, long sz, } if (ret == 1) { - if (ssl->buffers.weOwnAltKey) { - FreeDer(&ssl->buffers.altKey); + FreeSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); #endif - } if (AllocDer(&ssl->buffers.altKey, (word32)sz, ALT_PRIVATEKEY_TYPE, ssl->heap) != 0) { ret = 0; @@ -5157,12 +5162,10 @@ int wolfSSL_use_AltPrivateKey_Label(WOLFSSL* ssl, const char* label, int devId) if (ret == 1) { sz = (word32)XSTRLEN(label) + 1; - if (ssl->buffers.weOwnAltKey) { - FreeDer(&ssl->buffers.altKey); + FreeSslDer(&ssl->buffers.altKey, &ssl->buffers.weOwnAltKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); #endif - } if (AllocDer(&ssl->buffers.altKey, (word32)sz, ALT_PRIVATEKEY_TYPE, ssl->heap) != 0) { ret = 0; @@ -5251,14 +5254,17 @@ int wolfSSL_use_certificate_chain_buffer(WOLFSSL* ssl, const unsigned char* in, * @return 0 on failure. */ static int wolfssl_add_to_chain(DerBuffer** chain, int weOwn, const byte* cert, - word32 certSz, void* heap) + word32 certSz, void* heap, WOLFSSL_CTX* ctx) { int res = 1; int ret; - DerBuffer* oldChain = *chain; + DerBuffer* oldChain; DerBuffer* newChain = NULL; word32 len = 0; + if (ctx != NULL && wolfSSL_RefWithMutexLock(&ctx->ref) != 0) + return 0; + oldChain = *chain; if (oldChain != NULL) { /* Get length of previous chain. */ len = oldChain->length; @@ -5289,13 +5295,28 @@ static int wolfssl_add_to_chain(DerBuffer** chain, int weOwn, const byte* cert, XMEMCPY(newChain->buffer + len + CERT_HEADER_SZ, cert, certSz); /* Dispose of old chain if we own it. */ - if (weOwn) { + if (ctx != NULL) { + if (*chain != NULL) + ctx->certChainChanged = 1; FreeDer(chain); } - /* Replace chain. */ - *chain = newChain; + else if (weOwn) { + FreeDer(chain); + } + if (res == 1) { + /* Replace chain. */ + *chain = newChain; + if (ctx != NULL) + ctx->certChainCnt++; + } + else { + FreeDer(&newChain); + } } + if (ctx != NULL && wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) + WOLFSSL_MSG("Failed to unlock CTX certificate mutex"); + return res; } #endif @@ -5320,10 +5341,7 @@ static int wolfssl_ctx_add_to_chain(WOLFSSL_CTX* ctx, const byte* der, /* Add chain to DER buffer. */ res = wolfssl_add_to_chain(&ctx->certChain, 1, der, (word32)derSz, - ctx->heap); - if (res == 1) { - ctx->certChainCnt++; - } + ctx->heap, ctx); return res; } @@ -5401,6 +5419,7 @@ int wolfSSL_CTX_use_certificate(WOLFSSL_CTX *ctx, WOLFSSL_X509 *x) { int res = 1; int ret; + DerBuffer* newCert = NULL; WOLFSSL_ENTER("wolfSSL_CTX_use_certificate"); @@ -5412,10 +5431,12 @@ int wolfSSL_CTX_use_certificate(WOLFSSL_CTX *ctx, WOLFSSL_X509 *x) if (res == 1) { /* Replace certificate buffer with one holding the new certificate. */ - FreeDer(&ctx->certificate); - ret = AllocCopyDer(&ctx->certificate, x->derCert->buffer, + ret = AllocCopyDer(&newCert, x->derCert->buffer, x->derCert->length, CERT_TYPE, ctx->heap); + if (ret == 0) + ret = wolfSSL_CtxDerReplace(ctx, 0, newCert, 0); if (ret != 0) { + FreeDer(&newCert); res = 0; } } @@ -5507,10 +5528,7 @@ int wolfSSL_CTX_add1_chain_cert(WOLFSSL_CTX* ctx, WOLFSSL_X509* x509) /* Add DER encoding to chain. The certificate is presented to the * peer only and must not become a CA to verify peers with. */ ret = wolfssl_add_to_chain(&ctx->certChain, 1, - x509->derCert->buffer, x509->derCert->length, ctx->heap); - if (ret == 1) { - ctx->certChainCnt++; - } + x509->derCert->buffer, x509->derCert->length, ctx->heap, ctx); /* Store cert in stack to free it later. */ if ((ret == 1) && (ctx->x509Chain == NULL)) { /* Create a stack for certificates. */ @@ -5572,8 +5590,8 @@ int wolfSSL_add0_chain_cert(WOLFSSL* ssl, WOLFSSL_X509* x509) else if (ret == 1) { /* Add DER encoding to chain. */ ret = wolfssl_add_to_chain(&ssl->buffers.certChain, - ssl->buffers.weOwnCertChain, x509->derCert->buffer, - x509->derCert->length, ssl->heap); + 1, x509->derCert->buffer, + x509->derCert->length, ssl->heap, NULL); if (ret == 1) { /* We now own cert chain. */ ssl->buffers.weOwnCertChain = 1; @@ -5654,6 +5672,7 @@ int wolfSSL_clear_chain_certs(WOLFSSL* ssl) FreeDer(&ssl->buffers.certChain); ssl->buffers.weOwnCertChain = 0; } + FreeDer(&ssl->buffers.certChain); ssl->buffers.certChain = NULL; ssl->buffers.certChainCnt = 0; diff --git a/src/tls13.c b/src/tls13.c index 57f09e0bac1..c0008a04cea 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -11458,21 +11458,18 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) } ssl->buffers.keyType = ssl->buffers.altKeyType; ssl->buffers.keySz = ssl->buffers.altKeySz; - /* If we own it, free key before overriding it. */ - if (ssl->buffers.weOwnKey) { - FreeDer(&ssl->buffers.key); - #ifdef WOLFSSL_BLIND_PRIVATE_KEY - FreeDer(&ssl->buffers.keyMask); - #endif + /* Both key pointers retain their own DER reference. */ + if (AliasSslDer(&ssl->buffers.key, + &ssl->buffers.weOwnKey, ssl->buffers.altKey) != 0) { + ERROR_OUT(BAD_MUTEX_E, exit_scv); } - - /* Swap keys */ - ssl->buffers.key = ssl->buffers.altKey; ssl->buffers.weOwnKey = ssl->buffers.weOwnAltKey; - /* buffers.key is the only owner of the alt key now. */ - ssl->buffers.weOwnAltKey = 0; - #ifdef WOLFSSL_BLIND_PRIVATE_KEY + if (RefDer(ssl->buffers.altKeyMask) != 0) { + FreeSslDer(&ssl->buffers.key, &ssl->buffers.weOwnKey); + ERROR_OUT(BAD_MUTEX_E, exit_scv); + } + FreeDer(&ssl->buffers.keyMask); ssl->buffers.keyMask = ssl->buffers.altKeyMask; /* Unblind the alternative key before decoding */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); diff --git a/tests/api.c b/tests/api.c index 0e2747575fa..09d57c3384a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -5114,6 +5114,299 @@ static int test_wolfSSL_CTX_add1_chain_cert_tls13_handshake(void) return EXPECT_RESULT(); } +/* A connection created before a CTX chain change keeps its certificate and + * chain through the handshake. Exercise both append and reload. */ +static int test_wolfSSL_CTX_chain_change_after_new(void) +{ + EXPECT_DECLS; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_TLS) && \ + defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) + test_ssl_memio_ctx test_ctx; + WOLFSSL_X509* x509 = NULL; + WOLFSSL* newer = NULL; + int mode; + int version; + int modeCount = 3; + int oldCnt; + word32 oldLen; + +#if defined(SESSION_CERTS) && defined(SSL_CTRL_CHAIN) + modeCount++; +#endif + for (version = 0; version < 2 && EXPECT_SUCCESS(); version++) { +#ifndef WOLFSSL_TLS13 + if (version == 1) + break; +#endif +#ifdef WOLFSSL_NO_TLS12 + if (version == 0) + continue; +#endif + for (mode = 0; mode < modeCount && EXPECT_SUCCESS(); mode++) { + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); +#ifndef WOLFSSL_NO_TLS12 + if (version == 0) { + test_ctx.s_cb.method = wolfTLSv1_2_server_method; + test_ctx.c_cb.method = wolfTLSv1_2_client_method; + } +#endif +#ifdef WOLFSSL_TLS13 + if (version == 1) { + test_ctx.s_cb.method = wolfTLSv1_3_server_method; + test_ctx.c_cb.method = wolfTLSv1_3_client_method; + } +#endif + test_ctx.s_cb.certPemFile = "./certs/intermediate/server-chain.pem"; + test_ctx.s_cb.keyPemFile = "./certs/server-key.pem"; + test_ctx.c_cb.caPemFile = "./certs/ca-cert.pem"; + ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + if (EXPECT_SUCCESS()) { + ExpectNotNull(test_ctx.s_ssl->buffers.certChain); + ExpectIntGT(test_ctx.s_ssl->buffers.certChainCnt, 0); +#ifdef WOLFSSL_COPY_CERT + ExpectFalse(test_ctx.s_ssl->buffers.certChain == + test_ctx.s_ctx->certChain); + ExpectFalse(test_ctx.s_ssl->buffers.certificate == + test_ctx.s_ctx->certificate); +#else + ExpectTrue(test_ctx.s_ssl->buffers.certChain == + test_ctx.s_ctx->certChain); + ExpectTrue(test_ctx.s_ssl->buffers.certificate == + test_ctx.s_ctx->certificate); +#endif + } + if (EXPECT_SUCCESS()) { + oldCnt = test_ctx.s_ssl->buffers.certChainCnt; + oldLen = test_ctx.s_ssl->buffers.certChain->length; + if (mode == 0) { + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file( + "./certs/ca-cert.pem", WOLFSSL_FILETYPE_PEM)); + if (EXPECT_SUCCESS() && + wolfSSL_CTX_add_extra_chain_cert(test_ctx.s_ctx, + x509) == 1) { + x509 = NULL; /* add_extra takes ownership on success. */ + } + else { + ExpectIntEQ(0, 1); + } + } + else if (mode == 1) { + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file( + "./certs/ca-cert.pem", WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_CTX_add1_chain_cert(test_ctx.s_ctx, + x509), 1); + } + else if (mode == 2) { + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file( + test_ctx.s_ctx, + "./certs/intermediate/server-chain-short.pem"), 1); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(test_ctx.s_ctx, + "./certs/client-key.pem", WOLFSSL_FILETYPE_PEM), 1); + } +#if defined(SESSION_CERTS) && defined(SSL_CTRL_CHAIN) + else { + ExpectIntEQ(wolfSSL_CTX_ctrl(test_ctx.s_ctx, + SSL_CTRL_CHAIN, 0, NULL), WOLFSSL_SUCCESS); + } +#endif + ExpectIntEQ(test_ctx.s_ssl->buffers.certChainCnt, oldCnt); + ExpectIntEQ(test_ctx.s_ssl->buffers.certChain->length, oldLen); + ExpectNotNull(newer = wolfSSL_new(test_ctx.s_ctx)); + if (newer != NULL) { + ExpectIntEQ(newer->buffers.certChainCnt, + test_ctx.s_ctx->certChainCnt); + ExpectIntNE(newer->buffers.certChainCnt, oldCnt); + } + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + } + wolfSSL_X509_free(x509); + x509 = NULL; + wolfSSL_free(newer); + newer = NULL; + test_ssl_memio_cleanup(&test_ctx); + } + } +#endif + return EXPECT_RESULT(); +} + +/* The old DER stays alive until every connection releases its reference. */ +static int test_wolfSSL_CTX_shared_der_references(void) +{ + EXPECT_DECLS; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_TLS) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(WOLFSSL_COPY_CERT) && defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* first = NULL; + WOLFSSL* second = NULL; + DerBuffer* oldCert = NULL; + DerBuffer* oldChain = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, + "./certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, + "./certs/server-key.pem", WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(first = wolfSSL_new(ctx)); + ExpectNotNull(second = wolfSSL_new(ctx)); + if (EXPECT_SUCCESS()) { + oldCert = ctx->certificate; + oldChain = ctx->certChain; + ExpectNotNull(oldCert); + ExpectNotNull(oldChain); + ExpectTrue(first->buffers.certificate == oldCert); + ExpectTrue(second->buffers.certChain == oldChain); + ExpectIntEQ(wolfSSL_RefCur(oldCert->ref), 3); + ExpectIntEQ(wolfSSL_RefCur(oldChain->ref), 3); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, + "./certs/intermediate/server-chain-short.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_RefCur(oldCert->ref), 2); + ExpectIntEQ(wolfSSL_RefCur(oldChain->ref), 2); + wolfSSL_free(first); + first = NULL; + ExpectIntEQ(wolfSSL_RefCur(oldCert->ref), 1); + ExpectIntEQ(wolfSSL_RefCur(oldChain->ref), 1); + ExpectIntGT(second->buffers.certificate->length, 0); + ExpectIntGT(second->buffers.certChain->length, 0); + } + wolfSSL_free(first); + wolfSSL_free(second); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* A CTX key reload must not free a key still used by two connections. */ +static int test_wolfSSL_CTX_shared_key_references(void) +{ + EXPECT_DECLS; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_TLS) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(WOLFSSL_COPY_KEY) && !defined(WOLFSSL_BLIND_PRIVATE_KEY) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* first = NULL; + WOLFSSL* second = NULL; + DerBuffer* oldKey = NULL; + byte* saved = NULL; + word32 len = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, + "./certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, + "./certs/server-key.pem", WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(first = wolfSSL_new(ctx)); + ExpectNotNull(second = wolfSSL_new(ctx)); + if (EXPECT_SUCCESS()) { + oldKey = ctx->privateKey; + ExpectNotNull(oldKey); + if (oldKey != NULL) { + len = oldKey->length; + ExpectNotNull(saved = (byte*)XMALLOC(len, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + } + } + if (EXPECT_SUCCESS()) { + XMEMCPY(saved, oldKey->buffer, len); + ExpectTrue(first->buffers.key == oldKey); + ExpectTrue(second->buffers.key == oldKey); + ExpectIntEQ(wolfSSL_RefCur(oldKey->ref), 3); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, + "./certs/client-key.pem", WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_RefCur(oldKey->ref), 2); + wolfSSL_free(first); + first = NULL; + ExpectIntEQ(wolfSSL_RefCur(oldKey->ref), 1); + ExpectIntEQ(XMEMCMP(second->buffers.key->buffer, saved, len), 0); + } + wolfSSL_free(first); + wolfSSL_free(second); + wolfSSL_CTX_free(ctx); + if (saved != NULL) { + ForceZero(saved, len); + XFREE(saved, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_PRIVATE_KEY_ID) && \ + defined(WOLFSSL_PEM_TO_DER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_BLIND_PRIVATE_KEY) && \ + !defined(WOLFSSL_SWDEV) +#define TEST_PUBLIC_KEY_DEV_ID 0x691 +static int test_public_key_device_cb(int devId, wc_CryptoInfo* info, void* ctx) +{ + (void)devId; + (void)info; + (void)ctx; + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} +#endif + +/* Public keys used to select an external private key are also wiped when the + * last holder releases them, but must remain usable until then. */ +static int test_wolfSSL_CTX_public_key_lifetime(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_PRIVATE_KEY_ID) && \ + defined(WOLFSSL_PEM_TO_DER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_BLIND_PRIVATE_KEY) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + byte* saved = NULL; + word32 len = 0; +#ifdef WOLFSSL_SWDEV + int devId = WC_SWDEV_ID; +#else + int devId = TEST_PUBLIC_KEY_DEV_ID; + int registered = wc_CryptoCb_RegisterDevice(devId, + test_public_key_device_cb, NULL); + ExpectIntEQ(registered, 0); +#endif + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + if (ctx != NULL) { + wolfSSL_CTX_SetDevId(ctx, devId); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, + "./certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, + "./certs/server-keyPub.pem", WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + } + if (EXPECT_SUCCESS() && ssl != NULL) { + ExpectNotNull(ssl->buffers.key); + ExpectIntEQ(ssl->buffers.key->type, PUBLICKEY_TYPE); + ExpectIntEQ(ssl->buffers.key->zeroOnFree, 1); + len = ssl->buffers.key->length; + ExpectNotNull(saved = (byte*)XMALLOC(len, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + } + if (EXPECT_SUCCESS() && saved != NULL) { + XMEMCPY(saved, ssl->buffers.key->buffer, len); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, + "./certs/client-keyPub.pem", WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + ExpectIntEQ(XMEMCMP(ssl->buffers.key->buffer, saved, len), 0); + } + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + XFREE(saved, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#ifndef WOLFSSL_SWDEV + if (registered == 0) + wc_CryptoCb_UnRegisterDevice(devId); +#endif +#endif + return EXPECT_RESULT(); +} + /* wolfSSL_CTX_add_extra_chain_cert must account for every certificate it * appends, and must not count one it failed to append. */ static int test_wolfSSL_CTX_add_extra_chain_cert_counts(void) @@ -47647,6 +47940,10 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_add0_add1_chain_cert_increments_count), TEST_DECL(test_wolfSSL_CTX_add0_add1_chain_cert_increments_count), TEST_DECL(test_wolfSSL_CTX_add1_chain_cert_tls13_handshake), + TEST_DECL(test_wolfSSL_CTX_chain_change_after_new), + TEST_DECL(test_wolfSSL_CTX_shared_der_references), + TEST_DECL(test_wolfSSL_CTX_shared_key_references), + TEST_DECL(test_wolfSSL_CTX_public_key_lifetime), TEST_DECL(test_wolfSSL_CTX_add_extra_chain_cert_counts), TEST_DECL(test_wolfSSL_clear_chain_certs), TEST_DECL(test_wolfSSL_clear_chain_certs_handshake), diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index 8afa237e079..f6581464a38 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -6257,8 +6257,8 @@ int test_ocsp_status_verify_cb_would_block(void) defined(HAVE_TLS_EXTENSIONS) && !defined(NO_WOLFSSL_SERVER) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_COPY_CERT) && \ - !defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(NO_SHA) && \ - !defined(NO_ASN_TIME) + !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_SHA) && !defined(NO_ASN_TIME) /* Number of times the server's OCSP IO callback has been called. */ static int test_ocsp_ctx_request_cache_cb_cnt; @@ -6434,6 +6434,30 @@ int test_ocsp_ctx_request_cache(void) ssl_s = NULL; } + /* Replacing a certificate after publication must not reuse the cached + * request for that certificate, even when the cache stays alive. */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, + "./certs/ocsp/server2-cert.pem", WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, + "./certs/ocsp/server2-key.pem", WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + ExpectPtrEq(ctx_s->certOcspRequest, cached); + test_ocsp_ctx_request_cache_differs = 0; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseOCSPStapling(ssl_c, WOLFSSL_CSR_OCSP, 0), + WOLFSSL_SUCCESS); + /* The canned response is for server1, so handshake success is not + * expected. Inspect the request sent to the responder instead. */ + (void)test_memio_do_handshake(ssl_c, ssl_s, 10, NULL); + ExpectIntEQ(test_ocsp_ctx_request_cache_cb_cnt, 4); + ExpectIntEQ(test_ocsp_ctx_request_cache_differs, 1); + ExpectPtrEq(ctx_s->certOcspRequest, cached); + } + wolfSSL_free(ssl_c); wolfSSL_free(ssl_s); wolfSSL_CTX_free(ctx_c); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 4b9eeaaec70..61ce32e09c9 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -27274,6 +27274,13 @@ int AllocDer(DerBuffer** pDer, word32 length, int type, void* heap) der->heap = heap; der->buffer = (byte*)der + sizeof(DerBuffer); der->length = length; + wolfSSL_RefInit(&der->ref, &ret); + if (ret != 0) { + XFREE(der, heap, dynType); + *pDer = NULL; + return ret; + } + der->refInitialized = 1; ret = 0; /* Success */ } else { ret = BAD_FUNC_ARG; @@ -27292,20 +27299,43 @@ int AllocCopyDer(DerBuffer** pDer, const unsigned char* buff, word32 length, return ret; } +int RefDer(DerBuffer* der) +{ + int ret = 0; + if (der != NULL) { + if (!der->refInitialized) + return BAD_STATE_E; + wolfSSL_RefInc(&der->ref, &ret); + } + return ret; +} + void FreeDer(DerBuffer** pDer) { if (pDer && *pDer) { - DerBuffer* der = (DerBuffer*)*pDer; - - /* ForceZero private keys */ - if (((der->type == PRIVATEKEY_TYPE) || - (der->type == ALT_PRIVATEKEY_TYPE)) && der->buffer != NULL) { - ForceZero(der->buffer, der->length); + DerBuffer* der = *pDer; + int isZero = 1; + int ret = 0; + int counted = der->refInitialized; + + /* A zero-initialized manually constructed buffer has no reference. */ + if (counted) { + wolfSSL_RefDec(&der->ref, &isZero, &ret); + if (ret != 0) + return; + } + if (isZero) { + if (((der->type == PRIVATEKEY_TYPE) || + (der->type == ALT_PRIVATEKEY_TYPE) || der->zeroOnFree) && + der->buffer != NULL) { + ForceZero(der->buffer, der->length); + } + der->buffer = NULL; + der->length = 0; + if (counted) + wolfSSL_RefFree(&der->ref); + XFREE(der, der->heap, der->dynType); } - der->buffer = NULL; - der->length = 0; - XFREE(der, der->heap, der->dynType); - *pDer = NULL; } } diff --git a/wolfcrypt/src/port/maxim/maxq10xx.c b/wolfcrypt/src/port/maxim/maxq10xx.c index bdfa722c072..43260af90e0 100644 --- a/wolfcrypt/src/port/maxim/maxq10xx.c +++ b/wolfcrypt/src/port/maxim/maxq10xx.c @@ -2389,9 +2389,7 @@ int wolfSSL_maxq10xx_load_certificate(WOLFSSL *ssl) { ssl->maxq_ctx.device_cert = maxq_der; - if (ssl->buffers.weOwnCert) { - FreeDer(&ssl->buffers.certificate); - } + FreeSslDer(&ssl->buffers.certificate, &ssl->buffers.weOwnCert); ssl->buffers.certificate = maxq_der; ssl->buffers.weOwnCert = 1; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index d0aa5473165..47d0eaaf56f 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4509,6 +4509,8 @@ struct WOLFSSL_CTX { #ifndef NO_CERTS DerBuffer* certificate; DerBuffer* certChain; + byte certificateChanged; + byte certChainChanged; int certChainCnt; /* chain after self, in DER, with leading size for each cert */ #if !defined(WOLFSSL_NO_CA_NAMES) && defined(OPENSSL_EXTRA) @@ -7713,6 +7715,16 @@ WOLFSSL_TEST_VIS void wolfSSL_ResourceFree(WOLFSSL* ssl); /* Micrium uses */ #ifndef NO_CERTS + WOLFSSL_LOCAL void FreeSslDer(DerBuffer** der, byte* weOwn); + WOLFSSL_LOCAL int AliasSslDer(DerBuffer** der, byte* weOwn, + DerBuffer* src); + WOLFSSL_LOCAL int wolfSSL_CtxDerReplace(WOLFSSL_CTX* ctx, int chain, + DerBuffer* replacement, int chainCount); + WOLFSSL_LOCAL int wolfSSL_CertMatchesCtx(WOLFSSL* ssl, int allowCache); + WOLFSSL_LOCAL int wolfSSL_ChainMatchesCtx(WOLFSSL* ssl); + WOLFSSL_LOCAL int wolfSSL_SetCertBuffersFromCtx(WOLFSSL* ssl, + WOLFSSL_CTX* ctx); + WOLFSSL_LOCAL int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff, long sz, int format, int type, WOLFSSL* ssl, long* used, int userChain, int verify, diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 9fa398d8652..d25c952fb83 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2946,6 +2946,7 @@ WOLFSSL_API int AllocDer(DerBuffer** der, word32 length, int type, WOLFSSL_LOCAL int AllocCopyDer(DerBuffer** der, const unsigned char* buff, word32 length, int type, void* heap); WOLFSSL_API void FreeDer(DerBuffer** der); +WOLFSSL_LOCAL int RefDer(DerBuffer* der); #ifdef WOLFSSL_ASN_PARSE_KEYUSAGE WOLFSSL_LOCAL int ParseKeyUsageStr(const char* value, word16* keyUsage, diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 63dc5426298..ce385719a94 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -32,6 +32,7 @@ This library defines the interface APIs for X509 certificates. #define WOLF_CRYPT_ASN_PUBLIC_H #include +#include #ifndef NO_DSA #include #endif @@ -272,13 +273,17 @@ enum Ctc_Misc { WOLF_ENUM_DUMMY_LAST_ELEMENT(Ctc_Misc) }; -/* DER buffer */ +/* DER buffer. Buffers constructed without wc_AllocDer must be zero-initialized + * before use so that wc_FreeDer can distinguish them from counted buffers. */ typedef struct DerBuffer { byte* buffer; void* heap; word32 length; int type; /* enum CertType */ int dynType; /* DYNAMIC_TYPE_* */ + wolfSSL_Ref ref; + byte refInitialized; + byte zeroOnFree; /* Buffer was used as a private key or key identifier. */ } DerBuffer; typedef struct WOLFSSL_ASN1_TIME {