From a898936218bc279b5d7cdf76d58a25e7a2d419cb Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 15 Jul 2013 15:57:16 -0400 Subject: [PATCH] Add tests for ALPN functionality. Conflicts: ssl/ssltest.c --- apps/s_client.c | 1 + ssl/ssltest.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ test/testssl | 12 ++++ 3 files changed, 174 insertions(+) diff --git a/apps/s_client.c b/apps/s_client.c index b7e4fbb408..61ad29134b 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -1329,6 +1329,7 @@ bad: goto end; } SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); + OPENSSL_free(alpn); } #endif #ifndef OPENSSL_NO_TLSEXT diff --git a/ssl/ssltest.c b/ssl/ssltest.c index 1be60c06a8..cb764395cf 100644 --- a/ssl/ssltest.c +++ b/ssl/ssltest.c @@ -370,6 +370,127 @@ static int verify_npn(SSL *client, SSL *server) } #endif +static const char *alpn_client; +static const char *alpn_server; +static const char *alpn_expected; +static unsigned char *alpn_selected; + +/* next_protos_parse parses a comma separated list of strings into a string + * in a format suitable for passing to SSL_CTX_set_next_protos_advertised. + * outlen: (output) set to the length of the resulting buffer on success. + * err: (maybe NULL) on failure, an error message line is written to this BIO. + * in: a NUL termianted string like "abc,def,ghi" + * + * returns: a malloced buffer or NULL on failure. + */ +static unsigned char *next_protos_parse(unsigned short *outlen, const char *in) + { + size_t len; + unsigned char *out; + size_t i, start = 0; + + len = strlen(in); + if (len >= 65535) + return NULL; + + out = OPENSSL_malloc(strlen(in) + 1); + if (!out) + return NULL; + + for (i = 0; i <= len; ++i) + { + if (i == len || in[i] == ',') + { + if (i - start > 255) + { + OPENSSL_free(out); + return NULL; + } + out[start] = i - start; + start = i + 1; + } + else + out[i+1] = in[i]; + } + + *outlen = len + 1; + return out; + } + +static int cb_server_alpn(SSL *s, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) + { + unsigned char *protos; + unsigned short protos_len; + + protos = next_protos_parse(&protos_len, alpn_server); + if (protos == NULL) + { + fprintf(stderr, "failed to parser ALPN server protocol string: %s\n", alpn_server); + abort(); + } + + if (SSL_select_next_proto((unsigned char**) out, outlen, protos, protos_len, in, inlen) != + OPENSSL_NPN_NEGOTIATED) + { + OPENSSL_free(protos); + return SSL_TLSEXT_ERR_NOACK; + } + + /* Make a copy of the selected protocol which will be freed in verify_alpn. */ + alpn_selected = OPENSSL_malloc(*outlen); + memcpy(alpn_selected, *out, *outlen); + *out = alpn_selected; + + OPENSSL_free(protos); + return SSL_TLSEXT_ERR_OK; + } + +static int verify_alpn(SSL *client, SSL *server) + { + const unsigned char *client_proto, *server_proto; + unsigned int client_proto_len = 0, server_proto_len = 0; + SSL_get0_alpn_selected(client, &client_proto, &client_proto_len); + SSL_get0_alpn_selected(server, &server_proto, &server_proto_len); + + if (alpn_selected != NULL) + { + OPENSSL_free(alpn_selected); + alpn_selected = NULL; + } + + if (client_proto_len != server_proto_len || + memcmp(client_proto, server_proto, client_proto_len) != 0) + { + BIO_printf(bio_stdout, "ALPN selected protocols differ!\n"); + goto err; + } + + if (client_proto_len > 0 && alpn_expected == NULL) + { + BIO_printf(bio_stdout, "ALPN unexpectedly negotiated\n"); + goto err; + } + + if (alpn_expected != NULL && + (client_proto_len != strlen(alpn_expected) || + memcmp(client_proto, alpn_expected, client_proto_len) != 0)) + { + BIO_printf(bio_stdout, "ALPN selected protocols not equal to expected protocol: %s\n", alpn_expected); + goto err; + } + + return 0; + +err: + BIO_printf(bio_stdout, "ALPN results: client: '"); + BIO_write(bio_stdout, client_proto, client_proto_len); + BIO_printf(bio_stdout, "', server: '"); + BIO_write(bio_stdout, server_proto, server_proto_len); + BIO_printf(bio_stdout, "'\n"); + BIO_printf(bio_stdout, "ALPN configured: client: '%s', server: '%s'\n", alpn_client, alpn_server); + return -1; + } + #define SCT_EXT_TYPE 18 /* WARNING : below extension types are *NOT* IETF assigned, and @@ -689,6 +810,9 @@ static void sv_usage(void) fprintf(stderr," -serverinfo_sct - have client offer and expect SCT\n"); fprintf(stderr," -serverinfo_tack - have client offer and expect TACK\n"); fprintf(stderr," -custom_ext - try various custom extension callbacks\n"); + fprintf(stderr," -alpn_client - have client side offer ALPN\n"); + fprintf(stderr," -alpn_server - have server side offer ALPN\n"); + fprintf(stderr," -alpn_expected - the ALPN protocol that should be negotiated\n"); } static void print_details(SSL *c_ssl, const char *prefix) @@ -1118,6 +1242,21 @@ int main(int argc, char *argv[]) { custom_ext = 1; } + else if (strcmp(*argv,"-alpn_client") == 0) + { + if (--argc < 1) goto bad; + alpn_client = *(++argv); + } + else if (strcmp(*argv,"-alpn_server") == 0) + { + if (--argc < 1) goto bad; + alpn_server = *(++argv); + } + else if (strcmp(*argv,"-alpn_expected") == 0) + { + if (--argc < 1) goto bad; + alpn_expected = *(++argv); + } else { fprintf(stderr,"unknown option %s\n",*argv); @@ -1487,6 +1626,23 @@ bad: custom_ext_3_srv_second_cb, NULL); } + if (alpn_server) + SSL_CTX_set_alpn_select_cb(s_ctx, cb_server_alpn, NULL); + + if (alpn_client) + { + unsigned short alpn_len; + unsigned char *alpn = next_protos_parse(&alpn_len, alpn_client); + + if (alpn == NULL) + { + BIO_printf(bio_err, "Error parsing -alpn_client argument\n"); + goto end; + } + SSL_CTX_set_alpn_protos(c_ctx, alpn, alpn_len); + OPENSSL_free(alpn); + } + c_ssl=SSL_new(c_ctx); s_ssl=SSL_new(s_ctx); @@ -1949,6 +2105,11 @@ int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count, ret = 1; goto err; } + if (verify_alpn(c_ssl, s_ssl) < 0) + { + ret = 1; + goto err; + } if (custom_ext_error) { diff --git a/test/testssl b/test/testssl index 71524a0663..6a4efdb0dc 100644 --- a/test/testssl +++ b/test/testssl @@ -195,6 +195,18 @@ $ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct -serverinf $ssltest -bio_pair -tls1 -custom_ext -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1 +############################################################################# +# ALPN tests + +$ssltest -bio_pair -tls1 -alpn_client foo -alpn_server bar || exit 1 +$ssltest -bio_pair -tls1 -alpn_client foo -alpn_server foo -alpn_expected foo || exit 1 +$ssltest -bio_pair -tls1 -alpn_client foo,bar -alpn_server foo -alpn_expected foo || exit 1 +$ssltest -bio_pair -tls1 -alpn_client bar,foo -alpn_server foo -alpn_expected foo || exit 1 +$ssltest -bio_pair -tls1 -alpn_client bar,foo -alpn_server foo,bar -alpn_expected foo || exit 1 +$ssltest -bio_pair -tls1 -alpn_client bar,foo -alpn_server bar,foo -alpn_expected bar || exit 1 +$ssltest -bio_pair -tls1 -alpn_client foo,bar -alpn_server bar,foo -alpn_expected bar || exit 1 +$ssltest -bio_pair -tls1 -alpn_client baz -alpn_server bar,foo || exit 1 + if ../util/shlib_wrap.sh ../apps/openssl no-srp; then echo skipping SRP tests else -- 2.25.1