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++;