diff --git a/.github/workflows/nginx.yml b/.github/workflows/nginx.yml index c85161a0e2..015fbb60e2 100644 --- a/.github/workflows/nginx.yml +++ b/.github/workflows/nginx.yml @@ -12,6 +12,10 @@ concurrency: cancel-in-progress: true # END OF COMMON SECTION +# clang has better sanitizer support +env: + CC: clang + jobs: build_wolfssl: name: Build wolfSSL @@ -31,7 +35,8 @@ jobs: uses: wolfSSL/actions-build-autotools-project@v1 with: path: wolfssl - configure: --enable-nginx ${{ env.wolf_debug_flags }} + configure: >- + --enable-nginx --enable-curve25519 --enable-ed25519 ${{ env.wolf_debug_flags }} install: true - name: tar build-dir @@ -50,6 +55,41 @@ jobs: matrix: include: # in general we want to pass all tests that match *ssl* + - ref: 1.28.1 + test-ref: 0fccfcef1278263416043e0bbb3e0116b84026e4 + # Following tests pass with sanitizer on + sanitize-ok: >- + h2_ssl_proxy_cache.t h2_ssl.t h2_ssl_variables.t + h2_ssl_verify_client.t mail_imap_ssl.t mail_ssl_session_reuse.t + mail_ssl.t proxy_ssl_certificate_cache.t + proxy_ssl_certificate_empty.t proxy_ssl_certificate.t + proxy_ssl_certificate_vars.t proxy_ssl_name.t ssl_cache_reload.t + ssl_certificate_aux.t ssl_certificate_cache.t + ssl_certificate_chain.t ssl_certificates.t ssl_certificate.t + ssl_client_escaped_cert.t ssl_crl.t ssl_curve.t ssl_ocsp.t + ssl_password_file.t ssl_proxy_upgrade.t ssl_reject_handshake.t + ssl_session_reuse.t ssl_session_ticket_key.t ssl_sni_protocols.t + ssl_sni_reneg.t ssl_sni_sessions.t ssl_sni.t ssl_stapling.t ssl.t + ssl_verify_client.t ssl_verify_client_trusted.t ssl_verify_depth.t + stream_proxy_ssl_certificate_cache.t stream_proxy_ssl_certificate.t + stream_proxy_ssl_certificate_vars.t + stream_proxy_ssl_name_complex.t stream_proxy_ssl_name.t + stream_ssl_alpn.t stream_ssl_certificate_cache.t + stream_ssl_certificate.t stream_ssl_ocsp.t stream_ssl_preread_alpn.t + stream_ssl_preread_protocol.t stream_ssl_preread.t + stream_ssl_reject_handshake.t stream_ssl_session_reuse.t + stream_ssl_sni_protocols.t stream_ssl_stapling.t stream_ssl.t + stream_ssl_variables.t stream_ssl_verify_client.t + stream_upstream_zone_ssl.t upstream_zone_ssl.t + uwsgi_ssl_certificate.t uwsgi_ssl_certificate_vars.t + # Following tests do not pass with sanitizer on (with OpenSSL too) + sanitize-not-ok: >- + grpc_ssl.t h2_proxy_request_buffering_ssl.t h2_proxy_ssl.t + proxy_request_buffering_ssl.t proxy_ssl_conf_command.t + proxy_ssl_keepalive.t proxy_ssl.t proxy_ssl_verify.t ssl_cache.t + stream_proxy_protocol_ssl.t stream_proxy_ssl_conf_command.t + stream_proxy_ssl.t stream_proxy_ssl_verify.t + - ref: 1.25.0 test-ref: 5b2894ea1afd01a26c589ce11f310df118e42592 # Following tests pass with sanitizer on @@ -120,30 +160,17 @@ jobs: - name: untar build-dir run: tar -xf build-dir.tgz - - name: Install dependencies - run: | - sudo cpan -iT Proc::Find + - name: Openssl version + run: openssl version -a - # Locking in the version of SSLeay used with testing - - name: Download and install Net::SSLeay 1.94 manually - run: | - curl -LO https://www.cpan.org/modules/by-module/Net/CHRISN/Net-SSLeay-1.94.tar.gz - tar -xzf Net-SSLeay-1.94.tar.gz - cd Net-SSLeay-1.94 - perl Makefile.PL - make - sudo make install - - # SSL version 2.091 changes '' return to undef causing test case to fail. - # Locking in the test version to use as 2.090 - - name: Download and install IO::Socket::SSL 2.090 manually + - name: Setup Perl environment + uses: shogo82148/actions-setup-perl@v1 + with: + perl-version: '5.38.2' + + - name: Install dependencies run: | - curl -LO https://www.cpan.org/modules/by-module/IO/IO-Socket-SSL-2.090.tar.gz - tar -xzf IO-Socket-SSL-2.090.tar.gz - cd IO-Socket-SSL-2.090 - perl Makefile.PL - make - sudo make install + cpanm --notest Proc::Find Net::SSLeay@1.94 IO::Socket::SSL@2.090 - name: Checkout wolfssl-nginx uses: actions/checkout@v4 @@ -211,17 +238,14 @@ jobs: run: | echo "nginx_c_flags=-O0" >> $GITHUB_ENV - - name: workaround high-entropy ASLR - # not needed after either an update to llvm or runner is done - run: sudo sysctl vm.mmap_rnd_bits=28 - - name: Build nginx with sanitizer working-directory: nginx run: | ./auto/configure --with-wolfssl=$GITHUB_WORKSPACE/build-dir --with-http_ssl_module \ --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module \ --with-http_v2_module --with-mail --with-mail_ssl_module \ - --with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1 -g3 ${{ env.nginx_c_flags }}' \ + --with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1 -g3 \ + ${{ env.nginx_c_flags }}' \ --with-ld-opt='-fsanitize=address ${{ env.nginx_c_flags }}' make -j @@ -229,19 +253,16 @@ jobs: working-directory: nginx run: ldd objs/nginx | grep wolfssl - - if: ${{ runner.debug }} - name: Run nginx-tests with sanitizer (debug) + - name: Create LSAN suppression file working-directory: nginx-tests run: | - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib \ - TMPDIR=$GITHUB_WORKSPACE TEST_NGINX_VERBOSE=y TEST_NGINX_CATLOG=y \ - TEST_NGINX_BINARY=../nginx/objs/nginx prove -v ${{ matrix.sanitize-ok }} + echo "leak:ngx_worker_process_init" > lsan.supp - if: ${{ !runner.debug }} name: Run nginx-tests with sanitizer working-directory: nginx-tests run: | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib \ + LSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/nginx-tests/lsan.supp \ TMPDIR=$GITHUB_WORKSPACE TEST_NGINX_BINARY=../nginx/objs/nginx \ prove ${{ matrix.sanitize-ok }} - diff --git a/configure.ac b/configure.ac index 788b29a811..958fd926f9 100644 --- a/configure.ac +++ b/configure.ac @@ -2641,7 +2641,8 @@ if test "$ENABLED_LIBWEBSOCKETS" = "yes" || test "$ENABLED_OPENVPN" = "yes" || \ test "$ENABLED_OPENRESTY" = "yes" || test "$ENABLED_RSYSLOG" = "yes" || \ test "$ENABLED_KRB" = "yes" || test "$ENABLED_CHRONY" = "yes" || \ test "$ENABLED_FFMPEG" = "yes" || test "$ENABLED_STRONGSWAN" = "yes" || \ - test "$ENABLED_OPENLDAP" = "yes" || test "x$ENABLED_MOSQUITTO" = "xyes" || test "$ENABLED_HITCH" = "yes" + test "$ENABLED_OPENLDAP" = "yes" || test "x$ENABLED_MOSQUITTO" = "xyes" || \ + test "$ENABLED_HITCH" = "yes" || test "$ENABLED_NGINX" = "yes" then ENABLED_OPENSSLALL="yes" fi diff --git a/src/crl.c b/src/crl.c index d3b93bf407..c68449269b 100644 --- a/src/crl.c +++ b/src/crl.c @@ -1127,6 +1127,22 @@ WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl) return ret; } +int wolfSSL_X509_CRL_up_ref(WOLFSSL_X509_CRL* crl) +{ + if (crl) { + int ret; + wolfSSL_RefInc(&crl->ref, &ret); + if (ret != 0) { + WOLFSSL_MSG("Failed to lock x509 mutex"); + return WOLFSSL_FAILURE; + } + + return WOLFSSL_SUCCESS; + } + + return WOLFSSL_FAILURE; +} + /* returns WOLFSSL_SUCCESS on success. Does not take ownership of newcrl */ int wolfSSL_X509_STORE_add_crl(WOLFSSL_X509_STORE *store, WOLFSSL_X509_CRL *newcrl) { diff --git a/src/internal.c b/src/internal.c index cf48dbf120..fa5c3081d0 100644 --- a/src/internal.c +++ b/src/internal.c @@ -38616,6 +38616,41 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, XMEMCPY(it->sessionCtx, ssl->sessionCtx, ID_LEN); #endif +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + /* Store peer certificate in ticket for session resumption. + * Try ssl->peerCert first, then ssl->session->chain as fallback. */ + { + const byte* certDer = NULL; + word32 certDerSz = 0; + + if (ssl->peerCert.derCert != NULL && + ssl->peerCert.derCert->length > 0) { + /* Use current peer certificate */ + certDer = ssl->peerCert.derCert->buffer; + certDerSz = ssl->peerCert.derCert->length; + } +#ifdef SESSION_CERTS + else if (ssl->session->chain.count > 0) { + /* Use peer certificate from session chain */ + certDer = ssl->session->chain.certs[0].buffer; + certDerSz = ssl->session->chain.certs[0].length; + } +#endif + + if (certDer != NULL && certDerSz > 0 && + certDerSz <= MAX_TICKET_PEER_CERT_SZ) { + c16toa((word16)certDerSz, it->peerCertLen); + XMEMCPY(it->peerCert, certDer, certDerSz); + } + else { + if (certDerSz > MAX_TICKET_PEER_CERT_SZ) + WOLFSSL_MSG("Peer cert too large for ticket, skipping"); + c16toa(0, it->peerCertLen); + } + } +#endif + #ifdef WOLFSSL_TICKET_HAVE_ID { const byte* id = NULL; @@ -38995,6 +39030,49 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ato16(it->namedGroup, &ssl->session->namedGroup); #endif } + +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + /* Restore peer certificate from ticket to session chain and peerCert */ + { + word16 peerCertLen = 0; + ato16(it->peerCertLen, &peerCertLen); + + if (peerCertLen > 0 && peerCertLen <= MAX_TICKET_PEER_CERT_SZ) { +#ifdef SESSION_CERTS + /* Clear existing chain and add the peer certificate */ + ssl->session->chain.count = 0; + AddSessionCertToChain(&ssl->session->chain, + it->peerCert, peerCertLen); +#endif + /* Also decode into ssl->peerCert for direct access */ + { + int ret; + DecodedCert* dCert; + + dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap, + DYNAMIC_TYPE_DCERT); + if (dCert != NULL) { + InitDecodedCert(dCert, it->peerCert, peerCertLen, ssl->heap); + ret = ParseCertRelative(dCert, CERT_TYPE, 0, NULL, NULL); + if (ret == 0) { + FreeX509(&ssl->peerCert); + InitX509(&ssl->peerCert, 0, ssl->heap); + ret = CopyDecodedToX509(&ssl->peerCert, dCert); + if (ret != 0) { + /* Failed to copy - clear peerCert */ + FreeX509(&ssl->peerCert); + InitX509(&ssl->peerCert, 0, ssl->heap); + } + } + FreeDecodedCert(dCert); + XFREE(dCert, ssl->heap, DYNAMIC_TYPE_DCERT); + } + } + } + } +#endif + ssl->version.minor = it->pv.minor; } @@ -39039,6 +39117,23 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifdef OPENSSL_EXTRA it->sessionCtxSz = sess->sessionCtxSz; XMEMCPY(it->sessionCtx, sess->sessionCtx, sess->sessionCtxSz); +#endif +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + defined(SESSION_CERTS) && !defined(NO_CERT_IN_TICKET) + /* Store peer certificate from session chain */ + if (sess->chain.count > 0) { + word32 certLen = sess->chain.certs[0].length; + if (certLen <= MAX_TICKET_PEER_CERT_SZ) { + c16toa((word16)certLen, it->peerCertLen); + XMEMCPY(it->peerCert, sess->chain.certs[0].buffer, certLen); + } + else { + c16toa(0, it->peerCertLen); + } + } + else { + c16toa(0, it->peerCertLen); + } #endif } diff --git a/src/ssl.c b/src/ssl.c index d3961330e6..a16b44a749 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -12058,7 +12058,8 @@ int wolfSSL_set_compression(WOLFSSL* ssl) if (ssl->buffers.weOwnKey) { WOLFSSL_MSG("Unloading key"); - ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length); + 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); @@ -12069,7 +12070,8 @@ int wolfSSL_set_compression(WOLFSSL* ssl) #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->buffers.weOwnAltKey) { WOLFSSL_MSG("Unloading alt key"); - ForceZero(ssl->buffers.altKey->buffer, ssl->buffers.altKey->length); + 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); @@ -14919,6 +14921,23 @@ WOLFSSL_X509* wolfSSL_get_certificate(WOLFSSL* ssl) if (ssl->buffers.weOwnCert) { 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) { + /* Compare cert buffers to make sure they are the same */ + if (ssl->buffers.certificate == NULL || + ssl->buffers.certificate->buffer == NULL || + (ssl->buffers.certificate->length == + ssl->ctx->certificate->length && + XMEMCMP(ssl->buffers.certificate->buffer, + ssl->ctx->certificate->buffer, + ssl->buffers.certificate->length) == 0)) { + ssl->ourCert = ssl->ctx->ourCert; + return ssl->ctx->ourCert; + } + } if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); return NULL; @@ -20953,6 +20972,8 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->buffers.weOwnKey = 1; } else { + if (ssl->buffers.key != NULL && ssl->buffers.weOwnKey) + FreeDer(&ssl->buffers.key); ssl->buffers.key = ctx->privateKey; } #else diff --git a/src/ssl_misc.c b/src/ssl_misc.c index ced4570fbc..db29ad5e89 100644 --- a/src/ssl_misc.c +++ b/src/ssl_misc.c @@ -230,6 +230,11 @@ static int wolfssl_file_len(XFILE fp, long* fileSz) /* Get file offset at end of file. */ curr = (long)XFTELL(fp); if (curr < 0) { + if (errno == ESPIPE) { + WOLFSSL_ERROR_MSG("wolfssl_file_len: file is a pipe"); + *fileSz = 0; + return 0; + } ret = WOLFSSL_BAD_FILE; } } diff --git a/src/ssl_sk.c b/src/ssl_sk.c index 4b01b443fe..d5cc61da17 100644 --- a/src/ssl_sk.c +++ b/src/ssl_sk.c @@ -402,6 +402,17 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) break; } break; + case STACK_TYPE_X509_CRL: + if (src->data.crl == NULL) { + break; + } + dst->data.crl = wolfSSL_X509_CRL_dup(src->data.crl); + if (dst->data.crl == NULL) { + WOLFSSL_MSG("wolfSSL_X509_CRL_dup error"); + err = 1; + break; + } + break; case STACK_TYPE_X509_OBJ: #if defined(OPENSSL_ALL) if (src->data.x509_obj == NULL) { @@ -429,7 +440,6 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) case STACK_TYPE_BY_DIR_entry: case STACK_TYPE_BY_DIR_hash: case STACK_TYPE_DIST_POINT: - case STACK_TYPE_X509_CRL: default: WOLFSSL_MSG("Unsupported stack type"); err = 1; @@ -441,7 +451,8 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) /* Duplicate the stack of nodes. * - * TODO: OpenSSL does a shallow copy but we have wolfSSL_shallow_sk_dup(). + * OpenSSL does a shallow copy but we map to wolfSSL_shallow_sk_dup() + * when we want a shallow copy. * * Data is copied/duplicated - deep copy. * diff --git a/src/x509.c b/src/x509.c index 013e67e6ab..66262c7c62 100644 --- a/src/x509.c +++ b/src/x509.c @@ -4393,6 +4393,14 @@ WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new(void) return s; } +WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new_null(void) +{ + WOLFSSL_STACK* s = wolfSSL_sk_new_null(); + if (s != NULL) + s->type = STACK_TYPE_X509_CRL; + return s; +} + void wolfSSL_sk_X509_CRL_pop_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk, void (*f) (WOLFSSL_X509_CRL*)) { @@ -5393,14 +5401,25 @@ static WOLFSSL_X509* loadX509orX509REQFromBuffer( /* ready to be decoded. */ if (der != NULL && der->buffer != NULL) { WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + /* For TRUSTED_CERT_TYPE, the DER buffer contains the certificate + * followed by auxiliary trust info. ParseCertRelative expects CERT_TYPE + * and will parse only the certificate portion, ignoring the rest. */ + int parseType = (type == TRUSTED_CERT_TYPE) ? CERT_TYPE : type; WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT, ret=MEMORY_ERROR); if (WC_VAR_OK(cert)) { InitDecodedCert(cert, der->buffer, der->length, NULL); - ret = ParseCertRelative(cert, type, 0, NULL, NULL); + ret = ParseCertRelative(cert, parseType, 0, NULL, NULL); if (ret == 0) { + /* For TRUSTED_CERT_TYPE, truncate the DER buffer to exclude + * auxiliary trust data. ParseCertRelative sets srcIdx to the + * end of the certificate, so we adjust cert->maxIdx accordingly. */ + if (type == TRUSTED_CERT_TYPE && cert->srcIdx < cert->maxIdx) { + cert->maxIdx = cert->srcIdx; + } + x509 = (WOLFSSL_X509*)XMALLOC(sizeof(WOLFSSL_X509), NULL, DYNAMIC_TYPE_X509); if (x509 != NULL) { @@ -11905,22 +11924,21 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, int pemSz; long i = 0, l, footerSz; const char* footer = NULL; + int streaming = 0; /* Flag to indicate if source is streaming (FIFO) */ WOLFSSL_ENTER("loadX509orX509REQFromPemBio"); - if (bp == NULL || (type != CERT_TYPE && type != CERTREQ_TYPE)) { + if (bp == NULL || (type != CERT_TYPE && type != CERTREQ_TYPE && + type != TRUSTED_CERT_TYPE)) { WOLFSSL_LEAVE("wolfSSL_PEM_read_bio_X509", BAD_FUNC_ARG); return NULL; } if ((l = wolfSSL_BIO_get_len(bp)) <= 0) { - /* No certificate in buffer */ -#if defined (WOLFSSL_HAPROXY) - WOLFSSL_ERROR(PEM_R_NO_START_LINE); -#else - WOLFSSL_ERROR(ASN_NO_PEM_HEADER); -#endif - return NULL; + /* No certificate size available - could be FIFO or other streaming + * source. Use MAX_X509_SIZE as initial buffer, will resize if needed. */ + l = MAX_X509_SIZE; + streaming = 1; } pemSz = (int)l; @@ -11935,30 +11953,91 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, return NULL; } footerSz = (long)XSTRLEN(footer); + + /* For TRUSTED_CERT_TYPE, also prepare to check for regular CERT footer + * as the file might contain regular certificates instead of TRUSTED format */ + const char* altFooter = NULL; + long altFooterSz = 0; + if (type == TRUSTED_CERT_TYPE) { + wc_PemGetHeaderFooter(CERT_TYPE, NULL, &altFooter); + if (altFooter != NULL) { + altFooterSz = (long)XSTRLEN(altFooter); + } + } /* TODO: Inefficient * reading in one byte at a time until see the footer */ while ((l = wolfSSL_BIO_read(bp, (char *)&pem[i], 1)) == 1) { i++; + + /* Check if buffer is full and we're reading from streaming source */ + if (i >= pemSz && streaming) { + /* Double the buffer size for streaming sources */ + int newSz = pemSz * 2; + unsigned char* newPem; + + /* Sanity check: don't grow beyond reasonable limit */ + if (newSz > MAX_X509_SIZE * 16) { + WOLFSSL_MSG("PEM data too large for streaming source"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; + } + + newPem = (unsigned char*)XMALLOC(newSz, 0, DYNAMIC_TYPE_PEM); + if (newPem == NULL) { + WOLFSSL_MSG("Failed to resize PEM buffer"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; + } + + XMEMCPY(newPem, pem, pemSz); + XMEMSET(newPem + pemSz, 0, newSz - pemSz); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + pem = newPem; + pemSz = newSz; + } + else if (i > pemSz) { + /* Buffer full for non-streaming source - this shouldn't happen */ + WOLFSSL_MSG("PEM buffer overflow"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; + } + + /* Check for the expected footer OR alternate footer (for TRUSTED_CERT_TYPE) */ + int foundFooter = 0; if (i > footerSz && XMEMCMP((char *)&pem[i-footerSz], footer, footerSz) == 0) { - if (wolfSSL_BIO_read(bp, (char *)&pem[i], 1) == 1) { + foundFooter = 1; + } + else if (altFooter && i > altFooterSz && + XMEMCMP((char *)&pem[i-altFooterSz], altFooter, + altFooterSz) == 0) { + foundFooter = 1; + } + + if (foundFooter) { + if (i < pemSz && wolfSSL_BIO_read(bp, (char *)&pem[i], 1) == 1) { /* attempt to read newline following footer */ i++; - if (pem[i-1] == '\r') { + if (i < pemSz && pem[i-1] == '\r') { /* found \r , Windows line ending is \r\n so try to read one * more byte for \n, ignoring return value */ - (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); + if (i < pemSz) { + (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); + } } } break; } } - if (l == 0) + if (l == 0 && i == 0) { WOLFSSL_ERROR(ASN_NO_PEM_HEADER); + } if (i > pemSz) { WOLFSSL_MSG("Error parsing PEM"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; } else { pemSz = (int)i; @@ -11968,6 +12047,12 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, CERTREQ_TYPE, cb, u); else #endif + /* Use TRUSTED_CERT_TYPE if input was TRUSTED CERTIFICATE format, + * otherwise use CERT_TYPE for regular certificates */ + if (type == TRUSTED_CERT_TYPE) + x509 = loadX509orX509REQFromBuffer(pem, pemSz, WOLFSSL_FILETYPE_PEM, + TRUSTED_CERT_TYPE, cb, u); + else x509 = loadX509orX509REQFromBuffer(pem, pemSz, WOLFSSL_FILETYPE_PEM, CERT_TYPE, cb, u); } @@ -12007,22 +12092,67 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, } if ((pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { - /* No certificate in buffer */ - WOLFSSL_ERROR(ASN_NO_PEM_HEADER); - return NULL; - } + /* No certificate size available - could be FIFO or other streaming + * source. Use MAX_X509_SIZE as initial buffer, read in loop. */ + int totalRead = 0; + int readSz; + + pemSz = MAX_X509_SIZE; + pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + return NULL; + } - pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + /* Read from streaming source until EOF or buffer full */ + while ((readSz = wolfSSL_BIO_read(bp, pem + totalRead, + pemSz - totalRead)) > 0) { + totalRead += readSz; - if (pem == NULL) { - return NULL; + /* If buffer is full, try to grow it */ + if (totalRead >= pemSz) { + int newSz = pemSz * 2; + unsigned char* newPem; + + /* Sanity check */ + if (newSz > MAX_X509_SIZE * 16) { + WOLFSSL_MSG("PEM data too large for streaming source"); + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + + newPem = (unsigned char*)XMALLOC(newSz, 0, DYNAMIC_TYPE_PEM); + if (newPem == NULL) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + + XMEMCPY(newPem, pem, pemSz); + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + pem = newPem; + pemSz = newSz; + } + } + + pemSz = totalRead; + if (pemSz <= 0) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } } + else { + /* Known size - allocate and read */ + pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + return NULL; + } - XMEMSET(pem, 0, pemSz); + XMEMSET(pem, 0, pemSz); - if (wolfSSL_BIO_read(bp, pem, pemSz) != pemSz) { - XFREE(pem, NULL, DYNAMIC_TYPE_PEM); - return NULL; + pemSz = wolfSSL_BIO_read(bp, pem, pemSz); + if (pemSz <= 0) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } } x509 = wolfSSL_X509_ACERT_load_certificate_buffer(pem, pemSz, @@ -12062,14 +12192,15 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, wc_pem_password_cb *cb, void *u) { - WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509"); + WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509_AUX"); /* AUX info is; trusted/rejected uses, friendly name, private key id, * and potentially a stack of "other" info. wolfSSL does not store * friendly name or private key id yet in WOLFSSL_X509 for human * readability and does not support extra trusted/rejected uses for - * root CA. */ - return wolfSSL_PEM_read_bio_X509(bp, x, cb, u); + * root CA. Use TRUSTED_CERT_TYPE to properly parse TRUSTED CERTIFICATE + * format and strip auxiliary data. */ + return loadX509orX509REQFromPemBio(bp, x, cb, u, TRUSTED_CERT_TYPE); } #ifdef WOLFSSL_CERT_REQ @@ -12128,16 +12259,60 @@ WOLFSSL_X509 *wolfSSL_PEM_read_bio_X509_REQ(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, WOLFSSL_X509_CRL* crl = NULL; if ((pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { - goto err; - } + /* No certificate size available - could be FIFO or other streaming + * source. Use MAX_X509_SIZE as initial buffer, read in loop. */ + int totalRead = 0; + int readSz; + + pemSz = MAX_X509_SIZE; + pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + goto err; + } - pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); - if (pem == NULL) { - goto err; + /* Read from streaming source until EOF or buffer full */ + while ((readSz = wolfSSL_BIO_read(bp, pem + totalRead, + pemSz - totalRead)) > 0) { + totalRead += readSz; + + /* If buffer is full, try to grow it */ + if (totalRead >= pemSz) { + int newSz = pemSz * 2; + unsigned char* newPem; + + /* Sanity check */ + if (newSz > MAX_X509_SIZE * 16) { + goto err; + } + + newPem = (unsigned char*)XMALLOC(newSz, 0, DYNAMIC_TYPE_PEM); + if (newPem == NULL) { + goto err; + } + + XMEMCPY(newPem, pem, pemSz); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + pem = newPem; + pemSz = newSz; + } + } + + pemSz = totalRead; + if (pemSz <= 0) { + goto err; + } } + else { + /* Known size - allocate and read */ + pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + goto err; + } - if (wolfSSL_BIO_read(bp, pem, pemSz) != pemSz) { - goto err; + pemSz = wolfSSL_BIO_read(bp, pem, pemSz); + if (pemSz <= 0) { + goto err; + } } if ((PemToDer(pem, pemSz, CRL_TYPE, &der, NULL, NULL, NULL)) < 0) { @@ -12149,6 +12324,9 @@ WOLFSSL_X509 *wolfSSL_PEM_read_bio_X509_REQ(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, } err: + if (pemSz == 0) { + WOLFSSL_ERROR(ASN_NO_PEM_HEADER); + } XFREE(pem, 0, DYNAMIC_TYPE_PEM); if (der != NULL) { FreeDer(&der); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 31466ca846..b790e83b23 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -23966,8 +23966,6 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt, cert->extensionsSz = (int)GetASNItem_Length( dataASN[X509CERTASN_IDX_TBS_EXT], cert->source); cert->extensionsIdx = dataASN[X509CERTASN_IDX_TBS_EXT].offset; - /* Advance past extensions. */ - cert->srcIdx = dataASN[X509CERTASN_IDX_SIGALGO_SEQ].offset; } } @@ -27299,6 +27297,27 @@ int PemToDer(const unsigned char* buff, long longSz, int type, footer = END_X509_CRL; } #endif + else if (type == CERT_TYPE + || type == CA_TYPE + || type == CHAIN_CERT_TYPE + || type == TRUSTED_PEER_TYPE) { + if (header == BEGIN_CERT) { + header = BEGIN_TRUSTED_CERT; + footer = END_TRUSTED_CERT; + } + else { + break; + } + } + else if (type == TRUSTED_CERT_TYPE) { + if (header == BEGIN_TRUSTED_CERT) { + header = BEGIN_CERT; + footer = END_CERT; + } + else { + break; + } + } else { break; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 037811e326..35085e0e3b 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3441,6 +3441,11 @@ WOLFSSL_LOCAL int TLSX_AddEmptyRenegotiationInfo(TLSX** extensions, void* heap); #endif /* HAVE_SECURE_RENEGOTIATION */ #ifdef HAVE_SESSION_TICKET +/* Max peer cert size for ticket: 1KB is reasonable for most RSA/ECC certs */ +#ifndef MAX_TICKET_PEER_CERT_SZ +#define MAX_TICKET_PEER_CERT_SZ 1024 +#endif + /* Our ticket format. All members need to be a byte or array of byte to * avoid alignment issues */ typedef struct InternalTicket { @@ -3466,6 +3471,11 @@ typedef struct InternalTicket { byte sessionCtxSz; /* sessionCtx length */ byte sessionCtx[ID_LEN]; /* app specific context id */ #endif /* OPENSSL_EXTRA */ +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + byte peerCertLen[OPAQUE16_LEN]; /* peer cert length */ + byte peerCert[MAX_TICKET_PEER_CERT_SZ]; /* peer certificate DER */ +#endif } InternalTicket; #ifndef WOLFSSL_TICKET_ENC_CBC_HMAC @@ -6355,12 +6365,20 @@ struct SystemCryptoPolicy { * for the caller to find so we clear the last error. */ #if defined(OPENSSL_EXTRA) && defined(WOLFSSL_HAVE_ERROR_QUEUE) -#define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) \ - (err) = wolfSSL_ERR_peek_last_error(); \ - if (wolfSSL_ERR_GET_LIB(err) == WOLFSSL_ERR_LIB_PEM && \ - wolfSSL_ERR_GET_REASON(err) == -WOLFSSL_PEM_R_NO_START_LINE_E) { \ - wc_RemoveErrorNode(-1); \ - } +#define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) \ +do { \ + (err) = wolfSSL_ERR_peek_last_error(); \ + if (wolfSSL_ERR_GET_LIB(err) == WOLFSSL_ERR_LIB_PEM && \ + wolfSSL_ERR_GET_REASON(err) == -WOLFSSL_PEM_R_NO_START_LINE_E) { \ + unsigned long peekErr; \ + do { \ + wc_RemoveErrorNode(-1); \ + peekErr = wolfSSL_ERR_peek_last_error(); \ + } while (wolfSSL_ERR_GET_LIB(peekErr) == WOLFSSL_ERR_LIB_PEM && \ + wolfSSL_ERR_GET_REASON(peekErr) == \ + -WOLFSSL_PEM_R_NO_START_LINE_E); \ + } \ +} while(0) #else #define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) (void)(err); #endif diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 5b8175564e..5d4f7b51fc 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -617,16 +617,18 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define sk_X509_push wolfSSL_sk_X509_push #define sk_X509_pop wolfSSL_sk_X509_pop #define sk_X509_pop_free wolfSSL_sk_X509_pop_free -#define sk_X509_dup wolfSSL_sk_dup +#define sk_X509_dup wolfSSL_shallow_sk_dup #define sk_X509_free wolfSSL_sk_X509_free #define X509_chain_up_ref wolfSSL_X509_chain_up_ref #define sk_X509_CRL_new wolfSSL_sk_X509_CRL_new +#define sk_X509_CRL_new_null wolfSSL_sk_X509_CRL_new_null #define sk_X509_CRL_pop_free wolfSSL_sk_X509_CRL_pop_free #define sk_X509_CRL_free wolfSSL_sk_X509_CRL_free #define sk_X509_CRL_push wolfSSL_sk_X509_CRL_push #define sk_X509_CRL_value wolfSSL_sk_X509_CRL_value #define sk_X509_CRL_num wolfSSL_sk_X509_CRL_num +#define sk_X509_CRL_dup wolfSSL_shallow_sk_dup #define sk_X509_OBJECT_new wolfSSL_sk_X509_OBJECT_new #define sk_X509_OBJECT_free wolfSSL_sk_X509_OBJECT_free @@ -807,6 +809,7 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define PEM_read_X509_CRL wolfSSL_PEM_read_X509_CRL #define X509_CRL_dup wolfSSL_X509_CRL_dup +#define X509_CRL_up_ref wolfSSL_X509_CRL_up_ref #define X509_CRL_free wolfSSL_X509_CRL_free #define X509_CRL_get_lastUpdate wolfSSL_X509_CRL_get_lastUpdate #define X509_CRL_get0_lastUpdate wolfSSL_X509_CRL_get_lastUpdate @@ -1312,7 +1315,7 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define sk_SSL_COMP_zero wolfSSL_sk_SSL_COMP_zero #define sk_SSL_CIPHER_value wolfSSL_sk_SSL_CIPHER_value #endif /* OPENSSL_ALL || WOLFSSL_HAPROXY */ -#define sk_SSL_CIPHER_dup wolfSSL_sk_dup +#define sk_SSL_CIPHER_dup wolfSSL_shallow_sk_dup #define sk_SSL_CIPHER_free wolfSSL_sk_SSL_CIPHER_free #define sk_SSL_CIPHER_find wolfSSL_sk_SSL_CIPHER_find diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index a1c7bedf45..4eaeb73b32 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1927,6 +1927,7 @@ WOLFSSL_API WOLFSSL_X509* wolfSSL_sk_X509_pop(WOLF_STACK_OF(WOLFSSL_X509)* sk); WOLFSSL_API void wolfSSL_sk_X509_free(WOLF_STACK_OF(WOLFSSL_X509)* sk); WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new(void); +WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new_null(void); WOLFSSL_API void wolfSSL_sk_X509_CRL_pop_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk, void (*f) (WOLFSSL_X509_CRL*)); WOLFSSL_API void wolfSSL_sk_X509_CRL_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk); @@ -3455,6 +3456,7 @@ WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, #endif #if defined(HAVE_CRL) && (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_up_ref(WOLFSSL_X509_CRL* crl); WOLFSSL_API void wolfSSL_X509_CRL_free(WOLFSSL_X509_CRL *crl); #endif