Commit a8989362 authored by Adam Langley's avatar Adam Langley Committed by Ben Laurie
Browse files

Add tests for ALPN functionality.

Conflicts:
	ssl/ssltest.c
parent a108f841
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1329,6 +1329,7 @@ bad:
			goto end;
			}
		SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len);
		OPENSSL_free(alpn);
		}
#endif
#ifndef OPENSSL_NO_TLSEXT
+161 −0
Original line number Diff line number Diff line
@@ -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 <string> - have client side offer ALPN\n");
	fprintf(stderr," -alpn_server <string> - have server side offer ALPN\n");
	fprintf(stderr," -alpn_expected <string> - 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)
		{
+12 −0
Original line number Diff line number Diff line
@@ -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