summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2014-12-10 14:58:57 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2014-12-10 14:58:57 +0000
commit998a9a02bff72b2fb0832bb36e435f837f673231 (patch)
treea7d530bd597d8e78cfb1d535e5e1eb2a89da222d /lib
parent578da4fcdc289719fdce0b6654bb987d54b0d349 (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.c7
-rw-r--r--lib/libssl/src/ssl/ssl.h41
-rw-r--r--lib/libssl/src/ssl/ssl3.h16
-rw-r--r--lib/libssl/src/ssl/ssl_lib.c86
-rw-r--r--lib/libssl/src/ssl/t1_lib.c155
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;