From 4e890224ed50302c531c8e7093ab46fed558a35b Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 28 Jun 2024 14:50:37 +0000 Subject: [PATCH] Add more regress coverage for SSL_select_next_proto() --- regress/lib/libssl/unit/ssl_set_alpn_protos.c | 292 +++++++++++++++++- 1 file changed, 291 insertions(+), 1 deletion(-) diff --git a/regress/lib/libssl/unit/ssl_set_alpn_protos.c b/regress/lib/libssl/unit/ssl_set_alpn_protos.c index 87dd4d9e5a0..6f3fcfbc2ae 100644 --- a/regress/lib/libssl/unit/ssl_set_alpn_protos.c +++ b/regress/lib/libssl/unit/ssl_set_alpn_protos.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_set_alpn_protos.c,v 1.2 2022/07/21 03:59:04 tb Exp $ */ +/* $OpenBSD: ssl_set_alpn_protos.c,v 1.3 2024/06/28 14:50:37 tb Exp $ */ /* * Copyright (c) 2022 Theo Buehler * @@ -20,6 +20,21 @@ #include +static void +hexdump(const unsigned char *buf, size_t len) +{ + size_t i; + + if (buf == NULL) { + fprintf(stderr, "(null), len %zu\n", len); + return; + } + for (i = 1; i <= len; i++) + fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n"); + if (len % 8) + fprintf(stderr, "\n"); +} + struct alpn_test { const char *description; const uint8_t protocols[24]; @@ -186,6 +201,279 @@ test_ssl_set_alpn_protos_edge_cases(void) return failed; } +static const struct select_next_proto_test { + const unsigned char *server_list; + size_t server_list_len; + const unsigned char *client_list; + size_t client_list_len; + int want_ret; + const unsigned char *want_out; + unsigned char want_out_len; /* yes, unsigned char */ +} select_next_proto_tests[] = { + { + .server_list = "\x01" "a" "\x01" "b" "\x01" "c", + .server_list_len = 6, + .client_list = "\x01" "a", + .client_list_len = 2, + .want_ret = OPENSSL_NPN_NEGOTIATED, + .want_out = "a", + .want_out_len = 1, + }, + { + .server_list = "\x01" "a" "\x01" "b" "\x01" "c", + .server_list_len = 6, + .client_list = "\x02" "aa" "\x01" "b" "\x01" "c", + .client_list_len = 7, + .want_ret = OPENSSL_NPN_NEGOTIATED, + .want_out = "b", + .want_out_len = 1, + }, + { + /* Use server preference. */ + .server_list = "\x01" "a" "\x01" "b" "\x01" "c", + .server_list_len = 6, + .client_list = "\x01" "c" "\x01" "b" "\x01" "a", + .client_list_len = 6, + .want_ret = OPENSSL_NPN_NEGOTIATED, + .want_out = "a", + .want_out_len = 1, + }, + { + /* Again server preference wins. */ + .server_list = "\x01" "a" "\x03" "bbb" "\x02" "cc", + .server_list_len = 9, + .client_list = "\x01" "z" "\x02" "cc" "\x03" "bbb", + .client_list_len = 9, + .want_ret = OPENSSL_NPN_NEGOTIATED, + .want_out = "bbb", + .want_out_len = 3, + }, + { + /* No overlap fails with first client protocol. */ + .server_list = "\x01" "a" "\x01" "b" "\x01" "c", + .server_list_len = 6, + .client_list = "\x01" "z" "\x01" "y", + .client_list_len = 4, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + .want_out = "z", + .want_out_len = 1, + }, + { + /* + * No server protocols is a misconfiguration, but should fail + * cleanly. + */ + .server_list = "", + .server_list_len = 0, + .client_list = "\x01" "a" "\x01" "b" "\x01" "c", + .client_list_len = 6, + .want_out = "a", + .want_out_len = 1, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * NULL server protocols is a programming error that fails + * cleanly. + */ + .server_list = NULL, + .server_list_len = 0, + .client_list = "\x01" "a" "\x01" "b" "\x01" "c", + .client_list_len = 6, + .want_out = "a", + .want_out_len = 1, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * Malformed server protocols is a misconfiguration, but it + * should fail cleanly. + */ + .server_list = "\x00", + .server_list_len = 1, + .client_list = "\x01" "a" "\x01" "b" "\x01" "c", + .client_list_len = 6, + .want_out = "a", + .want_out_len = 1, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * Malformed server protocols is a misconfiguration, but it + * should fail cleanly. + */ + .server_list = "\x01" "a" "\x03" "bb", + .server_list_len = 5, + .client_list = "\x01" "a" "\x01" "b" "\x01" "c", + .client_list_len = 6, + .want_out = "a", + .want_out_len = 1, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * Empty client protocols is not reachable from the ALPN + * callback. It fails cleanly with NULL protocol and 0 length. + */ + .server_list = "\x01" "a", + .server_list_len = 2, + .client_list = "", + .client_list_len = 0, + .want_out = NULL, + .want_out_len = 0, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * NULL client protocols is not reachable from the ALPN + * callback. It fails cleanly with NULL protocol and 0 length. + */ + .server_list = "\x01" "a", + .server_list_len = 2, + .client_list = NULL, + .client_list_len = 0, + .want_out = NULL, + .want_out_len = 0, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * Malformed client list fails cleanly with NULL protocol and + * 0 length. + */ + .server_list = "\x01" "a", + .server_list_len = 2, + .client_list = "\x01" "a" "\x02" "bb" "\x03" "cc" "\x04" "ddd", + .client_list_len = 12, + .want_out = NULL, + .want_out_len = 0, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* + * Malformed client list fails cleanly with NULL protocol and + * 0 length. + */ + .server_list = "\x01" "a", + .server_list_len = 2, + .client_list = "\x01" "a" "\x02" "bb" "\x00" "\x03" "ddd", + .client_list_len = 10, + .want_out = NULL, + .want_out_len = 0, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + + /* + * Some non-toy examples. + */ + + { + .server_list = "\x08" "http/1.1" "\x06" "spdy/1", + .server_list_len = 16, + .client_list = "\x08" "http/2.0" "\x08" "http/1.1", + .client_list_len = 18, + .want_out = "http/1.1", + .want_out_len = 8, + .want_ret = OPENSSL_NPN_NEGOTIATED, + }, + { + .server_list = "\x08" "http/2.0" "\x06" "spdy/1", + .server_list_len = 16, + .client_list = "\x08" "http/1.0" "\x08" "http/1.1", + .client_list_len = 18, + .want_out = "http/1.0", + .want_out_len = 8, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + .server_list = "\x08" "http/1.1" "\x08" "http/1.0", + .server_list_len = 18, + .client_list = "\x08" "http/1.0" "\x08" "http/1.1", + .client_list_len = 18, + .want_out = "http/1.1", + .want_out_len = 8, + .want_ret = OPENSSL_NPN_NEGOTIATED, + }, + { + /* Server malformed. */ + .server_list = "\x08" "http/1.1" "\x07" "http/1.0", + .server_list_len = 18, + .client_list = "\x08" "http/1.0" "\x08" "http/1.1", + .client_list_len = 18, + .want_out = "http/1.0", + .want_out_len = 8, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* Server malformed. */ + .server_list = "\x07" "http/1.1" "\x08" "http/1.0", + .server_list_len = 18, + .client_list = "\x08" "http/1.0" "\x08" "http/1.1", + .client_list_len = 18, + .want_out = "http/1.0", + .want_out_len = 8, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, + { + /* Client has trailing bytes. */ + .server_list = "\x08" "http/1.1" "\x08" "http/1.0", + .server_list_len = 18, + .client_list = "\x08" "http/1.0" "\x07" "http/1.1", + .client_list_len = 18, + .want_out = NULL, + .want_out_len = 0, + .want_ret = OPENSSL_NPN_NO_OVERLAP, + }, +}; + +#define N_SELECT_NEXT_PROTO_TESTS \ + (sizeof(select_next_proto_tests) / sizeof(select_next_proto_tests[0])) + +static int +select_next_proto_testcase(const struct select_next_proto_test *test) +{ + unsigned char *out; + unsigned char out_len; + int ret; + int failed = 0; + + ret = SSL_select_next_proto(&out, &out_len, test->server_list, + test->server_list_len, test->client_list, test->client_list_len); + + if (ret != test->want_ret || out_len != test->want_out_len || + (out == NULL && test->want_out != NULL) || + (out != NULL && test->want_out == NULL) || + (out != NULL && test->want_out != NULL && + memcmp(out, test->want_out, out_len) != 0)) { + fprintf(stderr, "FAIL: ret: %u (want %u), out_len: %u (want %u)\n", + ret, test->want_ret, out_len, test->want_out_len); + fprintf(stderr, "\ngot:\n"); + hexdump(out, out_len); + fprintf(stderr, "\nwant:\n"); + hexdump(test->want_out, test->want_out_len); + fprintf(stderr, "\nserver:\n"); + hexdump(test->server_list, test->server_list_len); + fprintf(stderr, "\nclient:\n"); + hexdump(test->client_list, test->client_list_len); + fprintf(stderr, "\n"); + failed = 1; + } + + return failed; +} + +static int +test_ssl_select_next_proto(void) +{ + size_t i; + int failed = 0; + + for (i = 0; i < N_SELECT_NEXT_PROTO_TESTS; i++) + failed |= select_next_proto_testcase(&select_next_proto_tests[i]); + + return failed; +} + int main(void) { @@ -197,6 +485,8 @@ main(void) failed |= test_ssl_set_alpn_protos_edge_cases(); + failed |= test_ssl_select_next_proto(); + if (!failed) printf("PASS %s\n", __FILE__); -- 2.20.1