From d2b23cd2b077de8507c49f632e20dfcdb653a35b Mon Sep 17 00:00:00 2001 From: Emilia Kasper Date: Mon, 20 Jun 2016 17:20:25 +0200 Subject: [PATCH] SSL test framework: port SNI tests Observe that the old tests were partly ill-defined: setting sn_server1 but not sn_server2 in ssltest_old.c does not enable the SNI callback. Fix this, and also explicitly test both flavours of SNI mismatch (ignore / fatal alert). Tests still pass. Reviewed-by: Rich Salz --- test/README.ssltest.md | 7 ++ test/handshake_helper.c | 77 ++++++++++-- test/recipes/80-test_ssl_old.t | 21 +--- test/ssl-tests/05-sni.conf | 180 +++++++++++++++++++++++++-- test/ssl-tests/05-sni.conf.in | 57 ++++++++- test/ssl-tests/06-sni-ticket.conf | 32 +++++ test/ssl-tests/06-sni-ticket.conf.in | 3 + test/ssl_test.c | 35 +++--- test/ssl_test_ctx.c | 44 +++++++ test/ssl_test_ctx.h | 24 +++- test/ssl_test_ctx_test.c | 16 +++ test/ssl_test_ctx_test.conf | 6 +- 12 files changed, 441 insertions(+), 61 deletions(-) diff --git a/test/README.ssltest.md b/test/README.ssltest.md index 5797cf6c7d..ea90efcfdc 100644 --- a/test/README.ssltest.md +++ b/test/README.ssltest.md @@ -61,6 +61,7 @@ The test section supports the following options: * ClientVerifyCallback - the client's custom certificate verify callback. Used to test callback behaviour. One of + - None - no custom callback (default) - AcceptAll - accepts all certificates. - RejectAll - rejects all certificates. @@ -70,6 +71,12 @@ The test section supports the following options: - None - do not use SNI (default) - server1 - the initial context - server2 - the secondary context + - invalid - an unknown context + +* ServerNameCallback - the SNI switching callback to use + - None - no callback (default) + - IgnoreMismatch - continue the handshake on SNI mismatch + - RejectMismatch - abort the handshake on SNI mismatch * SessionTicketExpected - whether or not a session ticket is expected - Ignore - do not check for a session ticket (default) diff --git a/test/handshake_helper.c b/test/handshake_helper.c index a60c9c6845..8a8dab02bb 100644 --- a/test/handshake_helper.c +++ b/test/handshake_helper.c @@ -24,6 +24,7 @@ typedef struct handshake_ex_data { int alert_sent; int alert_received; int session_ticket_do_not_call; + ssl_servername_t servername; } HANDSHAKE_EX_DATA; static int ex_data_idx; @@ -41,10 +42,25 @@ static void info_cb(const SSL *s, int where, int ret) } } -static int servername_cb(SSL *s, int *ad, void *arg) +/* + * Select the appropriate server CTX. + * Returns SSL_TLSEXT_ERR_OK if a match was found. + * If |ignore| is 1, returns SSL_TLSEXT_ERR_NOACK on mismatch. + * Otherwise, returns SSL_TLSEXT_ERR_ALERT_FATAL on mismatch. + * An empty SNI extension also returns SSL_TSLEXT_ERR_NOACK. + */ +static int select_server_ctx(SSL *s, void *arg, int ignore) { const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); - if (servername != NULL && !strcmp(servername, "server2")) { + HANDSHAKE_EX_DATA *ex_data = + (HANDSHAKE_EX_DATA*)(SSL_get_ex_data(s, ex_data_idx)); + + if (servername == NULL) { + ex_data->servername = SSL_TEST_SERVERNAME_SERVER1; + return SSL_TLSEXT_ERR_NOACK; + } + + if (strcmp(servername, "server2") == 0) { SSL_CTX *new_ctx = (SSL_CTX*)arg; SSL_set_SSL_CTX(s, new_ctx); /* @@ -54,8 +70,40 @@ static int servername_cb(SSL *s, int *ad, void *arg) */ SSL_clear_options(s, 0xFFFFFFFFL); SSL_set_options(s, SSL_CTX_get_options(new_ctx)); + + ex_data->servername = SSL_TEST_SERVERNAME_SERVER2; + return SSL_TLSEXT_ERR_OK; + } else if (strcmp(servername, "server1") == 0) { + ex_data->servername = SSL_TEST_SERVERNAME_SERVER1; + return SSL_TLSEXT_ERR_OK; + } else if (ignore) { + ex_data->servername = SSL_TEST_SERVERNAME_SERVER1; + return SSL_TLSEXT_ERR_NOACK; + } else { + /* Don't set an explicit alert, to test library defaults. */ + return SSL_TLSEXT_ERR_ALERT_FATAL; } - return SSL_TLSEXT_ERR_OK; +} + +/* + * (RFC 6066): + * If the server understood the ClientHello extension but + * does not recognize the server name, the server SHOULD take one of two + * actions: either abort the handshake by sending a fatal-level + * unrecognized_name(112) alert or continue the handshake. + * + * This behaviour is up to the application to configure; we test both + * configurations to ensure the state machine propagates the result + * correctly. + */ +static int servername_ignore_cb(SSL *s, int *ad, void *arg) +{ + return select_server_ctx(s, arg, 1); +} + +static int servername_reject_cb(SSL *s, int *ad, void *arg) +{ + return select_server_ctx(s, arg, 0); } static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) { @@ -106,14 +154,27 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, } /* link the two contexts for SNI purposes */ - SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_cb); - SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); + switch (test_ctx->servername_callback) { + case SSL_TEST_SERVERNAME_IGNORE_MISMATCH: + SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb); + SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); + break; + case SSL_TEST_SERVERNAME_REJECT_MISMATCH: + SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb); + SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); + break; + default: + break; + } + /* * The initial_ctx/session_ctx always handles the encrypt/decrypt of the * session ticket. This ticket_key callback is assigned to the second * session (assigned via SNI), and should never be invoked */ - SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb); + if (server2_ctx != NULL) + SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, + do_not_call_session_ticket_cb); if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb); @@ -333,9 +394,7 @@ HANDSHAKE_RESULT do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, ret.client_alert_received = server_ex_data.alert_received; ret.server_protocol = SSL_version(server); ret.client_protocol = SSL_version(client); - ret.servername = ((SSL_get_SSL_CTX(server) == server_ctx) - ? SSL_TEST_SERVERNAME_SERVER1 - : SSL_TEST_SERVERNAME_SERVER2); + ret.servername = server_ex_data.servername; if ((sess = SSL_get0_session(client)) != NULL) SSL_SESSION_get0_ticket(sess, &tick, &len); if (tick == NULL || len == 0) diff --git a/test/recipes/80-test_ssl_old.t b/test/recipes/80-test_ssl_old.t index 716f23b38f..becfbae890 100644 --- a/test/recipes/80-test_ssl_old.t +++ b/test/recipes/80-test_ssl_old.t @@ -79,7 +79,7 @@ my $client_sess="client.ss"; # new format in ssl_test.c and add recipes to 80-test_ssl_new.t instead. plan tests => 1 # For testss - + 13 # For the first testssl + + 12 # For the first testssl ; subtest 'test_ss' => sub { @@ -579,25 +579,6 @@ sub testssl { } }; - subtest 'SNI tests' => sub { - - plan tests => 7; - - SKIP: { - skip "TLSv1.x is not supported by this OpenSSL build", 7 - if $no_tls1 && $no_tls1_1 && $no_tls1_2; - - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo"]))); - ok(run(test([@ssltest, "-bio_pair", "-sn_server1", "foo"]))); - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "foo", "-sn_expect1"]))); - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "bar", "-sn_expect1"]))); - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect1"]))); - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "bar", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect2"]))); - # Negative test - make sure it doesn't crash, and doesn't switch contexts - ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foobar", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect1"]))); - } - }; - subtest 'ALPN tests' => sub { ###################################################################### diff --git a/test/ssl-tests/05-sni.conf b/test/ssl-tests/05-sni.conf index be219d519c..ef6db27ca1 100644 --- a/test/ssl-tests/05-sni.conf +++ b/test/ssl-tests/05-sni.conf @@ -1,35 +1,193 @@ # Generated with generate_ssl_tests.pl -num_tests = 1 +num_tests = 6 -test-0 = 0-SNI-default +test-0 = 0-SNI-switch-context +test-1 = 1-SNI-keep-context +test-2 = 2-SNI-no-server-support +test-3 = 3-SNI-no-client-support +test-4 = 4-SNI-bad-sni-ignore-mismatch +test-5 = 5-SNI-bad-sni-reject-mismatch # =========================================================== -[0-SNI-default] -ssl_conf = 0-SNI-default-ssl +[0-SNI-switch-context] +ssl_conf = 0-SNI-switch-context-ssl -[0-SNI-default-ssl] -server = 0-SNI-default-server -server2 = 0-SNI-default-server2 -client = 0-SNI-default-client +[0-SNI-switch-context-ssl] +server = 0-SNI-switch-context-server +server2 = 0-SNI-switch-context-server2 +client = 0-SNI-switch-context-client -[0-SNI-default-server] +[0-SNI-switch-context-server] Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem CipherString = DEFAULT PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem -[0-SNI-default-server2] +[0-SNI-switch-context-server2] Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem CipherString = DEFAULT PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem -[0-SNI-default-client] +[0-SNI-switch-context-client] CipherString = DEFAULT VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer [test-0] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch + + +# =========================================================== + +[1-SNI-keep-context] +ssl_conf = 1-SNI-keep-context-ssl + +[1-SNI-keep-context-ssl] +server = 1-SNI-keep-context-server +server2 = 1-SNI-keep-context-server2 +client = 1-SNI-keep-context-client + +[1-SNI-keep-context-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[1-SNI-keep-context-server2] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[1-SNI-keep-context-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-1] +ExpectedResult = Success +ExpectedServerName = server1 +ServerName = server1 +ServerNameCallback = IgnoreMismatch + + +# =========================================================== + +[2-SNI-no-server-support] +ssl_conf = 2-SNI-no-server-support-ssl + +[2-SNI-no-server-support-ssl] +server = 2-SNI-no-server-support-server +client = 2-SNI-no-server-support-client + +[2-SNI-no-server-support-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[2-SNI-no-server-support-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-2] +ExpectedResult = Success +ServerName = server1 + + +# =========================================================== + +[3-SNI-no-client-support] +ssl_conf = 3-SNI-no-client-support-ssl + +[3-SNI-no-client-support-ssl] +server = 3-SNI-no-client-support-server +server2 = 3-SNI-no-client-support-server2 +client = 3-SNI-no-client-support-client + +[3-SNI-no-client-support-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[3-SNI-no-client-support-server2] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[3-SNI-no-client-support-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-3] +ExpectedResult = Success +ExpectedServerName = server1 +ServerNameCallback = IgnoreMismatch + + +# =========================================================== + +[4-SNI-bad-sni-ignore-mismatch] +ssl_conf = 4-SNI-bad-sni-ignore-mismatch-ssl + +[4-SNI-bad-sni-ignore-mismatch-ssl] +server = 4-SNI-bad-sni-ignore-mismatch-server +server2 = 4-SNI-bad-sni-ignore-mismatch-server2 +client = 4-SNI-bad-sni-ignore-mismatch-client + +[4-SNI-bad-sni-ignore-mismatch-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[4-SNI-bad-sni-ignore-mismatch-server2] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[4-SNI-bad-sni-ignore-mismatch-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-4] +ExpectedResult = Success +ExpectedServerName = server1 +ServerName = invalid +ServerNameCallback = IgnoreMismatch + + +# =========================================================== + +[5-SNI-bad-sni-reject-mismatch] +ssl_conf = 5-SNI-bad-sni-reject-mismatch-ssl + +[5-SNI-bad-sni-reject-mismatch-ssl] +server = 5-SNI-bad-sni-reject-mismatch-server +server2 = 5-SNI-bad-sni-reject-mismatch-server2 +client = 5-SNI-bad-sni-reject-mismatch-client + +[5-SNI-bad-sni-reject-mismatch-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[5-SNI-bad-sni-reject-mismatch-server2] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[5-SNI-bad-sni-reject-mismatch-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-5] +ExpectedResult = ServerFail +ServerAlert = UnrecognizedName +ServerName = invalid +ServerNameCallback = RejectMismatch diff --git a/test/ssl-tests/05-sni.conf.in b/test/ssl-tests/05-sni.conf.in index de8dc77863..635ce9ae47 100644 --- a/test/ssl-tests/05-sni.conf.in +++ b/test/ssl-tests/05-sni.conf.in @@ -16,11 +16,64 @@ package ssltests; our @tests = ( { - name => "SNI-default", + name => "SNI-switch-context", server => { }, server2 => { }, client => { }, test => { "ServerName" => "server2", - "ExpectedResult" => "Success" }, + "ExpectedServerName" => "server2", + "ServerNameCallback" => "IgnoreMismatch", + "ExpectedResult" => "Success" }, + }, + { + name => "SNI-keep-context", + server => { }, + server2 => { }, + client => { }, + test => { "ServerName" => "server1", + "ExpectedServerName" => "server1", + "ServerNameCallback" => "IgnoreMismatch", + "ExpectedResult" => "Success" }, + }, + { + name => "SNI-no-server-support", + server => { }, + client => { }, + test => { "ServerName" => "server1", + "ExpectedResult" => "Success" }, + }, + { + name => "SNI-no-client-support", + server => { }, + server2 => { }, + client => { }, + test => { + # We expect that the callback is still called + # to let the application decide whether they tolerate + # missing SNI (as our test callback does). + "ExpectedServerName" => "server1", + "ServerNameCallback" => "IgnoreMismatch", + "ExpectedResult" => "Success" + }, + }, + { + name => "SNI-bad-sni-ignore-mismatch", + server => { }, + server2 => { }, + client => { }, + test => { "ServerName" => "invalid", + "ExpectedServerName" => "server1", + "ServerNameCallback" => "IgnoreMismatch", + "ExpectedResult" => "Success" }, + }, + { + name => "SNI-bad-sni-reject-mismatch", + server => { }, + server2 => { }, + client => { }, + test => { "ServerName" => "invalid", + "ServerNameCallback" => "RejectMismatch", + "ExpectedResult" => "ServerFail", + "ServerAlert" => "UnrecognizedName"}, }, ); diff --git a/test/ssl-tests/06-sni-ticket.conf b/test/ssl-tests/06-sni-ticket.conf index 99484ed4c8..b3bfda0b91 100644 --- a/test/ssl-tests/06-sni-ticket.conf +++ b/test/ssl-tests/06-sni-ticket.conf @@ -83,7 +83,9 @@ VerifyMode = Peer [test-1] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = Yes @@ -117,7 +119,9 @@ VerifyMode = Peer [test-2] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = Yes @@ -151,7 +155,9 @@ VerifyMode = Peer [test-3] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = Yes @@ -185,7 +191,9 @@ VerifyMode = Peer [test-4] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -219,7 +227,9 @@ VerifyMode = Peer [test-5] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -253,7 +263,9 @@ VerifyMode = Peer [test-6] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -287,7 +299,9 @@ VerifyMode = Peer [test-7] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -321,7 +335,9 @@ VerifyMode = Peer [test-8] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -355,7 +371,9 @@ VerifyMode = Peer [test-9] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -389,7 +407,9 @@ VerifyMode = Peer [test-10] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -423,7 +443,9 @@ VerifyMode = Peer [test-11] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -457,7 +479,9 @@ VerifyMode = Peer [test-12] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -491,7 +515,9 @@ VerifyMode = Peer [test-13] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -525,7 +551,9 @@ VerifyMode = Peer [test-14] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -559,7 +587,9 @@ VerifyMode = Peer [test-15] ExpectedResult = Success +ExpectedServerName = server1 ServerName = server1 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No @@ -593,7 +623,9 @@ VerifyMode = Peer [test-16] ExpectedResult = Success +ExpectedServerName = server2 ServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = No diff --git a/test/ssl-tests/06-sni-ticket.conf.in b/test/ssl-tests/06-sni-ticket.conf.in index 6cd57b61ad..8c95827a52 100644 --- a/test/ssl-tests/06-sni-ticket.conf.in +++ b/test/ssl-tests/06-sni-ticket.conf.in @@ -36,6 +36,9 @@ sub generate_tests() { }, "test" => { "ServerName" => $n, + "ExpectedServerName" => $n, + # We don't test mismatch here. + "ServerNameCallback" => "IgnoreMismatch", "ExpectedResult" => "Success", "SessionTicketExpected" => $result, } diff --git a/test/ssl_test.c b/test/ssl_test.c index 537d4b0e5a..060f73eeba 100644 --- a/test/ssl_test.c +++ b/test/ssl_test.c @@ -125,14 +125,13 @@ static int check_protocol(HANDSHAKE_RESULT result, SSL_TEST_CTX *test_ctx) static int check_servername(HANDSHAKE_RESULT result, SSL_TEST_CTX *test_ctx) { - if (test_ctx->servername != SSL_TEST_SERVERNAME_NONE - && result.servername != test_ctx->servername) { - fprintf(stderr, "Client ServerName mismatch, expected %s, got %s\n.", - ssl_servername_name(test_ctx->servername), - ssl_servername_name(result.servername)); - return 0; + if (result.servername != test_ctx->expected_servername) { + fprintf(stderr, "Client ServerName mismatch, expected %s, got %s\n.", + ssl_servername_name(test_ctx->expected_servername), + ssl_servername_name(result.servername)); + return 0; } - return 1; + return 1; } static int check_session_ticket(HANDSHAKE_RESULT result, SSL_TEST_CTX *test_ctx) @@ -176,40 +175,42 @@ static int execute_test(SSL_TEST_FIXTURE fixture) SSL_CTX *server_ctx = NULL, *server2_ctx = NULL, *client_ctx = NULL; SSL_TEST_CTX *test_ctx = NULL; HANDSHAKE_RESULT result; - const char *server2; test_ctx = SSL_TEST_CTX_create(conf, fixture.test_app); if (test_ctx == NULL) goto err; - /* Use ServerName to detect if we're testing SNI. */ - server2 = (test_ctx->servername != SSL_TEST_SERVERNAME_NONE) ? "server2" - : "server"; - #ifndef OPENSSL_NO_DTLS if (test_ctx->method == SSL_TEST_METHOD_DTLS) { server_ctx = SSL_CTX_new(DTLS_server_method()); - server2_ctx = SSL_CTX_new(DTLS_server_method()); + if (test_ctx->servername_callback != SSL_TEST_SERVERNAME_CB_NONE) { + server2_ctx = SSL_CTX_new(DTLS_server_method()); + OPENSSL_assert(server2_ctx != NULL); + } client_ctx = SSL_CTX_new(DTLS_client_method()); } #endif if (test_ctx->method == SSL_TEST_METHOD_TLS) { server_ctx = SSL_CTX_new(TLS_server_method()); - server2_ctx = SSL_CTX_new(TLS_server_method()); + if (test_ctx->servername_callback != SSL_TEST_SERVERNAME_CB_NONE) { + server2_ctx = SSL_CTX_new(TLS_server_method()); + OPENSSL_assert(server2_ctx != NULL); + } client_ctx = SSL_CTX_new(TLS_client_method()); } - OPENSSL_assert(server_ctx != NULL && server2_ctx != NULL && - client_ctx != NULL); + OPENSSL_assert(server_ctx != NULL && client_ctx != NULL); OPENSSL_assert(CONF_modules_load(conf, fixture.test_app, 0) > 0); if (!SSL_CTX_config(server_ctx, "server") - || !SSL_CTX_config(server2_ctx, server2) || !SSL_CTX_config(client_ctx, "client")) { goto err; } + if (server2_ctx != NULL && !SSL_CTX_config(server2_ctx, "server2")) + goto err; + result = do_handshake(server_ctx, server2_ctx, client_ctx, test_ctx); ret = check_test(result, test_ctx); diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c index d6e2843896..b06ab4828c 100644 --- a/test/ssl_test_ctx.c +++ b/test/ssl_test_ctx.c @@ -82,6 +82,7 @@ const char *ssl_test_result_name(ssl_test_result_t result) static const test_enum ssl_alerts[] = { {"UnknownCA", SSL_AD_UNKNOWN_CA}, {"HandshakeFailure", SSL_AD_HANDSHAKE_FAILURE}, + {"UnrecognizedName", SSL_AD_UNRECOGNIZED_NAME}, }; __owur static int parse_alert(int *alert, const char *value) @@ -164,6 +165,7 @@ static const test_enum ssl_servername[] = { {"None", SSL_TEST_SERVERNAME_NONE}, {"server1", SSL_TEST_SERVERNAME_SERVER1}, {"server2", SSL_TEST_SERVERNAME_SERVER2}, + {"invalid", SSL_TEST_SERVERNAME_INVALID}, }; __owur static int parse_servername(SSL_TEST_CTX *test_ctx, @@ -178,12 +180,52 @@ __owur static int parse_servername(SSL_TEST_CTX *test_ctx, return 1; } +__owur static int parse_expected_servername(SSL_TEST_CTX *test_ctx, + const char *value) +{ + int ret_value; + if (!parse_enum(ssl_servername, OSSL_NELEM(ssl_servername), + &ret_value, value)) { + return 0; + } + test_ctx->expected_servername = ret_value; + return 1; +} + const char *ssl_servername_name(ssl_servername_t server) { return enum_name(ssl_servername, OSSL_NELEM(ssl_servername), server); } +/***********************/ +/* ServerNameCallback. */ +/***********************/ + +static const test_enum ssl_servername_callbacks[] = { + {"None", SSL_TEST_SERVERNAME_CB_NONE}, + {"IgnoreMismatch", SSL_TEST_SERVERNAME_IGNORE_MISMATCH}, + {"RejectMismatch", SSL_TEST_SERVERNAME_REJECT_MISMATCH}, +}; + +__owur static int parse_servername_callback(SSL_TEST_CTX *test_ctx, + const char *value) +{ + int ret_value; + if (!parse_enum(ssl_servername_callbacks, + OSSL_NELEM(ssl_servername_callbacks), &ret_value, value)) { + return 0; + } + test_ctx->servername_callback = ret_value; + return 1; +} + +const char *ssl_servername_callback_name(ssl_servername_callback_t callback) +{ + return enum_name(ssl_servername_callbacks, + OSSL_NELEM(ssl_servername_callbacks), callback); +} + /*************************/ /* SessionTicketExpected */ /*************************/ @@ -254,6 +296,8 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = { { "Protocol", &parse_protocol }, { "ClientVerifyCallback", &parse_client_verify_callback }, { "ServerName", &parse_servername }, + { "ExpectedServerName", &parse_expected_servername }, + { "ServerNameCallback", &parse_servername_callback }, { "SessionTicketExpected", &parse_session_ticket }, { "Method", &parse_test_method }, }; diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h index 492d1d7aa8..c551a9baa7 100644 --- a/test/ssl_test_ctx.h +++ b/test/ssl_test_ctx.h @@ -29,9 +29,17 @@ typedef enum { typedef enum { SSL_TEST_SERVERNAME_NONE = 0, /* Default */ SSL_TEST_SERVERNAME_SERVER1, - SSL_TEST_SERVERNAME_SERVER2 + SSL_TEST_SERVERNAME_SERVER2, + SSL_TEST_SERVERNAME_INVALID } ssl_servername_t; +typedef enum { + SSL_TEST_SERVERNAME_CB_NONE = 0, /* Default */ + SSL_TEST_SERVERNAME_IGNORE_MISMATCH, + SSL_TEST_SERVERNAME_REJECT_MISMATCH +} ssl_servername_callback_t; + + typedef enum { SSL_TEST_SESSION_TICKET_IGNORE = 0, /* Default */ SSL_TEST_SESSION_TICKET_YES, @@ -61,6 +69,18 @@ typedef struct ssl_test_ctx { ssl_verify_callback_t client_verify_callback; /* One of a number of predefined server names use by the client */ ssl_servername_t servername; + /* + * The expected SNI context to use. + * We test server-side that the server switched to the expected context. + * Set by the callback upon success, so if the callback wasn't called or + * terminated with an alert, the servername will match with + * SSL_TEST_SERVERNAME_NONE. + * Note: in the event that the servername was accepted, the client should + * also receive an empty SNI extension back but we have no way of probing + * client-side via the API that this was the case. + */ + ssl_servername_t expected_servername; + ssl_servername_callback_t servername_callback; ssl_session_ticket_t session_ticket_expected; /* Whether the server/client CTX should use DTLS or TLS. */ ssl_test_method_t method; @@ -71,6 +91,8 @@ const char *ssl_alert_name(int alert); const char *ssl_protocol_name(int protocol); const char *ssl_verify_callback_name(ssl_verify_callback_t verify_callback); const char *ssl_servername_name(ssl_servername_t server); +const char *ssl_servername_callback_name(ssl_servername_callback_t + servername_callback); const char *ssl_session_ticket_name(ssl_session_ticket_t server); const char *ssl_test_method_name(ssl_test_method_t method); diff --git a/test/ssl_test_ctx_test.c b/test/ssl_test_ctx_test.c index 9824d66956..3818ba5de8 100644 --- a/test/ssl_test_ctx_test.c +++ b/test/ssl_test_ctx_test.c @@ -70,6 +70,18 @@ static int SSL_TEST_CTX_equal(SSL_TEST_CTX *ctx, SSL_TEST_CTX *ctx2) ssl_servername_name(ctx2->servername)); return 0; } + if (ctx->expected_servername != ctx2->expected_servername) { + fprintf(stderr, "ExpectedServerName mismatch: %s vs %s.\n", + ssl_servername_name(ctx->expected_servername), + ssl_servername_name(ctx2->expected_servername)); + return 0; + } + if (ctx->servername_callback != ctx2->servername_callback) { + fprintf(stderr, "ServerNameCallback mismatch: %s vs %s.\n", + ssl_servername_callback_name(ctx->servername_callback), + ssl_servername_callback_name(ctx2->servername_callback)); + return 0; + } if (ctx->session_ticket_expected != ctx2->session_ticket_expected) { fprintf(stderr, "SessionTicketExpected mismatch: %s vs %s.\n", ssl_session_ticket_name(ctx->session_ticket_expected), @@ -155,6 +167,9 @@ static int test_good_configuration() fixture.expected_ctx->protocol = TLS1_1_VERSION; fixture.expected_ctx->client_verify_callback = SSL_TEST_VERIFY_REJECT_ALL; fixture.expected_ctx->servername = SSL_TEST_SERVERNAME_SERVER2; + fixture.expected_ctx->expected_servername = SSL_TEST_SERVERNAME_SERVER2; + fixture.expected_ctx->servername_callback = + SSL_TEST_SERVERNAME_IGNORE_MISMATCH; fixture.expected_ctx->session_ticket_expected = SSL_TEST_SESSION_TICKET_YES; fixture.expected_ctx->method = SSL_TEST_METHOD_DTLS; EXECUTE_SSL_TEST_CTX_TEST(); @@ -167,6 +182,7 @@ static const char *bad_configurations[] = { "ssltest_unknown_protocol", "ssltest_unknown_verify_callback", "ssltest_unknown_servername", + "ssltest_unknown_servername_callback", "ssltest_unknown_session_ticket_expected", "ssltest_unknown_method", }; diff --git a/test/ssl_test_ctx_test.conf b/test/ssl_test_ctx_test.conf index 9c1057d23b..2fa54b58d9 100644 --- a/test/ssl_test_ctx_test.conf +++ b/test/ssl_test_ctx_test.conf @@ -6,6 +6,8 @@ ClientAlert = UnknownCA Protocol = TLSv1.1 ClientVerifyCallback = RejectAll ServerName = server2 +ExpectedServerName = server2 +ServerNameCallback = IgnoreMismatch SessionTicketExpected = Yes Method = DTLS @@ -27,9 +29,11 @@ ClientVerifyCallback = Foo [ssltest_unknown_servername] ServerName = Foo +[ssltest_unknown_servername_callback] +ServerNameCallback = Foo + [ssltest_unknown_session_ticket_expected] SessionTicketExpected = Foo [ssltest_unknown_method] Method = TLS2 - -- 2.25.1