diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2014-12-10 14:58:57 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2014-12-10 14:58:57 +0000 |
commit | 998a9a02bff72b2fb0832bb36e435f837f673231 (patch) | |
tree | a7d530bd597d8e78cfb1d535e5e1eb2a89da222d /lib | |
parent | 578da4fcdc289719fdce0b6654bb987d54b0d349 (diff) |
Add support for ALPN.
Based on OpenSSL and BoringSSL.
ok bcook@
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libssl/src/ssl/s3_lib.c | 7 | ||||
-rw-r--r-- | lib/libssl/src/ssl/ssl.h | 41 | ||||
-rw-r--r-- | lib/libssl/src/ssl/ssl3.h | 16 | ||||
-rw-r--r-- | lib/libssl/src/ssl/ssl_lib.c | 86 | ||||
-rw-r--r-- | lib/libssl/src/ssl/t1_lib.c | 155 |
5 files changed, 297 insertions, 8 deletions
diff --git a/lib/libssl/src/ssl/s3_lib.c b/lib/libssl/src/ssl/s3_lib.c index f2d2cb040d1..9897fba6c56 100644 --- a/lib/libssl/src/ssl/s3_lib.c +++ b/lib/libssl/src/ssl/s3_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: s3_lib.c,v 1.85 2014/11/18 05:33:43 miod Exp $ */ +/* $OpenBSD: s3_lib.c,v 1.86 2014/12/10 14:58:56 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -1905,6 +1905,8 @@ ssl3_free(SSL *s) sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free); BIO_free(s->s3->handshake_buffer); ssl3_free_digest_list(s); + free(s->s3->alpn_selected); + OPENSSL_cleanse(s->s3, sizeof *s->s3); free(s->s3); s->s3 = NULL; @@ -1939,6 +1941,9 @@ ssl3_clear(SSL *s) ssl3_free_digest_list(s); + free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + memset(s->s3, 0, sizeof *s->s3); s->s3->rbuf.buf = rp; s->s3->wbuf.buf = wp; diff --git a/lib/libssl/src/ssl/ssl.h b/lib/libssl/src/ssl/ssl.h index e8388923a4d..0059da6791c 100644 --- a/lib/libssl/src/ssl/ssl.h +++ b/lib/libssl/src/ssl/ssl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl.h,v 1.74 2014/12/10 14:51:00 bcook Exp $ */ +/* $OpenBSD: ssl.h,v 1.75 2014/12/10 14:58:56 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -861,9 +861,33 @@ struct ssl_ctx_st { unsigned int inlen, void *arg); void *next_proto_select_cb_arg; # endif + + /* + * ALPN information + * (we are in the process of transitioning from NPN to ALPN). + */ + + /* + * Server callback function that allows the server to select the + * protocol for the connection. + * out: on successful return, this must point to the raw protocol + * name (without the length prefix). + * outlen: on successful return, this contains the length of out. + * in: points to the client's list of supported protocols in + * wire-format. + * inlen: the length of in. + */ + int (*alpn_select_cb)(SSL *s, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, unsigned int inlen, + void *arg); + void *alpn_select_cb_arg; + + /* Client list of supported protocols in wire format. */ + unsigned char *alpn_client_proto_list; + unsigned int alpn_client_proto_list_len; + /* SRTP profiles we are willing to do from RFC 5764 */ STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; - }; #endif @@ -954,6 +978,15 @@ void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, #define OPENSSL_NPN_NO_OVERLAP 2 #endif +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, + unsigned int protos_len); +int SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos, + unsigned int protos_len); +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg), void *arg); +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned int *len); #define SSL_NOTHING 1 #define SSL_WRITING 2 @@ -1187,6 +1220,10 @@ struct ssl_st { unsigned int tlsext_hb_pending; /* Indicates if a HeartbeatRequest is in flight */ unsigned int tlsext_hb_seq; /* HeartbeatRequest sequence number */ + /* Client list of supported protocols in wire format. */ + unsigned char *alpn_client_proto_list; + unsigned int alpn_client_proto_list_len; + int renegotiate;/* 1 if we are renegotiating. * 2 if we are a server and are inside a handshake * (i.e. not just sending a HelloRequest) */ diff --git a/lib/libssl/src/ssl/ssl3.h b/lib/libssl/src/ssl/ssl3.h index 5b9e31754ba..2055f0f8344 100644 --- a/lib/libssl/src/ssl/ssl3.h +++ b/lib/libssl/src/ssl/ssl3.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl3.h,v 1.29 2014/11/18 05:33:43 miod Exp $ */ +/* $OpenBSD: ssl3.h,v 1.30 2014/12/10 14:58:56 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -497,6 +497,20 @@ typedef struct ssl3_state_st { int next_proto_neg_seen; #endif + /* + * ALPN information + * (we are in the process of transitioning from NPN to ALPN). + */ + + /* + * In a server these point to the selected ALPN protocol after the + * ClientHello has been processed. In a client these contain the + * protocol that the server selected once the ServerHello has been + * processed. + */ + unsigned char *alpn_selected; + unsigned int alpn_selected_len; + /* This is set to true if we believe that this is a version of Safari * running on OS X 10.6 or newer. We wish to know this because Safari * on 10.8 .. 10.8.3 has broken ECDHE-ECDSA support. */ diff --git a/lib/libssl/src/ssl/ssl_lib.c b/lib/libssl/src/ssl/ssl_lib.c index bdd47ff87f6..a03ee735ada 100644 --- a/lib/libssl/src/ssl/ssl_lib.c +++ b/lib/libssl/src/ssl/ssl_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_lib.c,v 1.90 2014/11/16 14:12:47 jsing Exp $ */ +/* $OpenBSD: ssl_lib.c,v 1.91 2014/12/10 14:58:56 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -337,6 +337,18 @@ SSL_new(SSL_CTX *ctx) s->next_proto_negotiated = NULL; # endif + if (s->ctx->alpn_client_proto_list != NULL) { + s->alpn_client_proto_list = + malloc(s->ctx->alpn_client_proto_list_len); + if (s->alpn_client_proto_list == NULL) + goto err; + memcpy(s->alpn_client_proto_list, + s->ctx->alpn_client_proto_list, + s->ctx->alpn_client_proto_list_len); + s->alpn_client_proto_list_len = + s->ctx->alpn_client_proto_list_len; + } + s->verify_result = X509_V_OK; s->method = ctx->method; @@ -551,6 +563,7 @@ SSL_free(SSL *s) #ifndef OPENSSL_NO_NEXTPROTONEG free(s->next_proto_negotiated); #endif + free(s->alpn_client_proto_list); #ifndef OPENSSL_NO_SRTP if (s->srtp_profiles) @@ -1629,6 +1642,75 @@ SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, } # endif +/* + * SSL_CTX_set_alpn_protos sets the ALPN protocol list to the specified + * protocols, which must be in wire-format (i.e. a series of non-empty, + * 8-bit length-prefixed strings). Returns 0 on success. + */ +int +SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, + unsigned int protos_len) +{ + free(ctx->alpn_client_proto_list); + if ((ctx->alpn_client_proto_list = malloc(protos_len)) == NULL) + return (1); + memcpy(ctx->alpn_client_proto_list, protos, protos_len); + ctx->alpn_client_proto_list_len = protos_len; + + return (0); +} + +/* + * SSL_set_alpn_protos sets the ALPN protocol list to the specified + * protocols, which must be in wire-format (i.e. a series of non-empty, + * 8-bit length-prefixed strings). Returns 0 on success. + */ +int +SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, + unsigned int protos_len) +{ + free(ssl->alpn_client_proto_list); + if ((ssl->alpn_client_proto_list = malloc(protos_len)) == NULL) + return (1); + memcpy(ssl->alpn_client_proto_list, protos, protos_len); + ssl->alpn_client_proto_list_len = protos_len; + + return (0); +} + +/* + * SSL_CTX_set_alpn_select_cb sets a callback function that is called during + * ClientHello processing in order to select an ALPN protocol from the + * client's list of offered protocols. + */ +void +SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, + int (*cb) (SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg), void *arg) +{ + ctx->alpn_select_cb = cb; + ctx->alpn_select_cb_arg = arg; +} + +/* + * SSL_get0_alpn_selected gets the selected ALPN protocol (if any). On return + * it sets data to point to len bytes of protocol name (not including the + * leading length-prefix byte). If the server didn't respond with* a negotiated + * protocol then len will be zero. + */ +void +SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned *len) +{ + *data = NULL; + *len = 0; + + if (ssl->s3 != NULL) { + *data = ssl->s3->alpn_selected; + *len = ssl->s3->alpn_selected_len; + } +} + int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen, const char *label, size_t llen, const unsigned char *p, size_t plen, @@ -1894,6 +1976,8 @@ SSL_CTX_free(SSL_CTX *a) ENGINE_finish(a->client_cert_engine); #endif + free(a->alpn_client_proto_list); + free(a); } diff --git a/lib/libssl/src/ssl/t1_lib.c b/lib/libssl/src/ssl/t1_lib.c index 8a7553e3e73..5df45ab3598 100644 --- a/lib/libssl/src/ssl/t1_lib.c +++ b/lib/libssl/src/ssl/t1_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: t1_lib.c,v 1.71 2014/12/06 13:51:06 jsing Exp $ */ +/* $OpenBSD: t1_lib.c,v 1.72 2014/12/10 14:58:56 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -878,6 +878,18 @@ skip_ext: } #endif + if (s->alpn_client_proto_list != NULL && + s->s3->tmp.finish_md_len == 0) { + if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) + return (NULL); + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); + s2n(2 + s->alpn_client_proto_list_len, ret); + s2n(s->alpn_client_proto_list_len, ret); + memcpy(ret, s->alpn_client_proto_list, + s->alpn_client_proto_list_len); + ret += s->alpn_client_proto_list_len; + } + #ifndef OPENSSL_NO_SRTP if (SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s)) { int el; @@ -1107,6 +1119,20 @@ ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) } #endif + if (s->s3->alpn_selected != NULL) { + const unsigned char *selected = s->s3->alpn_selected; + unsigned int len = s->s3->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; @@ -1114,6 +1140,76 @@ 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) +{ + const unsigned char *selected; + unsigned char selected_len; + unsigned int proto_len; + unsigned int i; + int r; + + if (s->ctx->alpn_select_cb == NULL) + return (1); + + if (data_len < 2) + goto parse_error; + + /* + * data should contain a uint16 length followed by a series of 8-bit, + * length-prefixed strings. + */ + i = ((unsigned int)data[0]) << 8 | ((unsigned int)data[1]); + data_len -= 2; + data += 2; + if (data_len != i) + goto parse_error; + + if (data_len < 2) + goto parse_error; + + for (i = 0; i < data_len; ) { + proto_len = data[i]; + i++; + + if (proto_len == 0) + goto parse_error; + + if (i + proto_len < i || i + proto_len > data_len) + goto parse_error; + + i += proto_len; + } + + r = s->ctx->alpn_select_cb(s, &selected, &selected_len, + data, data_len, s->ctx->alpn_select_cb_arg); + if (r == SSL_TLSEXT_ERR_OK) { + free(s->s3->alpn_selected); + if ((s->s3->alpn_selected = malloc(selected_len)) == NULL) { + *al = SSL_AD_INTERNAL_ERROR; + return (-1); + } + memcpy(s->s3->alpn_selected, selected, selected_len); + s->s3->alpn_selected_len = selected_len; + } + + return (1); + +parse_error: + *al = SSL_AD_DECODE_ERROR; + return (0); +} + /* ssl_check_for_safari attempts to fingerprint Safari using OS X * SecureTransport using the TLS extension block in |d|, of length |n|. * Safari, since 10.6, sends exactly these extensions, in this order: @@ -1211,6 +1307,8 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, #ifndef OPENSSL_NO_NEXTPROTONEG s->s3->next_proto_neg_seen = 0; #endif + free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG) ssl_check_for_safari(s, data, d, n); @@ -1520,7 +1618,8 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, } #ifndef OPENSSL_NO_NEXTPROTONEG else if (type == TLSEXT_TYPE_next_proto_neg && - s->s3->tmp.finish_md_len == 0) { + s->s3->tmp.finish_md_len == 0 && + s->s3->alpn_selected == NULL) { /* We shouldn't accept this extension on a * renegotiation. * @@ -1539,6 +1638,16 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, s->s3->next_proto_neg_seen = 1; } #endif + else if (type == + TLSEXT_TYPE_application_layer_protocol_negotiation && + s->ctx->alpn_select_cb != NULL && + s->s3->tmp.finish_md_len == 0) { + if (tls1_alpn_handle_client_hello(s, data, + size, al) != 1) + return (0); + /* ALPN takes precedence over NPN. */ + s->s3->next_proto_neg_seen = 0; + } /* session ticket processed earlier */ #ifndef OPENSSL_NO_SRTP @@ -1601,6 +1710,8 @@ ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, #ifndef OPENSSL_NO_NEXTPROTONEG s->s3->next_proto_neg_seen = 0; #endif + free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; if (data >= (d + n - 2)) goto ri_check; @@ -1716,7 +1827,45 @@ ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, s->s3->next_proto_neg_seen = 1; } #endif - else if (type == TLSEXT_TYPE_renegotiate) { + else if (type == + TLSEXT_TYPE_application_layer_protocol_negotiation) { + unsigned int len; + + /* We must have requested it. */ + if (s->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(s->s3->alpn_selected); + s->s3->alpn_selected = malloc(len); + if (s->s3->alpn_selected == NULL) { + *al = TLS1_AD_INTERNAL_ERROR; + return (0); + } + memcpy(s->s3->alpn_selected, data + 3, len); + s->s3->alpn_selected_len = len; + + } else if (type == TLSEXT_TYPE_renegotiate) { if (!ssl_parse_serverhello_renegotiate_ext(s, data, size, al)) return 0; renegotiate_seen = 1; |