From 4d875aa977b8bbe9969527101d0f2d7d67a77b0b Mon Sep 17 00:00:00 2001 From: doug <> Date: Sat, 26 Aug 2017 20:23:46 +0000 Subject: Rewrite ALPN extension using CBB/CBS and the new extension framework. ok bcook@ beck@ input + ok jsing@ --- src/lib/libssl/ssl_locl.h | 4 +- src/lib/libssl/ssl_tlsext.c | 149 ++++++++++- src/lib/libssl/ssl_tlsext.h | 9 +- src/lib/libssl/t1_lib.c | 140 +---------- src/regress/lib/libssl/tlsext/tlsexttest.c | 382 ++++++++++++++++++++++++++++- 5 files changed, 540 insertions(+), 144 deletions(-) diff --git a/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h index ddb3b30327..624df9c929 100644 --- a/src/lib/libssl/ssl_locl.h +++ b/src/lib/libssl/ssl_locl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_locl.h,v 1.190 2017/08/12 21:47:59 jsing Exp $ */ +/* $OpenBSD: ssl_locl.h,v 1.191 2017/08/26 20:23:46 doug Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -864,7 +864,7 @@ typedef struct ssl3_state_internal_st { * processed. */ unsigned char *alpn_selected; - unsigned int alpn_selected_len; + size_t alpn_selected_len; } SSL3_STATE_INTERNAL; #define S3I(s) (s->s3->internal) diff --git a/src/lib/libssl/ssl_tlsext.c b/src/lib/libssl/ssl_tlsext.c index 60daff6f8d..405882c0e9 100644 --- a/src/lib/libssl/ssl_tlsext.c +++ b/src/lib/libssl/ssl_tlsext.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.c,v 1.10 2017/08/23 15:39:38 doug Exp $ */ +/* $OpenBSD: ssl_tlsext.c,v 1.11 2017/08/26 20:23:46 doug Exp $ */ /* * Copyright (c) 2016, 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -23,6 +23,144 @@ #include "bytestring.h" #include "ssl_tlsext.h" +/* + * Supported Application-Layer Protocol Negotiation - RFC 7301 + */ + +int +tlsext_alpn_clienthello_needs(SSL *s) +{ + /* ALPN protos have been specified and this is the initial handshake */ + return s->internal->alpn_client_proto_list != NULL && + S3I(s)->tmp.finish_md_len == 0; +} + +int +tlsext_alpn_clienthello_build(SSL *s, CBB *cbb) +{ + CBB protolist; + + if (!CBB_add_u16_length_prefixed(cbb, &protolist)) + return 0; + + if (!CBB_add_bytes(&protolist, s->internal->alpn_client_proto_list, + s->internal->alpn_client_proto_list_len)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_alpn_clienthello_parse(SSL *s, CBS *cbs, int *alert) +{ + CBS proto_name_list, alpn; + const unsigned char *selected; + unsigned char selected_len; + int r; + + if (s->ctx->internal->alpn_select_cb == NULL) + return 1; + + if (!CBS_get_u16_length_prefixed(cbs, &alpn)) + goto err; + if (CBS_len(&alpn) < 2) + goto err; + if (CBS_len(cbs) != 0) + goto err; + + CBS_dup(&alpn, &proto_name_list); + while (CBS_len(&proto_name_list) > 0) { + CBS proto_name; + + if (!CBS_get_u8_length_prefixed(&proto_name_list, &proto_name)) + goto err; + if (CBS_len(&proto_name) == 0) + goto err; + } + + r = s->ctx->internal->alpn_select_cb(s, &selected, &selected_len, + CBS_data(&alpn), CBS_len(&alpn), + s->ctx->internal->alpn_select_cb_arg); + if (r == SSL_TLSEXT_ERR_OK) { + free(S3I(s)->alpn_selected); + if ((S3I(s)->alpn_selected = malloc(selected_len)) == NULL) { + *alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + memcpy(S3I(s)->alpn_selected, selected, selected_len); + S3I(s)->alpn_selected_len = selected_len; + } + + return 1; + + err: + *alert = SSL_AD_DECODE_ERROR; + return 0; +} + +int +tlsext_alpn_serverhello_needs(SSL *s) +{ + return S3I(s)->alpn_selected != NULL; +} + +int +tlsext_alpn_serverhello_build(SSL *s, CBB *cbb) +{ + CBB list, selected; + + if (!CBB_add_u16_length_prefixed(cbb, &list)) + return 0; + + if (!CBB_add_u8_length_prefixed(&list, &selected)) + return 0; + + if (!CBB_add_bytes(&selected, S3I(s)->alpn_selected, + S3I(s)->alpn_selected_len)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_alpn_serverhello_parse(SSL *s, CBS *cbs, int *alert) +{ + CBS list, proto; + + if (s->internal->alpn_client_proto_list == NULL) { + *alert = TLS1_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + if (!CBS_get_u16_length_prefixed(cbs, &list)) + goto err; + if (CBS_len(cbs) != 0) + goto err; + + if (!CBS_get_u8_length_prefixed(&list, &proto)) + goto err; + + if (CBS_len(&list) != 0) + goto err; + if (CBS_len(&proto) == 0) + goto err; + + if (!CBS_stow(&proto, &(S3I(s)->alpn_selected), + &(S3I(s)->alpn_selected_len))) + goto err; + + return 1; + + err: + *alert = TLS1_AD_DECODE_ERROR; + return 0; +} /* * Supported Elliptic Curves - RFC 4492 section 5.1.1 @@ -919,6 +1057,15 @@ static struct tls_extension tls_extensions[] = { .serverhello_build = tlsext_sigalgs_serverhello_build, .serverhello_parse = tlsext_sigalgs_serverhello_parse, }, + { + .type = TLSEXT_TYPE_application_layer_protocol_negotiation, + .clienthello_needs = tlsext_alpn_clienthello_needs, + .clienthello_build = tlsext_alpn_clienthello_build, + .clienthello_parse = tlsext_alpn_clienthello_parse, + .serverhello_needs = tlsext_alpn_serverhello_needs, + .serverhello_build = tlsext_alpn_serverhello_build, + .serverhello_parse = tlsext_alpn_serverhello_parse, + }, }; #define N_TLS_EXTENSIONS (sizeof(tls_extensions) / sizeof(*tls_extensions)) diff --git a/src/lib/libssl/ssl_tlsext.h b/src/lib/libssl/ssl_tlsext.h index bba8bdbea9..21f9bb1bf9 100644 --- a/src/lib/libssl/ssl_tlsext.h +++ b/src/lib/libssl/ssl_tlsext.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.h,v 1.8 2017/08/12 23:38:12 beck Exp $ */ +/* $OpenBSD: ssl_tlsext.h,v 1.9 2017/08/26 20:23:46 doug Exp $ */ /* * Copyright (c) 2016, 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -16,6 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +int tlsext_alpn_clienthello_needs(SSL *s); +int tlsext_alpn_clienthello_build(SSL *s, CBB *cbb); +int tlsext_alpn_clienthello_parse(SSL *s, CBS *cbs, int *alert); +int tlsext_alpn_serverhello_needs(SSL *s); +int tlsext_alpn_serverhello_build(SSL *s, CBB *cbb); +int tlsext_alpn_serverhello_parse(SSL *s, CBS *cbs, int *alert); + int tlsext_ri_clienthello_needs(SSL *s); int tlsext_ri_clienthello_build(SSL *s, CBB *cbb); int tlsext_ri_clienthello_parse(SSL *s, CBS *cbs, int *alert); diff --git a/src/lib/libssl/t1_lib.c b/src/lib/libssl/t1_lib.c index d71067d73c..eb1d96cc11 100644 --- a/src/lib/libssl/t1_lib.c +++ b/src/lib/libssl/t1_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: t1_lib.c,v 1.134 2017/08/13 21:10:42 bcook Exp $ */ +/* $OpenBSD: t1_lib.c,v 1.135 2017/08/26 20:23:46 doug Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -687,19 +687,6 @@ ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) return NULL; ret += len; - if (s->internal->alpn_client_proto_list != NULL && - S3I(s)->tmp.finish_md_len == 0) { - if ((size_t)(limit - ret) < - 6 + s->internal->alpn_client_proto_list_len) - return (NULL); - s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); - s2n(2 + s->internal->alpn_client_proto_list_len, ret); - s2n(s->internal->alpn_client_proto_list_len, ret); - memcpy(ret, s->internal->alpn_client_proto_list, - s->internal->alpn_client_proto_list_len); - ret += s->internal->alpn_client_proto_list_len; - } - #ifndef OPENSSL_NO_SRTP if (SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s)) { int el; @@ -778,20 +765,6 @@ ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) } #endif - if (S3I(s)->alpn_selected != NULL) { - const unsigned char *selected = S3I(s)->alpn_selected; - unsigned int len = S3I(s)->alpn_selected_len; - - if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) - return (NULL); - s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); - s2n(3 + len, ret); - s2n(1 + len, ret); - *ret++ = len; - memcpy(ret, selected, len); - ret += len; - } - if ((extdatalen = ret - p - 2) == 0) return p; @@ -799,71 +772,6 @@ ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) return ret; } -/* - * tls1_alpn_handle_client_hello is called to process the ALPN extension in a - * ClientHello. - * data: the contents of the extension, not including the type and length. - * data_len: the number of bytes in data. - * al: a pointer to the alert value to send in the event of a non-zero - * return. - * returns: 1 on success. - */ -static int -tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, - unsigned int data_len, int *al) -{ - CBS cbs, proto_name_list, alpn; - const unsigned char *selected; - unsigned char selected_len; - int r; - - if (s->ctx->internal->alpn_select_cb == NULL) - return (1); - - if (data_len < 2) - goto parse_error; - - CBS_init(&cbs, data, data_len); - - /* - * data should contain a uint16 length followed by a series of 8-bit, - * length-prefixed strings. - */ - if (!CBS_get_u16_length_prefixed(&cbs, &alpn) || - CBS_len(&alpn) < 2 || - CBS_len(&cbs) != 0) - goto parse_error; - - /* Validate data before sending to callback. */ - CBS_dup(&alpn, &proto_name_list); - while (CBS_len(&proto_name_list) > 0) { - CBS proto_name; - - if (!CBS_get_u8_length_prefixed(&proto_name_list, &proto_name) || - CBS_len(&proto_name) == 0) - goto parse_error; - } - - r = s->ctx->internal->alpn_select_cb(s, &selected, &selected_len, - CBS_data(&alpn), CBS_len(&alpn), - s->ctx->internal->alpn_select_cb_arg); - if (r == SSL_TLSEXT_ERR_OK) { - free(S3I(s)->alpn_selected); - if ((S3I(s)->alpn_selected = malloc(selected_len)) == NULL) { - *al = SSL_AD_INTERNAL_ERROR; - return (-1); - } - memcpy(S3I(s)->alpn_selected, selected, selected_len); - S3I(s)->alpn_selected_len = selected_len; - } - - return (1); - -parse_error: - *al = SSL_AD_DECODE_ERROR; - return (0); -} - int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) @@ -907,14 +815,6 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, if (!tlsext_clienthello_parse_one(s, &cbs, type, al)) return 0; - if (type == - TLSEXT_TYPE_application_layer_protocol_negotiation && - s->ctx->internal->alpn_select_cb != NULL && - S3I(s)->tmp.finish_md_len == 0) { - if (tls1_alpn_handle_client_hello(s, data, - size, al) != 1) - return (0); - } /* session ticket processed earlier */ #ifndef OPENSSL_NO_SRTP else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_use_srtp) { @@ -988,44 +888,6 @@ ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, size_t n, int *al) if (!tlsext_serverhello_parse_one(s, &cbs, type, al)) return 0; - if (type == TLSEXT_TYPE_application_layer_protocol_negotiation) { - unsigned int len; - - /* We must have requested it. */ - if (s->internal->alpn_client_proto_list == NULL) { - *al = TLS1_AD_UNSUPPORTED_EXTENSION; - return 0; - } - if (size < 4) { - *al = TLS1_AD_DECODE_ERROR; - return (0); - } - - /* The extension data consists of: - * uint16 list_length - * uint8 proto_length; - * uint8 proto[proto_length]; */ - len = ((unsigned int)data[0]) << 8 | - ((unsigned int)data[1]); - if (len != (unsigned int)size - 2) { - *al = TLS1_AD_DECODE_ERROR; - return (0); - } - len = data[2]; - if (len != (unsigned int)size - 3) { - *al = TLS1_AD_DECODE_ERROR; - return (0); - } - free(S3I(s)->alpn_selected); - S3I(s)->alpn_selected = malloc(len); - if (S3I(s)->alpn_selected == NULL) { - *al = TLS1_AD_INTERNAL_ERROR; - return (0); - } - memcpy(S3I(s)->alpn_selected, data + 3, len); - S3I(s)->alpn_selected_len = len; - - } #ifndef OPENSSL_NO_SRTP else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_use_srtp) { if (ssl_parse_serverhello_use_srtp_ext(s, data, diff --git a/src/regress/lib/libssl/tlsext/tlsexttest.c b/src/regress/lib/libssl/tlsext/tlsexttest.c index 950588ba47..30bbed9d8d 100644 --- a/src/regress/lib/libssl/tlsext/tlsexttest.c +++ b/src/regress/lib/libssl/tlsext/tlsexttest.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tlsexttest.c,v 1.12 2017/08/12 23:39:24 beck Exp $ */ +/* $OpenBSD: tlsexttest.c,v 1.13 2017/08/26 20:23:46 doug Exp $ */ /* * Copyright (c) 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -73,6 +73,383 @@ do { \ fprintf(stderr, msg, ##__VA_ARGS__); \ } while(0) +/* + * Supported Application-Layer Protocol Negotiation - RFC 7301 + * + * There are already extensive unit tests for this so this just + * tests the state info. + */ + +const uint8_t tlsext_alpn_multiple_protos_val[] = { + /* opaque ProtocolName<1..2^8-1> -- 'http/1.1' */ + 0x08, /* len */ + 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, + /* opaque ProtocolName<1..2^8-1> -- 'stun.nat' */ + 0x09, /* len */ + 0x73, 0x74, 0x75, 0x6e, 0x2e, 0x74, 0x75, 0x72, 0x6e +}; + +const uint8_t tlsext_alpn_multiple_protos[] = { + /* ProtocolName protocol_name_list<2..2^16-1> -- ALPN names */ + 0x00, 0x13, /* len of all names */ + /* opaque ProtocolName<1..2^8-1> -- 'http/1.1' */ + 0x08, /* len */ + 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, + /* opaque ProtocolName<1..2^8-1> -- 'stun.nat' */ + 0x09, /* len */ + 0x73, 0x74, 0x75, 0x6e, 0x2e, 0x74, 0x75, 0x72, 0x6e +}; + +const uint8_t tlsext_alpn_single_proto_val[] = { + /* opaque ProtocolName<1..2^8-1> -- 'http/1.1' */ + 0x08, /* len */ + 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 +}; + +const uint8_t tlsext_alpn_single_proto_name[] = { + 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 /* 'http/1.1' */ +}; + +const uint8_t tlsext_alpn_single_proto[] = { + /* ProtocolName protocol_name_list<2..2^16-1> -- ALPN names */ + 0x00, 0x09, /* len of all names */ + /* opaque ProtocolName<1..2^8-1> -- 'http/1.1' */ + 0x08, /* len */ + 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 +}; + +static int +test_tlsext_alpn_clienthello(void) +{ + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + uint8_t *data = NULL; + CBB cbb; + CBS cbs; + int failure, alert; + size_t dlen; + + CBB_init(&cbb, 0); + + failure = 1; + + if ((ssl_ctx = SSL_CTX_new(TLS_client_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + /* By default, we don't need this */ + if (tlsext_alpn_clienthello_needs(ssl)) { + FAIL("clienthello should not need ALPN by default"); + goto err; + } + + /* + * Prereqs: + * 1) Set s->internal->alpn_client_proto_list + * - Using SSL_set_alpn_protos() + * 2) We have not finished or renegotiated. + * - S3I(s)->tmp.finish_md_len == 0 + */ + if (SSL_set_alpn_protos(ssl, tlsext_alpn_single_proto_val, + sizeof(tlsext_alpn_single_proto_val)) != 0) { + FAIL("should be able to set ALPN to http/1.1"); + goto err; + } + if (!tlsext_alpn_clienthello_needs(ssl)) { + FAIL("clienthello should need ALPN by now"); + goto err; + } + + /* Make sure we can build the clienthello with a single proto. */ + + if (!tlsext_alpn_clienthello_build(ssl, &cbb)) { + FAIL("clienthello failed to build ALPN\n"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_alpn_single_proto)) { + FAIL("got clienthello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_single_proto)); + compare_data(data, dlen, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + goto err; + } + if (memcmp(data, tlsext_alpn_single_proto, dlen) != 0) { + FAIL("clienthello ALPN differs:\n"); + compare_data(data, dlen, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + goto err; + } + + CBB_cleanup(&cbb); + CBB_init(&cbb, 0); + free(data); + data = NULL; + + /* Make sure we can parse the single proto. */ + + CBS_init(&cbs, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + if (!tlsext_alpn_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse ALPN"); + goto err; + } + + if (ssl->internal->alpn_client_proto_list_len != + sizeof(tlsext_alpn_single_proto_val)) { + FAIL("got clienthello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_single_proto_val)); + compare_data(ssl->internal->alpn_client_proto_list, + ssl->internal->alpn_client_proto_list_len, + tlsext_alpn_single_proto_val, + sizeof(tlsext_alpn_single_proto_val)); + goto err; + } + if (memcmp(ssl->internal->alpn_client_proto_list, + tlsext_alpn_single_proto_val, + sizeof(tlsext_alpn_single_proto_val)) != 0) { + FAIL("clienthello ALPN differs:\n"); + compare_data(data, dlen, tlsext_alpn_single_proto_val, + sizeof(tlsext_alpn_single_proto_val)); + goto err; + } + + /* Make sure we can build the clienthello with multiple entries. */ + + if (SSL_set_alpn_protos(ssl, tlsext_alpn_multiple_protos_val, + sizeof(tlsext_alpn_multiple_protos_val)) != 0) { + FAIL("should be able to set ALPN to http/1.1"); + goto err; + } + if (!tlsext_alpn_clienthello_needs(ssl)) { + FAIL("clienthello should need ALPN by now"); + goto err; + } + + if (!tlsext_alpn_clienthello_build(ssl, &cbb)) { + FAIL("clienthello failed to build ALPN\n"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_alpn_multiple_protos)) { + FAIL("got clienthello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_multiple_protos)); + compare_data(data, dlen, tlsext_alpn_multiple_protos, + sizeof(tlsext_alpn_multiple_protos)); + goto err; + } + if (memcmp(data, tlsext_alpn_multiple_protos, dlen) != 0) { + FAIL("clienthello ALPN differs:\n"); + compare_data(data, dlen, tlsext_alpn_multiple_protos, + sizeof(tlsext_alpn_multiple_protos)); + goto err; + } + + /* Make sure we can parse multiple protos */ + + CBS_init(&cbs, tlsext_alpn_multiple_protos, + sizeof(tlsext_alpn_multiple_protos)); + if (!tlsext_alpn_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse ALPN"); + goto err; + } + + if (ssl->internal->alpn_client_proto_list_len != + sizeof(tlsext_alpn_multiple_protos_val)) { + FAIL("got clienthello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_multiple_protos_val)); + compare_data(ssl->internal->alpn_client_proto_list, + ssl->internal->alpn_client_proto_list_len, + tlsext_alpn_multiple_protos_val, + sizeof(tlsext_alpn_multiple_protos_val)); + goto err; + } + if (memcmp(ssl->internal->alpn_client_proto_list, + tlsext_alpn_multiple_protos_val, + sizeof(tlsext_alpn_multiple_protos_val)) != 0) { + FAIL("clienthello ALPN differs:\n"); + compare_data(data, dlen, tlsext_alpn_multiple_protos_val, + sizeof(tlsext_alpn_multiple_protos_val)); + goto err; + } + + /* Make sure we can remove the list and avoid ALPN */ + + free(ssl->internal->alpn_client_proto_list); + ssl->internal->alpn_client_proto_list = NULL; + ssl->internal->alpn_client_proto_list_len = 0; + + if (tlsext_alpn_clienthello_needs(ssl)) { + FAIL("clienthello should need ALPN by default"); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); +} + +static int +test_tlsext_alpn_serverhello(void) +{ + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + uint8_t *data = NULL; + CBB cbb; + CBS cbs; + int failure, alert; + size_t dlen; + + CBB_init(&cbb, 0); + + failure = 1; + + if ((ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + /* By default, ALPN isn't needed. */ + if (tlsext_alpn_serverhello_needs(ssl)) { + FAIL("serverhello should not need ALPN by default\n"); + goto err; + } + + /* + * The server has a single ALPN selection which is set by + * SSL_CTX_set_alpn_select_cb() and calls SSL_select_next_proto(). + * + * This will be a plain name and separate length. + */ + if ((S3I(ssl)->alpn_selected = malloc(sizeof(tlsext_alpn_single_proto_name))) == NULL) { + errx(1, "failed to malloc"); + } + memcpy(S3I(ssl)->alpn_selected, tlsext_alpn_single_proto_name, + sizeof(tlsext_alpn_single_proto_name)); + S3I(ssl)->alpn_selected_len = sizeof(tlsext_alpn_single_proto_name); + + if (!tlsext_alpn_serverhello_needs(ssl)) { + FAIL("serverhello should need ALPN after a protocol is selected\n"); + goto err; + } + + /* Make sure we can build a serverhello with one protocol */ + + if (!tlsext_alpn_serverhello_build(ssl, &cbb)) { + FAIL("serverhello should be able to build a response"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_alpn_single_proto)) { + FAIL("got clienthello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_single_proto)); + compare_data(data, dlen, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + goto err; + } + if (memcmp(data, tlsext_alpn_single_proto, dlen) != 0) { + FAIL("clienthello ALPN differs:\n"); + compare_data(data, dlen, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + goto err; + } + + CBB_cleanup(&cbb); + CBB_init(&cbb, 0); + free(data); + data = NULL; + + /* Make sure we can parse the single proto. */ + + CBS_init(&cbs, tlsext_alpn_single_proto, + sizeof(tlsext_alpn_single_proto)); + + /* Shouldn't be able to parse without requesting */ + if (tlsext_alpn_serverhello_parse(ssl, &cbs, &alert)) { + FAIL("Should only parse serverhello if we requested it"); + goto err; + } + + /* Should be able to parse once requested. */ + if (SSL_set_alpn_protos(ssl, tlsext_alpn_single_proto_val, + sizeof(tlsext_alpn_single_proto_val)) != 0) { + FAIL("should be able to set ALPN to http/1.1"); + goto err; + } + if (!tlsext_alpn_serverhello_parse(ssl, &cbs, &alert)) { + FAIL("Should be able to parse serverhello when we request it"); + goto err; + } + + if (S3I(ssl)->alpn_selected_len != + sizeof(tlsext_alpn_single_proto_name)) { + FAIL("got serverhello ALPN with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_alpn_single_proto_name)); + compare_data(S3I(ssl)->alpn_selected, + S3I(ssl)->alpn_selected_len, + tlsext_alpn_single_proto_name, + sizeof(tlsext_alpn_single_proto_name)); + goto err; + } + if (memcmp(S3I(ssl)->alpn_selected, + tlsext_alpn_single_proto_name, + sizeof(tlsext_alpn_single_proto_name)) != 0) { + FAIL("serverhello ALPN differs:\n"); + compare_data(S3I(ssl)->alpn_selected, + S3I(ssl)->alpn_selected_len, + tlsext_alpn_single_proto_name, + sizeof(tlsext_alpn_single_proto_name)); + goto err; + } + + /* + * We should NOT be able to build a serverhello with multiple + * protocol names. However, the existing code did not check for this + * case because it is passed in as an encoded value. + */ + + /* Make sure we can remove the list and avoid ALPN */ + + free(S3I(ssl)->alpn_selected); + S3I(ssl)->alpn_selected = NULL; + S3I(ssl)->alpn_selected_len = 0; + + if (tlsext_alpn_serverhello_needs(ssl)) { + FAIL("serverhello should need ALPN by default"); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); + +} + /* * Supported Elliptic Curves - RFC 4492 section 5.1.1. * @@ -1886,6 +2263,9 @@ main(int argc, char **argv) SSL_library_init(); + failed |= test_tlsext_alpn_clienthello(); + failed |= test_tlsext_alpn_serverhello(); + failed |= test_tlsext_ec_clienthello(); failed |= test_tlsext_ec_serverhello(); -- cgit v1.2.3-55-g6feb