diff --git a/ChangeLog b/ChangeLog index bfe342126..f1fb20d4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,7 @@ Features * Added Secure Renegotiation (RFC 5746) * Added predefined DHM groups from RFC 5114 * Added simple SSL session cache implementation + * Added ServerName extension parsing (SNI) at server side Changes * Removed redundant POLARSSL_DEBUG_MSG define diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 62ffba2d3..094a120dd 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -227,6 +227,7 @@ #define SSL_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ #define SSL_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ #define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ +#define SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ #define SSL_HS_HELLO_REQUEST 0 #define SSL_HS_CLIENT_HELLO 1 @@ -399,6 +400,7 @@ struct _ssl_context int (*f_vrfy)(void *, x509_cert *, int, int); int (*f_get_cache)(void *, ssl_session *); int (*f_set_cache)(void *, const ssl_session *); + int (*f_sni)(void *, ssl_context *, const unsigned char *, size_t); void *p_rng; /*!< context for the RNG function */ void *p_dbg; /*!< context for the debug function */ @@ -407,6 +409,7 @@ struct _ssl_context void *p_vrfy; /*!< context for verification */ void *p_get_cache; /*!< context for cache retrieval */ void *p_set_cache; /*!< context for cache store */ + void *p_sni; /*!< context for SNI extension */ /* * Session layer @@ -780,7 +783,8 @@ int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ); #endif /** - * \brief Set hostname for ServerName TLS Extension + * \brief Set hostname for ServerName TLS extension + * (client-side only) * * * \param ssl SSL context @@ -790,6 +794,30 @@ int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ); */ int ssl_set_hostname( ssl_context *ssl, const char *hostname ); +/** + * \brief Set server side ServerName TLS extension callback + * (optional, server-side only). + * + * If set, the ServerName callback is called whenever the + * server receives a ServerName TLS extension from the client + * during a handshake. The ServerName callback has the + * following parameters: (void *parameter, ssl_context *ssl, + * const unsigned char *hostname, size_t len). If a suitable + * certificate is found, the callback should set the + * certificate and key to use with ssl_set_own_cert() (and + * possibly adjust the CA chain as well) and return 0. The + * callback should return -1 to abort the handshake at this + * point. + * + * \param ssl SSL context + * \param f_sni verification function + * \param p_sni verification parameter + */ +void ssl_set_sni( ssl_context *ssl, + int (*f_sni)(void *, ssl_context *, const unsigned char *, + size_t), + void *p_sni ); + /** * \brief Set the maximum supported version sent from the client side * diff --git a/library/ssl_srv.c b/library/ssl_srv.c index e31145864..da3401536 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -34,6 +34,49 @@ #include #include +static int ssl_parse_servername_ext( ssl_context *ssl, + unsigned char *buf, + size_t len ) +{ + int ret; + size_t servername_list_size, hostname_len; + unsigned char *p; + + servername_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); + if( servername_list_size + 2 != len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + p = buf + 2; + while( servername_list_size > 0 ) + { + hostname_len = ( ( p[1] << 8 ) | p[2] ); + if( hostname_len + 3 > servername_list_size ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( p[0] == TLS_EXT_SERVERNAME_HOSTNAME ) + { + ret = ssl->f_sni( ssl->p_sni, ssl, p + 3, hostname_len ); + if( ret != 0 ) + { + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_UNRECOGNIZED_NAME ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + break; + } + + servername_list_size -= hostname_len + 3; + } + + return( 0 ); +} + static int ssl_parse_renegotiation_info( ssl_context *ssl, unsigned char *buf, size_t len ) @@ -330,6 +373,16 @@ have_ciphersuite: } switch( ext_id ) { + case TLS_EXT_SERVERNAME: + SSL_DEBUG_MSG( 3, ( "found ServerName extension" ) ); + if( ssl->f_sni == NULL ) + break; + + ret = ssl_parse_servername_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; + case TLS_EXT_RENEGOTIATION_INFO: SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) ); renegotiation_info_seen = 1; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index cc0f65c57..a33a56685 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3101,6 +3101,15 @@ int ssl_set_hostname( ssl_context *ssl, const char *hostname ) return( 0 ); } +void ssl_set_sni( ssl_context *ssl, + int (*f_sni)(void *, ssl_context *, + const unsigned char *, size_t), + void *p_sni ) +{ + ssl->f_sni = f_sni; + ssl->p_sni = p_sni; +} + void ssl_set_max_version( ssl_context *ssl, int major, int minor ) { ssl->max_major_ver = major;