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 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/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 a4634600b..f45d00e55 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" @@ -107,6 +108,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 @@ -152,6 +155,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 @@ -239,6 +245,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 @@ -262,6 +269,8 @@ #define TLS_EXT_SIG_ALG 13 +#define TLS_EXT_SESSION_TICKET 35 + #define TLS_EXT_RENEGOTIATION_INFO 0xFF01 /* @@ -311,7 +320,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; @@ -319,6 +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. @@ -338,6 +351,12 @@ 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 */ }; @@ -428,8 +447,25 @@ 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*/ + +#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 + */ +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 */ +}; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + struct _ssl_context { /* @@ -538,6 +574,13 @@ 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 */ @@ -549,6 +592,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 */ @@ -655,6 +699,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 ); @@ -774,15 +821,17 @@ 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 + * + * \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 @@ -998,6 +1047,26 @@ 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, + * 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 ); +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + /** * \brief Enable / Disable renegotiation support for connection when * initiated by peer @@ -1100,6 +1169,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/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 877d6cddd..ac728324f 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 @@ -315,6 +322,43 @@ 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 ) +{ + 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 ); + *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; +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + static int ssl_write_client_hello( ssl_context *ssl ) { int ret; @@ -395,7 +439,27 @@ 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; + } + +#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." + */ + 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; + } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ *p++ = (unsigned char) n; @@ -488,6 +552,11 @@ 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 ) ); @@ -587,6 +656,25 @@ 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 ) +{ + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED || + len != 0 ) + { + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ((void) buf); + + ssl->handshake->new_session_ticket = 1; + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + static int ssl_parse_server_hello( ssl_context *ssl ) { uint32_t t; @@ -825,6 +913,19 @@ 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" ) ); + + if( ( ret = ssl_parse_session_ticket_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); @@ -1834,6 +1935,100 @@ 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; + 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_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 + */ + 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 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + /* * SSL handshake -- client side -- single step */ @@ -1917,11 +2112,17 @@ int ssl_handshake_client_step( ssl_context *ssl ) break; /* - * <== ChangeCipherSpec + * <== ( NewSessionTicket ) + * ChangeCipherSpec * Finished */ case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); +#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; case SSL_SERVER_FINISHED: diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 6cbc5f406..0dbcdb5ed 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,262 @@ #include #endif +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/* + * 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 ); +} + +/* + * 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 ) +{ + int ret; + unsigned char * const start = ssl->out_msg + 10; + unsigned char *p = start; + 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; + + /* 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; + + /* 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 ); + + /* 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; + + /* Encrypt */ + if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->enc, AES_ENCRYPT, + enc_len, iv, state, state ) ) != 0 ) + { + return( ret ); + } + + /* Write length */ + *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( enc_len ) & 0xFF ); + p = state + enc_len; + + /* 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; + + SSL_DEBUG_BUF( 3, "session ticket structure", start, *tlen ); + + return( 0 ); +} + +/* + * Load session ticket (see ssl_write_ticket for structure) + */ +static int ssl_parse_ticket( ssl_context *ssl, + unsigned char *buf, + size_t len ) +{ + int ret; + ssl_session session; + 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; + unsigned char computed_mac[16]; + 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 ); + + 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 ); + + /* Check name */ + if( memcmp( key_name, ssl->ticket_keys->key_name, 16 ) != 0 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + /* 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, + 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" ) ); + 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 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + static int ssl_parse_servername_ext( ssl_context *ssl, const unsigned char *buf, size_t len ) @@ -323,6 +586,50 @@ 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 ) +{ + int ret; + + 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; + + SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); + + if( len == 0 ) + return( 0 ); + + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ) + { + SSL_DEBUG_MSG( 3, ( "ticket rejected: renegotiating" ) ); + return( 0 ); + } + + /* + * Failures are ok: just ignore the ticket and proceed. + */ + 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" ) ); + + ssl->handshake->resume = 1; + + /* Don't send a new ticket after all, this one is OK */ + ssl->handshake->new_session_ticket = 0; + + 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 ) { @@ -610,7 +917,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 ); @@ -873,6 +1180,16 @@ 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" ) ); + + ret = ssl_parse_session_ticket_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + default: SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", ext_id ) ); @@ -1005,6 +1322,31 @@ 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 ) +{ + 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; +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + static void ssl_write_renegotiation_ext( ssl_context *ssl, unsigned char *buf, size_t *olen ) @@ -1111,36 +1453,52 @@ 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->session_negotiate->length != 0 && + 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 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 ) + return( ret ); + } + else + { + 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 { /* - * 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 ) @@ -1150,6 +1508,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; @@ -1179,6 +1546,11 @@ 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 ) ); *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); @@ -2114,6 +2486,58 @@ 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; + size_t tlen; + + 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; + + 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 ); + + ssl->out_msglen = 10 + tlen; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + /* No need to remember writing a NewSessionTicket any more */ + ssl->handshake->new_session_ticket = 0; + + SSL_DEBUG_MSG( 2, ( "<= write new session ticket" ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + /* * SSL handshake -- server side -- single step */ @@ -2197,11 +2621,17 @@ int ssl_handshake_server_step( ssl_context *ssl ) break; /* - * ==> ChangeCipherSpec + * ==> ( NewSessionTicket ) + * ChangeCipherSpec * Finished */ case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); +#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; case SSL_SERVER_FINISHED: diff --git a/library/ssl_tls.c b/library/ssl_tls.c index b9fca4440..2585d6e23 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -76,6 +76,44 @@ 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 defined(POLARSSL_SSL_SESSION_TICKETS) + 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 ); + } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + + 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, @@ -2539,6 +2577,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 +2610,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++; @@ -2930,12 +2974,50 @@ int ssl_session_reset( ssl_context *ssl ) return( 0 ); } +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/* + * Allocate and initialize ticket keys + */ +static int ssl_ticket_keys_init( ssl_context *ssl ) +{ + int ret; + ssl_ticket_keys *tkeys; + unsigned char buf[16]; + + 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 ); + + 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 ); + } + + if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->mac_key, 16 ) ) != 0 ) + return( ret ); + + ssl->ticket_keys = tkeys; + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + /* * SSL set accessors */ 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 ) @@ -2989,10 +3071,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 ) @@ -3169,6 +3265,21 @@ 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; + + 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 ) ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + /* * SSL get accessors */ @@ -3222,6 +3333,19 @@ 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 ) +{ + if( ssl == NULL || + dst == NULL || + ssl->session == NULL || + ssl->endpoint != SSL_IS_CLIENT ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + return( ssl_session_copy( dst, ssl->session ) ); +} + /* * Perform a single step of the SSL handshake */ @@ -3540,6 +3664,10 @@ 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 ) ); } @@ -3590,6 +3718,10 @@ 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) { memset( ssl->hostname, 0, ssl->hostname_len ); diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 05dfdc700..291795f0d 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -59,6 +59,8 @@ #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 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-" \ @@ -96,6 +98,8 @@ 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 */ + int tickets; /* enable / disable session tickets */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -174,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" \ @@ -184,6 +195,8 @@ 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" \ + USAGE_TICKETS \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -226,6 +239,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 +253,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 +300,8 @@ 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; + opt.tickets = DFL_TICKETS; for( i = 1; i < argc; i++ ) { @@ -345,6 +362,18 @@ 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 > 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 ) @@ -652,6 +681,10 @@ 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 ); @@ -693,6 +726,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 +779,7 @@ int main( int argc, char *argv[] ) /* * 6. Write the GET request */ +send_request: printf( " > Write to server:" ); fflush( stdout ); @@ -789,6 +837,43 @@ int main( int argc, char *argv[] ) ssl_close_notify( &ssl ); + if( opt.reconnect != 0 ) + { + --opt.reconnect; + + 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 @@ -807,6 +892,7 @@ exit: x509_free( &cacert ); rsa_free( &rsa ); #endif + ssl_session_free( &saved_session ); ssl_free( &ssl ); memset( &ssl, 0, sizeof( ssl ) ); diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 57ff85c3a..aca0db526 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 ) @@ -144,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" \ @@ -152,6 +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" \ + USAGE_TICKETS \ " allow_legacy=%%d default: 0 (disabled)\n" \ " min_version=%%s default: \"ssl3\"\n" \ " max_version=%%s default: \"tls1_2\"\n" \ @@ -265,6 +275,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 +407,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 +628,10 @@ 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 );