From 7e250d48128f8f9e9c094bec451170e55141d137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 4 Apr 2014 16:08:41 +0200 Subject: [PATCH 1/7] Add ALPN interface --- include/polarssl/config.h | 13 +++++++++++++ include/polarssl/ssl.h | 30 ++++++++++++++++++++++++++++++ library/ssl_tls.c | 16 ++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 83aa9a306..78102f0a8 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -860,6 +860,19 @@ */ #define POLARSSL_SSL_PROTO_TLS1_2 +/** + * \def POLARSSL_SSL_ALPN + * + * Enable support for Application Layer Protocol Negotiation. + * draft-ietf-tls-applayerprotoneg-05 + * + * This is disabled by default in the 1.3.x line since it breaks ABI + * compatibility. + * + * Uncomment this macro to enable support for ALPN. + */ +#define POLARSSL_SSL_ALPN + /** * \def POLARSSL_SSL_SESSION_TICKETS * diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index c1aff67b5..e4a8dc763 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -762,6 +762,14 @@ struct _ssl_context size_t hostname_len; #endif +#if defined(POLARSSL_SSL_ALPN) + /* + * ALPN extension + */ + const char **alpn_list; /*!< ordered list of supported protocols */ + const char *alpn_chosen; /*!< negotiated protocol */ +#endif + /* * Secure renegotiation */ @@ -1232,6 +1240,28 @@ void ssl_set_sni( ssl_context *ssl, void *p_sni ); #endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ +#if defined(POLARSSL_SSL_ALPN) +/** + * \brief Set the supported Application Layer Protocols. + * + * \param ssl SSL context + * \param protos NULL-terminated list of supported protocols, + * in decreasing preference order. + */ +void ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ); + +/** + * \brief Get the name of the negotiated Application Layer Protocol. + * This function should be called after the handshake is + * completed. + * + * \param ssl SSL context + * + * \return Protcol name, or NULL if no protocol was negotiated. + */ +const char *ssl_get_alpn_protocol( const ssl_context *ssl ); +#endif /* POLARSSL_SSL_ALPN */ + /** * \brief Set the maximum supported version sent from the client side * and/or accepted at the server side diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 681b7c39d..eb293718e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3520,6 +3520,10 @@ int ssl_session_reset( ssl_context *ssl ) ssl->session = NULL; } +#if defined(POLARSSL_SSL_ALPN) + ssl->alpn_chosen = NULL; +#endif + if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) return( ret ); @@ -3914,6 +3918,18 @@ 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 ) +{ + ssl->alpn_list = protos; +} + +const char *ssl_get_alpn_protocol( const ssl_context *ssl ) +{ + return ssl->alpn_chosen; +} +#endif /* POLARSSL_SSL_ALPN */ + void ssl_set_max_version( ssl_context *ssl, int major, int minor ) { if( major >= SSL_MIN_MAJOR_VERSION && major <= SSL_MAX_MAJOR_VERSION && From 1bd2281260980d76c7cf76e830fea69707ef03b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Sat, 5 Apr 2014 14:34:07 +0200 Subject: [PATCH 2/7] Add an alpn option to ssl_client2 and ssl_server2 --- programs/ssl/ssl_client2.c | 58 +++++++++++++++++++++++++++++++++++++- programs/ssl/ssl_server2.c | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index b5bfaedd1..d4428f2ff 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -65,6 +65,7 @@ #define DFL_RECONNECT 0 #define DFL_RECO_DELAY 0 #define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED +#define DFL_ALPN_STRING NULL #define LONG_HEADER "User-agent: blah-blah-blah-blah-blah-blah-blah-blah-" \ "-01--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \ @@ -108,6 +109,7 @@ struct options int reconnect; /* attempt to resume session */ int reco_delay; /* delay in seconds before resuming session */ int tickets; /* enable / disable session tickets */ + const char *alpn_string; /* ALPN supported protocols */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -248,11 +250,19 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) #if defined(POLARSSL_TIMING_C) #define USAGE_TIME \ - " reco_delay=%%d default: 0 seconds\n" + " reco_delay=%%d default: 0 seconds\n" #else #define USAGE_TIME "" #endif /* POLARSSL_TIMING_C */ +#if defined(POLARSSL_SSL_ALPN) +#define USAGE_ALPN \ + " alpn=%%s default: \"\" (disabled)\n" \ + " example: spdy/1,http/1.1\n" +#else +#define USAGE_ALPN "" +#endif /* POLARSSL_SSL_ALPN */ + #define USAGE \ "\n usage: ssl_client2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -278,6 +288,7 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) USAGE_TICKETS \ USAGE_MAX_FRAG_LEN \ USAGE_TRUNC_HMAC \ + USAGE_ALPN \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -310,6 +321,9 @@ int main( int argc, char *argv[] ) #if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) unsigned char psk[256]; size_t psk_len = 0; +#endif +#if defined(POLARSSL_SSL_ALPN) + const char *alpn_list[10]; #endif const char *pers = "ssl_client2"; @@ -336,6 +350,9 @@ int main( int argc, char *argv[] ) x509_crt_init( &clicert ); pk_init( &pkey ); #endif +#if defined(POLARSSL_SSL_ALPN) + memset( alpn_list, 0, sizeof alpn_list ); +#endif if( argc == 0 ) { @@ -383,6 +400,7 @@ int main( int argc, char *argv[] ) opt.reconnect = DFL_RECONNECT; opt.reco_delay = DFL_RECO_DELAY; opt.tickets = DFL_TICKETS; + opt.alpn_string = DFL_ALPN_STRING; for( i = 1; i < argc; i++ ) { @@ -475,6 +493,10 @@ int main( int argc, char *argv[] ) if( opt.tickets < 0 || opt.tickets > 2 ) goto usage; } + else if( strcmp( p, "alpn" ) == 0 ) + { + opt.alpn_string = q; + } else if( strcmp( p, "min_version" ) == 0 ) { if( strcmp( q, "ssl3" ) == 0 ) @@ -635,6 +657,26 @@ int main( int argc, char *argv[] ) } #endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + { + p = (char *) opt.alpn_string; + i = 0; + + /* Leave room for a final NULL in alpn_list */ + while( i < (int) sizeof alpn_list - 1 && *p != '\0' ) + { + alpn_list[i++] = p; + + /* Terminate the current string and move on to next one */ + while( *p != ',' && *p != '\0' ) + p++; + if( *p == ',' ) + *p++ = '\0'; + } + } +#endif /* POLARSSL_SSL_ALPN */ + /* * 0. Initialize the RNG and the session data */ @@ -806,6 +848,11 @@ int main( int argc, char *argv[] ) ssl_set_truncated_hmac( &ssl, SSL_TRUNC_HMAC_ENABLED ); #endif +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + ssl_set_alpn_protocols( &ssl, alpn_list ); +#endif + ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg ); ssl_set_dbg( &ssl, my_debug, stdout ); @@ -878,6 +925,15 @@ int main( int argc, char *argv[] ) printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n", ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) ); +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + { + const char *alp = ssl_get_alpn_protocol( &ssl ); + printf( " [ Application Layer Protocol is %s ]\n", + alp ? alp : "(none)" ); + } +#endif + if( opt.reconnect != 0 ) { printf(" . Saving session for reuse..." ); diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 4e199c38a..758188b6c 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -85,6 +85,7 @@ #define DFL_CACHE_MAX -1 #define DFL_CACHE_TIMEOUT -1 #define DFL_SNI NULL +#define DFL_ALPN_STRING NULL #define LONG_RESPONSE "

01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \ "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \ @@ -131,6 +132,7 @@ struct options int cache_max; /* max number of session cache entries */ int cache_timeout; /* expiration delay of session cache entries */ char *sni; /* string decribing sni information */ + const char *alpn_string; /* ALPN supported protocols */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -245,6 +247,14 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len ) #define USAGE_MAX_FRAG_LEN "" #endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ +#if defined(POLARSSL_SSL_ALPN) +#define USAGE_ALPN \ + " alpn=%%s default: \"\" (disabled)\n" \ + " example: spdy/1,http/1.1\n" +#else +#define USAGE_ALPN "" +#endif /* POLARSSL_SSL_ALPN */ + #define USAGE \ "\n usage: ssl_server2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -267,6 +277,7 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len ) USAGE_TICKETS \ USAGE_CACHE \ USAGE_MAX_FRAG_LEN \ + USAGE_ALPN \ "\n" \ " min_version=%%s default: \"ssl3\"\n" \ " max_version=%%s default: \"tls1_2\"\n" \ @@ -429,6 +440,9 @@ int main( int argc, char *argv[] ) #if defined(POLARSSL_SNI) sni_entry *sni_info = NULL; #endif +#if defined(POLARSSL_SSL_ALPN) + const char *alpn_list[10]; +#endif #if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C) unsigned char alloc_buf[100000]; #endif @@ -456,6 +470,9 @@ int main( int argc, char *argv[] ) #if defined(POLARSSL_SSL_CACHE_C) ssl_cache_init( &cache ); #endif +#if defined(POLARSSL_SSL_ALPN) + memset( alpn_list, 0, sizeof alpn_list ); +#endif if( argc == 0 ) { @@ -504,6 +521,7 @@ int main( int argc, char *argv[] ) opt.cache_max = DFL_CACHE_MAX; opt.cache_timeout = DFL_CACHE_TIMEOUT; opt.sni = DFL_SNI; + opt.alpn_string = DFL_ALPN_STRING; for( i = 1; i < argc; i++ ) { @@ -653,6 +671,10 @@ int main( int argc, char *argv[] ) else goto usage; } + else if( strcmp( p, "alpn" ) == 0 ) + { + opt.alpn_string = q; + } else if( strcmp( p, "tickets" ) == 0 ) { opt.tickets = atoi( q ); @@ -760,6 +782,26 @@ int main( int argc, char *argv[] ) } #endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + { + p = (char *) opt.alpn_string; + i = 0; + + /* Leave room for a final NULL in alpn_list */ + while( i < (int) sizeof alpn_list - 1 && *p != '\0' ) + { + alpn_list[i++] = p; + + /* Terminate the current string and move on to next one */ + while( *p != ',' && *p != '\0' ) + p++; + if( *p == ',' ) + *p++ = '\0'; + } + } +#endif /* POLARSSL_SSL_ALPN */ + /* * 0. Initialize the RNG and the session data */ @@ -974,6 +1016,11 @@ int main( int argc, char *argv[] ) ssl_set_max_frag_len( &ssl, opt.mfl_code ); #endif +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + ssl_set_alpn_protocols( &ssl, alpn_list ); +#endif + ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg ); ssl_set_dbg( &ssl, my_debug, stdout ); @@ -1103,6 +1150,15 @@ reset: printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n", ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) ); +#if defined(POLARSSL_SSL_ALPN) + if( opt.alpn_string != NULL ) + { + const char *alp = ssl_get_alpn_protocol( &ssl ); + printf( " [ Application Layer Protocol is %s ]\n", + alp ? alp : "(none)" ); + } +#endif + #if defined(POLARSSL_X509_CRT_PARSE_C) /* * 5. Verify the server certificate 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 3/7] 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 e4a8dc763..aa5daf414 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 b509e376e..999a39b42 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 eb293718e..0365b92b3 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 ) From 89e35798aec47c2a1eda1b0bc32d2914f002e1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 7 Apr 2014 12:10:30 +0200 Subject: [PATCH 4/7] Implement ALPN server-side --- include/polarssl/ssl.h | 1 + library/ssl_srv.c | 114 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index aa5daf414..c866b6ffd 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -320,6 +320,7 @@ #define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ #define SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ #define SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */ +#define SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */ #define SSL_HS_HELLO_REQUEST 0 #define SSL_HS_CLIENT_HELLO 1 diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 16fb264dc..2cbc79853 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -683,6 +683,69 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_SESSION_TICKETS */ +#if defined(POLARSSL_SSL_ALPN) +static int ssl_parse_alpn_ext( ssl_context *ssl, + unsigned char *buf, size_t len ) +{ + size_t list_len, cur_len; + const unsigned char *theirs, *start, *end; + const char **ours; + + /* If ALPN not configured, just ignore the extension */ + if( ssl->alpn_list == NULL ) + return( 0 ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + + /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ + if( len < 4 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + list_len = ( buf[0] << 8 ) | buf[1]; + if( list_len != len - 2 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + /* + * Use our order of preference + */ + start = buf + 2; + end = buf + len; + for( ours = ssl->alpn_list; *ours != NULL; ours++ ) + { + for( theirs = start; theirs != end; theirs += cur_len ) + { + /* If the list is well formed, we should get equality first */ + if( theirs > end ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + cur_len = *theirs++; + + /* Empty strings MUST NOT be included */ + if( cur_len == 0 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + if( cur_len == strlen( *ours ) && + memcmp( theirs, *ours, cur_len ) == 0 ) + { + ssl->alpn_chosen = *ours; + return( 0 ); + } + } + } + + /* If we get there, no match was found */ + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); +} +#endif /* POLARSSL_SSL_ALPN */ + /* * Auxiliary functions for ServerHello parsing and related actions */ @@ -1385,6 +1448,16 @@ static int ssl_parse_client_hello( ssl_context *ssl ) break; #endif /* POLARSSL_SSL_SESSION_TICKETS */ +#if defined(POLARSSL_SSL_ALPN) + case TLS_EXT_ALPN: + SSL_DEBUG_MSG( 3, ( "found ALPN extension" ) ); + + ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); @@ -1625,6 +1698,42 @@ static void ssl_write_supported_point_formats_ext( ssl_context *ssl, } #endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ +#if defined(POLARSSL_SSL_ALPN ) +static void ssl_write_alpn_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + if( ssl->alpn_chosen == NULL ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, alpn extension" ) ); + + /* + * 0 . 1 ext identifier + * 2 . 3 ext length + * 4 . 5 protocol list length + * 6 . 6 protocol name length + * 7 . 7+n protocol name + */ + buf[0] = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF ); + buf[1] = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF ); + + *olen = 7 + strlen( ssl->alpn_chosen ); + + buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF ); + buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF ); + + buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF ); + buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF ); + + buf[6] = (unsigned char)( ( ( *olen - 7 ) ) & 0xFF ); + + memcpy( buf + 7, ssl->alpn_chosen, *olen - 7 ); +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + static int ssl_write_server_hello( ssl_context *ssl ) { #if defined(POLARSSL_HAVE_TIME) @@ -1791,6 +1900,11 @@ static int ssl_write_server_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, ( "server hello, total extension length: %d", ext_len ) ); *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); From f6521de17b8f98a8b1876cb3a9e0cf9fe637ab57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 7 Apr 2014 12:42:04 +0200 Subject: [PATCH 5/7] Add ALPN tests to ssl-opt.sh Only self-op for now, required peer versions are a bit high: - OpenSSL 1.0.2-beta - GnuTLS 3.2.0 (released 2013-05-10) (gnutls-cli only) --- library/ssl_cli.c | 2 +- library/ssl_srv.c | 4 +-- tests/ssl-opt.sh | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 999a39b42..0a69f4d37 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -396,7 +396,7 @@ static void ssl_write_alpn_ext( ssl_context *ssl, return; } - SSL_DEBUG_MSG( 3, ( "client hello, adding ALPN extension" ) ); + SSL_DEBUG_MSG( 3, ( "client hello, adding alpn extension" ) ); *p++ = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF ); *p++ = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF ); diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 2cbc79853..08f6eea67 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -1450,7 +1450,7 @@ static int ssl_parse_client_hello( ssl_context *ssl ) #if defined(POLARSSL_SSL_ALPN) case TLS_EXT_ALPN: - SSL_DEBUG_MSG( 3, ( "found ALPN extension" ) ); + SSL_DEBUG_MSG( 3, ( "found alpn extension" ) ); ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ); if( ret != 0 ) @@ -1708,7 +1708,7 @@ static void ssl_write_alpn_ext( ssl_context *ssl, return; } - SSL_DEBUG_MSG( 3, ( "server hello, alpn extension" ) ); + SSL_DEBUG_MSG( 3, ( "server hello, adding alpn extension" ) ); /* * 0 . 1 ext identifier diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 60efe8d83..cc89bfe46 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -804,6 +804,8 @@ run_test "Non-blocking I/O #7 (session-id resume)" \ -C "ssl_handshake returned" \ -c "Read from server: .* bytes read" +# Tests for version negotiation + run_test "Version check #1 (all -> 1.2)" \ "$P_SRV" \ "$P_CLI" \ @@ -874,6 +876,92 @@ run_test "Version check #8 (srv min 1.2, cli max 1.1 -> fail)" \ -c "ssl_handshake returned" \ -s "SSL - Handshake protocol not within min/max boundaries" +# Tests for ALPN extension + +run_test "ALPN #0 (none)" \ + "$P_SRV debug_level=4" \ + "$P_CLI debug_level=4" \ + 0 \ + -C "client hello, adding alpn extension" \ + -S "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -S "server hello, adding alpn extension" \ + -C "found alpn extension " \ + -C "Application Layer Protocol is" \ + -S "Application Layer Protocol is" + +run_test "ALPN #1 (client only)" \ + "$P_SRV debug_level=4" \ + "$P_CLI debug_level=4 alpn=abc,1234" \ + 0 \ + -c "client hello, adding alpn extension" \ + -s "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -S "server hello, adding alpn extension" \ + -C "found alpn extension " \ + -c "Application Layer Protocol is (none)" \ + -S "Application Layer Protocol is" + +run_test "ALPN #2 (server only)" \ + "$P_SRV debug_level=4 alpn=abc,1234" \ + "$P_CLI debug_level=4" \ + 0 \ + -C "client hello, adding alpn extension" \ + -S "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -S "server hello, adding alpn extension" \ + -C "found alpn extension " \ + -C "Application Layer Protocol is" \ + -s "Application Layer Protocol is (none)" + +run_test "ALPN #3 (both, common cli1-srv1)" \ + "$P_SRV debug_level=4 alpn=abc,1234" \ + "$P_CLI debug_level=4 alpn=abc,1234" \ + 0 \ + -c "client hello, adding alpn extension" \ + -s "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -s "server hello, adding alpn extension" \ + -c "found alpn extension" \ + -c "Application Layer Protocol is abc" \ + -s "Application Layer Protocol is abc" + +run_test "ALPN #4 (both, common cli2-srv1)" \ + "$P_SRV debug_level=4 alpn=abc,1234" \ + "$P_CLI debug_level=4 alpn=1234,abc" \ + 0 \ + -c "client hello, adding alpn extension" \ + -s "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -s "server hello, adding alpn extension" \ + -c "found alpn extension" \ + -c "Application Layer Protocol is abc" \ + -s "Application Layer Protocol is abc" + +run_test "ALPN #5 (both, common cli1-srv2)" \ + "$P_SRV debug_level=4 alpn=abc,1234" \ + "$P_CLI debug_level=4 alpn=1234,abcde" \ + 0 \ + -c "client hello, adding alpn extension" \ + -s "found alpn extension" \ + -C "got an alert message, type: \\[2:120]" \ + -s "server hello, adding alpn extension" \ + -c "found alpn extension" \ + -c "Application Layer Protocol is 1234" \ + -s "Application Layer Protocol is 1234" + +run_test "ALPN #6 (both, no common)" \ + "$P_SRV debug_level=4 alpn=abc,123" \ + "$P_CLI debug_level=4 alpn=1234,abcde" \ + 1 \ + -c "client hello, adding alpn extension" \ + -s "found alpn extension" \ + -c "got an alert message, type: \\[2:120]" \ + -S "server hello, adding alpn extension" \ + -C "found alpn extension" \ + -C "Application Layer Protocol is 1234" \ + -S "Application Layer Protocol is 1234" + # Final report echo "------------------------------------------------------------------------" From 83d8c73c91344b0487983b2b1b9d9b141579a347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 7 Apr 2014 13:24:21 +0200 Subject: [PATCH 6/7] Disable ALPN by default --- include/polarssl/config.h | 2 +- tests/ssl-opt.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 78102f0a8..f6ea78cdd 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -871,7 +871,7 @@ * * Uncomment this macro to enable support for ALPN. */ -#define POLARSSL_SSL_ALPN +//#define POLARSSL_SSL_ALPN /** * \def POLARSSL_SSL_SESSION_TICKETS diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index cc89bfe46..916ce77ec 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -21,6 +21,8 @@ O_CLI="echo 'GET / HTTP/1.0' | $OPENSSL_CMD s_client" TESTS=0 FAILS=0 +CONFIG_H='../include/polarssl/config.h' + MEMCHECK=0 FILTER='.*' EXCLUDE='SSLv2' # disabled by default, needs OpenSSL compiled with SSLv2 @@ -878,6 +880,8 @@ run_test "Version check #8 (srv min 1.2, cli max 1.1 -> fail)" \ # Tests for ALPN extension +if grep '^#define POLARSSL_SSL_ALPN' $CONFIG_H >/dev/null; then + run_test "ALPN #0 (none)" \ "$P_SRV debug_level=4" \ "$P_CLI debug_level=4" \ @@ -962,6 +966,8 @@ run_test "ALPN #6 (both, no common)" \ -C "Application Layer Protocol is 1234" \ -S "Application Layer Protocol is 1234" +fi + # Final report echo "------------------------------------------------------------------------" From 27e36d342c525d7fc2923acf2cdd069d5aa54838 Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Tue, 8 Apr 2014 12:33:37 +0200 Subject: [PATCH 7/7] Support for the ALPN SSL extension (re-enabled in config.h) --- ChangeLog | 5 +++++ include/polarssl/config.h | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4e2d97be5..6d4228f1f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,12 @@ PolarSSL ChangeLog (Sorted per branch, date) +ABI Alert: ALPN changes the ABI for the next release. + = PolarSSL 1.3 branch +Features + * Support for the ALPN SSL extension + Changes * x509_crt_info() now prints information about parsed extensions as well diff --git a/include/polarssl/config.h b/include/polarssl/config.h index f6ea78cdd..223fce382 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -866,12 +866,9 @@ * Enable support for Application Layer Protocol Negotiation. * draft-ietf-tls-applayerprotoneg-05 * - * This is disabled by default in the 1.3.x line since it breaks ABI - * compatibility. - * - * Uncomment this macro to enable support for ALPN. + * Comment this macro to disable support for ALPN. */ -//#define POLARSSL_SSL_ALPN +#define POLARSSL_SSL_ALPN /** * \def POLARSSL_SSL_SESSION_TICKETS