static int ssl3_decode_server_hello( DSSL_Session* sess, u_char* data, uint32_t len ) { uint16_t server_version = 0; u_char* org_data = data; uint16_t session_id_len = 0; int session_id_match = 0; if( data[0] != 3 || data[1] > 3) return NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); if( len < SSL3_SERVER_HELLO_MIN_LEN ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); /* Server Version */ server_version = MAKE_UINT16( data[0], data[1] ); if( sess->version == 0 || server_version < sess->version ) { ssls_set_session_version( sess, server_version ); } data+= 2; /* ServerRandom */ _ASSERT_STATIC( sizeof(sess->server_random) == 32 ); memcpy( sess->server_random, data, sizeof( sess->server_random ) ); data+= 32; DEBUG_TRACE_BUF("server_random", sess->server_random, 32); /* session ID */ _ASSERT_STATIC( sizeof(sess->session_id) == 32 ); session_id_len = data[0]; data++; if( session_id_len > 0 ) { if ( session_id_len > 32 ) return NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); if( !IS_ENOUGH_LENGTH( org_data, len, data, session_id_len ) ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } if( sess->flags & SSF_CLIENT_SESSION_ID_SET && memcmp( sess->session_id, data, session_id_len ) == 0 ) { session_id_match = 1; } else { sess->flags &= ~SSF_CLIENT_SESSION_ID_SET; memcpy( sess->session_id, data, session_id_len ); } data += session_id_len; } /* Cipher Suite and Compression */ if( !IS_ENOUGH_LENGTH( org_data, len, data, 3 ) ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } sess->cipher_suite = MAKE_UINT16( data[0], data[1] ); sess->compression_method = data[2]; data += 3; sess->flags &= ~SSF_TLS_SERVER_SESSION_TICKET; /* clear server side TLS Session Ticket flag */ /* Process SSL Extensions, if present */ if(IS_ENOUGH_LENGTH( org_data, len, data, 2 )) { int t_len = MAKE_UINT16(data[0], data[1]); data += 2; if(!IS_ENOUGH_LENGTH( org_data, len, data, t_len)) return NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); /* cycle through extension records */ while(t_len >= 4) { int ext_type = MAKE_UINT16(data[0], data[1]); /* extension type */ int ext_len = MAKE_UINT16(data[2], data[3]); #ifdef NM_TRACE_SSL_HANDSHAKE DEBUG_TRACE2( "\nSSL extension: %s len: %d", SSL3_ExtensionTypeToString( ext_type ), ext_len ); #endif /* TLS Session Ticket extension found, set the flag */ if( ext_type == 0x0023) { sess->flags |= SSF_TLS_SERVER_SESSION_TICKET; } data += ext_len + 4; if(data > org_data + len) return NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); t_len -= ext_len + 4; } } if( session_id_match ) { if( sess->flags & SSF_TLS_SESSION_TICKET_SET) { int rc = ssls_init_from_tls_ticket( sess ); if( NM_IS_FAILED( rc ) ) return rc; } else { /* lookup session from the cache for stateful SSL renegotiation */ int rc = ssls_lookup_session( sess ); if( NM_IS_FAILED( rc ) ) return rc; } } if( sess->flags & SSF_CLIENT_SESSION_ID_SET ) { int rc = ssls_generate_keys( sess ); if( NM_IS_FAILED( rc ) ) return rc; } return DSSL_RC_OK; }
/* ========== SERVER-HELLO ========== */ static int ssl2_decode_server_hello( DSSL_Session* sess, u_char* data, uint32_t len, uint32_t* processed ) { int rc = DSSL_RC_OK; uint16_t certLen = 0; uint16_t cipherSpecLen = 0; uint16_t connectionIdLen = 0; int session_id_hit = 0; _ASSERT( processed && data && sess ); if( len < SSL20_SERVER_HELLO_MIN_LEN ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } /* check SESSION-ID-HIT */ session_id_hit = data[0]; /* decode CERTIFICATE-TYPE */ if( rc == DSSL_RC_OK && (data[1] && data[1] != SSL2_CT_X509_CERTIFICATE) ) { rc = NM_ERROR( DSSL_E_SSL2_INVALID_CERTIFICATE_TYPE ); } /* SERVER-VERSION*/ /* TODO: add server version check */ /* CERTIFICATE-LENGTH, CIPHER-SPECS-LENGTH, CONNECTION-ID-LENGTH */ if( rc == DSSL_RC_OK ) { certLen = MAKE_UINT16( data[4], data[5] ); cipherSpecLen = MAKE_UINT16( data[6], data[7] ); connectionIdLen = MAKE_UINT16( data[8], data[9] ); if( (uint32_t)certLen + cipherSpecLen + connectionIdLen + SSL20_SERVER_HELLO_MIN_LEN != len ) { rc = NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } else if( connectionIdLen < 16 || connectionIdLen > 32 ) { rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } if( rc == DSSL_RC_OK ) { u_char* connIdData = data + SSL20_SERVER_HELLO_MIN_LEN + certLen + cipherSpecLen; sess->server_connection_id_len = connectionIdLen; memset( sess->server_random, 0, sizeof(sess->server_random) ); memcpy( sess->server_random, connIdData, connectionIdLen ); } } /* TODO: Check the certificate to match what DSSL has been initialized with */ if( rc == DSSL_RC_OK ) { } if( session_id_hit ) { if( sess->flags & SSF_CLIENT_SESSION_ID_SET ) { rc = ssls_lookup_session( sess ); /* re-generate the session keys and turn on ciphers right away */ if( rc == DSSL_RC_OK) { rc = ssls2_generate_keys( sess, sess->ssl2_key_arg, sess->ssl2_key_arg_len ); } /* turn on new ciphers */ if( rc == DSSL_RC_OK ) { rc = dssl_decoder_stack_flip_cipher( &sess->c_dec ); if (rc == DSSL_RC_OK ) { rc = dssl_decoder_stack_flip_cipher( &sess->s_dec ); } } } else { /* client didn't send the session id */ rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } } if( rc == DSSL_RC_OK ) { *processed = certLen + cipherSpecLen + connectionIdLen + SSL20_SERVER_HELLO_MIN_LEN; } return rc; }