summaryrefslogtreecommitdiff
path: root/ssl
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2023-04-18 19:30:55 +0100
committerHugo Landau <hlandau@openssl.org>2023-05-12 14:47:12 +0100
commit8b7be3aa7e90d85441f5012624cece4dca33291e (patch)
treeb3ae474bb1d4c54264b7cf2e11f10a3e9c13f682 /ssl
parent19cb0887722b66e5db7ec0d339526608444a11ef (diff)
downloadopenssl-new-8b7be3aa7e90d85441f5012624cece4dca33291e.tar.gz
QUIC DISPATCH/APL: Implement SSL_set_default_stream_mode, default XSO refactor
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20765)
Diffstat (limited to 'ssl')
-rw-r--r--ssl/quic/quic_impl.c324
-rw-r--r--ssl/quic/quic_local.h3
-rw-r--r--ssl/ssl_err.c1
-rw-r--r--ssl/ssl_lib.c36
4 files changed, 308 insertions, 56 deletions
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c
index 6a28c00c75..4550ee3be0 100644
--- a/ssl/quic/quic_impl.c
+++ b/ssl/quic/quic_impl.c
@@ -20,7 +20,11 @@
static void aon_write_finish(QUIC_XSO *xso);
static int create_channel(QUIC_CONNECTION *qc);
static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs);
-static int qc_try_create_default_xso(QUIC_CONNECTION *qc, int remote_init);
+static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc);
+static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc);
+static void quic_lock(QUIC_CONNECTION *qc);
+static void quic_unlock(QUIC_CONNECTION *qc);
+static int quic_do_handshake(QUIC_CONNECTION *qc);
/*
* QUIC Front-End I/O API: Common Utilities
@@ -136,7 +140,7 @@ static int expect_quic(const SSL *s, QCTX *ctx)
ctx->is_stream = 0;
if (s == NULL)
- return QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
+ return QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_PASSED_NULL_PARAMETER, NULL);
switch (s->type) {
case SSL_TYPE_QUIC_CONNECTION:
@@ -165,22 +169,50 @@ static int expect_quic(const SSL *s, QCTX *ctx)
*
* remote_init determines if we expect the default XSO to be remotely created or
* not. If it is -1, do not instantiate a default XSO if one does not yet exist.
+ *
+ * Channel mutex is acquired and retained on success.
*/
-static int ossl_unused expect_quic_with_stream(const SSL *s, int remote_init,
- QCTX *ctx)
+QUIC_ACQUIRES_LOCK
+static int ossl_unused expect_quic_with_stream_lock(const SSL *s, int remote_init,
+ QCTX *ctx)
{
if (!expect_quic(s, ctx))
return 0;
+ quic_lock(ctx->qc);
+
if (ctx->xso == NULL && remote_init >= 0) {
- qc_try_create_default_xso(ctx->qc, remote_init);
+ if (ossl_quic_channel_is_term_any(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+ goto err;
+ }
+
+ /* If we haven't finished the handshake, try to advance it. */
+ if (quic_do_handshake(ctx->qc) < 1)
+ /* ossl_quic_do_handshake raised error here */
+ goto err;
+
+ if (remote_init == 0) {
+ if (!qc_try_create_default_xso_for_write(ctx->qc))
+ goto err;
+ } else {
+ if (!qc_wait_for_default_xso_for_read(ctx->qc))
+ goto err;
+ }
+
ctx->xso = ctx->qc->default_xso;
}
- if (ctx->xso == NULL)
- return QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, ERR_R_INTERNAL_ERROR, NULL);
+ if (ctx->xso == NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_NO_STREAM, NULL);
+ goto err;
+ }
+
+ return 1; /* lock held */
- return 1;
+err:
+ quic_unlock(ctx->qc);
+ return 0;
}
/*
@@ -193,7 +225,7 @@ static int ossl_unused expect_quic_conn_only(const SSL *s, QCTX *ctx)
return 0;
if (ctx->is_stream)
- return QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, ERR_R_INTERNAL_ERROR, NULL);
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_CONN_USE_ONLY, NULL);
return 1;
}
@@ -261,6 +293,7 @@ SSL *ossl_quic_new(SSL_CTX *ctx)
qc->as_server = 0; /* TODO(QUIC): server support */
qc->as_server_state = qc->as_server;
+ qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
qc->default_ssl_mode = qc->ssl.ctx->mode;
qc->default_blocking = 1;
qc->last_error = SSL_ERROR_NONE;
@@ -1119,50 +1152,123 @@ int ossl_quic_accept(SSL *s)
* exists). Note that this is NOT an error condition.
*/
QUIC_NEEDS_LOCK
-static int qc_try_create_default_xso(QUIC_CONNECTION *qc, int remote_init)
+static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc)
{
- QUIC_STREAM *qs;
+ uint64_t flags = 0;
- if (qc->default_xso != NULL)
- qc->default_xso_created = 1;
-
- if (qc->default_xso_created)
+ if (qc->default_xso_created
+ || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE)
/*
* We only do this once. If the user detaches a previously created
* default XSO we don't auto-create another one.
*/
- return 0;
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_NO_STREAM, NULL);
- if (!remote_init) {
- /*
- * We are writing to the stream first, so this is a locally-initiated
- * stream.
- */
- qc->default_xso = (QUIC_XSO *)ossl_quic_conn_stream_new(&qc->ssl, 0);
- if (qc->default_xso == NULL)
- return 0;
- } else {
- /*
- * Client is reading first. This means it is expecting to get data on a
- * stream the peer writes to first, meaning this is a remotely-initiated
- * stream. Ordinarily, we wait for the RXDP to handle a STREAM frame to
- * create such a stream. But in this case, special case it and create
- * the bookkeeping structures for the first peer-initiated bidirectional
- * stream so the client can start to wait on it.
- */
- qs = ossl_quic_channel_new_stream_remote(qc->ch,
- QUIC_STREAM_INITIATOR_SERVER
- | QUIC_STREAM_DIR_BIDI);
- if (qs == NULL)
- return 0;
-
- qc->default_xso = create_xso_from_stream(qc, qs);
- if (qc->default_xso == NULL) {
- ossl_quic_stream_map_release(ossl_quic_channel_get_qsm(qc->ch), qs);
+ /* Create a locally-initiated stream. */
+ if (qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_AUTO_UNI)
+ flags |= SSL_STREAM_FLAG_UNI;
+
+ qc->default_xso = (QUIC_XSO *)ossl_quic_conn_stream_new(&qc->ssl, flags);
+ if (qc->default_xso == NULL)
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+
+ qc->default_xso_created = 1;
+ return 1;
+}
+
+struct quic_wait_for_stream_args {
+ QUIC_CONNECTION *qc;
+ QUIC_STREAM *qs;
+ uint64_t expect_id;
+};
+
+QUIC_NEEDS_LOCK
+static int quic_wait_for_stream(void *arg)
+{
+ struct quic_wait_for_stream_args *args = arg;
+
+ if (!ossl_quic_channel_is_active(args->qc->ch)) {
+ /* If connection is torn down due to an error while blocking, stop. */
+ QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+ return -1;
+ }
+
+ args->qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(args->qc->ch),
+ args->expect_id);
+ if (args->qs != NULL)
+ return 1; /* stream now exists */
+
+ return 0; /* did not get a stream, keep trying */
+}
+
+QUIC_NEEDS_LOCK
+static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc)
+{
+ /* Called on a QCSO and we don't currently have a default stream. */
+ uint64_t expect_id;
+ QUIC_STREAM *qs;
+ int res;
+ struct quic_wait_for_stream_args wargs;
+
+ /*
+ * If default stream functionality is disabled or we already detached
+ * one, don't make another default stream and just fail.
+ */
+ if (qc->default_xso_created
+ || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE)
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_NO_STREAM, NULL);
+
+ /*
+ * The peer may have opened a stream since we last ticked. So tick and
+ * see if the stream with ordinal 0 (remote, bidi/uni based on stream
+ * mode) exists yet. QUIC stream IDs must be allocated in order, so the
+ * first stream created by a peer must have an ordinal of 0.
+ */
+ expect_id = qc->as_server
+ ? QUIC_STREAM_INITIATOR_CLIENT
+ : QUIC_STREAM_INITIATOR_SERVER;
+
+ expect_id |= (qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_AUTO_UNI)
+ ? QUIC_STREAM_DIR_UNI
+ : QUIC_STREAM_DIR_BIDI;
+
+ qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(qc->ch),
+ expect_id);
+ if (qs == NULL) {
+ ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch), 0);
+
+ qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(qc->ch),
+ expect_id);
+ }
+
+ if (qs == NULL) {
+ if (!qc_blocking_mode(qc))
+ /* Non-blocking mode, so just bail immediately. */
+ return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
+
+ /* Block until we have a stream. */
+ wargs.qc = qc;
+ wargs.qs = NULL;
+ wargs.expect_id = expect_id;
+
+ res = block_until_pred(qc, quic_wait_for_stream, &wargs, 0);
+ if (res == 0)
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+ else if (res < 0 || wargs.qs == NULL)
+ /* quic_wait_for_stream raised error here */
return 0;
- }
+
+ qs = wargs.qs;
}
+ /*
+ * We now have qs != NULL. Make it the default stream, creating the
+ * necessary XSO.
+ */
+ qc->default_xso = create_xso_from_stream(qc, qs);
+ if (qc->default_xso == NULL)
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+
qc->default_xso_created = 1;
return 1;
}
@@ -1218,6 +1324,7 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
if (xso == NULL)
goto err;
+ ctx.qc->default_xso_created = 1; /* inhibits default XSO */
quic_unlock(ctx.qc);
return &xso->ssl;
@@ -1508,11 +1615,11 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
*written = 0;
- if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx)) {
- return 0;
- }
+ if (len == 0)
+ return 1;
- quic_lock(ctx.qc);
+ if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, &ctx))
+ return 0;
partial_write = ((ctx.xso->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0);
@@ -1647,7 +1754,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
*bytes_read = 0;
- if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx))
+ if (!expect_quic(s, &ctx))
return 0;
quic_lock(ctx.qc);
@@ -1663,6 +1770,21 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
goto out;
}
+ if (ctx.xso == NULL) {
+ /*
+ * Called on a QCSO and we don't currently have a default stream.
+ *
+ * Wait until we get a stream initiated by the peer (blocking mode) or
+ * fail if we don't have one yet (non-blocking mode).
+ */
+ if (!qc_wait_for_default_xso_for_read(ctx.qc)) {
+ ret = 0; /* error already raised here */
+ goto out;
+ }
+
+ ctx.xso = ctx.qc->default_xso;
+ }
+
if (ctx.xso->stream == NULL) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
goto out;
@@ -1734,11 +1856,9 @@ static size_t ossl_quic_pending_int(const SSL *s)
size_t avail = 0;
int fin = 0;
- if (!expect_quic_with_stream(s, /*remote_init=*/-1, &ctx))
+ if (!expect_quic_with_stream_lock(s, /*remote_init=*/-1, &ctx))
return 0;
- quic_lock(ctx.qc);
-
if (ctx.xso->stream == NULL || ctx.xso->stream->rstream == NULL)
/* Cannot raise errors here because we are const, just fail. */
goto out;
@@ -1771,11 +1891,9 @@ int ossl_quic_conn_stream_conclude(SSL *s)
QCTX ctx;
QUIC_STREAM *qs;
- if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx))
+ if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, &ctx))
return 0;
- quic_lock(ctx.qc);
-
qs = ctx.xso->stream;
if (qs == NULL || qs->sstream == NULL) {
@@ -1874,11 +1992,105 @@ int ossl_quic_get_stream_type(SSL *s)
uint64_t ossl_quic_get_stream_id(SSL *s)
{
QCTX ctx;
+ uint64_t id;
- if (!expect_quic_with_stream(s, /*remote_init=*/-1, &ctx))
+ if (!expect_quic_with_stream_lock(s, /*remote_init=*/-1, &ctx))
return UINT64_MAX;
- return ctx.xso->stream->id;
+ id = ctx.xso->stream->id;
+ quic_unlock(ctx.qc);
+
+ return id;
+}
+
+/*
+ * SSL_set_default_stream_mode
+ * ---------------------------
+ */
+int ossl_quic_set_default_stream_mode(SSL *s, uint32_t mode)
+{
+ QCTX ctx;
+
+ if (!expect_quic_conn_only(s, &ctx))
+ return 0;
+
+ quic_lock(ctx.qc);
+
+ if (ctx.qc->default_xso_created)
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+ "too late to change default stream mode");
+
+ switch (mode) {
+ case SSL_DEFAULT_STREAM_MODE_NONE:
+ case SSL_DEFAULT_STREAM_MODE_AUTO_BIDI:
+ case SSL_DEFAULT_STREAM_MODE_AUTO_UNI:
+ ctx.qc->default_stream_mode = mode;
+ break;
+ default:
+ quic_unlock(ctx.qc);
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_PASSED_INVALID_ARGUMENT,
+ "bad default stream type");
+ }
+
+ quic_unlock(ctx.qc);
+ return 1;
+}
+
+/*
+ * SSL_detach_stream
+ * -----------------
+ */
+SSL *ossl_quic_detach_stream(SSL *s)
+{
+ QCTX ctx;
+ QUIC_XSO *xso;
+
+ if (!expect_quic_conn_only(s, &ctx))
+ return NULL;
+
+ quic_lock(ctx.qc);
+
+ xso = ctx.qc->default_xso;
+ ctx.qc->default_xso = NULL;
+
+ /* Calling this function inhibits default XSO autocreation. */
+ ctx.qc->default_xso_created = 1;
+
+ quic_unlock(ctx.qc);
+
+ return &xso->ssl;
+}
+
+/*
+ * SSL_attach_stream
+ * -----------------
+ */
+int ossl_quic_attach_stream(SSL *conn, SSL *stream)
+{
+ QCTX ctx;
+
+ if (!expect_quic_conn_only(conn, &ctx))
+ return 0;
+
+ if (stream == NULL || stream->type != SSL_TYPE_QUIC_XSO)
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_PASSED_NULL_PARAMETER,
+ "stream to attach must be a valid QUIC stream");
+
+ quic_lock(ctx.qc);
+
+ if (ctx.qc->default_xso != NULL) {
+ quic_unlock(ctx.qc);
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+ "connection already has a default stream");
+ }
+
+ ctx.qc->default_xso = (QUIC_XSO *)stream;
+
+ /* Calling this function inhibits default XSO autocreation. */
+ ctx.qc->default_xso_created = 1;
+
+ quic_unlock(ctx.qc);
+ return 1;
}
/*
diff --git a/ssl/quic/quic_local.h b/ssl/quic/quic_local.h
index 1358a55966..edc82a415e 100644
--- a/ssl/quic/quic_local.h
+++ b/ssl/quic/quic_local.h
@@ -172,6 +172,9 @@ struct quic_conn_st {
/* Have we created a default XSO yet? */
unsigned int default_xso_created : 1;
+ /* Default stream type. Defaults to SSL_DEFAULT_STREAM_MODE_AUTO_BIDI. */
+ uint32_t default_stream_mode;
+
/* SSL_set_mode. This is not used directly but inherited by new XSOs. */
uint32_t default_ssl_mode;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index f1464f7d64..520a297905 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -311,6 +311,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS),
"no shared signature algorithms"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SRTP_PROFILES), "no srtp profiles"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_STREAM), "no stream"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM),
"no suitable digest algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_GROUPS), "no suitable groups"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a7e3682291..c6cd2dabda 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -7352,6 +7352,42 @@ uint64_t SSL_get_stream_id(SSL *s)
#endif
}
+int SSL_set_default_stream_mode(SSL *s, uint32_t mode)
+{
+#ifndef OPENSSL_NO_QUIC
+ if (!IS_QUIC(s))
+ return 0;
+
+ return ossl_quic_set_default_stream_mode(s, mode);
+#else
+ return 0;
+#endif
+}
+
+SSL *SSL_detach_stream(SSL *s)
+{
+#ifndef OPENSSL_NO_QUIC
+ if (!IS_QUIC(s))
+ return NULL;
+
+ return ossl_quic_detach_stream(s);
+#else
+ return NULL;
+#endif
+}
+
+int SSL_attach_stream(SSL *conn, SSL *stream)
+{
+#ifndef OPENSSL_NO_QUIC
+ if (!IS_QUIC(conn))
+ return 0;
+
+ return ossl_quic_attach_stream(conn, stream);
+#else
+ return NULL;
+#endif
+}
+
int SSL_add_expected_rpk(SSL *s, EVP_PKEY *rpk)
{
unsigned char *data = NULL;