From 747180391dc335985fe8f70aabdc3fe9f69d9559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 30 Jul 2013 12:41:56 +0200 Subject: [PATCH 01/24] Add ssl_get_session() to save session on client --- include/polarssl/ssl.h | 25 ++++++++++++++++++++----- library/ssl_tls.c | 25 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index a4634600b..3c8f1e6a5 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -774,13 +774,10 @@ void ssl_set_session_cache( ssl_context *ssl, * \brief Request resumption of session (client-side only) * Session data is copied from presented session structure. * - * Warning: session.peer_cert is cleared by the SSL/TLS layer on - * connection shutdown, so do not cache the pointer! Either set - * it to NULL or make a full copy of the certificate when - * storing the session for use in this function. - * * \param ssl SSL context * \param session session context + * + * \sa ssl_get_session() */ void ssl_set_session( ssl_context *ssl, const ssl_session *session ); @@ -1100,6 +1097,24 @@ const char *ssl_get_version( const ssl_context *ssl ); const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ); #endif /* POLARSSL_X509_PARSE_C */ +/** + * \brief Save session in order to resume it later (client-side only) + * Session data is copied to presented session structure. + * + * \warning Currently, peer certificate is lost in the operation. + * + * \param ssl SSL context + * \param session session context + * + * \return 0 if successful, + * POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or + * arguments are otherwise invalid + * + * \sa ssl_set_session() + */ +int ssl_get_session( const ssl_context *ssl, ssl_session *session ); + /** * \brief Perform the SSL handshake * diff --git a/library/ssl_tls.c b/library/ssl_tls.c index b9fca4440..6ecdceb70 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3222,6 +3222,31 @@ const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ) } #endif /* POLARSSL_X509_PARSE_C */ +int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) +{ + ssl_session *src; + + if( ssl == NULL || + dst == NULL || + ssl->session == NULL || + ssl->endpoint != SSL_IS_CLIENT ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + src = ssl->session; + + ssl_session_free( dst ); + memcpy( dst, src, sizeof( ssl_session ) ); + + /* + * For now, just set peer_cert to NULL, deep-copy not implemented yet + */ + dst->peer_cert = NULL; + + return( 0 ); +} + /* * Perform a single step of the SSL handshake */ From aaa1eab55a9dbba109ea3b54041828a8a2bf3f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 30 Jul 2013 13:43:43 +0200 Subject: [PATCH 02/24] Add an option to reconnect in ssl_client2 Purpose: test resuming sessions. --- programs/ssl/ssl_client2.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 05dfdc700..e8d38200c 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -59,6 +59,7 @@ #define DFL_AUTH_MODE SSL_VERIFY_OPTIONAL #define DFL_MFL_CODE SSL_MAX_FRAG_LEN_NONE #define DFL_TRUNC_HMAC 0 +#define DFL_RECONNECT 0 #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-" \ @@ -96,6 +97,7 @@ struct options int auth_mode; /* verify mode for connection */ unsigned char mfl_code; /* code for maximum fragment length */ int trunc_hmac; /* negotiate truncated hmac or not */ + int reconnect; /* attempt to resume session */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -184,6 +186,7 @@ static int my_verify( void *data, x509_cert *crt, int depth, int *flags ) " request_page=%%s default: \".\"\n" \ " renegotiation=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ + " reconnect=%%d default: 0 (disabled)\n" \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -226,6 +229,7 @@ int main( int argc, char *argv[] ) entropy_context entropy; ctr_drbg_context ctr_drbg; ssl_context ssl; + ssl_session saved_session; #if defined(POLARSSL_X509_PARSE_C) x509_cert cacert; x509_cert clicert; @@ -239,6 +243,7 @@ int main( int argc, char *argv[] ) */ server_fd = 0; memset( &ssl, 0, sizeof( ssl_context ) ); + memset( &saved_session, 0, sizeof( ssl_session ) ); #if defined(POLARSSL_X509_PARSE_C) memset( &cacert, 0, sizeof( x509_cert ) ); memset( &clicert, 0, sizeof( x509_cert ) ); @@ -285,6 +290,7 @@ int main( int argc, char *argv[] ) opt.auth_mode = DFL_AUTH_MODE; opt.mfl_code = DFL_MFL_CODE; opt.trunc_hmac = DFL_TRUNC_HMAC; + opt.reconnect = DFL_RECONNECT; for( i = 1; i < argc; i++ ) { @@ -345,6 +351,12 @@ int main( int argc, char *argv[] ) if( opt.allow_legacy < 0 || opt.allow_legacy > 1 ) goto usage; } + else if( strcmp( p, "reconnect" ) == 0 ) + { + opt.reconnect = atoi( q ); + if( opt.reconnect < 0 || opt.reconnect > 1 ) + goto usage; + } else if( strcmp( p, "min_version" ) == 0 ) { if( strcmp( q, "ssl3" ) == 0 ) @@ -693,6 +705,20 @@ int main( int argc, char *argv[] ) printf( " ok\n [ Ciphersuite is %s ]\n", ssl_get_ciphersuite( &ssl ) ); + if( opt.reconnect != 0 ) + { + printf(" . Saving session for reuse..." ); + fflush( stdout ); + + if( ( ret = ssl_get_session( &ssl, &saved_session ) ) != 0 ) + { + printf( " failed\n ! ssl_get_session returned -0x%x\n\n", -ret ); + goto exit; + } + + printf( " ok\n" ); + } + #if defined(POLARSSL_X509_PARSE_C) /* * 5. Verify the server certificate @@ -732,6 +758,7 @@ int main( int argc, char *argv[] ) /* * 6. Write the GET request */ +send_request: printf( " > Write to server:" ); fflush( stdout ); @@ -789,6 +816,43 @@ int main( int argc, char *argv[] ) ssl_close_notify( &ssl ); + if( opt.reconnect != 0 ) + { + opt.reconnect = 0; + + printf( " . Reconnecting with saved session..." ); + fflush( stdout ); + + if( ( ret = ssl_session_reset( &ssl ) ) != 0 ) + { + printf( " failed\n ! ssl_session_reset returned -0x%x\n\n", -ret ); + goto exit; + } + + ssl_set_session( &ssl, &saved_session ); + + if( ( ret = net_connect( &server_fd, opt.server_name, + opt.server_port ) ) != 0 ) + { + printf( " failed\n ! net_connect returned -0x%x\n\n", -ret ); + goto exit; + } + + while( ( ret = ssl_handshake( &ssl ) ) != 0 ) + { + if( ret != POLARSSL_ERR_NET_WANT_READ && + ret != POLARSSL_ERR_NET_WANT_WRITE ) + { + printf( " failed\n ! ssl_handshake returned -0x%x\n\n", -ret ); + goto exit; + } + } + + printf( " ok\n" ); + + goto send_request; + } + exit: #ifdef POLARSSL_ERROR_C From 5f280cc6cf62285a4a236ad580707f0a020bd46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 30 Jul 2013 16:58:14 +0200 Subject: [PATCH 03/24] Implement saving peer cert as part of session. --- library/ssl_tls.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 6ecdceb70..5337895d4 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3224,6 +3224,7 @@ const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ) int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) { + int ret; ssl_session *src; if( ssl == NULL || @@ -3239,10 +3240,23 @@ int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) ssl_session_free( dst ); memcpy( dst, src, sizeof( ssl_session ) ); - /* - * For now, just set peer_cert to NULL, deep-copy not implemented yet - */ - dst->peer_cert = NULL; +#if defined(POLARSSL_X509_PARSE_C) + if( src->peer_cert != NULL ) + { + if( ( dst->peer_cert = polarssl_malloc( sizeof(x509_cert) ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memset( dst->peer_cert, 0, sizeof(x509_cert) ); + + if( ( ret = x509parse_crt( dst->peer_cert, src->peer_cert->raw.p, + src->peer_cert->raw.len ) != 0 ) ) + { + polarssl_free( dst->peer_cert ); + dst->peer_cert = NULL; + return( ret ); + } + } +#endif /* POLARSSL_X509_PARSE_C */ return( 0 ); } From 75d440192ca3dfda6656ea1ef93fc0ff3a26f9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 14:44:04 +0200 Subject: [PATCH 04/24] Introduce ticket field in session structure --- include/polarssl/ssl.h | 3 +++ library/ssl_tls.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 3c8f1e6a5..2ffb54977 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -338,6 +338,9 @@ struct _ssl_session x509_cert *peer_cert; /*!< peer X.509 cert chain */ #endif /* POLARSSL_X509_PARSE_C */ + unsigned char *ticket; /*!< RFC 5077 session ticket */ + size_t ticket_len; /*!< session ticket length */ + unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ int trunc_hmac; /*!< flag for truncated hmac activation */ }; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 5337895d4..7e8ff343c 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3258,6 +3258,14 @@ int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) } #endif /* POLARSSL_X509_PARSE_C */ + if( src->ticket != NULL ) + { + if( ( dst->ticket = polarssl_malloc( src->ticket_len ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memcpy( dst->ticket, src->ticket, src->ticket_len ); + } + return( 0 ); } @@ -3579,6 +3587,8 @@ void ssl_session_free( ssl_session *session ) } #endif + polarssl_free( session->ticket ); + memset( session, 0, sizeof( ssl_session ) ); } From 60182ef9892acf4d5492fbb65545f8dc8c551484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 14:44:54 +0200 Subject: [PATCH 05/24] ssl_cli: write & parse session ticket extension --- include/polarssl/ssl.h | 2 ++ library/ssl_cli.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 2ffb54977..48263e554 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -262,6 +262,8 @@ #define TLS_EXT_SIG_ALG 13 +#define TLS_EXT_SESSION_TICKET 35 + #define TLS_EXT_RENEGOTIATION_INFO 0xFF01 /* diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 877d6cddd..9b64c9a3d 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -315,6 +315,35 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, *olen = 4; } +static void ssl_write_session_ticket_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + size_t tlen = ssl->session_negotiate->ticket_len; + + SSL_DEBUG_MSG( 3, ( "client hello, adding session ticket extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET ) & 0xFF ); + + *p++ = (unsigned char)( ( tlen >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( tlen ) & 0xFF ); + + *olen = 4; + + if( ssl->session_negotiate->ticket == NULL || + ssl->session_negotiate->ticket_len == 0 ) + { + return; + } + + SSL_DEBUG_MSG( 3, ( "sending session ticket of length %d", tlen ) ); + + memcpy( p, ssl->session_negotiate->ticket, tlen ); + + *olen += tlen; +} + static int ssl_write_client_hello( ssl_context *ssl ) { int ret; @@ -488,6 +517,9 @@ static int ssl_write_client_hello( ssl_context *ssl ) ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; + ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; + SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d", ext_len ) ); @@ -587,6 +619,19 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, return( 0 ); } +static int ssl_parse_session_ticket_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( len != 0 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + ((void) buf); + ((void) ssl); + + return( 0 ); +} + static int ssl_parse_server_hello( ssl_context *ssl ) { uint32_t t; @@ -825,6 +870,17 @@ static int ssl_parse_server_hello( ssl_context *ssl ) break; + case TLS_EXT_SESSION_TICKET: + SSL_DEBUG_MSG( 3, ( "found session_ticket extension" ) ); + + if( ( ret = ssl_parse_session_ticket_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); From a5cc6025e76ad3004b9ceccc38b783a54d161ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Wed, 31 Jul 2013 12:58:16 +0200 Subject: [PATCH 06/24] Parse NewSessionTicket message --- include/polarssl/error.h | 2 +- include/polarssl/ssl.h | 9 +++- library/error.c | 2 + library/ssl_cli.c | 108 ++++++++++++++++++++++++++++++++++++++- library/ssl_tls.c | 5 ++ 5 files changed, 122 insertions(+), 4 deletions(-) diff --git a/include/polarssl/error.h b/include/polarssl/error.h index f8c23e686..48de009ee 100644 --- a/include/polarssl/error.h +++ b/include/polarssl/error.h @@ -84,7 +84,7 @@ * ECP 4 4 (Started from top) * MD 5 4 * CIPHER 6 5 - * SSL 6 2 (Started from top) + * SSL 6 4 (Started from top) * SSL 7 31 * * Module dependent error code (5 bits 0x.08.-0x.F8.) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 48263e554..6d00c9dc0 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -107,6 +107,8 @@ #define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80 /**< Hardware acceleration function skipped / left alone data */ #define POLARSSL_ERR_SSL_COMPRESSION_FAILED -0x6F00 /**< Processing of the compression / decompression failed */ #define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION -0x6E80 /**< Handshake protocol not within min/max boundaries */ +#define POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET -0x6E00 /**< Processing of the NewSessionTicket handshake message failed. */ + /* * Various constants @@ -239,6 +241,7 @@ #define SSL_HS_HELLO_REQUEST 0 #define SSL_HS_CLIENT_HELLO 1 #define SSL_HS_SERVER_HELLO 2 +#define SSL_HS_NEW_SESSION_TICKET 4 #define SSL_HS_CERTIFICATE 11 #define SSL_HS_SERVER_KEY_EXCHANGE 12 #define SSL_HS_CERTIFICATE_REQUEST 13 @@ -313,7 +316,8 @@ typedef enum SSL_SERVER_FINISHED, SSL_FLUSH_BUFFERS, SSL_HANDSHAKE_WRAPUP, - SSL_HANDSHAKE_OVER + SSL_HANDSHAKE_OVER, + SSL_SERVER_NEW_SESSION_TICKET, } ssl_states; @@ -342,6 +346,7 @@ struct _ssl_session unsigned char *ticket; /*!< RFC 5077 session ticket */ size_t ticket_len; /*!< session ticket length */ + uint32_t ticket_lifetime; /*!< ticket lifetime hint */ unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ int trunc_hmac; /*!< flag for truncated hmac activation */ @@ -433,6 +438,8 @@ struct _ssl_handshake_params int resume; /*!< session resume indicator*/ int max_major_ver; /*!< max. major version client*/ int max_minor_ver; /*!< max. minor version client*/ + + int new_session_ticket; /*!< use NewSessionTicket? */ }; struct _ssl_context diff --git a/library/error.c b/library/error.c index 560c54cff..94d8dc11d 100644 --- a/library/error.c +++ b/library/error.c @@ -369,6 +369,8 @@ void polarssl_strerror( int ret, char *buf, size_t buflen ) snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" ); if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) ) snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET) ) + snprintf( buf, buflen, "SSL - Processing of the NewSessionTicket handshake message failed" ); #endif /* POLARSSL_SSL_TLS_C */ #if defined(POLARSSL_X509_PARSE_C) diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 9b64c9a3d..f572cabcc 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -30,6 +30,13 @@ #include "polarssl/debug.h" #include "polarssl/ssl.h" +#if defined(POLARSSL_MEMORY_C) +#include "polarssl/memory.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + #include #include @@ -627,7 +634,8 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); ((void) buf); - ((void) ssl); + + ssl->handshake->new_session_ticket = 1; return( 0 ); } @@ -1890,6 +1898,97 @@ static int ssl_write_certificate_verify( ssl_context *ssl ) !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +static int ssl_parse_new_session_ticket( ssl_context *ssl ) +{ + int ret; + uint32_t lifetime; + size_t ticket_len; + unsigned char *ticket; + + SSL_DEBUG_MSG( 2, ( "=> parse new session ticket" ) ); + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 0 . 0 handshake message type + * 1 . 3 handshake message length + * 4 . 7 ticket_lifetime_hint + * 8 . 9 ticket_len (n) + * 10 . 9+n ticket content + */ + if( ssl->in_msg[0] != SSL_HS_NEW_SESSION_TICKET || + ssl->in_hslen < 10 ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET ); + } + + lifetime = ( ssl->in_msg[4] << 24 ) | ( ssl->in_msg[5] << 16 ) | + ( ssl->in_msg[6] << 8 ) | ( ssl->in_msg[7] ); + + ticket_len = ( ssl->in_msg[8] << 8 ) | ( ssl->in_msg[9] ); + + if( ticket_len + 10 != ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET ); + } + + ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; + + SSL_DEBUG_MSG( 3, ( "ticket length: %d", ticket_len ) ); + + /* + * Zero-length ticket means the server changed his mind and doesn't want + * to send a ticket after all, so just forget it + */ + if( ticket_len == 0) + return( 0 ); + + polarssl_free( ssl->session_negotiate->ticket ); + ssl->session_negotiate->ticket = NULL; + ssl->session_negotiate->ticket_len = 0; + + if( ( ticket = polarssl_malloc( ticket_len ) ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "ticket malloc failed" ) ); + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + memcpy( ticket, ssl->in_msg + 10, ticket_len ); + + ssl->session_negotiate->ticket = ticket; + ssl->session_negotiate->ticket_len = ticket_len; + ssl->session_negotiate->ticket_lifetime = lifetime; + + /* + * RFC 5077 section 3.4: + * "If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello." + */ + SSL_DEBUG_MSG( 3, ( "ticket in use, discarding session id" ) ); + ssl->session_negotiate->length = 0; + + SSL_DEBUG_MSG( 2, ( "<= parse new session ticket" ) ); + + return( 0 ); +} + /* * SSL handshake -- client side -- single step */ @@ -1973,9 +2072,14 @@ int ssl_handshake_client_step( ssl_context *ssl ) break; /* - * <== ChangeCipherSpec + * <== ( NewSessionTicket ) + * ChangeCipherSpec * Finished */ + case SSL_SERVER_NEW_SESSION_TICKET: + ret = ssl_parse_new_session_ticket( ssl ); + break; + case SSL_SERVER_CHANGE_CIPHER_SPEC: ret = ssl_parse_change_cipher_spec( ssl ); break; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 7e8ff343c..bf45ec873 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2619,6 +2619,11 @@ int ssl_write_finished( ssl_context *ssl ) else ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; } + else if( ssl->endpoint == SSL_IS_CLIENT && + ssl->handshake->new_session_ticket != 0 ) + { + ssl->state = SSL_SERVER_NEW_SESSION_TICKET; + } else ssl->state++; From 6377e41ef56f8303ccc58a4ca82f3bf99495018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Wed, 31 Jul 2013 16:31:33 +0200 Subject: [PATCH 07/24] Complete client support for session tickets --- library/ssl_cli.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/library/ssl_cli.c b/library/ssl_cli.c index f572cabcc..9b643926d 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -431,7 +431,25 @@ static int ssl_write_client_hello( ssl_context *ssl ) if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || n < 16 || n > 32 || ssl->handshake->resume == 0 ) + { n = 0; + } + + /* + * RFC 5077 section 3.4: "When presenting a ticket, the client MAY + * generate and include a Session ID in the TLS ClientHello." + */ + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ssl->session_negotiate->ticket != NULL && + ssl->session_negotiate->ticket_len != 0 ) + { + ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, 32 ); + + if( ret != 0 ) + return( ret ); + + ssl->session_negotiate->length = n = 32; + } *p++ = (unsigned char) n; From 7a358b85803d085c068c72b288085af587de5e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 1 Aug 2013 11:47:56 +0200 Subject: [PATCH 08/24] ssl_srv: write & parse session ticket ext & msg --- library/ssl_srv.c | 106 +++++++++++++++++++++++++++++++++++++++++++++- library/ssl_tls.c | 5 +++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 6cbc5f406..ad5926213 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -323,6 +323,26 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, return( 0 ); } +static int ssl_parse_session_ticket_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + /* + * Remember the client asked for a ticket + */ + ssl->handshake->new_session_ticket = 1; + + if( len == 0 ) + return( 0 ); + + // TODO: verify the ticket, and if it is acceptable, use it to fill + // session_negotiated and set handshake->resume to 1 + ((void) buf); + ((void) ssl); + + return( 0 ); +} + #if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) static int ssl_parse_client_hello_v2( ssl_context *ssl ) { @@ -873,6 +893,14 @@ static int ssl_parse_client_hello( ssl_context *ssl ) return( ret ); break; + case TLS_EXT_SESSION_TICKET: + SSL_DEBUG_MSG( 3, ( "found session ticket extension" ) ); + + ret = ssl_parse_session_ticket_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); @@ -1005,6 +1033,29 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, *olen = 4; } +static void ssl_write_session_ticket_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->handshake->new_session_ticket == 0 ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, adding session ticket extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} + static void ssl_write_renegotiation_ext( ssl_context *ssl, unsigned char *buf, size_t *olen ) @@ -1179,6 +1230,9 @@ static int ssl_write_server_hello( ssl_context *ssl ) ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; + ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; + SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) ); *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); @@ -2114,6 +2168,51 @@ static int ssl_parse_certificate_verify( ssl_context *ssl ) !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +static int ssl_write_new_session_ticket( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> write new session ticket" ) ); + + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_NEW_SESSION_TICKET; + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 4 . 7 ticket_lifetime_hint (0 = unspecified) + * 8 . 9 ticket_len (n) + * 10 . 9+n ticket content + */ + ssl->out_msg[4] = 0x00; + ssl->out_msg[5] = 0x00; + ssl->out_msg[6] = 0x00; + ssl->out_msg[7] = 0x00; + + // TODO: generate and send actual ticket (empty for now) + + ssl->out_msglen = 10 + 0; + ssl->out_msg[8] = 0x00; + ssl->out_msg[9] = 0x00; + + SSL_DEBUG_BUF( 0, "out_msg", ssl->out_msg, ssl->out_msglen ); + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; + + SSL_DEBUG_MSG( 2, ( "<= write new session ticket" ) ); + + return( 0 ); +} + /* * SSL handshake -- server side -- single step */ @@ -2197,9 +2296,14 @@ int ssl_handshake_server_step( ssl_context *ssl ) break; /* - * ==> ChangeCipherSpec + * ==> ( NewSessionTicket ) + * ChangeCipherSpec * Finished */ + case SSL_SERVER_NEW_SESSION_TICKET: + ret = ssl_write_new_session_ticket( ssl ); + break; + case SSL_SERVER_CHANGE_CIPHER_SPEC: ret = ssl_write_change_cipher_spec( ssl ); break; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index bf45ec873..27a503f13 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2736,6 +2736,11 @@ int ssl_parse_finished( ssl_context *ssl ) if( ssl->endpoint == SSL_IS_SERVER ) ssl->state = SSL_HANDSHAKE_WRAPUP; } + else if( ssl->endpoint == SSL_IS_SERVER && + ssl->handshake->new_session_ticket != 0 ) + { + ssl->state = SSL_SERVER_NEW_SESSION_TICKET; + } else ssl->state++; From 94f6a79cde60ff1a5ecd39afd7d477d41554a50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 1 Aug 2013 14:33:49 +0200 Subject: [PATCH 09/24] Auxiliary functions to (de)serialize ssl_session --- library/ssl_srv.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ad5926213..e29477897 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -33,6 +33,13 @@ #include "polarssl/ecp.h" #endif +#if defined(POLARSSL_MEMORY_C) +#include "polarssl/memory.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + #include #include @@ -40,6 +47,106 @@ #include #endif +/* + * Serialize a session in the following format: + * 0 . n-1 session structure, n = sizeof(ssl_session) + * n . n+2 peer_cert length = m (0 if no certificate) + * n+3 . n+2+m peer cert ASN.1 + * + * Assumes ticket is NULL (always true on server side). + */ +static void ssl_save_session( const ssl_session *session, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; +#if defined(POLARSSL_X509_PARSE_C) + size_t cert_len; +#endif /* POLARSSL_X509_PARSE_C */ + + memcpy( p, session, sizeof( ssl_session ) ); + p += sizeof( ssl_session ); + +#if defined(POLARSSL_X509_PARSE_C) + ((ssl_session *) buf)->peer_cert = NULL; + + if( session->peer_cert == NULL ) + cert_len = 0; + else + cert_len = session->peer_cert->raw.len; + + *p++ = (unsigned char)( cert_len >> 16 & 0xFF ); + *p++ = (unsigned char)( cert_len >> 8 & 0xFF ); + *p++ = (unsigned char)( cert_len & 0xFF ); + + if( session->peer_cert != NULL ) + memcpy( p, session->peer_cert->raw.p, cert_len ); + + p += cert_len; +#endif /* POLARSSL_X509_PARSE_C */ + + *olen = p - buf; +} + +/* + * Unserialise session, see ssl_save_session() + */ +static int ssl_load_session( ssl_session *session, + const unsigned char *buf, size_t len ) +{ + int ret; + const unsigned char *p = buf; + const unsigned char * const end = buf + len; +#if defined(POLARSSL_X509_PARSE_C) + size_t cert_len; +#endif /* POLARSSL_X509_PARSE_C */ + + if( p + sizeof( ssl_session ) > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + memcpy( session, p, sizeof( ssl_session ) ); + p += sizeof( ssl_session ); + +#if defined(POLARSSL_X509_PARSE_C) + if( p + 3 > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2]; + p += 3; + + if( cert_len == 0 ) + { + session->peer_cert = NULL; + } + else + { + if( p + cert_len > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + session->peer_cert = polarssl_malloc( cert_len ); + + if( session->peer_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memset( session->peer_cert, 0, sizeof( x509_cert ) ); + + if( ( ret = x509parse_crt( session->peer_cert, p, cert_len ) ) != 0 ) + { + polarssl_free( session->peer_cert ); + free( session->peer_cert ); + session->peer_cert = NULL; + return( ret ); + } + + p += cert_len; + } +#endif /* POLARSSL_X509_PARSE_C */ + + if( p != end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + return( 0 ); +} + static int ssl_parse_servername_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) From 609bc81a76b8abb6c9315ba8be786c1e0266e2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 1 Aug 2013 15:08:40 +0200 Subject: [PATCH 10/24] ssl_srv: read & write ticket, unsecure for now --- library/ssl_srv.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index e29477897..bae58abd6 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -434,18 +434,33 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) { - /* - * Remember the client asked for a ticket - */ + ssl_session session; + + /* Remember the client asked us to send a ticket */ ssl->handshake->new_session_ticket = 1; if( len == 0 ) return( 0 ); - // TODO: verify the ticket, and if it is acceptable, use it to fill - // session_negotiated and set handshake->resume to 1 - ((void) buf); - ((void) ssl); + SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); + + /* + * Use a temporary session to preserve the current one of failures. + * Failures are ok: just ignore the ticket and proceed. + */ + if( ssl_load_session( &session, buf, len ) != 0 ) + { + SSL_DEBUG_MSG( 3, ( "failed to load ticket" ) ); + return( 0 ); + } + + SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); + + ssl_session_free( ssl->session_negotiate ); + memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) ); + memset( &session, 0, sizeof( ssl_session ) ); + + ssl->handshake->resume = 1; return( 0 ); } @@ -2278,6 +2293,7 @@ static int ssl_parse_certificate_verify( ssl_context *ssl ) static int ssl_write_new_session_ticket( ssl_context *ssl ) { int ret; + size_t tlen; SSL_DEBUG_MSG( 2, ( "=> write new session ticket" ) ); @@ -2299,13 +2315,12 @@ static int ssl_write_new_session_ticket( ssl_context *ssl ) ssl->out_msg[6] = 0x00; ssl->out_msg[7] = 0x00; - // TODO: generate and send actual ticket (empty for now) + ssl_save_session( ssl->session_negotiate, ssl->out_msg + 10, &tlen ); - ssl->out_msglen = 10 + 0; - ssl->out_msg[8] = 0x00; - ssl->out_msg[9] = 0x00; + ssl->out_msg[8] = (unsigned char)( ( tlen >> 8 ) & 0xFF ); + ssl->out_msg[9] = (unsigned char)( ( tlen ) & 0xFF ); - SSL_DEBUG_BUF( 0, "out_msg", ssl->out_msg, ssl->out_msglen ); + ssl->out_msglen = 10 + tlen; if( ( ret = ssl_write_record( ssl ) ) != 0 ) { From 72882b2079879a3d89134d7c35793189e8da93c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 13:36:00 +0200 Subject: [PATCH 11/24] Relax limit on ClientHello size --- library/ssl_srv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index bae58abd6..848c04fa2 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -752,7 +752,7 @@ static int ssl_parse_client_hello( ssl_context *ssl ) n = ( buf[3] << 8 ) | buf[4]; - if( n < 45 || n > 512 ) + if( n < 45 || n > 2048 ) { SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); From 3ffa3db80b00200d0ba0518fad99104f4f124e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 11:59:05 +0200 Subject: [PATCH 12/24] Fix server session ID handling with ticket --- library/ssl_srv.c | 68 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 848c04fa2..59c91c4b9 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -439,10 +439,16 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, /* Remember the client asked us to send a ticket */ ssl->handshake->new_session_ticket = 1; + SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); + if( len == 0 ) return( 0 ); - SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ) + { + SSL_DEBUG_MSG( 3, ( "ticket rejected: renegotiating" ) ); + return( 0 ); + } /* * Use a temporary session to preserve the current one of failures. @@ -456,6 +462,13 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); + /* + * Keep the session ID sent by the client, since we MUST send it back to + * inform him we're accepting the ticket (RFC 5077 section 3.4) + */ + session.length = ssl->session_negotiate->length; + memcpy( &session.id, ssl->session_negotiate->id, session.length ); + ssl_session_free( ssl->session_negotiate ); memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) ); memset( &session, 0, sizeof( ssl_session ) ); @@ -1284,36 +1297,44 @@ static int ssl_write_server_hello( ssl_context *ssl ) SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 ); /* - * 38 . 38 session id length - * 39 . 38+n session id - * 39+n . 40+n chosen ciphersuite - * 41+n . 41+n chosen compression alg. - * 42+n . 43+n extensions length - * 44+n . 43+n+m extensions + * Resume is 0 by default, see ssl_handshake_init(). + * It may be already set to 1 by ssl_parse_session_ticket_ext(). + * If not, try looking up session ID in our cache. */ - ssl->session_negotiate->length = n = 32; - *p++ = (unsigned char) ssl->session_negotiate->length; + if( ssl->handshake->resume == 0 && + ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ssl->f_get_cache != NULL && + ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) == 0 ) + { + ssl->handshake->resume = 1; + } - if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || - ssl->f_get_cache == NULL || - ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) != 0 ) + if( ssl->handshake->resume == 0 ) { /* - * Not found, create a new session id + * New session, create a new session id, + * unless we're about to issue a session ticket */ - ssl->handshake->resume = 0; ssl->state++; - if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, - n ) ) != 0 ) - return( ret ); + if( ssl->handshake->new_session_ticket == 0 ) + { + ssl->session_negotiate->length = n = 32; + if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, + n ) ) != 0 ) + return( ret ); + } + else + { + ssl->session_negotiate->length = 0; + memset( ssl->session_negotiate->id, 0, 32 ); + } } else { /* - * Found a matching session, resuming it + * Resuming a session */ - ssl->handshake->resume = 1; ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) @@ -1323,6 +1344,15 @@ static int ssl_write_server_hello( ssl_context *ssl ) } } + /* + * 38 . 38 session id length + * 39 . 38+n session id + * 39+n . 40+n chosen ciphersuite + * 41+n . 41+n chosen compression alg. + * 42+n . 43+n extensions length + * 44+n . 43+n+m extensions + */ + *p++ = (unsigned char) ssl->session_negotiate->length; memcpy( p, ssl->session_negotiate->id, ssl->session_negotiate->length ); p += ssl->session_negotiate->length; From 7cd5924cec939cde5d906d387b8ec04e82b11e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 13:24:41 +0200 Subject: [PATCH 13/24] Rework NewSessionTicket handling in state machine Fixes bug: NewSessionTicket was ommited in resumed sessions. --- library/ssl_cli.c | 14 +++++++------- library/ssl_srv.c | 12 ++++++------ library/ssl_tls.c | 10 ---------- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 9b643926d..40b9f23cc 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -1967,10 +1967,11 @@ static int ssl_parse_new_session_ticket( ssl_context *ssl ) return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET ); } - ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; - SSL_DEBUG_MSG( 3, ( "ticket length: %d", ticket_len ) ); + /* We're not waiting for a NewSessionTicket message any more */ + ssl->handshake->new_session_ticket = 0; + /* * Zero-length ticket means the server changed his mind and doesn't want * to send a ticket after all, so just forget it @@ -2094,12 +2095,11 @@ int ssl_handshake_client_step( ssl_context *ssl ) * ChangeCipherSpec * Finished */ - case SSL_SERVER_NEW_SESSION_TICKET: - ret = ssl_parse_new_session_ticket( ssl ); - break; - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); + if( ssl->handshake->new_session_ticket != 0 ) + ret = ssl_parse_new_session_ticket( ssl ); + else + ret = ssl_parse_change_cipher_spec( ssl ); break; case SSL_SERVER_FINISHED: diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 59c91c4b9..faa110ac1 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -2358,7 +2358,8 @@ static int ssl_write_new_session_ticket( ssl_context *ssl ) return( ret ); } - ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; + /* No need to remember writing a NewSessionTicket any more */ + ssl->handshake->new_session_ticket = 0; SSL_DEBUG_MSG( 2, ( "<= write new session ticket" ) ); @@ -2452,12 +2453,11 @@ int ssl_handshake_server_step( ssl_context *ssl ) * ChangeCipherSpec * Finished */ - case SSL_SERVER_NEW_SESSION_TICKET: - ret = ssl_write_new_session_ticket( ssl ); - break; - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); + if( ssl->handshake->new_session_ticket != 0 ) + ret = ssl_write_new_session_ticket( ssl ); + else + ret = ssl_write_change_cipher_spec( ssl ); break; case SSL_SERVER_FINISHED: diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 27a503f13..7e8ff343c 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2619,11 +2619,6 @@ int ssl_write_finished( ssl_context *ssl ) else ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; } - else if( ssl->endpoint == SSL_IS_CLIENT && - ssl->handshake->new_session_ticket != 0 ) - { - ssl->state = SSL_SERVER_NEW_SESSION_TICKET; - } else ssl->state++; @@ -2736,11 +2731,6 @@ int ssl_parse_finished( ssl_context *ssl ) if( ssl->endpoint == SSL_IS_SERVER ) ssl->state = SSL_HANDSHAKE_WRAPUP; } - else if( ssl->endpoint == SSL_IS_SERVER && - ssl->handshake->new_session_ticket != 0 ) - { - ssl->state = SSL_SERVER_NEW_SESSION_TICKET; - } else ssl->state++; From c086cce3d3f3c6a1b18c0f4bfab41686532394a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 14:13:02 +0200 Subject: [PATCH 14/24] Don't cache empty session ID nor resumed session --- library/ssl_srv.c | 1 + library/ssl_tls.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index faa110ac1..5b421fc94 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -1303,6 +1303,7 @@ static int ssl_write_server_hello( ssl_context *ssl ) */ if( ssl->handshake->resume == 0 && ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ssl->session_negotiate->length != 0 && ssl->f_get_cache != NULL && ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) == 0 ) { diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 7e8ff343c..e7902774b 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2539,6 +2539,8 @@ static void ssl_calc_finished_tls_sha384( void ssl_handshake_wrapup( ssl_context *ssl ) { + int resume = ssl->handshake->resume; + SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) ); /* @@ -2570,9 +2572,13 @@ void ssl_handshake_wrapup( ssl_context *ssl ) /* * Add cache entry */ - if( ssl->f_set_cache != NULL ) + if( ssl->f_set_cache != NULL && + ssl->session->length != 0 && + resume == 0 ) + { if( ssl->f_set_cache( ssl->p_set_cache, ssl->session ) != 0 ) SSL_DEBUG_MSG( 1, ( "cache did not store session" ) ); + } ssl->state++; From 593058e35e9043e6c41c6093b1c0435aa0e04786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 14:55:46 +0200 Subject: [PATCH 15/24] Don't renew ticket when the current one is OK --- library/ssl_srv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 5b421fc94..1059efc39 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -462,6 +462,11 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); + /* + * Don't send a new ticket after all, this one is OK + */ + ssl->handshake->new_session_ticket = 0; + /* * Keep the session ID sent by the client, since we MUST send it back to * inform him we're accepting the ticket (RFC 5077 section 3.4) From cf2e97eae2b4a06ac68bc394d61114849c7d97d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 15:04:36 +0200 Subject: [PATCH 16/24] ssl_client2: allow reconnecting twice --- programs/ssl/ssl_client2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index e8d38200c..9442dca23 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -354,7 +354,7 @@ int main( int argc, char *argv[] ) else if( strcmp( p, "reconnect" ) == 0 ) { opt.reconnect = atoi( q ); - if( opt.reconnect < 0 || opt.reconnect > 1 ) + if( opt.reconnect < 0 || opt.reconnect > 2 ) goto usage; } else if( strcmp( p, "min_version" ) == 0 ) @@ -818,7 +818,7 @@ send_request: if( opt.reconnect != 0 ) { - opt.reconnect = 0; + --opt.reconnect; printf( " . Reconnecting with saved session..." ); fflush( stdout ); From 06650f6a37a019d1b09cfe3be4ea60b819cc323d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 15:34:52 +0200 Subject: [PATCH 17/24] Fix reusing session more than once --- include/polarssl/ssl.h | 7 ++- library/ssl_tls.c | 90 ++++++++++++++++++++++---------------- programs/ssl/ssl_client2.c | 1 + 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 6d00c9dc0..a25ebea6f 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -789,9 +789,14 @@ void ssl_set_session_cache( ssl_context *ssl, * \param ssl SSL context * \param session session context * + * \return 0 if successful, + * POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or + * arguments are otherwise invalid + * * \sa ssl_get_session() */ -void ssl_set_session( ssl_context *ssl, const ssl_session *session ); +int ssl_set_session( ssl_context *ssl, const ssl_session *session ); /** * \brief Set the list of allowed ciphersuites diff --git a/library/ssl_tls.c b/library/ssl_tls.c index e7902774b..db87fe0c0 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -76,6 +76,42 @@ static unsigned int mfl_code_to_length[SSL_MAX_FRAG_LEN_INVALID] = 4096, /* SSL_MAX_FRAG_LEN_4096 */ }; +static int ssl_session_copy( ssl_session *dst, const ssl_session *src ) +{ + int ret; + + ssl_session_free( dst ); + memcpy( dst, src, sizeof( ssl_session ) ); + +#if defined(POLARSSL_X509_PARSE_C) + if( src->peer_cert != NULL ) + { + if( ( dst->peer_cert = polarssl_malloc( sizeof(x509_cert) ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memset( dst->peer_cert, 0, sizeof(x509_cert) ); + + if( ( ret = x509parse_crt( dst->peer_cert, src->peer_cert->raw.p, + src->peer_cert->raw.len ) != 0 ) ) + { + polarssl_free( dst->peer_cert ); + dst->peer_cert = NULL; + return( ret ); + } + } +#endif /* POLARSSL_X509_PARSE_C */ + + if( src->ticket != NULL ) + { + if( ( dst->ticket = polarssl_malloc( src->ticket_len ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memcpy( dst->ticket, src->ticket, src->ticket_len ); + } + + return( 0 ); +} + #if defined(POLARSSL_SSL_HW_RECORD_ACCEL) int (*ssl_hw_record_init)(ssl_context *ssl, const unsigned char *key_enc, const unsigned char *key_dec, @@ -2995,10 +3031,24 @@ void ssl_set_session_cache( ssl_context *ssl, ssl->p_set_cache = p_set_cache; } -void ssl_set_session( ssl_context *ssl, const ssl_session *session ) +int ssl_set_session( ssl_context *ssl, const ssl_session *session ) { - memcpy( ssl->session_negotiate, session, sizeof(ssl_session) ); + int ret; + + if( ssl == NULL || + session == NULL || + ssl->session_negotiate == NULL || + ssl->endpoint != SSL_IS_CLIENT ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + if( ( ret = ssl_session_copy( ssl->session_negotiate, session ) ) != 0 ) + return( ret ); + ssl->handshake->resume = 1; + + return( 0 ); } void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites ) @@ -3230,9 +3280,6 @@ const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ) int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) { - int ret; - ssl_session *src; - if( ssl == NULL || dst == NULL || ssl->session == NULL || @@ -3241,38 +3288,7 @@ int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); } - src = ssl->session; - - ssl_session_free( dst ); - memcpy( dst, src, sizeof( ssl_session ) ); - -#if defined(POLARSSL_X509_PARSE_C) - if( src->peer_cert != NULL ) - { - if( ( dst->peer_cert = polarssl_malloc( sizeof(x509_cert) ) ) == NULL ) - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - - memset( dst->peer_cert, 0, sizeof(x509_cert) ); - - if( ( ret = x509parse_crt( dst->peer_cert, src->peer_cert->raw.p, - src->peer_cert->raw.len ) != 0 ) ) - { - polarssl_free( dst->peer_cert ); - dst->peer_cert = NULL; - return( ret ); - } - } -#endif /* POLARSSL_X509_PARSE_C */ - - if( src->ticket != NULL ) - { - if( ( dst->ticket = polarssl_malloc( src->ticket_len ) ) == NULL ) - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - - memcpy( dst->ticket, src->ticket, src->ticket_len ); - } - - return( 0 ); + return( ssl_session_copy( dst, ssl->session ) ); } /* diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 9442dca23..d57ed07fa 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -871,6 +871,7 @@ exit: x509_free( &cacert ); rsa_free( &rsa ); #endif + ssl_session_free( &saved_session ); ssl_free( &ssl ); memset( &ssl, 0, sizeof( ssl ) ); From 306827e3bcd2f4fd600ebf5f4d55a4a16623f581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Fri, 2 Aug 2013 18:05:14 +0200 Subject: [PATCH 18/24] Prepare ticket structure for securing --- library/ssl_srv.c | 131 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 106 insertions(+), 25 deletions(-) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 1059efc39..ad9142c95 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -147,6 +147,106 @@ static int ssl_load_session( ssl_session *session, return( 0 ); } +/* + * Create session ticket, secured as recommended in RFC 5077 section 4: + * + * struct { + * opaque key_name[16]; + * opaque iv[16]; + * opaque encrypted_state<0..2^16-1>; + * opaque mac[32]; + * } ticket; + * + * (the internal state structure differs, however). + */ +static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) +{ + unsigned char * const start = ssl->out_msg + 10; + unsigned char *p = start; + size_t clear_len, enc_len; + + memset( p, 0, 16 ); // TODO: key_name + p += 16; + + memset( p, 0, 16 ); // TODO: iv + p += 16; + + ssl_save_session( ssl->session_negotiate, p + 2, &clear_len ); + SSL_DEBUG_BUF( 4, "session ticket cleartext", p, clear_len ); + + // TODO: encrypt ticket + enc_len = clear_len; + (void) enc_len; + + *p++ = (unsigned char)( ( clear_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( clear_len ) & 0xFF ); + p += clear_len; + + memset( p, 0, 32 ); // TODO: mac + p += 32; + + *tlen = p - start; + + SSL_DEBUG_BUF( 4, "final session ticket", start, *tlen ); + + return( 0 ); +} + +/* + * Load session ticket (see ssl_write_ticket for structure) + */ +static int ssl_parse_ticket( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + int ret; + ssl_session session; + const unsigned char *key_name = buf; + const unsigned char *iv = buf + 16; + const unsigned char *enc_len_p = iv + 16; + const unsigned char *ticket = enc_len_p + 2; + const unsigned char *mac; + size_t enc_len, clear_len; + + if( len < 34 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1]; + mac = ticket + enc_len; + + if( len != enc_len + 66 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + // TODO: check key_name + (void) key_name; + + // TODO: check hmac + (void) mac; + + // TODO: decrypt ticket + clear_len = enc_len; + + if( ( ret = ssl_load_session( &session, ticket, clear_len ) ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "failed to parse ticket content" ) ); + memset( &session, 0, sizeof( ssl_session ) ); + return( ret ); + } + + /* + * Keep the session ID sent by the client, since we MUST send it back to + * inform him we're accepting the ticket (RFC 5077 section 3.4) + */ + session.length = ssl->session_negotiate->length; + memcpy( &session.id, ssl->session_negotiate->id, session.length ); + + ssl_session_free( ssl->session_negotiate ); + memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) ); + memset( &session, 0, sizeof( ssl_session ) ); + + return( 0 ); +} + static int ssl_parse_servername_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) @@ -434,9 +534,7 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) { - ssl_session session; - - /* Remember the client asked us to send a ticket */ + /* Remember the client asked us to send a new ticket */ ssl->handshake->new_session_ticket = 1; SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); @@ -451,35 +549,18 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, } /* - * Use a temporary session to preserve the current one of failures. * Failures are ok: just ignore the ticket and proceed. */ - if( ssl_load_session( &session, buf, len ) != 0 ) - { - SSL_DEBUG_MSG( 3, ( "failed to load ticket" ) ); + if( ssl_parse_ticket( ssl, buf, len ) != 0 ) return( 0 ); - } SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); - /* - * Don't send a new ticket after all, this one is OK - */ - ssl->handshake->new_session_ticket = 0; - - /* - * Keep the session ID sent by the client, since we MUST send it back to - * inform him we're accepting the ticket (RFC 5077 section 3.4) - */ - session.length = ssl->session_negotiate->length; - memcpy( &session.id, ssl->session_negotiate->id, session.length ); - - ssl_session_free( ssl->session_negotiate ); - memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) ); - memset( &session, 0, sizeof( ssl_session ) ); - ssl->handshake->resume = 1; + /* Don't send a new ticket after all, this one is OK */ + ssl->handshake->new_session_ticket = 0; + return( 0 ); } @@ -2351,7 +2432,7 @@ static int ssl_write_new_session_ticket( ssl_context *ssl ) ssl->out_msg[6] = 0x00; ssl->out_msg[7] = 0x00; - ssl_save_session( ssl->session_negotiate, ssl->out_msg + 10, &tlen ); + ssl_write_ticket( ssl, &tlen ); ssl->out_msg[8] = (unsigned char)( ( tlen >> 8 ) & 0xFF ); ssl->out_msg[9] = (unsigned char)( ( tlen ) & 0xFF ); From aa0d4d1aff41111a44061c1e6226436d2fac842f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Sat, 3 Aug 2013 13:02:31 +0200 Subject: [PATCH 19/24] Add ssl_set_session_tickets() --- include/polarssl/ssl.h | 25 +++++++++++++++++++++++++ library/ssl_cli.c | 11 ++++++++++- library/ssl_srv.c | 3 +++ library/ssl_tls.c | 10 ++++++++++ programs/ssl/ssl_client2.c | 12 ++++++++++++ programs/ssl/ssl_server2.c | 12 ++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index a25ebea6f..5596cc1b4 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -154,6 +154,9 @@ #define SSL_TRUNC_HMAC_ENABLED 1 #define SSL_TRUNCATED_HMAC_LEN 10 /* 80 bits, rfc 6066 section 7 */ +#define SSL_SESSION_TICKETS_DISABLED 0 +#define SSL_SESSION_TICKETS_ENABLED 1 + /* * Size of the input / output buffer. * Note: the RFC defines the default size of SSL / TLS messages. If you @@ -561,6 +564,7 @@ struct _ssl_context int allow_legacy_renegotiation; /*!< allow legacy renegotiation */ const int *ciphersuite_list[4]; /*!< allowed ciphersuites / version */ int trunc_hmac; /*!< negotiate truncated hmac? */ + int session_tickets; /*!< use session tickets? */ #if defined(POLARSSL_DHM_C) mpi dhm_P; /*!< prime modulus for DHM */ @@ -667,6 +671,9 @@ int ssl_session_reset( ssl_context *ssl ); * * \param ssl SSL context * \param endpoint must be SSL_IS_CLIENT or SSL_IS_SERVER + * + * \note This function should be called right after ssl_init() since + * some other ssl_set_foo() functions depend on it. */ void ssl_set_endpoint( ssl_context *ssl, int endpoint ); @@ -1012,6 +1019,24 @@ int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code ); */ int ssl_set_truncated_hmac( ssl_context *ssl, int truncate ); +/** + * \brief Enable / Disable session tickets + * (Default: SSL_SESSION_TICKETS_ENABLED on client, + * SSL_SESSION_TICKETS_DISABLED on server) + * + * \note On server, ssl_set_rng() must be called before this function + * to allow generating the ticket encryption and + * authentication keys. + * + * \param ssl SSL context + * \param use_tickets Enable or disable (SSL_SESSION_TICKETS_ENABLED or + * SSL_SESSION_TICKETS_DISABLED) + * + * \return O if successful, + * or a specific error code (server only). + */ +int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ); + /** * \brief Enable / Disable renegotiation support for connection when * initiated by peer diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 40b9f23cc..402c85a47 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -328,6 +328,12 @@ static void ssl_write_session_ticket_ext( ssl_context *ssl, unsigned char *p = buf; size_t tlen = ssl->session_negotiate->ticket_len; + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ) + { + *olen = 0; + return; + } + SSL_DEBUG_MSG( 3, ( "client hello, adding session ticket extension" ) ); *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF ); @@ -648,8 +654,11 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) { - if( len != 0 ) + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED || + len != 0 ) + { return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } ((void) buf); diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ad9142c95..5b7db1718 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -534,6 +534,9 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) { + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ) + return( 0 ); + /* Remember the client asked us to send a new ticket */ ssl->handshake->new_session_ticket = 1; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index db87fe0c0..f7b9bd99c 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2978,6 +2978,9 @@ int ssl_session_reset( ssl_context *ssl ) void ssl_set_endpoint( ssl_context *ssl, int endpoint ) { ssl->endpoint = endpoint; + + if( endpoint == SSL_IS_CLIENT ) + ssl->session_tickets = SSL_SESSION_TICKETS_ENABLED; } void ssl_set_authmode( ssl_context *ssl, int authmode ) @@ -3225,6 +3228,13 @@ void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ) ssl->allow_legacy_renegotiation = allow_legacy; } +int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ) +{ + ssl->session_tickets = use_tickets; + + return( 0 ); +} + /* * SSL get accessors */ diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index d57ed07fa..4b92c19f4 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -60,6 +60,7 @@ #define DFL_MFL_CODE SSL_MAX_FRAG_LEN_NONE #define DFL_TRUNC_HMAC 0 #define DFL_RECONNECT 0 +#define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED #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-" \ @@ -98,6 +99,7 @@ struct options unsigned char mfl_code; /* code for maximum fragment length */ int trunc_hmac; /* negotiate truncated hmac or not */ int reconnect; /* attempt to resume session */ + int tickets; /* enable / disable session tickets */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -187,6 +189,7 @@ static int my_verify( void *data, x509_cert *crt, int depth, int *flags ) " renegotiation=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ " reconnect=%%d default: 0 (disabled)\n" \ + " tickets=%%d default: 1 (enabled)\n" \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -291,6 +294,7 @@ int main( int argc, char *argv[] ) opt.mfl_code = DFL_MFL_CODE; opt.trunc_hmac = DFL_TRUNC_HMAC; opt.reconnect = DFL_RECONNECT; + opt.tickets = DFL_TICKETS; for( i = 1; i < argc; i++ ) { @@ -357,6 +361,12 @@ int main( int argc, char *argv[] ) if( opt.reconnect < 0 || opt.reconnect > 2 ) goto usage; } + else if( strcmp( p, "tickets" ) == 0 ) + { + opt.tickets = atoi( q ); + if( opt.tickets < 0 || opt.tickets > 2 ) + goto usage; + } else if( strcmp( p, "min_version" ) == 0 ) { if( strcmp( q, "ssl3" ) == 0 ) @@ -664,6 +674,8 @@ int main( int argc, char *argv[] ) ssl_set_bio( &ssl, net_recv, &server_fd, net_send, &server_fd ); + ssl_set_session_tickets( &ssl, opt.tickets ); + if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER ) ssl_set_ciphersuites( &ssl, opt.force_ciphersuite ); diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 57ff85c3a..2ed74e404 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -69,6 +69,7 @@ #define DFL_MAX_VERSION -1 #define DFL_AUTH_MODE SSL_VERIFY_OPTIONAL #define DFL_MFL_CODE SSL_MAX_FRAG_LEN_NONE +#define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED #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" \ @@ -105,6 +106,7 @@ struct options int max_version; /* maximum protocol version accepted */ int auth_mode; /* verify mode for connection */ unsigned char mfl_code; /* code for maximum fragment length */ + int tickets; /* enable / disable session tickets */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -152,6 +154,7 @@ static void my_debug( void *ctx, int level, const char *str ) USAGE_IO \ " request_page=%%s default: \".\"\n" \ " renegotiation=%%d default: 1 (enabled)\n" \ + " tickets=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ " min_version=%%s default: \"ssl3\"\n" \ " max_version=%%s default: \"tls1_2\"\n" \ @@ -265,6 +268,7 @@ int main( int argc, char *argv[] ) opt.max_version = DFL_MAX_VERSION; opt.auth_mode = DFL_AUTH_MODE; opt.mfl_code = DFL_MFL_CODE; + opt.tickets = DFL_TICKETS; for( i = 1; i < argc; i++ ) { @@ -396,6 +400,12 @@ int main( int argc, char *argv[] ) else goto usage; } + else if( strcmp( p, "tickets" ) == 0 ) + { + opt.tickets = atoi( q ); + if( opt.tickets < 0 || opt.tickets > 1 ) + goto usage; + } else goto usage; } @@ -611,6 +621,8 @@ int main( int argc, char *argv[] ) ssl_cache_set, &cache ); #endif + ssl_set_session_tickets( &ssl, opt.tickets ); + if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER ) ssl_set_ciphersuites( &ssl, opt.force_ciphersuite ); From 779e42982c0d3462600a4ad4659110c6ab80b645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Sat, 3 Aug 2013 13:50:48 +0200 Subject: [PATCH 20/24] Start adding ticket keys (only key_name for now) --- include/polarssl/ssl.h | 14 ++++++++++++++ library/ssl_srv.c | 11 +++++++---- library/ssl_tls.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 5596cc1b4..b51cc876c 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -328,6 +328,7 @@ typedef struct _ssl_session ssl_session; typedef struct _ssl_context ssl_context; typedef struct _ssl_transform ssl_transform; typedef struct _ssl_handshake_params ssl_handshake_params; +typedef struct _ssl_ticket_keys ssl_ticket_keys; /* * This structure is used for storing current session data. @@ -445,6 +446,14 @@ struct _ssl_handshake_params int new_session_ticket; /*!< use NewSessionTicket? */ }; +/* + * Parameters needed to secure session tickets + */ +struct _ssl_ticket_keys +{ + unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ +}; + struct _ssl_context { /* @@ -553,6 +562,11 @@ struct _ssl_context const char *peer_cn; /*!< expected peer CN */ #endif /* POLARSSL_X509_PARSE_C */ + /* + * Support for generating and checking session tickets + */ + ssl_ticket_keys *ticket_keys; /*!< keys for ticket encryption */ + /* * User settings */ diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 5b7db1718..117830980 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -165,7 +165,10 @@ static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) unsigned char *p = start; size_t clear_len, enc_len; - memset( p, 0, 16 ); // TODO: key_name + if( ssl->ticket_keys == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + memcpy( p, ssl->ticket_keys->key_name, 16 ); p += 16; memset( p, 0, 16 ); // TODO: iv @@ -208,7 +211,7 @@ static int ssl_parse_ticket( ssl_context *ssl, const unsigned char *mac; size_t enc_len, clear_len; - if( len < 34 ) + if( len < 34 || ssl->ticket_keys == NULL ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1]; @@ -217,8 +220,8 @@ static int ssl_parse_ticket( ssl_context *ssl, if( len != enc_len + 66 ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - // TODO: check key_name - (void) key_name; + if( memcmp( key_name, ssl->ticket_keys->key_name, 16 ) != 0 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); // TODO: check hmac (void) mac; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index f7b9bd99c..f4d6e2489 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2972,6 +2972,28 @@ int ssl_session_reset( ssl_context *ssl ) return( 0 ); } +/* + * Allocate and initialize ticket keys + */ +static int ssl_ticket_keys_init( ssl_context *ssl ) +{ + int ret; + ssl_ticket_keys *tkeys; + + if( ssl->ticket_keys != NULL ) + return( 0 ); + + if( ( tkeys = polarssl_malloc( sizeof( ssl_ticket_keys ) ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->key_name, 16 ) ) != 0 ) + return( ret ); + + ssl->ticket_keys = tkeys; + + return( 0 ); +} + /* * SSL set accessors */ @@ -3232,7 +3254,13 @@ int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ) { ssl->session_tickets = use_tickets; - return( 0 ); + if( ssl->endpoint == SSL_IS_CLIENT ) + return( 0 ); + + if( ssl->f_rng == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + return( ssl_ticket_keys_init( ssl ) ); } /* @@ -3671,6 +3699,8 @@ void ssl_free( ssl_context *ssl ) polarssl_free( ssl->session ); } + polarssl_free( ssl->ticket_keys ); + if ( ssl->hostname != NULL) { memset( ssl->hostname, 0, ssl->hostname_len ); From 990c51a55722d9e1e7b92c376ba1526e7b721ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Sat, 3 Aug 2013 15:37:58 +0200 Subject: [PATCH 21/24] Encrypt session tickets --- include/polarssl/ssl.h | 3 ++ library/ssl_srv.c | 100 +++++++++++++++++++++++++++++++---------- library/ssl_tls.c | 8 ++++ 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index b51cc876c..6b5124db4 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -35,6 +35,7 @@ #include "sha1.h" #include "sha256.h" #include "sha512.h" +#include "aes.h" #include "ssl_ciphersuites.h" @@ -452,6 +453,8 @@ struct _ssl_handshake_params struct _ssl_ticket_keys { unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ + aes_context enc; /*!< encryption context */ + aes_context dec; /*!< decryption context */ }; struct _ssl_context diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 117830980..cbec7952b 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -161,36 +161,56 @@ static int ssl_load_session( ssl_session *session, */ static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) { + int ret; unsigned char * const start = ssl->out_msg + 10; unsigned char *p = start; - size_t clear_len, enc_len; + unsigned char *state; + unsigned char iv[16]; + size_t clear_len, enc_len, pad_len, i; if( ssl->ticket_keys == NULL ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + /* Write key name */ memcpy( p, ssl->ticket_keys->key_name, 16 ); p += 16; - memset( p, 0, 16 ); // TODO: iv + /* Generate and write IV (with a copy for aes_crypt) */ + if( ( ret = ssl->f_rng( ssl->p_rng, p, 16 ) ) != 0 ) + return( ret ); + memcpy( iv, p, 16 ); p += 16; - ssl_save_session( ssl->session_negotiate, p + 2, &clear_len ); - SSL_DEBUG_BUF( 4, "session ticket cleartext", p, clear_len ); + /* Dump session state */ + state = p + 2; + ssl_save_session( ssl->session_negotiate, state, &clear_len ); + SSL_DEBUG_BUF( 3, "session ticket cleartext", state, clear_len ); - // TODO: encrypt ticket - enc_len = clear_len; - (void) enc_len; + /* Apply PKCS padding */ + pad_len = 16 - clear_len % 16; + enc_len = clear_len + pad_len; + for( i = clear_len; i < enc_len; i++ ) + state[i] = (unsigned char) pad_len; - *p++ = (unsigned char)( ( clear_len >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( clear_len ) & 0xFF ); - p += clear_len; + /* Encrypt */ + if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->enc, AES_ENCRYPT, + enc_len, iv, state, state ) ) != 0 ) + { + return( ret ); + } - memset( p, 0, 32 ); // TODO: mac + /* Write length */ + *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( enc_len ) & 0xFF ); + p = state + enc_len; + + /* Compute and write MAC */ + memset( p, 0, 32 ); p += 32; *tlen = p - start; - SSL_DEBUG_BUF( 4, "final session ticket", start, *tlen ); + SSL_DEBUG_BUF( 3, "session ticket structure", start, *tlen ); return( 0 ); } @@ -199,17 +219,20 @@ static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) * Load session ticket (see ssl_write_ticket for structure) */ static int ssl_parse_ticket( ssl_context *ssl, - const unsigned char *buf, + unsigned char *buf, size_t len ) { int ret; ssl_session session; - const unsigned char *key_name = buf; - const unsigned char *iv = buf + 16; - const unsigned char *enc_len_p = iv + 16; - const unsigned char *ticket = enc_len_p + 2; - const unsigned char *mac; - size_t enc_len, clear_len; + unsigned char *key_name = buf; + unsigned char *iv = buf + 16; + unsigned char *enc_len_p = iv + 16; + unsigned char *ticket = enc_len_p + 2; + unsigned char *mac; + size_t enc_len, clear_len, i; + unsigned char pad_len; + + SSL_DEBUG_BUF( 3, "session ticket structure", buf, len ); if( len < 34 || ssl->ticket_keys == NULL ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); @@ -220,15 +243,35 @@ static int ssl_parse_ticket( ssl_context *ssl, if( len != enc_len + 66 ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + /* Check name */ if( memcmp( key_name, ssl->ticket_keys->key_name, 16 ) != 0 ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); // TODO: check hmac (void) mac; - // TODO: decrypt ticket - clear_len = enc_len; + /* Decrypt */ + if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->dec, AES_DECRYPT, + enc_len, iv, ticket, ticket ) ) != 0 ) + { + return( ret ); + } + /* Check PKCS padding */ + pad_len = ticket[enc_len - 1]; + + ret = 0; + for( i = 2; i < pad_len; i++ ) + if( ticket[enc_len - i] != pad_len ) + ret = POLARSSL_ERR_SSL_BAD_INPUT_DATA; + if( ret != 0 ) + return( ret ); + + clear_len = enc_len - pad_len; + + SSL_DEBUG_BUF( 3, "session ticket cleartext", ticket, clear_len ); + + /* Actually load session */ if( ( ret = ssl_load_session( &session, ticket, clear_len ) ) != 0 ) { SSL_DEBUG_MSG( 1, ( "failed to parse ticket content" ) ); @@ -534,9 +577,11 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, } static int ssl_parse_session_ticket_ext( ssl_context *ssl, - const unsigned char *buf, + unsigned char *buf, size_t len ) { + int ret; + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ) return( 0 ); @@ -557,8 +602,11 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, /* * Failures are ok: just ignore the ticket and proceed. */ - if( ssl_parse_ticket( ssl, buf, len ) != 0 ) + if( ( ret = ssl_parse_ticket( ssl, buf, len ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_parse_ticket", ret ); return( 0 ); + } SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); @@ -2438,7 +2486,11 @@ static int ssl_write_new_session_ticket( ssl_context *ssl ) ssl->out_msg[6] = 0x00; ssl->out_msg[7] = 0x00; - ssl_write_ticket( ssl, &tlen ); + if( ( ret = ssl_write_ticket( ssl, &tlen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_ticket", ret ); + tlen = 0; + } ssl->out_msg[8] = (unsigned char)( ( tlen >> 8 ) & 0xFF ); ssl->out_msg[9] = (unsigned char)( ( tlen ) & 0xFF ); diff --git a/library/ssl_tls.c b/library/ssl_tls.c index f4d6e2489..fb01c6c5a 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2979,6 +2979,7 @@ static int ssl_ticket_keys_init( ssl_context *ssl ) { int ret; ssl_ticket_keys *tkeys; + unsigned char buf[32]; if( ssl->ticket_keys != NULL ) return( 0 ); @@ -2989,6 +2990,13 @@ static int ssl_ticket_keys_init( ssl_context *ssl ) if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->key_name, 16 ) ) != 0 ) return( ret ); + if( ( ret = ssl->f_rng( ssl->p_rng, buf, 16 ) ) != 0 || + ( ret = aes_setkey_enc( &tkeys->enc, buf, 128 ) ) != 0 || + ( ret = aes_setkey_dec( &tkeys->dec, buf, 128 ) ) != 0 ) + { + return( ret ); + } + ssl->ticket_keys = tkeys; return( 0 ); From 56dc9e8bba509816ba80e347ed381c3ea71621b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Sat, 3 Aug 2013 17:16:31 +0200 Subject: [PATCH 22/24] Authenticate session tickets. --- include/polarssl/ssl.h | 1 + library/ssl_srv.c | 16 ++++++++++++---- library/ssl_tls.c | 5 ++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 6b5124db4..6570081fe 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -455,6 +455,7 @@ struct _ssl_ticket_keys unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ aes_context enc; /*!< encryption context */ aes_context dec; /*!< decryption context */ + unsigned char mac_key[16]; /*!< authentication key */ }; struct _ssl_context diff --git a/library/ssl_srv.c b/library/ssl_srv.c index cbec7952b..f94fda50a 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -204,8 +204,8 @@ static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) *p++ = (unsigned char)( ( enc_len ) & 0xFF ); p = state + enc_len; - /* Compute and write MAC */ - memset( p, 0, 32 ); + /* Compute and write MAC( key_name + iv + enc_state_len + enc_state ) */ + sha256_hmac( ssl->ticket_keys->mac_key, 16, start, p - start, p, 0 ); p += 32; *tlen = p - start; @@ -229,6 +229,7 @@ static int ssl_parse_ticket( ssl_context *ssl, unsigned char *enc_len_p = iv + 16; unsigned char *ticket = enc_len_p + 2; unsigned char *mac; + unsigned char computed_mac[16]; size_t enc_len, clear_len, i; unsigned char pad_len; @@ -247,8 +248,15 @@ static int ssl_parse_ticket( ssl_context *ssl, if( memcmp( key_name, ssl->ticket_keys->key_name, 16 ) != 0 ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - // TODO: check hmac - (void) mac; + /* Check mac */ + sha256_hmac( ssl->ticket_keys->mac_key, 16, buf, len - 32, + computed_mac, 0 ); + ret = 0; + for( i = 0; i < 32; i++ ) + if( mac[i] != computed_mac[i] ) + ret = POLARSSL_ERR_SSL_INVALID_MAC; + if( ret != 0 ) + return( ret ); /* Decrypt */ if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->dec, AES_DECRYPT, diff --git a/library/ssl_tls.c b/library/ssl_tls.c index fb01c6c5a..a0bf9ce1d 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2979,7 +2979,7 @@ static int ssl_ticket_keys_init( ssl_context *ssl ) { int ret; ssl_ticket_keys *tkeys; - unsigned char buf[32]; + unsigned char buf[16]; if( ssl->ticket_keys != NULL ) return( 0 ); @@ -2997,6 +2997,9 @@ static int ssl_ticket_keys_init( ssl_context *ssl ) return( ret ); } + if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->mac_key, 16 ) ) != 0 ) + return( ret ); + ssl->ticket_keys = tkeys; return( 0 ); From a503a63b8578fd2b8fec1809a0e11088edc04027 Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Wed, 14 Aug 2013 13:48:06 +0200 Subject: [PATCH 23/24] Made session tickets support configurable from config.h --- include/polarssl/config.h | 12 ++++++++++++ include/polarssl/ssl.h | 12 ++++++++++++ library/ssl_cli.c | 14 ++++++++++++++ library/ssl_srv.c | 23 ++++++++++++++++++++++- library/ssl_tls.c | 10 ++++++++++ programs/ssl/ssl_client2.c | 11 ++++++++++- programs/ssl/ssl_server2.c | 11 ++++++++++- 7 files changed, 90 insertions(+), 3 deletions(-) diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 6cabebb2f..6fa95c433 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -528,6 +528,18 @@ */ #define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO +/** + * \def POLARSSL_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL + * + * Requires: POLARSSL_AES_C + * POLARSSL_SHA256_C + * + * Comment this macro to disable support for SSL session tickets + */ +#define POLARSSL_SSL_SESSION_TICKETS + /** * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION * diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 6570081fe..f45d00e55 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -329,7 +329,9 @@ typedef struct _ssl_session ssl_session; typedef struct _ssl_context ssl_context; typedef struct _ssl_transform ssl_transform; typedef struct _ssl_handshake_params ssl_handshake_params; +#if defined(POLARSSL_SSL_SESSION_TICKETS) typedef struct _ssl_ticket_keys ssl_ticket_keys; +#endif /* * This structure is used for storing current session data. @@ -349,9 +351,11 @@ struct _ssl_session x509_cert *peer_cert; /*!< peer X.509 cert chain */ #endif /* POLARSSL_X509_PARSE_C */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) unsigned char *ticket; /*!< RFC 5077 session ticket */ size_t ticket_len; /*!< session ticket length */ uint32_t ticket_lifetime; /*!< ticket lifetime hint */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ int trunc_hmac; /*!< flag for truncated hmac activation */ @@ -444,9 +448,12 @@ struct _ssl_handshake_params int max_major_ver; /*!< max. major version client*/ int max_minor_ver; /*!< max. minor version client*/ +#if defined(POLARSSL_SSL_SESSION_TICKETS) int new_session_ticket; /*!< use NewSessionTicket? */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ }; +#if defined(POLARSSL_SSL_SESSION_TICKETS) /* * Parameters needed to secure session tickets */ @@ -457,6 +464,7 @@ struct _ssl_ticket_keys aes_context dec; /*!< decryption context */ unsigned char mac_key[16]; /*!< authentication key */ }; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ struct _ssl_context { @@ -566,10 +574,12 @@ struct _ssl_context const char *peer_cn; /*!< expected peer CN */ #endif /* POLARSSL_X509_PARSE_C */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) /* * Support for generating and checking session tickets */ ssl_ticket_keys *ticket_keys; /*!< keys for ticket encryption */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /* * User settings @@ -1037,6 +1047,7 @@ int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code ); */ int ssl_set_truncated_hmac( ssl_context *ssl, int truncate ); +#if defined(POLARSSL_SSL_SESSION_TICKETS) /** * \brief Enable / Disable session tickets * (Default: SSL_SESSION_TICKETS_ENABLED on client, @@ -1054,6 +1065,7 @@ int ssl_set_truncated_hmac( ssl_context *ssl, int truncate ); * or a specific error code (server only). */ int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ); +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /** * \brief Enable / Disable renegotiation support for connection when diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 402c85a47..ac728324f 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -322,6 +322,7 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, *olen = 4; } +#if defined(POLARSSL_SSL_SESSION_TICKETS) static void ssl_write_session_ticket_ext( ssl_context *ssl, unsigned char *buf, size_t *olen ) { @@ -356,6 +357,7 @@ static void ssl_write_session_ticket_ext( ssl_context *ssl, *olen += tlen; } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ static int ssl_write_client_hello( ssl_context *ssl ) { @@ -441,6 +443,7 @@ static int ssl_write_client_hello( ssl_context *ssl ) n = 0; } +#if defined(POLARSSL_SSL_SESSION_TICKETS) /* * RFC 5077 section 3.4: "When presenting a ticket, the client MAY * generate and include a Session ID in the TLS ClientHello." @@ -456,6 +459,7 @@ static int ssl_write_client_hello( ssl_context *ssl ) ssl->session_negotiate->length = n = 32; } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ *p++ = (unsigned char) n; @@ -548,8 +552,10 @@ static int ssl_write_client_hello( ssl_context *ssl ) ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; +#if defined(POLARSSL_SSL_SESSION_TICKETS) ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; +#endif SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d", ext_len ) ); @@ -650,6 +656,7 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, return( 0 ); } +#if defined(POLARSSL_SSL_SESSION_TICKETS) static int ssl_parse_session_ticket_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) @@ -666,6 +673,7 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ static int ssl_parse_server_hello( ssl_context *ssl ) { @@ -905,6 +913,7 @@ static int ssl_parse_server_hello( ssl_context *ssl ) break; +#if defined(POLARSSL_SSL_SESSION_TICKETS) case TLS_EXT_SESSION_TICKET: SSL_DEBUG_MSG( 3, ( "found session_ticket extension" ) ); @@ -915,6 +924,7 @@ static int ssl_parse_server_hello( ssl_context *ssl ) } break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", @@ -1925,6 +1935,7 @@ static int ssl_write_certificate_verify( ssl_context *ssl ) !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) static int ssl_parse_new_session_ticket( ssl_context *ssl ) { int ret; @@ -2016,6 +2027,7 @@ static int ssl_parse_new_session_ticket( ssl_context *ssl ) return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /* * SSL handshake -- client side -- single step @@ -2105,9 +2117,11 @@ int ssl_handshake_client_step( ssl_context *ssl ) * Finished */ case SSL_SERVER_CHANGE_CIPHER_SPEC: +#if defined(POLARSSL_SSL_SESSION_TICKETS) if( ssl->handshake->new_session_ticket != 0 ) ret = ssl_parse_new_session_ticket( ssl ); else +#endif ret = ssl_parse_change_cipher_spec( ssl ); break; diff --git a/library/ssl_srv.c b/library/ssl_srv.c index f94fda50a..0dbcdb5ed 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -47,6 +47,7 @@ #include #endif +#if defined(POLARSSL_SSL_SESSION_TICKETS) /* * Serialize a session in the following format: * 0 . n-1 session structure, n = sizeof(ssl_session) @@ -300,6 +301,7 @@ static int ssl_parse_ticket( ssl_context *ssl, return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ static int ssl_parse_servername_ext( ssl_context *ssl, const unsigned char *buf, @@ -584,6 +586,7 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, return( 0 ); } +#if defined(POLARSSL_SSL_SESSION_TICKETS) static int ssl_parse_session_ticket_ext( ssl_context *ssl, unsigned char *buf, size_t len ) @@ -625,6 +628,7 @@ static int ssl_parse_session_ticket_ext( ssl_context *ssl, return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ #if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) static int ssl_parse_client_hello_v2( ssl_context *ssl ) @@ -1176,6 +1180,7 @@ static int ssl_parse_client_hello( ssl_context *ssl ) return( ret ); break; +#if defined(POLARSSL_SSL_SESSION_TICKETS) case TLS_EXT_SESSION_TICKET: SSL_DEBUG_MSG( 3, ( "found session ticket extension" ) ); @@ -1183,6 +1188,7 @@ static int ssl_parse_client_hello( ssl_context *ssl ) if( ret != 0 ) return( ret ); break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", @@ -1316,6 +1322,7 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, *olen = 4; } +#if defined(POLARSSL_SSL_SESSION_TICKETS) static void ssl_write_session_ticket_ext( ssl_context *ssl, unsigned char *buf, size_t *olen ) @@ -1338,6 +1345,7 @@ static void ssl_write_session_ticket_ext( ssl_context *ssl, *olen = 4; } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ static void ssl_write_renegotiation_ext( ssl_context *ssl, unsigned char *buf, @@ -1466,11 +1474,12 @@ static int ssl_write_server_hello( ssl_context *ssl ) */ ssl->state++; +#if defined(POLARSSL_SSL_SESSION_TICKETS) if( ssl->handshake->new_session_ticket == 0 ) { ssl->session_negotiate->length = n = 32; if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, - n ) ) != 0 ) + n ) ) != 0 ) return( ret ); } else @@ -1478,6 +1487,12 @@ static int ssl_write_server_hello( ssl_context *ssl ) ssl->session_negotiate->length = 0; memset( ssl->session_negotiate->id, 0, 32 ); } +#else + ssl->session_negotiate->length = n = 32; + if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, + n ) ) != 0 ) + return( ret ); +#endif /* POLARSSL_SSL_SESSION_TICKETS */ } else { @@ -1531,8 +1546,10 @@ static int ssl_write_server_hello( ssl_context *ssl ) ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; +#if defined(POLARSSL_SSL_SESSION_TICKETS) ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; +#endif SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) ); @@ -2469,6 +2486,7 @@ static int ssl_parse_certificate_verify( ssl_context *ssl ) !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) static int ssl_write_new_session_ticket( ssl_context *ssl ) { int ret; @@ -2518,6 +2536,7 @@ static int ssl_write_new_session_ticket( ssl_context *ssl ) return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /* * SSL handshake -- server side -- single step @@ -2607,9 +2626,11 @@ int ssl_handshake_server_step( ssl_context *ssl ) * Finished */ case SSL_SERVER_CHANGE_CIPHER_SPEC: +#if defined(POLARSSL_SSL_SESSION_TICKETS) if( ssl->handshake->new_session_ticket != 0 ) ret = ssl_write_new_session_ticket( ssl ); else +#endif ret = ssl_write_change_cipher_spec( ssl ); break; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index a0bf9ce1d..2585d6e23 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -101,6 +101,7 @@ static int ssl_session_copy( ssl_session *dst, const ssl_session *src ) } #endif /* POLARSSL_X509_PARSE_C */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) if( src->ticket != NULL ) { if( ( dst->ticket = polarssl_malloc( src->ticket_len ) ) == NULL ) @@ -108,6 +109,7 @@ static int ssl_session_copy( ssl_session *dst, const ssl_session *src ) memcpy( dst->ticket, src->ticket, src->ticket_len ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ return( 0 ); } @@ -2972,6 +2974,7 @@ int ssl_session_reset( ssl_context *ssl ) return( 0 ); } +#if defined(POLARSSL_SSL_SESSION_TICKETS) /* * Allocate and initialize ticket keys */ @@ -3004,6 +3007,7 @@ static int ssl_ticket_keys_init( ssl_context *ssl ) return( 0 ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /* * SSL set accessors @@ -3261,6 +3265,7 @@ void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ) ssl->allow_legacy_renegotiation = allow_legacy; } +#if defined(POLARSSL_SSL_SESSION_TICKETS) int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ) { ssl->session_tickets = use_tickets; @@ -3273,6 +3278,7 @@ int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ) return( ssl_ticket_keys_init( ssl ) ); } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ /* * SSL get accessors @@ -3658,7 +3664,9 @@ void ssl_session_free( ssl_session *session ) } #endif +#if defined(POLARSSL_SSL_SESSION_TICKETS) polarssl_free( session->ticket ); +#endif memset( session, 0, sizeof( ssl_session ) ); } @@ -3710,7 +3718,9 @@ void ssl_free( ssl_context *ssl ) polarssl_free( ssl->session ); } +#if defined(POLARSSL_SSL_SESSION_TICKETS) polarssl_free( ssl->ticket_keys ); +#endif if ( ssl->hostname != NULL) { diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 4b92c19f4..291795f0d 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -178,6 +178,13 @@ static int my_verify( void *data, x509_cert *crt, int depth, int *flags ) #define USAGE_PSK "" #endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) +#define USAGE_TICKETS \ + " tickets=%%d default: 1 (enabled)\n" +#else +#define USAGE_TICKETS "" +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + #define USAGE \ "\n usage: ssl_client2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -189,7 +196,7 @@ static int my_verify( void *data, x509_cert *crt, int depth, int *flags ) " renegotiation=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ " reconnect=%%d default: 0 (disabled)\n" \ - " tickets=%%d default: 1 (enabled)\n" \ + USAGE_TICKETS \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -674,7 +681,9 @@ int main( int argc, char *argv[] ) ssl_set_bio( &ssl, net_recv, &server_fd, net_send, &server_fd ); +#if defined(POLARSSL_SSL_SESSION_TICKETS) ssl_set_session_tickets( &ssl, opt.tickets ); +#endif if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER ) ssl_set_ciphersuites( &ssl, opt.force_ciphersuite ); diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 2ed74e404..aca0db526 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -146,6 +146,13 @@ static void my_debug( void *ctx, int level, const char *str ) #define USAGE_PSK "" #endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) +#define USAGE_TICKETS \ + " tickets=%%d default: 1 (enabled)\n" +#else +#define USAGE_TICKETS "" +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + #define USAGE \ "\n usage: ssl_server2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -154,7 +161,7 @@ static void my_debug( void *ctx, int level, const char *str ) USAGE_IO \ " request_page=%%s default: \".\"\n" \ " renegotiation=%%d default: 1 (enabled)\n" \ - " tickets=%%d default: 1 (enabled)\n" \ + USAGE_TICKETS \ " allow_legacy=%%d default: 0 (disabled)\n" \ " min_version=%%s default: \"ssl3\"\n" \ " max_version=%%s default: \"tls1_2\"\n" \ @@ -621,7 +628,9 @@ int main( int argc, char *argv[] ) ssl_cache_set, &cache ); #endif +#if defined(POLARSSL_SSL_SESSION_TICKETS) ssl_set_session_tickets( &ssl, opt.tickets ); +#endif if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER ) ssl_set_ciphersuites( &ssl, opt.force_ciphersuite ); From 936539ad4b64ba9200a7fe004d4edd6c4a5946f5 Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Wed, 14 Aug 2013 13:49:20 +0200 Subject: [PATCH 24/24] Updated Changelog to reflect addition of session tickets --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index e0c1ec903..505d90a3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,7 @@ Features * Support for truncated_hmac extension (RFC 6066) * Support for zeros-and-length (ANSI X.923) padding, one-and-zeros (ISO/IEC 7816-4) padding and zero padding in the cipher layer + * Support for session tickets (RFC 5077) Changes * Introduced separate SSL Ciphersuites module that is based on