From 0b874dc58031a6d859e8c975fc70f65a91afa0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 7 Apr 2014 10:57:45 +0200 Subject: [PATCH] Implement ALPN client-side --- include/polarssl/ssl.h | 6 ++- library/ssl_cli.c | 111 +++++++++++++++++++++++++++++++++++++++++ library/ssl_tls.c | 21 +++++++- 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index e4a8dc7639..aa5daf4147 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -348,6 +348,8 @@ #define TLS_EXT_SIG_ALG 13 +#define TLS_EXT_ALPN 16 + #define TLS_EXT_SESSION_TICKET 35 #define TLS_EXT_RENEGOTIATION_INFO 0xFF01 @@ -1247,8 +1249,10 @@ void ssl_set_sni( ssl_context *ssl, * \param ssl SSL context * \param protos NULL-terminated list of supported protocols, * in decreasing preference order. + * + * \return 0 on success, or POLARSSL_ERR_SSL_BAD_INPUT_DATA. */ -void ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ); +int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ); /** * \brief Get the name of the negotiated Application Layer Protocol. diff --git a/library/ssl_cli.c b/library/ssl_cli.c index b509e376e8..999a39b428 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -383,6 +383,54 @@ static void ssl_write_session_ticket_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_SESSION_TICKETS */ +#if defined(POLARSSL_SSL_ALPN) +static void ssl_write_alpn_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + const char **cur; + + if( ssl->alpn_list == NULL ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding ALPN extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + + /* Skip writing extension and list length for now */ + p += 4; + + for( cur = ssl->alpn_list; *cur != NULL; cur++ ) + { + *p = (unsigned char)( strlen( *cur ) & 0xFF ); + memcpy( p + 1, *cur, *p ); + p += 1 + *p; + } + + *olen = p - buf; + + /* List length = olen - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */ + buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF ); + buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF ); + + /* Extension length = olen - 2 (ext_type) - 2 (ext_len) */ + buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF ); + buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF ); +} +#endif /* POLARSSL_SSL_ALPN */ + static int ssl_write_client_hello( ssl_context *ssl ) { int ret; @@ -595,6 +643,11 @@ static int ssl_write_client_hello( ssl_context *ssl ) ext_len += olen; #endif +#if defined(POLARSSL_SSL_ALPN) + ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d", ext_len ) ); @@ -753,6 +806,54 @@ static int ssl_parse_supported_point_formats_ext( ssl_context *ssl, } #endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ +#if defined(POLARSSL_SSL_ALPN) +static int ssl_parse_alpn_ext( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + size_t list_len, name_len; + const char **p; + + /* If we didn't send it, the server shouldn't send it */ + if( ssl->alpn_list == NULL ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + * + * the "ProtocolNameList" MUST contain exactly one "ProtocolName" + */ + + /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ + if( len < 4 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + list_len = ( buf[0] << 8 ) | buf[1]; + if( list_len != len - 2 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + name_len = buf[2]; + if( name_len != list_len - 1 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + /* Check that the server chosen protocol was in our list and save it */ + for( p = ssl->alpn_list; *p != NULL; p++ ) + { + if( name_len == strlen( *p ) && + memcmp( buf + 3, *p, name_len ) == 0 ) + { + ssl->alpn_chosen = *p; + return( 0 ); + } + } + + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); +} +#endif /* POLARSSL_SSL_ALPN */ + static int ssl_parse_server_hello( ssl_context *ssl ) { int ret, i, comp; @@ -1023,6 +1124,16 @@ static int ssl_parse_server_hello( ssl_context *ssl ) break; #endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ +#if defined(POLARSSL_SSL_ALPN) + case TLS_EXT_ALPN: + SSL_DEBUG_MSG( 3, ( "found alpn extension" ) ); + + if( ( ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ) ) != 0 ) + return( ret ); + + break; +#endif /* POLARSSL_SSL_ALPN */ + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); diff --git a/library/ssl_tls.c b/library/ssl_tls.c index eb293718e8..0365b92b3f 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3919,9 +3919,28 @@ void ssl_set_sni( ssl_context *ssl, #endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ #if defined(POLARSSL_SSL_ALPN) -void ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ) +int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ) { + size_t cur_len, tot_len; + const char **p; + + /* + * "Empty strings MUST NOT be included and byte strings MUST NOT be + * truncated". Check lengths now rather than later. + */ + tot_len = 0; + for( p = protos; *p != NULL; p++ ) + { + cur_len = strlen( *p ); + tot_len += cur_len; + + if( cur_len == 0 || cur_len > 255 || tot_len > 65535 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + ssl->alpn_list = protos; + + return( 0 ); } const char *ssl_get_alpn_protocol( const ssl_context *ssl )