From b55a5117d90453ea937b1db15959d9a22bf02416 Mon Sep 17 00:00:00 2001 From: Dmitri Tikhonov Date: Wed, 6 May 2020 09:35:33 -0400 Subject: [PATCH] Release 2.14.6 - [BUGFIX] Fix amplification mitigation in 0-RTT case. - [BUGFIX] IETF mini connection should not tickable if cannot send a packet due to amplification. - [BUGFIX] Fail if active_connection_id_limit TP is smaller than 2. - [BUGFIX] Qlog server certificates for IETF QUIC connections. - [BUGFIX] Uninitialized struct padding usage in tokgen (benign). - [BUGFIX] Incorrect argument to shi_lookup() (benign). --- CHANGELOG | 10 +++++ docs/conf.py | 2 +- include/lsquic.h | 2 +- src/liblsquic/lsquic_conn_public.h | 5 +++ src/liblsquic/lsquic_enc_sess_ietf.c | 16 +++++-- src/liblsquic/lsquic_ev_log.c | 20 +++++++++ src/liblsquic/lsquic_ev_log.h | 11 +++++ src/liblsquic/lsquic_full_conn_ietf.c | 23 ++++++++++ src/liblsquic/lsquic_mini_conn_ietf.c | 9 +++- src/liblsquic/lsquic_packet_out.h | 2 +- src/liblsquic/lsquic_qlog.c | 43 +++++++++++++++++++ src/liblsquic/lsquic_qlog.h | 5 +++ src/liblsquic/lsquic_send_ctl.c | 61 +++++++++++++++++++++++---- src/liblsquic/lsquic_send_ctl.h | 4 ++ src/liblsquic/lsquic_tokgen.c | 13 ++++-- src/liblsquic/lsquic_trans_params.c | 1 + test/unittests/CMakeLists.txt | 1 + test/unittests/test_tokgen.c | 40 ++++++++++++++++++ 18 files changed, 248 insertions(+), 20 deletions(-) create mode 100644 test/unittests/test_tokgen.c diff --git a/CHANGELOG b/CHANGELOG index 9a367f5..9ae39e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +2020-05-06 + - 2.14.6 + - [BUGFIX] Fix amplification mitigation in 0-RTT case. + - [BUGFIX] IETF mini connection should not tickable if cannot send + a packet due to amplification. + - [BUGFIX] Fail if active_connection_id_limit TP is smaller than 2. + - [BUGFIX] Qlog server certificates for IETF QUIC connections. + - [BUGFIX] Uninitialized struct padding usage in tokgen (benign). + - [BUGFIX] Incorrect argument to shi_lookup() (benign). + 2020-04-29 - 2.14.5 - [BUGFIX] In coalesced datagram, ignore packets whose CID does not match. diff --git a/docs/conf.py b/docs/conf.py index 8a9c86e..e171bd4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ author = u'LiteSpeed Technologies' # The short X.Y version version = u'2.14' # The full version, including alpha/beta/rc tags -release = u'2.14.5' +release = u'2.14.6' # -- General configuration --------------------------------------------------- diff --git a/include/lsquic.h b/include/lsquic.h index 0186328..3a32ada 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -25,7 +25,7 @@ extern "C" { #define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MINOR_VERSION 14 -#define LSQUIC_PATCH_VERSION 5 +#define LSQUIC_PATCH_VERSION 6 /** * Engine flags: diff --git a/src/liblsquic/lsquic_conn_public.h b/src/liblsquic/lsquic_conn_public.h index 47dfeb0..f78d0eb 100644 --- a/src/liblsquic/lsquic_conn_public.h +++ b/src/liblsquic/lsquic_conn_public.h @@ -57,6 +57,11 @@ struct lsquic_conn_public { #if LSQUIC_EXTRA_CHECKS unsigned long stream_frame_bytes; #endif + /* "unsigned" is wide enough: these values are only used for amplification + * limit before initial path is validated. + */ + unsigned bytes_in; /* successfully processed */ + unsigned bytes_out; }; #endif diff --git a/src/liblsquic/lsquic_enc_sess_ietf.c b/src/liblsquic/lsquic_enc_sess_ietf.c index 342d4a5..99c34f0 100644 --- a/src/liblsquic/lsquic_enc_sess_ietf.c +++ b/src/liblsquic/lsquic_enc_sess_ietf.c @@ -1019,9 +1019,15 @@ verify_server_cert_callback (SSL *ssl, uint8_t *out_alert) return ssl_verify_invalid; } - s = enc_sess->esi_enpub->enp_verify_cert( - enc_sess->esi_enpub->enp_verify_ctx, chain); - return s == 0 ? ssl_verify_ok : ssl_verify_invalid; + EV_LOG_CERT_CHAIN(LSQUIC_LOG_CONN_ID, chain); + if (enc_sess->esi_enpub->enp_verify_cert) + { + s = enc_sess->esi_enpub->enp_verify_cert( + enc_sess->esi_enpub->enp_verify_ctx, chain); + return s == 0 ? ssl_verify_ok : ssl_verify_invalid; + } + else + return ssl_verify_ok; } @@ -1302,7 +1308,9 @@ init_client (struct enc_sess_iquic *const enc_sess) SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb); if (enc_sess->esi_enpub->enp_kli) SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - if (enc_sess->esi_enpub->enp_verify_cert) + if (enc_sess->esi_enpub->enp_verify_cert + || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT) + || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG)) SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER, verify_server_cert_callback); SSL_CTX_set_early_data_enabled(ssl_ctx, 1); diff --git a/src/liblsquic/lsquic_ev_log.c b/src/liblsquic/lsquic_ev_log.c index b4f5ba0..e6c6367 100644 --- a/src/liblsquic/lsquic_ev_log.c +++ b/src/liblsquic/lsquic_ev_log.c @@ -10,6 +10,8 @@ #include #include +#include + #include "lsquic.h" #include "lsquic_types.h" #include "lsquic_int_types.h" @@ -566,6 +568,24 @@ lsquic_ev_log_check_certs (const lsquic_cid_t *cid, const lsquic_str_t **certs, } +void +lsquic_ev_log_cert_chain (const lsquic_cid_t *cid, struct stack_st_X509 *chain) +{ + X509_NAME *name; + X509 *cert; + unsigned i; + char buf[0x100]; + + for (i = 0; i < sk_X509_num(chain); ++i) + { + cert = sk_X509_value(chain, i); + name = X509_get_subject_name(cert); + LCID("cert #%u: name: %s", i, + X509_NAME_oneline(name, buf, sizeof(buf))); + } +} + + void lsquic_ev_log_version_negotiation (const lsquic_cid_t *cid, const char *action, const char *ver) diff --git a/src/liblsquic/lsquic_ev_log.h b/src/liblsquic/lsquic_ev_log.h index 621cb0f..ed23a4d 100644 --- a/src/liblsquic/lsquic_ev_log.h +++ b/src/liblsquic/lsquic_ev_log.h @@ -17,6 +17,7 @@ struct lsquic_packet_out; struct parse_funcs; struct stream_frame; struct uncompressed_headers; +struct stack_st_X509; /* Log a generic event not tied to any particular connection */ @@ -329,6 +330,16 @@ lsquic_ev_log_check_certs (const lsquic_cid_t *, const lsquic_str_t **, size_t); lsquic_qlog_check_certs(__VA_ARGS__); \ } while (0) +void +lsquic_ev_log_cert_chain (const lsquic_cid_t *, struct stack_st_X509 *); + +#define EV_LOG_CERT_CHAIN(...) do { \ + if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \ + lsquic_ev_log_cert_chain(__VA_ARGS__); \ + if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG)) \ + lsquic_qlog_cert_chain(__VA_ARGS__); \ +} while (0) + void lsquic_ev_log_version_negotiation (const lsquic_cid_t *, const char *, const char *); diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index ad5aa60..258e930 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -138,6 +138,12 @@ enum ifull_conn_flags }; +enum more_flags +{ + MF_VALIDATE_PATH = 1 << 0, +}; + + #define N_PATHS 2 enum send @@ -315,6 +321,7 @@ struct ietf_full_conn unsigned ifc_max_streams_in[N_SDS]; uint64_t ifc_max_stream_data_uni; enum ifull_conn_flags ifc_flags; + enum more_flags ifc_mflags; enum send_flags ifc_send_flags; enum send_flags ifc_delayed_send; struct { @@ -1318,6 +1325,12 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub, conn->ifc_paths[0].cop_path = imc->imc_path; conn->ifc_paths[0].cop_flags = COP_VALIDATED; conn->ifc_used_paths = 1 << 0; + if (imc->imc_flags & IMC_ADDR_VALIDATED) + lsquic_send_ctl_path_validated(&conn->ifc_send_ctl); + else + conn->ifc_mflags |= MF_VALIDATE_PATH; + conn->ifc_pub.bytes_out = imc->imc_bytes_out; + conn->ifc_pub.bytes_in = imc->imc_bytes_in; if (imc->imc_flags & IMC_PATH_CHANGED) { LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE"); @@ -6284,6 +6297,14 @@ process_regular_packet (struct ietf_full_conn *conn, else conn->ifc_spin_bit = !lsquic_packet_in_spin_bit(packet_in); } + conn->ifc_pub.bytes_in += packet_in->pi_data_sz; + if ((conn->ifc_mflags & MF_VALIDATE_PATH) && + (packet_in->pi_header_type == HETY_NOT_SET + || packet_in->pi_header_type == HETY_HANDSHAKE)) + { + conn->ifc_mflags &= ~MF_VALIDATE_PATH; + lsquic_send_ctl_path_validated(&conn->ifc_send_ctl); + } return 0; case REC_ST_DUP: LSQ_INFO("packet %"PRIu64" is a duplicate", packet_in->pi_packno); @@ -6482,6 +6503,8 @@ ietf_full_conn_ci_packet_sent (struct lsquic_conn *lconn, lsquic_alarmset_set(&conn->ifc_alset, AL_BLOCKED_KA, packet_out->po_sent + (1 + (7 & lsquic_crand_get_nybble( conn->ifc_enpub->enp_crand))) * 1000000); + conn->ifc_pub.bytes_out += lsquic_packet_out_sent_sz(&conn->ifc_conn, + packet_out); } diff --git a/src/liblsquic/lsquic_mini_conn_ietf.c b/src/liblsquic/lsquic_mini_conn_ietf.c index 555f0af..2d0b58f 100644 --- a/src/liblsquic/lsquic_mini_conn_ietf.c +++ b/src/liblsquic/lsquic_mini_conn_ietf.c @@ -46,6 +46,9 @@ static const struct conn_iface mini_conn_ietf_iface; static unsigned highest_bit_set (unsigned long long); +static int +imico_can_send (const struct ietf_mini_conn *, size_t); + static const enum header_type el2hety[] = { @@ -625,11 +628,15 @@ ietf_mini_conn_ci_is_tickable (struct lsquic_conn *lconn) { struct ietf_mini_conn *const conn = (struct ietf_mini_conn *) lconn; const struct lsquic_packet_out *packet_out; + size_t packet_size; if (conn->imc_enpub->enp_flags & ENPUB_CAN_SEND) TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next) if (!(packet_out->po_flags & PO_SENT)) - return 1; + { + packet_size = lsquic_packet_out_total_sz(lconn, packet_out); + return imico_can_send(conn, packet_size + IQUIC_TAG_LEN); + } return 0; } diff --git a/src/liblsquic/lsquic_packet_out.h b/src/liblsquic/lsquic_packet_out.h index 9ea81d6..4245af5 100644 --- a/src/liblsquic/lsquic_packet_out.h +++ b/src/liblsquic/lsquic_packet_out.h @@ -214,7 +214,7 @@ typedef struct lsquic_packet_out lconn->cn_pf->pf_packout_max_header_size(lconn, po_flags, dcid_len)) #define lsquic_packet_out_total_sz(lconn, p) (\ - lconn->cn_pf->pf_packout_size(lconn, p)) + (lconn)->cn_pf->pf_packout_size(lconn, p)) #if __GNUC__ #if LSQUIC_EXTRA_CHECKS diff --git a/src/liblsquic/lsquic_qlog.c b/src/liblsquic/lsquic_qlog.c index ab8038f..c391801 100644 --- a/src/liblsquic/lsquic_qlog.c +++ b/src/liblsquic/lsquic_qlog.c @@ -14,6 +14,9 @@ #include #endif +#include +#include + #include "lsquic.h" #include "lsquic_types.h" #include "lsquic_int_types.h" @@ -203,6 +206,46 @@ lsquic_qlog_check_certs (const lsquic_cid_t* cid, const lsquic_str_t **certs, } +void +lsquic_qlog_cert_chain (const lsquic_cid_t* cid, struct stack_st_X509 *chain) +{ + X509 *cert; + unsigned i; + unsigned char *buf; + char *hexbuf, *newbuf; + size_t hexbuf_sz; + int len; + lsquic_time_t now; + + now = lsquic_time_now(); + hexbuf = NULL; + hexbuf_sz = 0; + for (i = 0; i < sk_X509_num(chain); ++i) + { + cert = sk_X509_value(chain, i); + buf = NULL; + len = i2d_X509(cert, &buf); + if (len <= 0) + break; + if ((size_t) len * 2 + 1 > hexbuf_sz) + { + hexbuf_sz = len * 2 + 1; + newbuf = realloc(hexbuf, hexbuf_sz); + if (!newbuf) + break; + hexbuf = newbuf; + } + lsquic_hexstr(buf, (size_t) len, hexbuf, hexbuf_sz); + LCID("[%" PRIu64 ",\"SECURITY\",\"CHECK_CERT\",\"CERTLOG\"," + "{\"certificate\":\"%s\"}]", now, hexbuf); + OPENSSL_free(buf); + } + + if (hexbuf) + free(hexbuf); +} + + void lsquic_qlog_version_negotiation (const lsquic_cid_t* cid, const char *action, const char *ver) diff --git a/src/liblsquic/lsquic_qlog.h b/src/liblsquic/lsquic_qlog.h index 494ed55..8182902 100644 --- a/src/liblsquic/lsquic_qlog.h +++ b/src/liblsquic/lsquic_qlog.h @@ -10,6 +10,8 @@ #include "lsquic_packet_common.h" #include "lsquic_str.h" +struct stack_st_X509; + /* EventCategory CONNECTIVITY @@ -83,6 +85,9 @@ lsquic_qlog_zero_rtt (const lsquic_cid_t *); void lsquic_qlog_check_certs (const lsquic_cid_t *, const lsquic_str_t **, size_t); +void +lsquic_qlog_cert_chain (const lsquic_cid_t *, struct stack_st_X509 *); + void lsquic_qlog_version_negotiation (const lsquic_cid_t *, const char *, const char *); diff --git a/src/liblsquic/lsquic_send_ctl.c b/src/liblsquic/lsquic_send_ctl.c index bd95997..9b0b2dd 100644 --- a/src/liblsquic/lsquic_send_ctl.c +++ b/src/liblsquic/lsquic_send_ctl.c @@ -119,6 +119,12 @@ send_ctl_all_bytes_out (const struct lsquic_send_ctl *ctl); static void send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl); +static int +send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl); + +static int +send_ctl_can_send (struct lsquic_send_ctl *ctl); + #ifdef NDEBUG static #elif __GNUC__ @@ -363,6 +369,11 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset, ctl->sc_flags |= SC_SANITY_CHECK; #endif ctl->sc_gap = UINT64_MAX - 1 /* Can't have +1 == 0 */; + if ((ctl->sc_conn_pub->lconn->cn_flags & (LSCONN_IETF|LSCONN_SERVER)) + == (LSCONN_IETF|LSCONN_SERVER)) + ctl->sc_can_send = send_ctl_can_send_pre_hsk; + else + ctl->sc_can_send = send_ctl_can_send; } @@ -1101,8 +1112,6 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl, packet_out->po_packno); #if LSQUIC_CONN_STATS ++ctl->sc_conn_pub->conn_stats->out.acked_via_loss; - LSQ_DEBUG("acking via loss record %"PRIu64, - packet_out->po_packno); #endif } else @@ -1367,13 +1376,8 @@ lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *ctl) } -#ifndef NDEBUG -#if __GNUC__ -__attribute__((weak)) -#endif -#endif -int -lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl) +static int +send_ctl_can_send (struct lsquic_send_ctl *ctl) { const unsigned n_out = send_ctl_all_bytes_out(ctl); LSQ_DEBUG("%s: n_out: %u (unacked_all: %u); cwnd: %"PRIu64, __func__, @@ -1400,6 +1404,37 @@ lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl) } +static int +send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl) +{ + unsigned bytes_in, bytes_out; + + bytes_in = ctl->sc_conn_pub->bytes_in; + bytes_out = ctl->sc_conn_pub->bytes_out + ctl->sc_bytes_scheduled; + if (bytes_out >= bytes_in * 2 + bytes_in / 2 /* This should work out + to around 3 on average */) + { + LSQ_DEBUG("%s: amplification block: %u bytes in, %u bytes out", + __func__, bytes_in, bytes_out); + return 0; + } + else + return send_ctl_can_send(ctl); +} + + +#ifndef NDEBUG +#if __GNUC__ +__attribute__((weak)) +#endif +#endif +int +lsquic_send_ctl_can_send (struct lsquic_send_ctl *ctl) +{ + return ctl->sc_can_send(ctl); +} + + /* Like lsquic_send_ctl_can_send(), but no mods */ static int send_ctl_could_send (const struct lsquic_send_ctl *ctl) @@ -2986,3 +3021,11 @@ lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *ctl) rand = lsquic_crand_get_byte(ctl->sc_enpub->enp_crand); ctl->sc_gap = ctl->sc_cur_packno + 1 + rand; } + + +void +lsquic_send_ctl_path_validated (struct lsquic_send_ctl *ctl) +{ + LSQ_DEBUG("path validated: switch to regular can_send"); + ctl->sc_can_send = send_ctl_can_send; +} diff --git a/src/liblsquic/lsquic_send_ctl.h b/src/liblsquic/lsquic_send_ctl.h index 00c8aa0..4a0ec3d 100644 --- a/src/liblsquic/lsquic_send_ctl.h +++ b/src/liblsquic/lsquic_send_ctl.h @@ -64,6 +64,7 @@ typedef struct lsquic_send_ctl { lsquic_time_t sc_largest_acked_sent_time; lsquic_time_t sc_last_sent_time; lsquic_time_t sc_last_rto_time; + int (*sc_can_send)(struct lsquic_send_ctl *); unsigned sc_bytes_unacked_retx; unsigned sc_bytes_scheduled; union { @@ -383,4 +384,7 @@ lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *); #define lsquic_send_ctl_n_unacked(ctl_) ((ctl_)->sc_n_in_flight_retx) +void +lsquic_send_ctl_path_validated (struct lsquic_send_ctl *); + #endif diff --git a/src/liblsquic/lsquic_tokgen.c b/src/liblsquic/lsquic_tokgen.c index 99bd285..0971293 100644 --- a/src/liblsquic/lsquic_tokgen.c +++ b/src/liblsquic/lsquic_tokgen.c @@ -92,11 +92,18 @@ get_or_generate_state (struct lsquic_engine_public *enpub, time_t now, size_t bufsz; struct { time_t now; - unsigned char buf[20]; - } srst_ikm; + unsigned char buf[24]; + } +#if __GNUC__ + /* This is more of a documentation note: this struct should already + * have a multiple-of-eight size. + */ + __attribute__((packed)) +#endif + srst_ikm; data = shm_state; - sz = sizeof(shm_state); + sz = sizeof(*shm_state); s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &data, &sz); if (s == 1) diff --git a/src/liblsquic/lsquic_trans_params.c b/src/liblsquic/lsquic_trans_params.c index 2f90a51..291c76d 100644 --- a/src/liblsquic/lsquic_trans_params.c +++ b/src/liblsquic/lsquic_trans_params.c @@ -152,6 +152,7 @@ static const uint64_t min_vals[MAX_NUMERIC_TPI + 1] = /* On the other hand, we do enforce the lower bound. */ [TPI_MAX_PACKET_SIZE] = 1200, [TPI_MIN_ACK_DELAY] = 1, + [TPI_ACTIVE_CONNECTION_ID_LIMIT] = 2, }; diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 67a21b0..171dd81 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -67,6 +67,7 @@ SET(TESTS stop_waiting_gquic_be streamgen streamparse + tokgen trapa varint ver_nego diff --git a/test/unittests/test_tokgen.c b/test/unittests/test_tokgen.c new file mode 100644 index 0000000..b1e5e29 --- /dev/null +++ b/test/unittests/test_tokgen.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ +#include +#include +#include +#include + +#include "lsquic.h" +#include "lsquic_types.h" +#include "lsquic_int_types.h" +#include "lsquic_mm.h" +#include "lsquic_tokgen.h" +#include "lsquic_stock_shi.h" +#include "lsquic_engine_public.h" + +int +main (int argc, char **argv) +{ + struct lsquic_engine_public enpub = { + .enp_shi_ctx = lsquic_stock_shared_hash_new(), + .enp_shi = &stock_shi, + }; + struct token_generator *tg; + unsigned char token[16]; + unsigned i; + lsquic_cid_t cid; + + memset(&cid, 0, sizeof(cid)); + cid.len = 8; + + tg = lsquic_tg_new(&enpub); + + lsquic_tg_generate_sreset(tg, &cid, token); + for (i = 0; i < sizeof(token); ++i) + printf("%02X", token[i]); + printf("\n"); + + lsquic_tg_destroy(tg); + + return 0; +}