Skip to content

nginx 1.28.1 fixes#9711

Open
julek-wolfssl wants to merge 1 commit intowolfSSL:masterfrom
julek-wolfssl:nginx-1.28.0
Open

nginx 1.28.1 fixes#9711
julek-wolfssl wants to merge 1 commit intowolfSSL:masterfrom
julek-wolfssl:nginx-1.28.0

Conversation

@julek-wolfssl
Copy link
Member

@julek-wolfssl julek-wolfssl commented Jan 23, 2026

Depends on wolfSSL/wolfssl-nginx#31

wolfssl/internal.h

  • InternalTicket struct gains a flexible array member: A new peerCert[] field (with a preceding peerCertLen[2]) is added to InternalTicket. This allows the peer's DER-encoded certificate to be stored directly inside the session ticket.
  • ExternalTicket struct becomes variable-length: The enc_ticket field is changed from a fixed-size array to a flexible array member (byte enc_ticket[]). The mac field is removed from the struct — the MAC is now placed dynamically after the encrypted data in enc_ticket.

src/internal.c

  • The GetRecordHeader function now only adds MAX_COMP_EXTRA to the maximum allowed record size when ssl->options.usingCompression is true, tightening the length validation. The max fragment length extension check is now much stricter.
  • Peer certificate is serialized into the ticket: During ticket creation, the code attempts to find the peer certificate from ssl->peerCert or from ssl->session->chain (fallback). If found and within MAX_TICKET_PEER_CERT_SZ, it's copied into it->peerCert. DTLS is explicitly excluded (peer cert length set to 0) to keep ticket size small for MTU constraints. If HAVE_MAX_FRAGMENT is defined and max fragment is not MAX_RECORD_SIZE for TLS 1.3, the cert is also skipped since SendTls13NewSessionTicket doesn't support fragmentation yet.
  • Peer certificate restoration from ticket: On successful ticket decryption, if the ticket contains a peer certificate (peerCertLen > 0), it is decoded back into ssl->peerCert via ParseCertRelative/CopyDecodedToX509, and also added to ssl->session->chain via AddSessionCertToChain.
  • The CLEAR_ASN_NO_PEM_HEADER_ERROR macro was rewritten to loop and remove all consecutive PEM no-start-line errors (not just the last one), wrapped in a do { ... } while(0) for safety.
  • The SendTicket function is simplified to use SendHandshakeMsg to support fragmenting the larger ticket.

src/x509.c

  • loadX509orX509REQFromPemBio now accepts TRUSTED_CERT_TYPE in addition to CERT_TYPE and CERTREQ_TYPE.
  • Streaming BIO support: When wolfSSL_BIO_get_len() returns ≤ 0 (e.g., pipes/FIFOs), the function no longer returns an error. Instead, it sets an initial buffer of MAX_X509_SIZE and dynamically grows (doubling) up to MAX_BIO_READ_BUFFER (MAX_X509_SIZE * 16) as data is read byte-by-byte.
  • Alternate footer detection: For TRUSTED_CERT_TYPE, the PEM reader also checks for the regular CERT_TYPE footer (-----END CERTIFICATE-----) in addition to the trusted cert footer (-----END TRUSTED CERTIFICATE-----), so it can parse either format.
  • Removed two lines that set cert->srcIdx to SIGALGO_SEQ offset. This makes cert->srcIdx reflect the end of parsed certificate data. This is used by loadX509orX509REQFromBuffer to detect where auxiliary trust data begins in trusted certificates.

src/ssl_sk.c

  • Added a STACK_TYPE_X509_CRL case to wolfssl_sk_dup_data that calls wolfSSL_X509_CRL_dup for deep-copying CRL stack elements. Previously, STACK_TYPE_X509_CRL fell through to the unsupported default case.

wolfssl/openssl/ssl.h

  • sk_X509_dup now maps to wolfSSL_shallow_sk_dup (was wolfSSL_sk_dup/deep copy). This matches OpenSSL's behavior where sk_X509_dup does a shallow copy.
  • sk_SSL_CIPHER_dup similarly changed to wolfSSL_shallow_sk_dup.

src/ssl_api_cert.c

  • When ssl->ourCert is NULL and the SSL owns its cert, the function now checks if ssl->ctx->ourCert points to the same certificate (by comparing DER buffers). If so, it returns the ctx's X509 pointer directly. This maintains pointer compatibility for applications (like nginx OCSP stapling) that use the X509* from SSL_CTX_use_certificate as a lookup key.

src/bio.c

  • When wolfssl_file_len returns WOLFSSL_BAD_FILETYPE (now returned for pipes/FIFOs), wolfSSL_BIO_get_len treats it as length 0 instead of propagating the error.

tests/test-maxfrag.conf and tests/test-maxfrag-dtls.conf

  • Removed DHE-RSA-AES256-GCM-SHA384 test entries because the ClientKeyExchange doesn't fit in the selected max fragment length.

@julek-wolfssl julek-wolfssl force-pushed the nginx-1.28.0 branch 3 times, most recently from 5f54234 to c37bd2f Compare February 5, 2026 18:55
@julek-wolfssl
Copy link
Member Author

Retest this please AgentOfflineException

@julek-wolfssl julek-wolfssl force-pushed the nginx-1.28.0 branch 2 times, most recently from 622feb1 to 1814bce Compare February 6, 2026 15:43
@julek-wolfssl julek-wolfssl marked this pull request as ready for review February 6, 2026 15:46
@julek-wolfssl julek-wolfssl self-assigned this Feb 6, 2026
### `wolfssl/internal.h`

- **`InternalTicket` struct gains a flexible array member**: A new `peerCert[]` field (with a preceding `peerCertLen[2]`) is added to `InternalTicket`. This allows the peer's DER-encoded certificate to be stored directly inside the session ticket.
- **`ExternalTicket` struct becomes variable-length**: The `enc_ticket` field is changed from a fixed-size array to a flexible array member (`byte enc_ticket[]`). The `mac` field is removed from the struct — the MAC is now placed dynamically after the encrypted data in `enc_ticket`.

### `src/internal.c`

- The `GetRecordHeader` function now only adds `MAX_COMP_EXTRA` to the maximum allowed record size when `ssl->options.usingCompression` is true, tightening the length validation. The max fragment length extension check is now much stricter.
- **Peer certificate is serialized into the ticket**: During ticket creation, the code attempts to find the peer certificate from `ssl->peerCert` or from `ssl->session->chain` (fallback). If found and within `MAX_TICKET_PEER_CERT_SZ`, it's copied into `it->peerCert`. DTLS is explicitly excluded (peer cert length set to 0) to keep ticket size small for MTU constraints. If `HAVE_MAX_FRAGMENT` is defined and max fragment is not `MAX_RECORD_SIZE` for TLS 1.3, the cert is also skipped since `SendTls13NewSessionTicket` doesn't support fragmentation yet.
- **Peer certificate restoration from ticket**: On successful ticket decryption, if the ticket contains a peer certificate (`peerCertLen > 0`), it is decoded back into `ssl->peerCert` via `ParseCertRelative`/`CopyDecodedToX509`, and also added to `ssl->session->chain` via `AddSessionCertToChain`.
- The `CLEAR_ASN_NO_PEM_HEADER_ERROR` macro was rewritten to loop and remove all consecutive PEM no-start-line errors (not just the last one), wrapped in a `do { ... } while(0)` for safety.
- The `SendTicket` function is simplified to use `SendHandshakeMsg` to support fragmenting the larger ticket.

---

### `src/x509.c`

- `loadX509orX509REQFromPemBio` now accepts `TRUSTED_CERT_TYPE` in addition to `CERT_TYPE` and `CERTREQ_TYPE`.
- **Streaming BIO support**: When `wolfSSL_BIO_get_len()` returns ≤ 0 (e.g., pipes/FIFOs), the function no longer returns an error. Instead, it sets an initial buffer of `MAX_X509_SIZE` and dynamically grows (doubling) up to `MAX_BIO_READ_BUFFER` (`MAX_X509_SIZE * 16`) as data is read byte-by-byte.
- **Alternate footer detection**: For `TRUSTED_CERT_TYPE`, the PEM reader also checks for the regular `CERT_TYPE` footer (`-----END CERTIFICATE-----`) in addition to the trusted cert footer (`-----END TRUSTED CERTIFICATE-----`), so it can parse either format.
- Removed two lines that set `cert->srcIdx` to `SIGALGO_SEQ` offset. This makes `cert->srcIdx` reflect the end of parsed certificate data. This is used by `loadX509orX509REQFromBuffer` to detect where auxiliary trust data begins in trusted certificates.

---

### `src/ssl_sk.c`

- Added a `STACK_TYPE_X509_CRL` case to `wolfssl_sk_dup_data` that calls `wolfSSL_X509_CRL_dup` for deep-copying CRL stack elements. Previously, `STACK_TYPE_X509_CRL` fell through to the unsupported default case.

---

### `wolfssl/openssl/ssl.h`

- `sk_X509_dup` now maps to `wolfSSL_shallow_sk_dup` (was `wolfSSL_sk_dup`/deep copy). This matches OpenSSL's behavior where `sk_X509_dup` does a shallow copy.
- `sk_SSL_CIPHER_dup` similarly changed to `wolfSSL_shallow_sk_dup`.

---

### `src/ssl_api_cert.c`

- When `ssl->ourCert` is `NULL` and the SSL owns its cert, the function now checks if `ssl->ctx->ourCert` points to the same certificate (by comparing DER buffers). If so, it returns the ctx's `X509` pointer directly. This maintains pointer compatibility for applications (like nginx OCSP stapling) that use the `X509*` from `SSL_CTX_use_certificate` as a lookup key.

### `src/bio.c`

- When `wolfssl_file_len` returns `WOLFSSL_BAD_FILETYPE` (now returned for pipes/FIFOs), `wolfSSL_BIO_get_len` treats it as length 0 instead of propagating the error.

---

### `tests/test-maxfrag.conf` and `tests/test-maxfrag-dtls.conf`

- Removed `DHE-RSA-AES256-GCM-SHA384` test entries because the ClientKeyExchange doesn't fit in the selected max fragment length.
#endif

/* Calculate actual internal ticket size */
#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add after setting the common base.

internalTicketSz = WOLF...
#if defined...
internalTicketSz += peerCertSz;
#endif


#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
!defined(NO_CERT_IN_TICKET)
/* Restore peer certificate from ticket to session chain and peerCert */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract to new function.

ssl->buffers.weOwnKey = 1;
}
else {
if (ssl->buffers.key != NULL && ssl->buffers.weOwnKey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to move this out.
Both branches of if statement free it if not NULL and we own it.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line length

@SparkiDev SparkiDev assigned julek-wolfssl and unassigned SparkiDev Feb 12, 2026
@dgarske dgarske requested a review from Copilot February 16, 2026 21:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements fixes for nginx 1.28.1 compatibility, primarily addressing session ticket handling and certificate management. The changes enable peer certificates to be stored in session tickets and add support for streaming BIO sources.

Changes:

  • Modified session ticket structures to support variable-length peer certificates
  • Enhanced PEM reading to handle streaming sources (pipes/FIFOs)
  • Updated stack operations for proper shallow/deep copying semantics
  • Fixed memory handling in certificate and key management

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
wolfssl/internal.h Made session ticket structures variable-length to store peer certificates; moved MAX_PSK_ID_LEN definition
src/internal.c Added peer certificate serialization/restoration in tickets; improved ticket encryption/decryption
src/x509.c Added streaming BIO support; implemented TRUSTED_CERT_TYPE parsing; refactored AIA string handling
wolfssl/openssl/ssl.h Changed sk_X509_dup and sk_SSL_CIPHER_dup to shallow copy semantics
src/ssl_sk.c Implemented X509_CRL deep copy support in stack duplication
src/ssl_api_cert.c Added certificate pointer compatibility check for nginx OCSP stapling
src/bio.c Handled WOLFSSL_BAD_FILETYPE for pipe/FIFO sources
wolfssl/ssl.h Added X509_CRL_new_null and X509_CRL_up_ref function declarations
src/crl.c Implemented X509_CRL_up_ref for reference counting
tests/test-maxfrag*.conf Removed DHE-RSA-AES256-GCM-SHA384 tests that don't fit in max fragment
.github/workflows/nginx.yml Updated workflow for nginx 1.28.1 testing with sanitizers
configure.ac Added nginx to OPENSSLALL enable condition
Comments suppressed due to low confidence (1)

src/internal.c:1

  • Consider extracting the compression extra calculation into a named constant or inline function for better readability and reusability, as this pattern appears twice in consecutive blocks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
!defined(NO_CERT_IN_TICKET)
byte peerCertLen[OPAQUE16_LEN]; /* peer cert length */
byte peerCert[]; /* peer certificate DER - variable length */
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should clarify that this flexible array member must be the last field in the struct, as required by C standards. Consider adding '(must be last field)' to the comment.

Suggested change
byte peerCert[]; /* peer certificate DER - variable length */
byte peerCert[]; /* peer certificate DER - variable length
* (must be last field) */

Copilot uses AI. Check for mistakes.
Comment on lines +3512 to +3513
byte enc_ticket[]; /* encrypted ticket - var length
* + total mac - 32 */
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multi-line comment format is inconsistent with the rest of the codebase. The comment should clarify that the MAC follows the encrypted data within this array, not as a separate field.

Suggested change
byte enc_ticket[]; /* encrypted ticket - var length
* + total mac - 32 */
byte enc_ticket[]; /* encrypted ticket data (var len)
* followed by MAC bytes */

Copilot uses AI. Check for mistakes.
/* attempt to read newline following footer */
i++;
if (pem[i-1] == '\r') {
if (i < pemSz && pem[i-1] == '\r') {
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bounds check 'i < pemSz' is repeated three times in close proximity. Consider consolidating this logic to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
@@ -35,6 +35,8 @@
#include <wolfssl/openssl/x509v3.h>
#endif

Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This magic number '16' should be documented. Add a comment explaining why the buffer limit is set to 16 times MAX_X509_SIZE.

Suggested change
/* Use a generous upper bound: allow BIO reads up to 16 times MAX_X509_SIZE
* to account for encoding overhead and multiple concatenated certificates. */

Copilot uses AI. Check for mistakes.
Comment on lines +872 to +873
if (ssl->buffers.key != NULL && ssl->buffers.key->buffer != NULL)
ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null check for ssl->buffers.key->buffer is good, but consider also checking if length is greater than 0 before calling ForceZero to avoid unnecessary function calls.

Copilot uses AI. Check for mistakes.
Comment on lines +249 to +250
--with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1 -g3 \
${{ env.nginx_c_flags }}' \
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line continuation backslash on line 249 is inside the configure arguments. Ensure the shell properly handles this continuation within the single-quoted string.

Suggested change
--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 }}" \

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants