int ssl_detect_server_hello_version( u_char* data, uint32_t len, uint16_t* ver ) { int rc = DSSL_RC_OK; _ASSERT( ver != NULL ); _ASSERT( data != NULL ); if( data[0] & 0x80 && len >= SSL20_SERVER_HELLO_MIN_LEN && data[2] == SSL2_MT_SERVER_HELLO ) { *ver = MAKE_UINT16( data[5], data[6] ); } else if( data[0] == SSL3_RT_HANDSHAKE && len > 11 && data[1] == SSL3_VERSION_MAJOR && data[5] == SSL3_MT_SERVER_HELLO ) { uint16_t sever_hello_ver = MAKE_UINT16( data[9], data[10] ); *ver = MAKE_UINT16( data[1], data[2] ); if( *ver > sever_hello_ver ) rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } else if( data[0] == SSL3_RT_ALERT && len == 7 && data[1] == SSL3_VERSION_MAJOR && MAKE_UINT16( data[3], data[4] ) == 2 ) { /* this is an SSL3 Alert message - the server didn't like this session */ *ver = MAKE_UINT16( data[1], data[2] ); } else { rc = NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); } return rc; }
int ssl_detect_client_hello_version( u_char* data, uint32_t len, uint16_t* ver ) { int rc = DSSL_RC_OK; _ASSERT( ver != NULL ); _ASSERT( data != NULL ); /* SSL v2 header can be sent even by never clients */ if( data[0] & 0x80 && len >= 3 && data[2] == SSL2_MT_CLIENT_HELLO ) { *ver = MAKE_UINT16( data[3], data[4] ); } else if ( data[0] == SSL3_RT_HANDSHAKE && len > 11 && data[1] == SSL3_VERSION_MAJOR && data[5] == SSL3_MT_CLIENT_HELLO ) { uint16_t client_hello_ver = MAKE_UINT16( data[9], data[10] ); *ver = MAKE_UINT16( data[1], data[2] ); if( *ver > client_hello_ver ) rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } else { rc = NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); } return rc; }
int ssl3_alert_decoder( void* decoder_stack, NM_PacketDir dir, u_char* data, uint32_t len, uint32_t* processed ) { dssl_decoder_stack* stack = (dssl_decoder_stack*) decoder_stack; UNUSED_PARAM(dir); if( len != 2 ) return NM_ERROR( NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ) ); if( data[0] == 2 ) { stack->state = SS_FatalAlert; } /* Close notify? */ if( data[1] == 0 ) { stack->state = SS_SeenCloseNotify; } #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE2( "\nAlert received: %s (%d)", ( (stack->state == SS_FatalAlert) ? "fatal alert" : ((stack->state == SS_SeenCloseNotify) ? "close_notify alert" : "unknown alert")), (int) MAKE_UINT16( data[0], data[1] ) ); #endif (*processed) = len; return DSSL_RC_OK; }
static int ssl3_decode_new_session_ticket(DSSL_Session* sess, u_char* data, uint32_t len ) { uint16_t sz = 0; if(len < 6) return NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); sz = MAKE_UINT16(data[4], data[5]); if(len != sz + 6) return NM_ERROR(DSSL_E_SSL_PROTOCOL_ERROR); return ssls_store_new_ticket( sess, data + 6, sz ); }
int ssl3_record_layer_decoder( void* decoder_stack, NM_PacketDir dir, u_char* data, uint32_t len, uint32_t* processed ) { int rc = DSSL_E_UNSPECIFIED_ERROR; uint32_t recLen = 0, totalRecLen = 0; uint8_t record_type = 0; dssl_decoder_stack* stack = (dssl_decoder_stack*) decoder_stack; dssl_decoder* next_decoder = NULL; int decrypt_buffer_aquired = 0; int decompress_buffer_aquired = 0; _ASSERT( stack ); _ASSERT( processed ); _ASSERT( stack->sess ); if( stack->state > SS_Established ) { #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE1( "[!]Unexpected SSL record after %s", ( (stack->state == SS_FatalAlert) ? "fatal alert" : "close_notify alert") ); #endif return NM_ERROR( DSSL_E_SSL_UNEXPECTED_TRANSMISSION ); } /* special case for a first client hello */ if( stack->sess->version == 0 ) { _ASSERT( dir == ePacketDirFromClient ); rc = ssl_decode_first_client_hello( stack->sess, data, len, processed ); return rc; } if( len < SSL3_HEADER_LEN ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); if( data[1] != 3) return NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); /* Decode record type */ record_type = data[0]; totalRecLen = recLen = MAKE_UINT16( data[3], data[4] ); data += SSL3_HEADER_LEN; len -= SSL3_HEADER_LEN; #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE3( "\n==>Decoding SSL v3 Record from %s, type: %d, len: %d\n{\n", ((dir == ePacketDirFromClient)?"client":"server"), (int) record_type, (int) recLen ); #endif rc = DSSL_RC_OK; if( len < recLen ) { rc = DSSL_RC_WOULD_BLOCK; } if( rc == DSSL_RC_OK && stack->cipher ) { rc = ssl_decrypt_record( stack, data, recLen, &data, &recLen, &decrypt_buffer_aquired ); } /* check if the record length is still within bounds (failed decryption, etc) */ if( rc == DSSL_RC_OK && (recLen > RFC_2246_MAX_COMPRESSED_LENGTH || recLen > len || (stack->sess->version < TLS1_2_VERSION && stack->md && recLen < EVP_MD_size(stack->md))) ) { rc = NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); } if( rc == DSSL_RC_OK && stack->md ) { u_char mac[EVP_MAX_MD_SIZE*2]; u_char* rec_mac = NULL; int l = EVP_MD_size( stack->md ); int ivl = EVP_CIPHER_iv_length( stack->cipher->cipher ); if ( EVP_CIPH_CBC_MODE == stack->sess->cipher_mode || EVP_CIPH_STREAM_CIPHER == stack->sess->cipher_mode ) recLen -= l; rec_mac = data+recLen; memset(mac, 0, sizeof(mac) ); /* TLS 1.1 and later: remove explicit IV for non-stream ciphers */ if ( EVP_CIPH_CBC_MODE == stack->sess->cipher_mode ) { if (stack->sess->version >= TLS1_1_VERSION ) { if (ivl <= recLen) { recLen -= ivl; data += ivl; } } } /* AEAD ciphers have no mac */ if ( EVP_CIPH_CBC_MODE == stack->sess->cipher_mode || EVP_CIPH_STREAM_CIPHER == stack->sess->cipher_mode ) { rc = stack->sess->caclulate_mac_proc( stack, record_type, data, recLen, mac ); if( rc == DSSL_RC_OK ) { rc = memcmp( mac, rec_mac, l ) == 0 ? DSSL_RC_OK : NM_ERROR( DSSL_E_SSL_INVALID_MAC ); } } } if( rc == DSSL_RC_OK && stack->compression_method != 0 ) { rc = ssl_decompress_record( stack, data, recLen, &data, &recLen, &decompress_buffer_aquired ); } DEBUG_TRACE_BUF("decompressed", data, recLen); if( rc == DSSL_RC_OK ) { switch( record_type ) { case SSL3_RT_HANDSHAKE: next_decoder = &stack->dhandshake; break; case SSL3_RT_CHANGE_CIPHER_SPEC: next_decoder = &stack->dcss; break; case SSL3_RT_APPLICATION_DATA: next_decoder = &stack->dappdata; break; case SSL3_RT_ALERT: next_decoder = &stack->dalert; break; default: rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } } if( rc == DSSL_RC_OK ) { _ASSERT( next_decoder != NULL ); rc = dssl_decoder_process( next_decoder, dir, data, recLen ); } if( rc == DSSL_RC_OK ) { *processed = totalRecLen + SSL3_HEADER_LEN; } if( decrypt_buffer_aquired ) { ssls_release_decrypt_buffer( stack->sess ); } if( decompress_buffer_aquired ) { ssls_release_decompress_buffer( stack->sess ); } #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE1( "\n} rc: %d\n", (int) rc); #endif if( stack->state == SS_SeenCloseNotify ) { stack->sess->flags |= SSF_CLOSE_NOTIFY_RECEIVED; } else if ( stack->state == SS_FatalAlert ) { stack->sess->flags |= SSF_FATAL_ALERT_RECEIVED; } return rc; }
int ssl3_record_layer_decoder( void* decoder_stack, NM_PacketDir dir, u_char* data, uint32_t len, uint32_t* processed ) { int rc = DSSL_E_UNSPECIFIED_ERROR; uint32_t recLen = 0, totalRecLen = 0; uint8_t record_type = 0; dssl_decoder_stack* stack = (dssl_decoder_stack*) decoder_stack; dssl_decoder* next_decoder = NULL; int decrypt_buffer_aquired = 0; int decompress_buffer_aquired = 0; int i = 0; char * data2 = NULL; uint32_t recLen2 = 0; int block_size = 0; DEBUG_TRACE1("ssl_record_layer_decoder - start. len: %d\n", len); _ASSERT( stack ); _ASSERT( processed ); _ASSERT( stack->sess ); /* for (i=0; i < len; i++) { printf("0x%02X ", data[i]); } */ if( stack->state > SS_Established ) { #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE1( "[!]Unexpected SSL record after %s", ( (stack->state == SS_FatalAlert) ? "fatal alert" : "close_notify alert") ); #endif return NM_ERROR( DSSL_E_SSL_UNEXPECTED_TRANSMISSION ); } /* special case for a first client hello */ DEBUG_TRACE1("ssl_record_layer_decoder - version: 0x%02X\n", stack->sess->version); if( stack->sess->version == 0 ) { _ASSERT( dir == ePacketDirFromClient ); rc = ssl_decode_first_client_hello( stack->sess, data, len, processed ); return rc; } if( len < SSL3_HEADER_LEN ) return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); if( data[1] != 3) return NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); /* Decode record type */ record_type = data[0]; totalRecLen = recLen = MAKE_UINT16( data[3], data[4] ); DEBUG_TRACE1("ssl_record_layer_decoder - record_type: %d\n", record_type); DEBUG_TRACE1("ssl_record_layer_decoder - recLen: %d\n", recLen); /* for (i = 0; i < 128; i++) { printf("ssl_tls_record_layer_decoder - data before skip header size [%d]: %d\n", i, data[i]); } */ data += SSL3_HEADER_LEN; len -= SSL3_HEADER_LEN; DEBUG_TRACE1("ssl_record_layer_decoder - len after header adjustments: %d\n", len); /* for (i=0; i < len; i++) { printf("0x%02X ", data[i]); } */ #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE2( "\n==>Decoding SSL v3 Record, type: %d, len: %d\n{\n", (int) record_type, (int) recLen ); #endif rc = DSSL_RC_OK; if( len < recLen ) { rc = DSSL_RC_WOULD_BLOCK; DEBUG_TRACE0("ssl_tls_record_layer_decoder - rc is DSSL_RC_WOULD_BLOCK\n"); } if( rc == DSSL_RC_OK && stack->cipher ) { rc = ssl_decrypt_record( stack, data, recLen, &data, &recLen, &decrypt_buffer_aquired,&block_size ); DEBUG_TRACE1("ssl_record_layer_decoder - ssl_decrypt_record ret: %d\n", rc); } /* check if the record length is still within bounds (failed decryption, etc) */ if( rc == DSSL_RC_OK && (recLen > RFC_2246_MAX_COMPRESSED_LENGTH || recLen > len || (stack->md && recLen < EVP_MD_size(stack->md))) ) { rc = NM_ERROR(DSSL_E_SSL_INVALID_RECORD_LENGTH); } if( rc == DSSL_RC_OK && stack->md ) { u_char mac[EVP_MAX_MD_SIZE]; u_char* rec_mac = NULL; DEBUG_TRACE1("ssl_record_layer_decoder - data using len: %d\n", len); /* for (i=0; i < len; i++) { printf("0x%02X ", data[i]); } */ recLen -= EVP_MD_size( stack->md ); rec_mac = data+recLen; memset(mac, 0, sizeof(mac) ); // Fix - skip iv for TLS 1.1 DEBUG_TRACE1("ssl_record_layer_decoder - stack->version: 0x%02X\n", stack->version); DEBUG_TRACE1("ssl_record_layer_decoder - block_size: %d\n", block_size); if (stack->version > TLS1_VERSION && block_size > 1) { DEBUG_TRACE0("ssl_record_layer_decoder - activated fix for TLS 1.1 (skip 16 bytes)\n"); data2 = data + block_size; recLen2 = recLen - block_size; rc = stack->sess->caclulate_mac_proc( stack, record_type, data2, recLen2, mac ); } else { rc = stack->sess->caclulate_mac_proc( stack, record_type, data, recLen, mac ); } DEBUG_TRACE1("ssl_record_layer_decoder - caclulate_mac_proc result: %d\n", rc); if( rc == DSSL_RC_OK ) { DEBUG_TRACE1("ssl_record_layer_decoder - caclulate_mac_proc memcmp size(i.e. EVP_MD_size(stack->md)): %d\n", EVP_MD_size(stack->md)); DEBUG_TRACE0("ssl_record_layer_decoder - mac vs. rec_mac:\n"); if (IsDebugEnabled()) { for (i=0; i < EVP_MD_size(stack->md); i++) { DEBUG_TRACE2("0x%02X vs. 0x%02X\n", mac[i], rec_mac[i]); } } rc = memcmp( mac, rec_mac, EVP_MD_size(stack->md) ) == 0 ? DSSL_RC_OK : NM_ERROR( DSSL_E_SSL_INVALID_MAC ); } } if( rc == DSSL_RC_OK && stack->compression_method != 0 ) { rc = ssl_decompress_record( stack, data, recLen, &data, &recLen, &decompress_buffer_aquired ); DEBUG_TRACE1("ssl_record_layer_decoder - ssl_decompress_record call ended. rc: %d\n", rc); } if( rc == DSSL_RC_OK ) { DEBUG_TRACE1("ssl_record_layer_decoder - record_type: %d\n", record_type); switch( record_type ) { case SSL3_RT_HANDSHAKE: DEBUG_TRACE0("ssl_record_layer_decoder - SSL3_RT_HANDSHAKE\n"); next_decoder = &stack->dhandshake; break; case SSL3_RT_CHANGE_CIPHER_SPEC: DEBUG_TRACE0("ssl_record_layer_decoder - SSL3_RT_CHANGE_CIPHER_SPEC\n"); next_decoder = &stack->dcss; break; case SSL3_RT_APPLICATION_DATA: DEBUG_TRACE0("ssl_record_layer_decoder - SSL3_RT_APPLICATION_DATA\n"); next_decoder = &stack->dappdata; break; case SSL3_RT_ALERT: DEBUG_TRACE0("ssl_record_layer_decoder - SSL3_RT_ALERT\n"); next_decoder = &stack->dalert; break; default: DEBUG_TRACE0("ssl_record_layer_decoder - record_type not found\n"); rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } } if( rc == DSSL_RC_OK ) { _ASSERT( next_decoder != NULL ); DEBUG_TRACE1("ssl_record_layer_decoder - calling dssl_decoder_process. handler is: %x\n", next_decoder->handler); // Fix - for TLS 1.1 continue if (data2 == NULL) rc = dssl_decoder_process( next_decoder, dir, data, recLen ); else rc = dssl_decoder_process( next_decoder, dir, data2, recLen2 ); DEBUG_TRACE1("ssl_record_layer_decoder - dssl_decoder_process ret: %d\n", rc); } if( rc == DSSL_RC_OK ) { *processed = totalRecLen + SSL3_HEADER_LEN; } if( decrypt_buffer_aquired ) { ssls_release_decrypt_buffer( stack->sess ); } if( decompress_buffer_aquired ) { ssls_release_decompress_buffer( stack->sess ); } #ifdef NM_TRACE_SSL_RECORD DEBUG_TRACE1( "\n} rc: %d\n", (int) rc); #endif if( stack->state == SS_SeenCloseNotify ) { stack->sess->flags |= SSF_CLOSE_NOTIFY_RECEIVED; } else if ( stack->state == SS_FatalAlert ) { stack->sess->flags |= SSF_FATAL_ALERT_RECEIVED; } DEBUG_TRACE1("ssl_record_layer_decoder - end. rc = %d\n", rc); return rc; }
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; }
/* ========= ClientKeyExchange ========= */ int ssl3_decode_client_key_exchange( DSSL_Session* sess, u_char* data, uint32_t len ) { EVP_PKEY *pk = NULL; u_char* org_data = data; uint32_t org_len = len; int pms_len = 0; int rc = DSSL_RC_OK; if( sess->version < SSL3_VERSION || sess->version > TLS1_2_VERSION ) { return NM_ERROR( DSSL_E_SSL_UNKNOWN_VERSION ); } /* TLS is different as it sends the record length, while SSL3 implementaions don't (due to a bug in Netscape implementation) */ if( sess->version > SSL3_VERSION ) { uint16_t recLen = 0; if( !IS_ENOUGH_LENGTH( org_data, org_len, data, 2 ) ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } recLen = MAKE_UINT16( data[0], data[1] ); if( len != (uint32_t)recLen + 2 ) { /*TODO: set an option to tolerate this bug?*/ return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } /* advance */ data += len - recLen; len = recLen; } if( !IS_ENOUGH_LENGTH( org_data, org_len, data, SSL_MAX_MASTER_KEY_LENGTH ) ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } pk = ssls_get_session_private_key( sess ); /* if SSL server key is not found, try to find a matching one from the key pool */ if(pk == NULL) { _ASSERT( sess->last_packet); pk = ssls_try_ssl_keys( sess, data, len ); /* if a matching key found, register it with the server IP:port */ if(pk != NULL) { if( ssls_register_ssl_key( sess, pk ) == DSSL_RC_OK) { /* ssls_register_ssl_key clones the key, query the key back */ pk = ssls_get_session_private_key( sess ); } else { pk = NULL; } } } if(!pk) { ssls_register_missing_key_server( sess ); return NM_ERROR( DSSL_E_SSL_SERVER_KEY_UNKNOWN ); } if(pk->type != EVP_PKEY_RSA) return NM_ERROR( DSSL_E_SSL_CANNOT_DECRYPT_NON_RSA ); pms_len = RSA_private_decrypt( len, data, sess->PMS, pk->pkey.rsa, RSA_PKCS1_PADDING ); if( pms_len != SSL_MAX_MASTER_KEY_LENGTH ) { return NM_ERROR( DSSL_E_SSL_CORRUPTED_PMS ); } if( MAKE_UINT16( sess->PMS[0], sess->PMS[1] ) != sess->client_version ) { return NM_ERROR( DSSL_E_SSL_PMS_VERSION_ROLLBACK ); } rc = ssls_decode_master_secret( sess ); OPENSSL_cleanse(sess->PMS, sizeof(sess->PMS) ); if( rc != DSSL_RC_OK ) return rc; rc = ssls_generate_keys( sess ); if( rc == DSSL_RC_OK ) { ssls_store_session( sess ); } 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; }
/* ========== 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; }
/* ========== CLIENT-MASTER-KEY ========== */ static int ssl2_decode_client_master_key( DSSL_Session* sess, u_char* data, uint32_t len, uint32_t* processed ) { int rc = DSSL_RC_OK; uint16_t clearKeyLen = 0; uint16_t encKeyLen = 0; uint16_t keyArgLen = 0; u_char* pClearKey = NULL; u_char* pEncKey = NULL; u_char* pKeyArg = NULL; _ASSERT( processed && data && sess ); if( len < SSL20_CLIENT_MASTER_KEY_MIN_LEN ) { return NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } /* CIPHER-KIND - convert to 2 byte DSSL_Session::cipher_suite */ rc = DSSL_ConvertSSL2CipherSuite( data, &sess->cipher_suite ); /* CLEAR-KEY-LENGTH, ENCRYPTED-KEY-LENGTH, KEY-ARG-LENGTH */ if( rc == DSSL_RC_OK ) { clearKeyLen = MAKE_UINT16( data[3], data[4] ); encKeyLen = MAKE_UINT16( data[5], data[6] ); keyArgLen = MAKE_UINT16( data[7], data[8] ); if( len != (uint32_t)clearKeyLen + encKeyLen + keyArgLen + SSL20_CLIENT_MASTER_KEY_MIN_LEN ) { rc = NM_ERROR( DSSL_E_SSL_INVALID_RECORD_LENGTH ); } *processed = len; } /* reconstitute the master secret */ if( rc == DSSL_RC_OK ) { EVP_PKEY *pk = NULL; pClearKey = data + SSL20_CLIENT_MASTER_KEY_MIN_LEN; pEncKey = pClearKey + clearKeyLen; pKeyArg = pEncKey + encKeyLen; if( clearKeyLen ) { memcpy( sess->master_secret, pClearKey, clearKeyLen ); } pk = ssls_get_session_private_key( sess ); /* if SSL server key is not found, try to find a matching one from the key pool */ if(pk == NULL) { u_char buff[1024]; _ASSERT( sess->last_packet); memcpy(buff, pEncKey, encKeyLen); pk = ssls_try_ssl_keys( sess, pEncKey, encKeyLen ); /* if a matching key found, register it with the server IP:port */ if(pk != NULL) { if( ssls_register_ssl_key( sess, pk ) == DSSL_RC_OK) { /* ssls_register_ssl_key clones the key, query the key back */ //pk = ssls_get_session_private_key( sess ); } else { pk = NULL; } } } if( pk ) { uint32_t encLen2 = RSA_private_decrypt( encKeyLen, pEncKey, sess->master_secret + clearKeyLen, pk->pkey.rsa, RSA_PKCS1_PADDING ); if( clearKeyLen + encLen2 >= sizeof( sess->master_secret ) ) { rc = NM_ERROR( DSSL_E_SSL_PROTOCOL_ERROR ); } sess->master_key_len = clearKeyLen + encLen2; } else { rc = NM_ERROR( DSSL_E_SSL_SERVER_KEY_UNKNOWN ); ssls_register_missing_key_server( sess ); } } /* generate session keys */ if( rc == DSSL_RC_OK ) { rc = ssls2_generate_keys( sess, pKeyArg, keyArgLen ); } /* 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 ); } } return rc; }
/* ========== 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; }