Stream: add PP2_CLIENT_CERT_SESS and PP2_CLIENT_CERT_CONN flags.

Set the PP2_CLIENT_CERT_SESS bit (0x04) in the client field of
the PP2_TYPE_SSL TLV whenever the ssl_cn sub-TLV is configured.
Presence of ssl_cn implies the CN was extracted from a client
certificate, so the session has a verified certificate.

Set PP2_CLIENT_CERT_CONN (0x02) additionally when the downstream
connection is TLS and the TLS session was not reused
(!SSL_session_reused), meaning the certificate was presented in
this specific connection.

Update the ssl_tlv and ssl_verify tests: servers that configure
ssl_cn now expect client flags 0x05 instead of 0x01.
This commit is contained in:
Vadim Zhestikov 2026-04-16 13:49:15 -07:00
parent 0c1bf24208
commit 59ec2ee651
3 changed files with 31 additions and 6 deletions

View file

@ -864,9 +864,9 @@ static ngx_array_t *
ngx_stream_proxy_build_tlvs(ngx_stream_session_t *s, ngx_array_t *conf_tlvs,
size_t *sizep)
{
u_char *blob, *q;
u_char *blob, *q, client_flags;
ngx_int_t verify_i;
ngx_uint_t i, n_ssl, has_ssl_verify;
ngx_uint_t i, n_ssl, has_ssl_verify, has_ssl_cn;
uint32_t verify;
size_t ssl_sub_total;
ngx_str_t *vals;
@ -889,6 +889,7 @@ ngx_stream_proxy_build_tlvs(ngx_stream_session_t *s, ngx_array_t *conf_tlvs,
*sizep = 0;
n_ssl = 0;
has_ssl_verify = 0;
has_ssl_cn = 0;
verify = 0xFFFFFFFF; /* default: not verified */
ssl_sub_total = 0;
ctlv = conf_tlvs->elts;
@ -914,6 +915,11 @@ ngx_stream_proxy_build_tlvs(ngx_stream_session_t *s, ngx_array_t *conf_tlvs,
n_ssl++;
ssl_sub_total += 3 + vals[i].len; /* sub-TLV: type(1)+len(2)+val */
/* ssl_cn presence drives PP2_CLIENT_CERT_SESS / CERT_CONN flags */
if (ctlv[i].type == 0x22) {
has_ssl_cn = 1;
}
} else {
*sizep += 3 + vals[i].len; /* regular TLV wire size */
}
@ -948,8 +954,25 @@ ngx_stream_proxy_build_tlvs(ngx_stream_session_t *s, ngx_array_t *conf_tlvs,
q = blob;
/* PP2_CLIENT_SSL (0x01): client connected over SSL/TLS */
*q++ = 0x01;
/*
* PP2_CLIENT_SSL (0x01): client connected over SSL/TLS
* PP2_CLIENT_CERT_CONN (0x02): certificate verified in this connection
* PP2_CLIENT_CERT_SESS (0x04): certificate verified in this session
*/
client_flags = 0x01; /* PP2_CLIENT_SSL */
if (has_ssl_cn) {
client_flags |= 0x04; /* PP2_CLIENT_CERT_SESS */
#if (NGX_SSL)
if (s->connection->ssl != NULL
&& !SSL_session_reused(s->connection->ssl->connection))
{
client_flags |= 0x02; /* PP2_CLIENT_CERT_CONN */
}
#endif
}
*q++ = client_flags;
/* verify field in network byte order */
*q++ = (u_char) (verify >> 24);

View file

@ -118,6 +118,7 @@ $t->waitforsocket('127.0.0.1:' . port(8083));
# ssl_version [36]: type 0x21, len 7, value "TLSv1.3"
# ssl_cipher [46]: type 0x23, len 22, value "TLS_AES_256_GCM_SHA384"
# ssl_cn [71]: type 0x22, len 11, value "example.com"
# client byte = PP2_CLIENT_SSL|PP2_CLIENT_CERT_SESS = 0x05 (ssl_cn is set)
# payload at offset 85
my $d1 = stream('127.0.0.1:' . port(8080))->io('hello');
@ -125,7 +126,7 @@ my $d1 = stream('127.0.0.1:' . port(8080))->io('hello');
is(unpack('n', substr($d1, 14, 2)), 69, 'ssl_tlv len field');
is(unpack('C', substr($d1, 28, 1)), 0x20, 'ssl_tlv outer type');
is(unpack('n', substr($d1, 29, 2)), 54, 'ssl_tlv outer length');
is(unpack('C', substr($d1, 31, 1)), 0x01, 'ssl_tlv client flags');
is(unpack('C', substr($d1, 31, 1)), 0x05, 'ssl_tlv client flags');
is(unpack('N', substr($d1, 32, 4)), 0xFFFFFFFF, 'ssl_tlv verify');
is(unpack('C', substr($d1, 36, 1)), 0x21, 'ssl_version type');

View file

@ -76,6 +76,7 @@ $t->waitforsocket('127.0.0.1:' . port(8082));
# verify field = 42 = 0x0000002A (big-endian)
# ssl_version [36]: type 0x21, len 7, value "TLSv1.3"
# ssl_cn [46]: type 0x22, len 11, value "example.com"
# client byte = PP2_CLIENT_SSL|PP2_CLIENT_CERT_SESS = 0x05 (ssl_cn is set)
# payload at offset 60
my $d1 = stream('127.0.0.1:' . port(8080))->io('hello');
@ -83,7 +84,7 @@ my $d1 = stream('127.0.0.1:' . port(8080))->io('hello');
is(unpack('n', substr($d1, 14, 2)), 44, 'ssl_verify len field');
is(unpack('C', substr($d1, 28, 1)), 0x20, 'ssl_verify outer type');
is(unpack('n', substr($d1, 29, 2)), 29, 'ssl_verify outer length');
is(unpack('C', substr($d1, 31, 1)), 0x01, 'ssl_verify client flags');
is(unpack('C', substr($d1, 31, 1)), 0x05, 'ssl_verify client flags');
is(unpack('N', substr($d1, 32, 4)), 42, 'ssl_verify verify field');
is(unpack('C', substr($d1, 36, 1)), 0x21, 'ssl_verify ssl_version type');