From: Matt Caswell Date: Tue, 29 Nov 2022 11:26:08 +0000 (+0000) Subject: Add QUIC-TLS server support X-Git-Tag: openssl-3.2.0-alpha1~1387 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e3a55fd14cb4424fd62516345d918cdf0d9cdcc;p=thirdparty%2Fopenssl.git Add QUIC-TLS server support Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/19748) --- diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h index 66862103220..4610627b5e1 100644 --- a/include/internal/quic_tserver.h +++ b/include/internal/quic_tserver.h @@ -36,7 +36,8 @@ typedef struct quic_tserver_args_st { BIO *net_rbio, *net_wbio; } QUIC_TSERVER_ARGS; -QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args); +QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args, + const char *certfile, const char *keyfile); void ossl_quic_tserver_free(QUIC_TSERVER *srv); diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 4331e6412b9..5b895cb48a3 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -131,7 +131,7 @@ SSL *ossl_quic_new(SSL_CTX *ctx) goto err; } - qc->tls = ossl_ssl_connection_new_int(ctx, TLS_client_method()); + qc->tls = ossl_ssl_connection_new_int(ctx, TLS_method()); if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) goto err; /* override the user_ssl of the inner connection */ diff --git a/ssl/quic/quic_tls.c b/ssl/quic/quic_tls.c index d7e3dd4acdc..5ce71a2e4fd 100644 --- a/ssl/quic/quic_tls.c +++ b/ssl/quic/quic_tls.c @@ -641,6 +641,7 @@ int ossl_quic_tls_tick(QUIC_TLS *qtls) if (!qtls->configured) { SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s); + SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(sc); BIO *nullbio; /* @@ -649,9 +650,16 @@ int ossl_quic_tls_tick(QUIC_TLS *qtls) */ /* ALPN is a requirement for QUIC and must be set */ - if (sc->ext.alpn == NULL || sc->ext.alpn_len == 0) { - qtls->inerror = 1; - return 0; + if (qtls->args.is_server) { + if (sctx->ext.alpn_select_cb == NULL) { + qtls->inerror = 1; + return 0; + } + } else { + if (sc->ext.alpn == NULL || sc->ext.alpn_len == 0) { + qtls->inerror = 1; + return 0; + } } if (!SSL_set_min_proto_version(qtls->args.s, TLS1_3_VERSION)) { qtls->inerror = 1; @@ -661,7 +669,8 @@ int ossl_quic_tls_tick(QUIC_TLS *qtls) ossl_ssl_set_custom_record_layer(sc, &quic_tls_record_method, qtls); if (!ossl_tls_add_custom_ext_intern(NULL, &sc->cert->custext, - ENDPOINT_CLIENT, + qtls->args.is_server ? ENDPOINT_SERVER + : ENDPOINT_CLIENT, TLSEXT_TYPE_quic_transport_parameters, SSL_EXT_TLS1_3_ONLY | SSL_EXT_CLIENT_HELLO @@ -685,9 +694,14 @@ int ossl_quic_tls_tick(QUIC_TLS *qtls) */ SSL_set_bio(qtls->args.s, nullbio, nullbio); + if (qtls->args.is_server) + SSL_set_accept_state(qtls->args.s); + else + SSL_set_connect_state(qtls->args.s); + qtls->configured = 1; } - ret = SSL_connect(qtls->args.s); + ret = SSL_do_handshake(qtls->args.s); if (ret <= 0) { switch (SSL_get_error(qtls->args.s, ret)) { case SSL_ERROR_WANT_READ: @@ -713,12 +727,6 @@ int ossl_quic_tls_set_transport_params(QUIC_TLS *qtls, const unsigned char *transport_params, size_t transport_params_len) { - if (!ossl_assert(!qtls->args.is_server)) { - ERR_raise(ERR_LIB_SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - qtls->inerror = 1; - return 0; - } - qtls->local_transport_params = transport_params; qtls->local_transport_params_len = transport_params_len; return 1; diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c index 96fb668b241..6795d4ec465 100644 --- a/ssl/quic/quic_tserver.c +++ b/ssl/quic/quic_tserver.c @@ -24,6 +24,12 @@ struct quic_tserver_st { */ QUIC_CHANNEL *ch; + /* SSL_CTX for creating the underlying TLS connection */ + SSL_CTX *ctx; + + /* SSL for the underlying TLS connection */ + SSL *tls; + /* Our single bidirectional application data stream. */ QUIC_STREAM *stream0; @@ -34,7 +40,21 @@ struct quic_tserver_st { unsigned int connected : 1; }; -QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args) +static int alpn_select_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) +{ + unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' }; + + if (SSL_select_next_proto((unsigned char **)out, outlen, alpn, sizeof(alpn), + in, inlen) != OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + return SSL_TLSEXT_ERR_OK; +} + +QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args, + const char *certfile, const char *keyfile) { QUIC_TSERVER *srv = NULL; QUIC_CHANNEL_ARGS ch_args = {0}; @@ -47,8 +67,25 @@ QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args) srv->args = *args; + srv->ctx = SSL_CTX_new_ex(srv->args.libctx, srv->args.propq, TLS_method()); + if (srv->ctx == NULL) + goto err; + + if (SSL_CTX_use_certificate_file(srv->ctx, certfile, SSL_FILETYPE_PEM) <= 0) + goto err; + + if (SSL_CTX_use_PrivateKey_file(srv->ctx, keyfile, SSL_FILETYPE_PEM) <= 0) + goto err; + + SSL_CTX_set_alpn_select_cb(srv->ctx, alpn_select_cb, srv); + + srv->tls = SSL_new(srv->ctx); + if (srv->tls == NULL) + goto err; + ch_args.libctx = srv->args.libctx; ch_args.propq = srv->args.propq; + ch_args.tls = srv->tls; ch_args.is_server = 1; if ((srv->ch = ossl_quic_channel_new(&ch_args)) == NULL) @@ -80,6 +117,8 @@ void ossl_quic_tserver_free(QUIC_TSERVER *srv) ossl_quic_channel_free(srv->ch); BIO_free(srv->args.net_rbio); BIO_free(srv->args.net_wbio); + SSL_free(srv->tls); + SSL_CTX_free(srv->ctx); OPENSSL_free(srv); } diff --git a/test/quic_tserver_test.c b/test/quic_tserver_test.c index 0e070cb1f29..c2a831c89c4 100644 --- a/test/quic_tserver_test.c +++ b/test/quic_tserver_test.c @@ -18,6 +18,8 @@ static const char msg1[] = "The quick brown fox jumped over the lazy dogs."; static char msg2[1024], msg3[1024]; +static const char *certfile, *keyfile; + static int is_want(SSL *s, int ret) { int ec = SSL_get_error(s, ret); @@ -43,6 +45,7 @@ static int test_tserver(void) size_t l = 0, s_total_read = 0, s_total_written = 0, c_total_read = 0; int s_begin_write = 0; OSSL_TIME start_time; + unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' }; ina.s_addr = htonl(0x7f000001UL); @@ -80,7 +83,8 @@ static int test_tserver(void) tserver_args.net_rbio = s_net_bio; tserver_args.net_wbio = s_net_bio; - if (!TEST_ptr(tserver = ossl_quic_tserver_new(&tserver_args))) { + if (!TEST_ptr(tserver = ossl_quic_tserver_new(&tserver_args, certfile, + keyfile))) { BIO_free(s_net_bio); goto err; } @@ -107,6 +111,10 @@ static int test_tserver(void) if (!TEST_ptr(c_ssl = SSL_new(c_ctx))) goto err; + /* 0 is a success for SSL_set_alpn_protos() */ + if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn)))) + goto err; + /* Takes ownership of our reference to the BIO. */ SSL_set0_rbio(c_ssl, c_net_bio); @@ -215,8 +223,19 @@ err: return testresult; } +OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") + int setup_tests(void) { + if (!test_skip_common_options()) { + TEST_error("Error parsing test options\n"); + return 0; + } + + if (!TEST_ptr(certfile = test_get_argument(0)) + || !TEST_ptr(keyfile = test_get_argument(1))) + return 0; + ADD_TEST(test_tserver); return 1; } diff --git a/test/recipes/70-test_quic_tserver.t b/test/recipes/70-test_quic_tserver.t index 76c24c36c53..4ff2d208b65 100644 --- a/test/recipes/70-test_quic_tserver.t +++ b/test/recipes/70-test_quic_tserver.t @@ -6,14 +6,16 @@ # in the file LICENSE in the source distribution or at # https://www.openssl.org/source/license.html -use OpenSSL::Test; +use OpenSSL::Test qw/:DEFAULT srctop_file/; use OpenSSL::Test::Utils; -setup("test_quic_txp"); +setup("test_quic_tserver"); plan skip_all => "QUIC protocol is not supported by this OpenSSL build" if disabled('quic'); plan tests => 1; -ok(run(test(["quic_tserver_test"]))); +ok(run(test(["quic_tserver_test", + srctop_file("test", "certs", "servercert.pem"), + srctop_file("test", "certs", "serverkey.pem")])));