Implement SSL_new_from_listener()
SSL_new_from_listner() creates QUIC connection object (QCSO) from listener. Caller can use the object retuned from SSL_new_from_listener() to connect to remote QUIC server. The QCSO created here shares engine/port with listener. the change is covered by `test_ssl_new_from_listener()` in test/quicapitest.c Reviewed-by: Neil Horman <nhorman@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26138)
This commit is contained in:
parent
6bae611c99
commit
0b15147a37
3 changed files with 275 additions and 9 deletions
|
|
@ -263,10 +263,10 @@ static int ch_init(QUIC_CHANNEL *ch)
|
|||
ch->have_qsm = 1;
|
||||
|
||||
if (!ch->is_server
|
||||
&& !ossl_quic_lcidm_generate_initial(ch->lcidm, ch, &txp_args.cur_scid))
|
||||
&& !ossl_quic_lcidm_generate_initial(ch->lcidm, ch, &ch->init_scid))
|
||||
goto err;
|
||||
|
||||
/* We use a zero-length SCID. */
|
||||
txp_args.cur_scid = ch->init_scid;
|
||||
txp_args.cur_dcid = ch->init_dcid;
|
||||
txp_args.ack_delay_exponent = 3;
|
||||
txp_args.qtx = ch->qtx;
|
||||
|
|
@ -1316,7 +1316,6 @@ static int ch_on_transport_params(const unsigned char *params,
|
|||
goto malformed;
|
||||
}
|
||||
|
||||
/* Must match SCID of first Initial packet from server. */
|
||||
if (!ossl_quic_conn_id_eq(&ch->init_scid, &cid)) {
|
||||
reason = TP_REASON_EXPECTED_VALUE("INITIAL_SCID");
|
||||
goto malformed;
|
||||
|
|
@ -1797,9 +1796,8 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch)
|
|||
&ch->init_dcid))
|
||||
goto err;
|
||||
} else {
|
||||
/* Client always uses an empty SCID. */
|
||||
if (ossl_quic_wire_encode_transport_param_bytes(&wpkt, QUIC_TPARAM_INITIAL_SCID,
|
||||
NULL, 0) == NULL)
|
||||
if (!ossl_quic_wire_encode_transport_param_cid(&wpkt, QUIC_TPARAM_INITIAL_SCID,
|
||||
&ch->init_scid))
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1257,8 +1257,7 @@ int ossl_quic_conn_set_initial_peer_addr(SSL *s,
|
|||
return 1;
|
||||
}
|
||||
|
||||
ctx.qc->init_peer_addr = *peer_addr;
|
||||
return 1;
|
||||
return BIO_ADDR_copy(&ctx.qc->init_peer_addr, peer_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -4328,10 +4327,114 @@ err:
|
|||
/*
|
||||
* SSL_new_from_listener
|
||||
* ---------------------
|
||||
* code here is derived from ossl_quic_new(). The `ssl` argument is
|
||||
* a listener object which already comes with QUIC port/engine. The newly
|
||||
* created QUIC connection object (QCSO) is going to share the port/engine
|
||||
* with listener (`ssl`). The `ssl` also becomes a parent of QCSO created
|
||||
* by this function. The caller uses QCSO instance to connect to
|
||||
* remote QUIC server.
|
||||
*
|
||||
* The QCSO created here requires us to also create a channel so we
|
||||
* can connect to remote server.
|
||||
*/
|
||||
SSL *ossl_quic_new_from_listener(SSL *ssl, uint64_t flags)
|
||||
{
|
||||
/* TODO(QUIC SERVER): Implement SSL_new_from_listener */
|
||||
QCTX ctx;
|
||||
QUIC_CONNECTION *qc;
|
||||
QUIC_LISTENER *ql;
|
||||
SSL_CONNECTION *sc = NULL;
|
||||
|
||||
if (flags != 0)
|
||||
return NULL;
|
||||
|
||||
if (!expect_quic_listener(ssl, &ctx))
|
||||
return NULL;
|
||||
|
||||
if (!SSL_up_ref(&ctx.ql->obj.ssl))
|
||||
return NULL;
|
||||
|
||||
qctx_lock(&ctx);
|
||||
|
||||
ql = ctx.ql;
|
||||
|
||||
if ((qc = OPENSSL_zalloc(sizeof(*qc))) == NULL) {
|
||||
QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: setting a listener here is needed so `qc_cleanup()` does the right
|
||||
* thing. Setting listener to ql avoids premature destruction of port in
|
||||
* qc_cleanup()
|
||||
*/
|
||||
qc->listener = ql;
|
||||
qc->engine = ql->engine;
|
||||
qc->port = ql->port;
|
||||
/* create channel */
|
||||
#if defined(OPENSSL_THREADS)
|
||||
/* this is the engine mutex */
|
||||
qc->mutex = ql->mutex;
|
||||
#endif
|
||||
#if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST)
|
||||
qc->is_thread_assisted
|
||||
= ((ql->obj.domain_flags & SSL_DOMAIN_FLAG_THREAD_ASSISTED) != 0);
|
||||
#endif
|
||||
|
||||
/* Create the handshake layer. */
|
||||
qc->tls = ossl_ssl_connection_new_int(ql->obj.ssl.ctx, NULL, TLS_method());
|
||||
if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) {
|
||||
QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
|
||||
goto err;
|
||||
}
|
||||
sc->s3.flags |= TLS1_FLAGS_QUIC;
|
||||
|
||||
qc->default_ssl_options = OSSL_QUIC_PERMITTED_OPTIONS;
|
||||
qc->last_error = SSL_ERROR_NONE;
|
||||
|
||||
/*
|
||||
* This is QCSO, we don't expect to accept connections
|
||||
* on success the channel assumes ownership of tls, we need
|
||||
* to grab reference for qc.
|
||||
*/
|
||||
qc->ch = ossl_quic_port_create_outgoing(qc->port, qc->tls);
|
||||
|
||||
ossl_quic_channel_set_msg_callback(qc->ch, ql->obj.ssl.ctx->msg_callback, &qc->obj.ssl);
|
||||
ossl_quic_channel_set_msg_callback_arg(qc->ch, ql->obj.ssl.ctx->msg_callback_arg);
|
||||
|
||||
/*
|
||||
* We deliberately pass NULL for engine and port, because we don't want to
|
||||
* to turn QCSO we create here into an event leader, nor port leader.
|
||||
* Both those roles are occupied already by listener (`ssl`) we use
|
||||
* to create a new QCSO here.
|
||||
*/
|
||||
if (!ossl_quic_obj_init(&qc->obj, ql->obj.ssl.ctx,
|
||||
SSL_TYPE_QUIC_CONNECTION,
|
||||
&ql->obj.ssl, NULL, NULL)) {
|
||||
QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Initialise libssl APL-related state. */
|
||||
qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
|
||||
qc->default_ssl_mode = qc->obj.ssl.ctx->mode;
|
||||
qc->default_ssl_options = qc->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
|
||||
qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO;
|
||||
qc->last_error = SSL_ERROR_NONE;
|
||||
|
||||
qc_update_reject_policy(qc);
|
||||
|
||||
qctx_unlock(&ctx);
|
||||
|
||||
return &qc->obj.ssl;
|
||||
|
||||
err:
|
||||
if (qc != NULL) {
|
||||
qc_cleanup(qc, /* have_lock= */ 0);
|
||||
OPENSSL_free(qc);
|
||||
}
|
||||
qctx_unlock(&ctx);
|
||||
SSL_free(&ctx.ql->obj.ssl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2346,6 +2346,170 @@ static int test_early_ticks(void)
|
|||
return testresult;
|
||||
}
|
||||
|
||||
static int select_alpn(SSL *ssl, const unsigned char **out,
|
||||
unsigned char *out_len, const unsigned char *in,
|
||||
unsigned int in_len, void *arg)
|
||||
{
|
||||
static unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
|
||||
|
||||
if (SSL_select_next_proto((unsigned char **)out, out_len, alpn, sizeof(alpn),
|
||||
in, in_len) == OPENSSL_NPN_NEGOTIATED)
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
static SSL_CTX *create_server_ctx(void)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
||||
if (!TEST_ptr(ssl_ctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_server_method()))
|
||||
|| !TEST_true(SSL_CTX_use_certificate_file(ssl_ctx, cert, SSL_FILETYPE_PEM))
|
||||
|| !TEST_true(SSL_CTX_use_PrivateKey_file(ssl_ctx, privkey, SSL_FILETYPE_PEM))) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
ssl_ctx = NULL;
|
||||
} else {
|
||||
SSL_CTX_set_alpn_select_cb(ssl_ctx, select_alpn, NULL);
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
|
||||
}
|
||||
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
static BIO_ADDR *create_addr(struct in_addr *ina, short int port)
|
||||
{
|
||||
BIO_ADDR *addr = NULL;
|
||||
|
||||
if (!TEST_ptr(addr = BIO_ADDR_new()))
|
||||
return NULL;
|
||||
|
||||
if (!TEST_true(BIO_ADDR_rawmake(addr, AF_INET, ina, sizeof(struct in_addr),
|
||||
htons(port)))) {
|
||||
BIO_ADDR_free(addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int bio_addr_bind(BIO *bio, BIO_ADDR *addr)
|
||||
{
|
||||
int bio_caps = BIO_DGRAM_CAP_HANDLES_DST_ADDR | BIO_DGRAM_CAP_HANDLES_SRC_ADDR;
|
||||
|
||||
if (!TEST_true(BIO_dgram_set_caps(bio, bio_caps)))
|
||||
return 0;
|
||||
|
||||
if (!TEST_int_eq(BIO_dgram_set0_local_addr(bio, addr), 1))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SSL *ql_create(SSL_CTX *ssl_ctx, BIO *bio)
|
||||
{
|
||||
SSL *qserver;
|
||||
|
||||
if (!TEST_ptr(qserver = SSL_new_listener(ssl_ctx, 0))) {
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SSL_set_bio(qserver, bio, bio);
|
||||
|
||||
if (!TEST_true(SSL_listen(qserver))) {
|
||||
SSL_free(qserver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qserver;
|
||||
}
|
||||
|
||||
static int qc_init(SSL *qconn, BIO_ADDR *dst_addr)
|
||||
{
|
||||
static unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
|
||||
|
||||
if (!TEST_true(SSL_set1_initial_peer_addr(qconn, dst_addr)))
|
||||
return 0;
|
||||
|
||||
if (!TEST_false(SSL_set_alpn_protos(qconn, alpn, sizeof(alpn))))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_ssl_new_from_listener(void)
|
||||
{
|
||||
SSL_CTX *lctx = NULL, *sctx = NULL;
|
||||
SSL *qlistener = NULL, *qserver = NULL, *qconn = 0;
|
||||
int testresult = 0;
|
||||
int chk;
|
||||
BIO *lbio = NULL, *sbio = NULL;
|
||||
BIO_ADDR *addr = NULL;
|
||||
struct in_addr ina;
|
||||
|
||||
ina.s_addr = htonl(0x1f000001);
|
||||
if (!TEST_ptr(lctx = create_server_ctx())
|
||||
|| !TEST_ptr(sctx = create_server_ctx())
|
||||
|| !TEST_true(BIO_new_bio_dgram_pair(&lbio, 0, &sbio, 0)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_ptr(addr = create_addr(&ina, 8040)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(bio_addr_bind(lbio, addr)))
|
||||
goto err;
|
||||
addr = NULL;
|
||||
|
||||
if (!TEST_ptr(addr = create_addr(&ina, 4080)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(bio_addr_bind(sbio, addr)))
|
||||
goto err;
|
||||
addr = NULL;
|
||||
|
||||
qlistener = ql_create(lctx, lbio);
|
||||
lbio = NULL;
|
||||
if (!TEST_ptr(qlistener))
|
||||
goto err;
|
||||
|
||||
qserver = ql_create(sctx, sbio);
|
||||
sbio = NULL;
|
||||
if (!TEST_ptr(qserver))
|
||||
goto err;
|
||||
|
||||
if (!TEST_ptr(qconn = SSL_new_from_listener(qlistener, 0)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_ptr(addr = create_addr(&ina, 4080)))
|
||||
goto err;
|
||||
|
||||
chk = qc_init(qconn, addr);
|
||||
if (!TEST_true(chk))
|
||||
goto err;
|
||||
|
||||
while ((chk = SSL_do_handshake(qconn)) == -1) {
|
||||
SSL_handle_events(qserver);
|
||||
SSL_handle_events(qlistener);
|
||||
}
|
||||
|
||||
if (!TEST_int_gt(chk, 0)) {
|
||||
TEST_info("SSL_do_handshake() failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
testresult = 1;
|
||||
err:
|
||||
SSL_free(qconn);
|
||||
SSL_free(qlistener);
|
||||
SSL_free(qserver);
|
||||
BIO_free(lbio);
|
||||
BIO_free(sbio);
|
||||
SSL_CTX_free(sctx);
|
||||
SSL_CTX_free(lctx);
|
||||
BIO_ADDR_free(addr);
|
||||
|
||||
return testresult;
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n")
|
||||
|
|
@ -2441,6 +2605,7 @@ int setup_tests(void)
|
|||
ADD_TEST(test_session_cb);
|
||||
ADD_TEST(test_domain_flags);
|
||||
ADD_TEST(test_early_ticks);
|
||||
ADD_TEST(test_ssl_new_from_listener);
|
||||
return 1;
|
||||
err:
|
||||
cleanup_tests();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue