From 5701cdcd029bf64592e8d302e288b2fed2c343a5 Mon Sep 17 00:00:00 2001
From: Paul Bakker
Date: Thu, 27 Sep 2012 21:49:42 +0000
Subject: [PATCH] - Added ServerName extension parsing (SNI) at server side
---
ChangeLog | 1 +
include/polarssl/ssl.h | 30 +++++++++++++++++++++++-
library/ssl_srv.c | 53 ++++++++++++++++++++++++++++++++++++++++++
library/ssl_tls.c | 9 +++++++
4 files changed, 92 insertions(+), 1 deletion(-)
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;