From c3a04ea2fdd073e55b57e70e4f17f3ccbaa8c8a6 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Tue, 18 Apr 2023 19:30:56 +0100 Subject: QUIC DISPATCH/APL: Add SSL_stream_reset and status query APIs Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/20765) --- include/internal/quic_ssl.h | 14 ++++ include/openssl/ssl.h.in | 32 ++++++++ ssl/quic/quic_impl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++ ssl/ssl_lib.c | 75 ++++++++++++++++++ util/libssl.num | 6 ++ 5 files changed, 312 insertions(+) diff --git a/include/internal/quic_ssl.h b/include/internal/quic_ssl.h index ed17005843..050bfe9d24 100644 --- a/include/internal/quic_ssl.h +++ b/include/internal/quic_ssl.h @@ -77,6 +77,20 @@ __owur int ossl_quic_set_incoming_stream_reject_policy(SSL *s, int policy, __owur SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags); __owur size_t ossl_quic_get_accept_stream_queue_len(SSL *s); +__owur int ossl_quic_stream_reset(SSL *ssl, + const SSL_STREAM_RESET_ARGS *args, + size_t args_len); + +__owur int ossl_quic_get_stream_read_state(SSL *ssl); +__owur int ossl_quic_get_stream_write_state(SSL *ssl); +__owur int ossl_quic_get_stream_read_error_code(SSL *ssl, + uint64_t *app_error_code); +__owur int ossl_quic_get_stream_write_error_code(SSL *ssl, + uint64_t *app_error_code); +__owur int ossl_quic_get_conn_close_info(SSL *ssl, + SSL_CONN_CLOSE_INFO *info, + size_t info_len); + /* * Used to override ossl_time_now() for debug purposes. Must be called before * connecting. diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index dcc7cfdb3a..81d49e6299 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -2318,6 +2318,38 @@ __owur int SSL_shutdown_ex(SSL *ssl, uint64_t flags, __owur int SSL_stream_conclude(SSL *ssl, uint64_t flags); +typedef struct ssl_stream_reset_args_st { + uint64_t quic_error_code; +} SSL_STREAM_RESET_ARGS; + +__owur int SSL_stream_reset(SSL *ssl, + const SSL_STREAM_RESET_ARGS *args, + size_t args_len); + +#define SSL_STREAM_STATE_NONE 0 +#define SSL_STREAM_STATE_OK 1 +#define SSL_STREAM_STATE_WRONG_DIR 2 +#define SSL_STREAM_STATE_FINISHED 3 +#define SSL_STREAM_STATE_RESET_LOCAL 4 +#define SSL_STREAM_STATE_RESET_REMOTE 5 +#define SSL_STREAM_STATE_CONN_CLOSED 6 +__owur int SSL_get_stream_read_state(SSL *ssl); +__owur int SSL_get_stream_write_state(SSL *ssl); + +__owur int SSL_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code); +__owur int SSL_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code); + +typedef struct ssl_conn_close_info_st { + uint64_t error_code; + char *reason; + size_t reason_len; + char is_local, is_transport; +} SSL_CONN_CLOSE_INFO; + +__owur int SSL_get_conn_close_info(SSL *ssl, + SSL_CONN_CLOSE_INFO *info, + size_t info_len); + # ifndef OPENSSL_NO_DEPRECATED_1_1_0 # define SSL_cache_hit(s) SSL_session_reused(s) # endif diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index e3402e5c65..fbcc85e52a 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -2305,6 +2305,191 @@ size_t ossl_quic_get_accept_stream_queue_len(SSL *s) return v; } +/* + * SSL_stream_reset + * ---------------- + */ +int ossl_quic_stream_reset(SSL *ssl, + const SSL_STREAM_RESET_ARGS *args, + size_t args_len) +{ + QCTX ctx; + QUIC_STREAM_MAP *qsm; + QUIC_STREAM *qs; + uint64_t error_code; + + if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/0, &ctx)) + return 0; + + qsm = ossl_quic_channel_get_qsm(ctx.qc->ch); + qs = ctx.xso->stream; + error_code = (args != NULL ? args->quic_error_code : 0); + + ossl_quic_stream_map_reset_stream_send_part(qsm, qs, error_code); + + quic_unlock(ctx.qc); + return 1; +} + +/* + * SSL_get_stream_read_state + * ------------------------- + */ +static void quic_classify_stream(QUIC_CONNECTION *qc, + QUIC_STREAM *qs, + int is_write, + int *state, + uint64_t *app_error_code) +{ + int local_init; + uint64_t final_size; + + local_init = (ossl_quic_stream_is_server_init(qs) == qc->as_server); + + if (app_error_code != NULL) + *app_error_code = UINT64_MAX; + else + app_error_code = &final_size; /* throw away value */ + + if (!ossl_quic_stream_is_bidi(qs) && local_init != is_write) { + /* + * Unidirectional stream and this direction of transmission doesn't + * exist. + */ + *state = SSL_STREAM_STATE_WRONG_DIR; + } else if (ossl_quic_channel_is_term_any(qc->ch)) { + /* Connection already closed. */ + *state = SSL_STREAM_STATE_CONN_CLOSED; + } else if (!is_write && qs->recv_fin_retired) { + /* Application has read a FIN. */ + *state = SSL_STREAM_STATE_FINISHED; + } else if ((!is_write && qs->stop_sending) + || (is_write && qs->reset_stream)) { + /* + * Stream has been reset locally. FIN takes precedence over this for the + * read case as the application need not care if the stream is reset + * after a FIN has been successfully processed. + */ + *state = SSL_STREAM_STATE_RESET_LOCAL; + *app_error_code = !is_write + ? qs->stop_sending_aec + : qs->reset_stream_aec; + } else if ((!is_write && qs->peer_reset_stream) + || (is_write && qs->peer_stop_sending)) { + /* + * Stream has been reset remotely. */ + *state = SSL_STREAM_STATE_RESET_REMOTE; + *app_error_code = !is_write + ? qs->peer_reset_stream_aec + : qs->peer_stop_sending_aec; + } else if (is_write && ossl_quic_sstream_get_final_size(qs->sstream, + &final_size)) { + /* + * Stream has been finished. Stream reset takes precedence over this for + * the write case as peer may not have received all data. + */ + *state = SSL_STREAM_STATE_FINISHED; + } else { + /* Stream still healthy. */ + *state = SSL_STREAM_STATE_OK; + } +} + +static int quic_get_stream_state(SSL *ssl, int is_write) +{ + QCTX ctx; + int state; + + if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx)) + return SSL_STREAM_STATE_NONE; + + quic_classify_stream(ctx.qc, ctx.xso->stream, is_write, &state, NULL); + quic_unlock(ctx.qc); + return state; +} + +int ossl_quic_get_stream_read_state(SSL *ssl) +{ + return quic_get_stream_state(ssl, /*is_write=*/0); +} + +/* + * SSL_get_stream_write_state + * -------------------------- + */ +int ossl_quic_get_stream_write_state(SSL *ssl) +{ + return quic_get_stream_state(ssl, /*is_write=*/1); +} + +/* + * SSL_get_stream_read_error_code + * ------------------------------ + */ +static int quic_get_stream_error_code(SSL *ssl, int is_write, + uint64_t *app_error_code) +{ + QCTX ctx; + int state; + + if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx)) + return -1; + + quic_classify_stream(ctx.qc, ctx.xso->stream, /*is_write=*/0, + &state, app_error_code); + + quic_unlock(ctx.qc); + switch (state) { + case SSL_STREAM_STATE_FINISHED: + return 0; + case SSL_STREAM_STATE_RESET_LOCAL: + case SSL_STREAM_STATE_RESET_REMOTE: + return 1; + default: + return -1; + } +} + +int ossl_quic_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code) +{ + return quic_get_stream_error_code(ssl, /*is_write=*/0, app_error_code); +} + +/* + * SSL_get_stream_write_error_code + * ------------------------------- + */ +int ossl_quic_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code) +{ + return quic_get_stream_error_code(ssl, /*is_write=*/1, app_error_code); +} + +/* + * SSL_get_conn_close_info + * ----------------------- + */ +int ossl_quic_get_conn_close_info(SSL *ssl, + SSL_CONN_CLOSE_INFO *info, + size_t info_len) +{ + QCTX ctx; + const QUIC_TERMINATE_CAUSE *tc; + + if (!expect_quic_conn_only(ssl, &ctx)) + return -1; + + tc = ossl_quic_channel_get_terminate_cause(ctx.qc->ch); + if (tc == NULL) + return 0; + + info->error_code = tc->error_code; + info->reason = NULL; /* TODO(QUIC): Wire reason */ + info->reason_len = 0; + info->is_local = !tc->remote; + info->is_transport = !tc->app; + return 1; +} + /* * QUIC Front-End I/O API: SSL_CTX Management * ========================================== diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 1d84ac39dc..3fb80824de 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -7424,6 +7424,81 @@ size_t SSL_get_accept_stream_queue_len(SSL *s) #endif } +int SSL_stream_reset(SSL *s, + const SSL_STREAM_RESET_ARGS *args, + size_t args_len) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return 0; + + return ossl_quic_stream_reset(s, args, args_len); +#else + return 0; +#endif +} + +int SSL_get_stream_read_state(SSL *s) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return SSL_STREAM_STATE_NONE; + + return ossl_quic_get_stream_read_state(s); +#else + return SSL_STREAM_STATE_NONE; +#endif +} + +int SSL_get_stream_write_state(SSL *s) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return SSL_STREAM_STATE_NONE; + + return ossl_quic_get_stream_write_state(s); +#else + return SSL_STREAM_STATE_NONE; +#endif +} + +int SSL_get_stream_read_error_code(SSL *s, uint64_t *app_error_code) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return -1; + + return ossl_quic_get_stream_read_error_code(s, app_error_code); +#else + return -1; +#endif +} + +int SSL_get_stream_write_error_code(SSL *s, uint64_t *app_error_code) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return -1; + + return ossl_quic_get_stream_write_error_code(s, app_error_code); +#else + return -1; +#endif +} + +int SSL_get_conn_close_info(SSL *s, SSL_CONN_CLOSE_INFO *info, + size_t info_len) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return -1; + + return ossl_quic_get_conn_close_info(s, info, info_len); +#else + return -1; +#endif +} + int SSL_add_expected_rpk(SSL *s, EVP_PKEY *rpk) { unsigned char *data = NULL; diff --git a/util/libssl.num b/util/libssl.num index b99ed33a9e..457e75f801 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -571,3 +571,9 @@ SSL_attach_stream ? 3_2_0 EXIST::FUNCTION: SSL_set_incoming_stream_reject_policy ? 3_2_0 EXIST::FUNCTION: SSL_accept_stream ? 3_2_0 EXIST::FUNCTION: SSL_get_accept_stream_queue_len ? 3_2_0 EXIST::FUNCTION: +SSL_stream_reset ? 3_2_0 EXIST::FUNCTION: +SSL_get_stream_read_state ? 3_2_0 EXIST::FUNCTION: +SSL_get_stream_write_state ? 3_2_0 EXIST::FUNCTION: +SSL_get_stream_read_error_code ? 3_2_0 EXIST::FUNCTION: +SSL_get_stream_write_error_code ? 3_2_0 EXIST::FUNCTION: +SSL_get_conn_close_info ? 3_2_0 EXIST::FUNCTION: -- cgit v1.2.1