int DSSL_SessionProcessData( DSSL_Session* sess, NM_PacketDir dir, u_char* data, uint32_t len ) { int rc = DSSL_RC_OK; dssl_decoder_stack* dec = NULL; if( dir == ePacketDirInvalid ) return NM_ERROR( DSSL_E_INVALID_PARAMETER ); dec = (dir == ePacketDirFromClient) ? &sess->c_dec : &sess->s_dec; if( !sslc_is_decoder_stack_set( dec ) ) { uint16_t ver = 0; int is_client = (dir == ePacketDirFromClient); if( is_client ) { rc = ssl_detect_client_hello_version( data, len, &ver ); } else { rc = ssl_detect_server_hello_version( data, len, &ver ); /* update the client decoder after the server have declared the actual version of the session */ DEBUG_TRACE2("DSSL_SessionProcessData - sess->version: 0x%02X, ver is: 0x%02X\n", sess->version, ver); if( rc == DSSL_RC_OK && sess->version != ver ) { sess->c_dec.version = ver; rc = dssl_decoder_stack_set( &sess->c_dec, sess, ver, 1 ); } ssls_set_session_version( sess, ver ); } if( rc == DSSL_RC_OK ) { dec->version = ver; rc = dssl_decoder_stack_set( dec, sess, ver, is_client ); } } if( rc == DSSL_RC_OK ) rc = dssl_decoder_stack_process( dec, dir, data, len ); /* check if a session with a first-time automapped key failed */ if( NM_IS_FAILED( rc ) && sess->flags & SSF_TEST_SSL_KEY ) { if(sess->event_callback) { (*sess->event_callback)( sess->user_data, eSslMappedKeyFailed, sess->ssl_si ); } DSSL_MoveServerToMissingKeyList( sess->env, sess->ssl_si ); sess->ssl_si = NULL; } if( NM_IS_FAILED( rc ) && sess->error_callback && rc != DSSL_E_SSL_SERVER_KEY_UNKNOWN ) { sess->error_callback( sess->user_data, rc ); } return rc; }
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; }
static int ssl3_decode_client_hello( DSSL_Session* sess, u_char* data, uint32_t len ) { u_char* org_data = data; int t_len = 0; /* record the handshake start time */ sess->handshake_start = sess->last_packet->pcap_header.ts; if( data[0] != 3 || data[1] > 3) return NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); /* 2 bytes client version */ sess->client_version = MAKE_UINT16( data[0], data[1] ); ssls_set_session_version( sess, MAKE_UINT16( data[0], data[1] ) ); data+= 2; /* make sure */ if( data + 32 > org_data + len ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); /* 32 bytes ClientRandom */ memcpy( sess->client_random, data, 32 ); data+= 32; DEBUG_TRACE_BUF("client_random", sess->client_random, 32); /* check session ID length */ if( data[0] > 32 ) return NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); if( data[0] > 0 ) { /* Session ID set */ if( data + data[0] > org_data + len ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); memcpy( sess->session_id, data+1, data[0] ); sess->flags |= SSF_CLIENT_SESSION_ID_SET; data += data[0] + 1; } else { /* no Session ID */ sess->flags &= ~SSF_CLIENT_SESSION_ID_SET; ++data; } /* Cypher Suites */ if(data + 1 >= org_data + len) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); t_len = MAKE_UINT16(data[0], data[1]) + 2; /* cypher suites + cypher sute length size */ data += t_len; /* skip cypher suites */ /* Compression Method */ if(data >= org_data + len) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); if(data + data[0] + 1 > org_data + len) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); t_len = data[0] + 1; data += t_len; /* skip compression methods */ /* Extensions */ /* clear all previous extension fields */ ssls_free_extension_data(sess); if(data >= org_data + len) return DSSL_RC_OK; if(data + 2 > org_data + len) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); t_len = MAKE_UINT16(data[0], data[1]); data += 2; /* positon at the beginning of the first extension record, if any*/ 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 */ if( ext_type == 0x0023) { /* non empty ticket passed, store it */ if(ext_len > 0) { sess->flags |= SSF_TLS_SESSION_TICKET_SET; sess->session_ticket = (u_char*) malloc(ext_len); if(sess->session_ticket == NULL) return NM_ERROR(DSSL_E_OUT_OF_MEMORY); memcpy(sess->session_ticket, data+4, ext_len); sess->session_ticket_len = ext_len; } } data += ext_len + 4; if(data > org_data + len) return NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); t_len -= ext_len + 4; } return DSSL_RC_OK; }
/* ========== CLIENT-HELLO ========== */ static int ssl2_decode_client_hello( DSSL_Session* sess, u_char* data, uint32_t len, uint32_t* processed ) { int rc = DSSL_RC_OK; uint32_t sessionIdLen = 0, challengeLen = 0, cipherSpecLen = 0; _ASSERT( processed && data && sess ); /* record the handshake start time */ sess->handshake_start = sess->last_packet->pcap_header.ts; if( len < SSL20_CLIENT_HELLO_MIN_LEN ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); if( data[0] == 0 && data[1] == 2 ) { sess->client_version = SSL2_VERSION; rc = ssls_set_session_version( sess, SSL2_VERSION ); } else if( data[0] == 3 ) { /* SSLv3 or TLS1 in a v2 header */ sess->client_version = MAKE_UINT16(data[0], data[1]); rc = ssls_set_session_version( sess, MAKE_UINT16(data[0], data[1]) ); } else { rc = NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); } /* validate the record format */ if( rc == DSSL_RC_OK ) { /* CIPHER-SPECS-LENGTH */ cipherSpecLen = MAKE_UINT16( data[2], data[3] ); /* SESSION-ID-LENGTH */ sessionIdLen = MAKE_UINT16( data[4], data[5] ); /* CHALLENGE-LENGTH */ challengeLen = MAKE_UINT16( data[6], data[7] ); if( challengeLen + sessionIdLen + cipherSpecLen + SSL20_CLIENT_HELLO_MIN_LEN != len ) { rc = NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } } /* validate and set the session ID */ if( rc == DSSL_RC_OK ) { if( sessionIdLen == 16 ) { u_char* sessionId = data + SSL20_CLIENT_HELLO_MIN_LEN + cipherSpecLen; _ASSERT( sessionIdLen <= sizeof( sess->session_id ) ); memset( sess->session_id, 0, sizeof( sess->session_id ) ); memcpy( sess->session_id, sessionId, sessionIdLen ); sess->flags |= SSF_CLIENT_SESSION_ID_SET; } else { sess->flags &= ~SSF_CLIENT_SESSION_ID_SET; if (sessionIdLen != 0 ) { /* session ID length must be either 16 or 0 for SSL v2 */ rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } } } /* validate and set the client random aka Challenge */ if( rc == DSSL_RC_OK ) { if( challengeLen < 16 || challengeLen > 32 ) { rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } else { u_char* challenge = data + SSL20_CLIENT_HELLO_MIN_LEN + cipherSpecLen + sessionIdLen; _ASSERT( challengeLen <= sizeof( sess->client_random ) ); memset( sess->client_random, 0, sizeof( sess->client_random ) ); memcpy( sess->client_random, challenge, challengeLen ); sess->client_challenge_len = challengeLen; /* may need this flag later to convert CHALLENGE to SSL v3 CLIENT_RANDOM */ sess->flags |= SSF_SSLV2_CHALLENGE; } } if( rc == DSSL_RC_OK ) { *processed = len; } return rc; }