You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			593 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Diff
		
	
			
		
		
	
	
			593 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Diff
		
	
| From 5ebeb8b5d90f9f47418b6b8d898ace8f1b4d4104 Mon Sep 17 00:00:00 2001
 | |
| From: Adam Langley <agl@chromium.org>
 | |
| Date: Mon, 15 Apr 2013 18:07:47 -0400
 | |
| 
 | |
| This change adds support for ALPN[1] in OpenSSL. ALPN is the IETF
 | |
| blessed version of NPN and we'll be supporting both ALPN and NPN for
 | |
| some time yet.
 | |
| 
 | |
| [1] https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-00
 | |
| ---
 | |
|  apps/s_client.c |  40 +++++++++++++-
 | |
|  ssl/s3_lib.c    |  13 +++++
 | |
|  ssl/ssl.h       |  45 +++++++++++++++
 | |
|  ssl/ssl3.h      |  10 ++++
 | |
|  ssl/ssl_lib.c   |  87 +++++++++++++++++++++++++++++
 | |
|  ssl/t1_lib.c    | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 | |
|  ssl/tls1.h      |   3 +
 | |
|  7 files changed, 362 insertions(+), 3 deletions(-)
 | |
| 
 | |
| diff --git a/apps/s_client.c b/apps/s_client.c
 | |
| index 791e277..cb1efcd 100644
 | |
| --- a/apps/s_client.c
 | |
| +++ b/apps/s_client.c
 | |
| @@ -359,6 +359,7 @@ static void sc_usage(void)
 | |
|  	BIO_printf(bio_err," -no_ticket        - disable use of RFC4507bis session tickets\n");
 | |
|  # ifndef OPENSSL_NO_NEXTPROTONEG
 | |
|  	BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
 | |
| +	BIO_printf(bio_err," -alpn arg         - enable ALPN extension, considering named protocols supported (comma-separated list)\n");
 | |
|  # endif
 | |
|  #endif
 | |
|  	BIO_printf(bio_err," -cutthrough       - enable 1-RTT full-handshake for strong ciphers\n");
 | |
| @@ -611,6 +612,7 @@ int MAIN(int argc, char **argv)
 | |
|          {NULL,0};
 | |
|  # ifndef OPENSSL_NO_NEXTPROTONEG
 | |
|  	const char *next_proto_neg_in = NULL;
 | |
| +	const char *alpn_in = NULL;
 | |
|  # endif
 | |
|  #endif
 | |
|  	char *sess_in = NULL;
 | |
| @@ -883,6 +885,11 @@ int MAIN(int argc, char **argv)
 | |
|  			if (--argc < 1) goto bad;
 | |
|  			next_proto_neg_in = *(++argv);
 | |
|  			}
 | |
| +		else if (strcmp(*argv,"-alpn") == 0)
 | |
| +			{
 | |
| +			if (--argc < 1) goto bad;
 | |
| +			alpn_in = *(++argv);
 | |
| +			}
 | |
|  # endif
 | |
|  #endif
 | |
|  		else if (strcmp(*argv,"-cutthrough") == 0)
 | |
| @@ -1157,9 +1164,23 @@ bad:
 | |
|  	 */
 | |
|  	if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1);
 | |
|  
 | |
| -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
 | |
| +#if !defined(OPENSSL_NO_TLSEXT)
 | |
| +# if !defined(OPENSSL_NO_NEXTPROTONEG)
 | |
|  	if (next_proto.data)
 | |
|  		SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
 | |
| +# endif
 | |
| +	if (alpn_in)
 | |
| +		{
 | |
| +		unsigned short alpn_len;
 | |
| +		unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in);
 | |
| +
 | |
| +		if (alpn == NULL)
 | |
| +			{
 | |
| +			BIO_printf(bio_err, "Error parsing -alpn argument\n");
 | |
| +			goto end;
 | |
| +			}
 | |
| +		SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len);
 | |
| +		}
 | |
|  #endif
 | |
|  
 | |
|  	/* Enable handshake cutthrough for client connections using
 | |
| @@ -2077,7 +2098,8 @@ static void print_stuff(BIO *bio, SSL *s, int full)
 | |
|  	}
 | |
|  #endif
 | |
|  
 | |
| -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
 | |
| +#if !defined(OPENSSL_NO_TLSEXT)
 | |
| +# if !defined(OPENSSL_NO_NEXTPROTONEG)
 | |
|  	if (next_proto.status != -1) {
 | |
|  		const unsigned char *proto;
 | |
|  		unsigned int proto_len;
 | |
| @@ -2086,6 +2108,20 @@ static void print_stuff(BIO *bio, SSL *s, int full)
 | |
|  		BIO_write(bio, proto, proto_len);
 | |
|  		BIO_write(bio, "\n", 1);
 | |
|  	}
 | |
| +	{
 | |
| +		const unsigned char *proto;
 | |
| +		unsigned int proto_len;
 | |
| +		SSL_get0_alpn_selected(s, &proto, &proto_len);
 | |
| +		if (proto_len > 0)
 | |
| +			{
 | |
| +			BIO_printf(bio, "ALPN protocol: ");
 | |
| +			BIO_write(bio, proto, proto_len);
 | |
| +			BIO_write(bio, "\n", 1);
 | |
| +			}
 | |
| +		else
 | |
| +			BIO_printf(bio, "No ALPN negotiated\n");
 | |
| +	}
 | |
| +# endif
 | |
|  #endif
 | |
|  
 | |
|  #ifndef OPENSSL_NO_SRTP
 | |
| diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
 | |
| index 5e46393..2cd1654 100644
 | |
| --- a/ssl/s3_lib.c
 | |
| +++ b/ssl/s3_lib.c
 | |
| @@ -2996,6 +2996,11 @@ void ssl3_free(SSL *s)
 | |
|  		BIO_free(s->s3->handshake_buffer);
 | |
|  	}
 | |
|  	if (s->s3->handshake_dgst) ssl3_free_digest_list(s);
 | |
| +#ifndef OPENSSL_NO_TLSEXT
 | |
| +	if (s->s3->alpn_selected)
 | |
| +		OPENSSL_free(s->s3->alpn_selected);
 | |
| +#endif
 | |
| +
 | |
|  #ifndef OPENSSL_NO_SRP
 | |
|  	SSL_SRP_CTX_free(s);
 | |
|  #endif
 | |
| @@ -3055,6 +3060,14 @@ void ssl3_clear(SSL *s)
 | |
|  	if (s->s3->handshake_dgst) {
 | |
|  		ssl3_free_digest_list(s);
 | |
|  	}	
 | |
| +
 | |
| +#if !defined(OPENSSL_NO_TLSEXT)
 | |
| +	if (s->s3->alpn_selected)
 | |
| +		{
 | |
| +		free(s->s3->alpn_selected);
 | |
| +		s->s3->alpn_selected = NULL;
 | |
| +		}
 | |
| +#endif
 | |
|  	memset(s->s3,0,sizeof *s->s3);
 | |
|  	s->s3->rbuf.buf = rp;
 | |
|  	s->s3->wbuf.buf = wp;
 | |
| diff --git a/ssl/ssl.h b/ssl/ssl.h
 | |
| index e8c73fa..612c7aa 100644
 | |
| --- a/ssl/ssl.h
 | |
| +++ b/ssl/ssl.h
 | |
| @@ -1019,6 +1019,31 @@ struct ssl_ctx_st
 | |
|  				    void *arg);
 | |
|  	void *next_proto_select_cb_arg;
 | |
|  # endif
 | |
| +
 | |
| +	/* ALPN information
 | |
| +	 * (we are in the process of transitioning from NPN to ALPN.) */
 | |
| +
 | |
| +	/* For a server, this contains a 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;
 | |
| +
 | |
| +	/* For a client, this contains the list of supported protocols in wire
 | |
| +	 * format. */
 | |
| +	unsigned char* alpn_client_proto_list;
 | |
| +	unsigned alpn_client_proto_list_len;
 | |
| +
 | |
|          /* SRTP profiles we are willing to do from RFC 5764 */
 | |
|          STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;  
 | |
|  
 | |
| @@ -1120,6 +1145,21 @@ void SSL_get0_next_proto_negotiated(const SSL *s,
 | |
|  #define OPENSSL_NPN_NO_OVERLAP	2
 | |
|  #endif
 | |
|  
 | |
| +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos,
 | |
| +			    unsigned protos_len);
 | |
| +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos,
 | |
| +			unsigned 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 *len);
 | |
| +
 | |
|  #ifndef OPENSSL_NO_PSK
 | |
|  /* the maximum length of the buffer given to callbacks containing the
 | |
|   * resulting identity/psk */
 | |
| @@ -1422,6 +1462,11 @@ struct ssl_st
 | |
|  	char tlsext_channel_id_enabled;
 | |
|  	/* The client's Channel ID private key. */
 | |
|  	EVP_PKEY *tlsext_channel_id_private;
 | |
| +
 | |
| +	/* For a client, this contains the list of supported protocols in wire
 | |
| +	 * format. */
 | |
| +	unsigned char* alpn_client_proto_list;
 | |
| +	unsigned alpn_client_proto_list_len;
 | |
|  #else
 | |
|  #define session_ctx ctx
 | |
|  #endif /* OPENSSL_NO_TLSEXT */
 | |
| diff --git a/ssl/ssl3.h b/ssl/ssl3.h
 | |
| index 3229995..28c46d5 100644
 | |
| --- a/ssl/ssl3.h
 | |
| +++ b/ssl/ssl3.h
 | |
| @@ -551,6 +551,16 @@ typedef struct ssl3_state_st
 | |
|  	 *     each are big-endian values. */
 | |
|  	unsigned char tlsext_channel_id[64];
 | |
|  
 | |
| +	/* 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 alpn_selected_len;
 | |
| +
 | |
|  	/* These point to the digest function to use for signatures made with
 | |
|  	 * each type of public key. A NULL value indicates that the default
 | |
|  	 * digest should be used, which is SHA1 as of TLS 1.2.
 | |
| diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
 | |
| index e360550..b472423 100644
 | |
| --- a/ssl/ssl_lib.c
 | |
| +++ b/ssl/ssl_lib.c
 | |
| @@ -359,6 +359,17 @@ SSL *SSL_new(SSL_CTX *ctx)
 | |
|  # ifndef OPENSSL_NO_NEXTPROTONEG
 | |
|  	s->next_proto_negotiated = NULL;
 | |
|  # endif
 | |
| +
 | |
| +	if (s->ctx->alpn_client_proto_list)
 | |
| +		{
 | |
| +		s->alpn_client_proto_list =
 | |
| +			OPENSSL_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;
 | |
| +		}
 | |
|  #endif
 | |
|  
 | |
|  	s->verify_result=X509_V_OK;
 | |
| @@ -564,6 +575,8 @@ void SSL_free(SSL *s)
 | |
|  		OPENSSL_free(s->tlsext_ocsp_resp);
 | |
|  	if (s->tlsext_channel_id_private)
 | |
|  		EVP_PKEY_free(s->tlsext_channel_id_private);
 | |
| +	if (s->alpn_client_proto_list)
 | |
| +		OPENSSL_free(s->alpn_client_proto_list);
 | |
|  #endif
 | |
|  
 | |
|  	if (s->client_CA != NULL)
 | |
| @@ -1615,6 +1628,78 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned
 | |
|  	ctx->next_proto_select_cb_arg = arg;
 | |
|  	}
 | |
|  # endif
 | |
| +
 | |
| +/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|.
 | |
| + * |protos| 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 protos_len)
 | |
| +	{
 | |
| +	if (ctx->alpn_client_proto_list)
 | |
| +		OPENSSL_free(ctx->alpn_client_proto_list);
 | |
| +
 | |
| +	ctx->alpn_client_proto_list = OPENSSL_malloc(protos_len);
 | |
| +	if (!ctx->alpn_client_proto_list)
 | |
| +		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 on |ssl| to |protos|.
 | |
| + * |protos| 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 protos_len)
 | |
| +	{
 | |
| +	if (ssl->alpn_client_proto_list)
 | |
| +		OPENSSL_free(ssl->alpn_client_proto_list);
 | |
| +
 | |
| +	ssl->alpn_client_proto_list = OPENSSL_malloc(protos_len);
 | |
| +	if (!ssl->alpn_client_proto_list)
 | |
| +		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 on |ctx| 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) from |ssl|.
 | |
| + * 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;
 | |
| +	if (ssl->s3)
 | |
| +		*data = ssl->s3->alpn_selected;
 | |
| +	if (*data == NULL)
 | |
| +		*len = 0;
 | |
| +	else
 | |
| +		*len = ssl->s3->alpn_selected_len;
 | |
| +	}
 | |
|  #endif
 | |
|  
 | |
|  int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
 | |
| @@ -1955,6 +2040,8 @@ void SSL_CTX_free(SSL_CTX *a)
 | |
|  #ifndef OPENSSL_NO_TLSEXT
 | |
|  	if (a->tlsext_channel_id_private)
 | |
|  		EVP_PKEY_free(a->tlsext_channel_id_private);
 | |
| +	if (a->alpn_client_proto_list != NULL)
 | |
| +		OPENSSL_free(a->alpn_client_proto_list);
 | |
|  #endif
 | |
|  
 | |
|  	OPENSSL_free(a);
 | |
| diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
 | |
| index 1f93a6f..b2e049a 100644
 | |
| --- a/ssl/t1_lib.c
 | |
| +++ b/ssl/t1_lib.c
 | |
| @@ -659,6 +659,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
 | |
|  		s2n(0,ret);
 | |
|  		}
 | |
|  
 | |
| +	if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len)
 | |
| +		{
 | |
| +		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_get_srtp_profiles(s))
 | |
|                  {
 | |
| @@ -879,6 +891,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 | |
|  		s2n(0,ret);
 | |
|  		}
 | |
|  
 | |
| +	if (s->s3->alpn_selected)
 | |
| +		{
 | |
| +		const unsigned char *selected = s->s3->alpn_selected;
 | |
| +		unsigned 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;
 | |
|  
 | |
| @@ -966,6 +993,76 @@ static void ssl_check_for_safari(SSL *s, const unsigned char *data, const unsign
 | |
|  	s->is_probably_safari = 1;
 | |
|  }
 | |
|  
 | |
| +/* 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: 0 on success. */
 | |
| +static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data,
 | |
| +					 unsigned data_len, int *al)
 | |
| +	{
 | |
| +	unsigned i;
 | |
| +	unsigned proto_len;
 | |
| +	const unsigned char *selected;
 | |
| +	unsigned char selected_len;
 | |
| +	int r;
 | |
| +
 | |
| +	if (s->ctx->alpn_select_cb == NULL)
 | |
| +		return 0;
 | |
| +
 | |
| +	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) data[0]) << 8 |
 | |
| +	    ((unsigned) 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) {
 | |
| +		if (s->s3->alpn_selected)
 | |
| +			OPENSSL_free(s->s3->alpn_selected);
 | |
| +		s->s3->alpn_selected = OPENSSL_malloc(selected_len);
 | |
| +		if (!s->s3->alpn_selected)
 | |
| +			{
 | |
| +			*al = SSL_AD_INTERNAL_ERROR;
 | |
| +			return -1;
 | |
| +			}
 | |
| +		memcpy(s->s3->alpn_selected, selected, selected_len);
 | |
| +		s->s3->alpn_selected_len = selected_len;
 | |
| +	}
 | |
| +	return 0;
 | |
| +
 | |
| +parse_error:
 | |
| +	*al = SSL_AD_DECODE_ERROR;
 | |
| +	return -1;
 | |
| +	}
 | |
| +
 | |
|  int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
 | |
|  	{
 | |
|  	unsigned short type;
 | |
| @@ -988,6 +1085,12 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 | |
|  	s->s3->next_proto_neg_seen = 0;
 | |
|  #endif
 | |
|  
 | |
| +	if (s->s3->alpn_selected)
 | |
| +		{
 | |
| +		OPENSSL_free(s->s3->alpn_selected);
 | |
| +		s->s3->alpn_selected = NULL;
 | |
| +		}
 | |
| +
 | |
|  #ifndef OPENSSL_NO_HEARTBEATS
 | |
|  	s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
 | |
|  	                       SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 | |
| @@ -1420,7 +1523,8 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 | |
|  #endif
 | |
|  #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.
 | |
| @@ -1444,6 +1548,16 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 | |
|  		else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled)
 | |
|  			s->s3->tlsext_channel_id_valid = 1;
 | |
|  
 | |
| +		else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
 | |
| +			 s->ctx->alpn_select_cb &&
 | |
| +			 s->s3->tmp.finish_md_len == 0)
 | |
| +			{
 | |
| +			if (tls1_alpn_handle_client_hello(s, data, size, al) != 0)
 | |
| +				return 0;
 | |
| +			/* ALPN takes precedence over NPN. */
 | |
| +			s->s3->next_proto_neg_seen = 0;
 | |
| +			}
 | |
| +
 | |
|  		/* session ticket processed earlier */
 | |
|  #ifndef OPENSSL_NO_SRTP
 | |
|  		else if (type == TLSEXT_TYPE_use_srtp)
 | |
| @@ -1508,6 +1622,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 | |
|  	s->s3->next_proto_neg_seen = 0;
 | |
|  #endif
 | |
|  
 | |
| +	if (s->s3->alpn_selected)
 | |
| +		{
 | |
| +		OPENSSL_free(s->s3->alpn_selected);
 | |
| +		s->s3->alpn_selected = NULL;
 | |
| +		}
 | |
| +
 | |
|  #ifndef OPENSSL_NO_HEARTBEATS
 | |
|  	s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
 | |
|  	                       SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 | |
| @@ -1677,6 +1797,51 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 | |
|  		else if (type == TLSEXT_TYPE_channel_id)
 | |
|  			s->s3->tlsext_channel_id_valid = 1;
 | |
|  
 | |
| +		else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation)
 | |
| +			{
 | |
| +			unsigned 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 = data[0];
 | |
| +			len <<= 8;
 | |
| +			len |= data[1];
 | |
| +			if (len != (unsigned) size - 2)
 | |
| +				{
 | |
| +				*al = TLS1_AD_DECODE_ERROR;
 | |
| +				return 0;
 | |
| +				}
 | |
| +			len = data[2];
 | |
| +			if (len != (unsigned) size - 3)
 | |
| +				{
 | |
| +				*al = TLS1_AD_DECODE_ERROR;
 | |
| +				return 0;
 | |
| +				}
 | |
| +			if (s->s3->alpn_selected)
 | |
| +				OPENSSL_free(s->s3->alpn_selected);
 | |
| +			s->s3->alpn_selected = OPENSSL_malloc(len);
 | |
| +			if (!s->s3->alpn_selected)
 | |
| +				{
 | |
| +				*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))
 | |
| diff --git a/ssl/tls1.h b/ssl/tls1.h
 | |
| index 8fc1ff4..c6670f4 100644
 | |
| --- a/ssl/tls1.h
 | |
| +++ b/ssl/tls1.h
 | |
| @@ -230,6 +230,9 @@ extern "C" {
 | |
|  /* ExtensionType value from RFC5620 */
 | |
|  #define TLSEXT_TYPE_heartbeat	15
 | |
|  
 | |
| +/* ExtensionType value from draft-ietf-tls-applayerprotoneg-00 */
 | |
| +#define TLSEXT_TYPE_application_layer_protocol_negotiation 16
 | |
| +
 | |
|  /* ExtensionType value from RFC4507 */
 | |
|  #define TLSEXT_TYPE_session_ticket		35
 | |
|  
 | |
| -- 
 | |
| 1.8.2.1
 | |
| 
 |