diff --git a/bin/http_server.c b/bin/http_server.c index 664de68f..5ac0d2e8 100644 --- a/bin/http_server.c +++ b/bin/http_server.c @@ -27,6 +27,7 @@ #include #include +#include #include "lsquic.h" #include "../src/liblsquic/lsquic_hash.h" @@ -361,6 +362,38 @@ http_server_on_goaway (lsquic_conn_t *conn) } +static void +http_server_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status status) +{ + STACK_OF(X509) *chain; + X509_NAME *name; + X509 *cert; + unsigned i; + char buf[100]; + + if (status != LSQ_HSK_OK && status != LSQ_HSK_RESUMED_OK) + return; + + chain = lsquic_conn_get_full_cert_chain(conn); + if (!chain) + { + LSQ_DEBUG("peer did not provide full certificate chain"); + return; + } + + for (i = 0; i < sk_X509_num(chain); ++i) + { + cert = sk_X509_value(chain, i); + name = X509_get_subject_name(cert); + LSQ_INFO("full cert #%u: name: %s", i, + X509_NAME_oneline(name, buf, sizeof(buf))); + X509_free(cert); + } + + sk_X509_free(chain); +} + + static void http_server_on_conn_closed (lsquic_conn_t *conn) { @@ -1086,6 +1119,7 @@ const struct lsquic_stream_if http_server_if = { .on_write = http_server_on_write, .on_close = http_server_on_close, .on_goaway_received = http_server_on_goaway, + .on_hsk_done = http_server_on_hsk_done, }; @@ -1202,6 +1236,7 @@ const struct lsquic_stream_if hq_server_if = { .on_read = hq_server_on_read, .on_write = hq_server_on_write, .on_close = http_server_on_close, + .on_hsk_done = http_server_on_hsk_done, }; #endif @@ -1771,6 +1806,7 @@ const struct lsquic_stream_if interop_http_server_if = { .on_read = http_server_interop_on_read, .on_write = http_server_interop_on_write, .on_close = http_server_on_close, + .on_hsk_done = http_server_on_hsk_done, }; #endif /* HAVE_REGEX */ diff --git a/docs/apiref.rst b/docs/apiref.rst index 518dbf82..b6a870c7 100644 --- a/docs/apiref.rst +++ b/docs/apiref.rst @@ -2039,6 +2039,13 @@ Miscellaneous Connection Functions The caller releases the stack using sk_X509_free(). +.. function:: struct stack_st_X509 * lsquic_conn_get_full_cert_chain (lsquic_conn_t *conn) + + Get full certificate chain returned by the server/client. This can be used for + server/client certificate verification. + + The caller releases the stack using sk_X509_free(). + .. function:: lsquic_conn_ctx_t * lsquic_conn_get_ctx (const lsquic_conn_t *conn) Get user-supplied context associated with the connection. diff --git a/include/lsquic.h b/include/lsquic.h index 7bbcacbe..2c882e64 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -1808,6 +1808,16 @@ lsquic_stream_has_unacked_data (lsquic_stream_t *s); struct stack_st_X509 * lsquic_conn_get_server_cert_chain (lsquic_conn_t *); +/** + * Get the full certificate chain returned by the server/lient. + * This can be used for server/client certificate verification. + * + * The caller releases the stack using sk_X509_free(). + */ +struct stack_st_X509 * +lsquic_conn_get_full_cert_chain (lsquic_conn_t *); + + /** Returns ID of the stream */ lsquic_stream_id_t lsquic_stream_id (const lsquic_stream_t *s); diff --git a/src/liblsquic/lsquic_conn.c b/src/liblsquic/lsquic_conn.c index d3d723ee..fc8068a6 100644 --- a/src/liblsquic/lsquic_conn.c +++ b/src/liblsquic/lsquic_conn.c @@ -138,6 +138,16 @@ lsquic_conn_get_server_cert_chain (struct lsquic_conn *lconn) } +struct stack_st_X509 * +lsquic_conn_get_full_cert_chain (struct lsquic_conn *lconn) +{ + if (lconn->cn_enc_session) + return lconn->cn_esf_c->esf_get_full_cert_chain(lconn->cn_enc_session); + else + return NULL; +} + + void lsquic_conn_make_stream (struct lsquic_conn *lconn) { diff --git a/src/liblsquic/lsquic_enc_sess.h b/src/liblsquic/lsquic_enc_sess.h index dcf67ef9..5485349a 100644 --- a/src/liblsquic/lsquic_enc_sess.h +++ b/src/liblsquic/lsquic_enc_sess.h @@ -118,6 +118,9 @@ struct enc_session_funcs_common struct stack_st_X509 * (*esf_get_server_cert_chain) (enc_session_t *); + struct stack_st_X509 * + (*esf_get_full_cert_chain) (enc_session_t *); + int (*esf_verify_reset_token) (enc_session_t *, const unsigned char *, size_t); diff --git a/src/liblsquic/lsquic_enc_sess_ietf.c b/src/liblsquic/lsquic_enc_sess_ietf.c index 9339dc38..c8087d80 100644 --- a/src/liblsquic/lsquic_enc_sess_ietf.c +++ b/src/liblsquic/lsquic_enc_sess_ietf.c @@ -89,6 +89,9 @@ free_handshake_keys (struct enc_sess_iquic *); static struct stack_st_X509 * iquic_esf_get_server_cert_chain (enc_session_t *); +static struct stack_st_X509 * +iquic_esf_get_full_cert_chain (enc_session_t *); + static void maybe_drop_SSL (struct enc_sess_iquic *); @@ -2612,6 +2615,22 @@ iquic_esf_get_server_cert_chain (enc_session_t *enc_session_p) } +static struct stack_st_X509 * +iquic_esf_get_full_cert_chain (enc_session_t *enc_session_p) +{ + struct enc_sess_iquic *const enc_sess = enc_session_p; + STACK_OF(X509) *chain; + + if (enc_sess->esi_ssl) + { + chain = SSL_get_peer_full_cert_chain(enc_sess->esi_ssl); + return X509_chain_up_ref(chain); + } + else + return NULL; +} + + static const char * iquic_esf_cipher (enc_session_t *enc_session_p) { @@ -2864,6 +2883,8 @@ const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1 = .esf_tag_len = IQUIC_TAG_LEN, .esf_get_server_cert_chain = iquic_esf_get_server_cert_chain, + .esf_get_full_cert_chain + = iquic_esf_get_full_cert_chain, .esf_get_sni = iquic_esf_get_sni, .esf_cipher = iquic_esf_cipher, .esf_keysize = iquic_esf_keysize, @@ -2883,6 +2904,8 @@ const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1_no_flush .esf_tag_len = IQUIC_TAG_LEN, .esf_get_server_cert_chain = iquic_esf_get_server_cert_chain, + .esf_get_full_cert_chain + = iquic_esf_get_full_cert_chain, .esf_get_sni = iquic_esf_get_sni, .esf_cipher = iquic_esf_cipher, .esf_keysize = iquic_esf_keysize,