/** * Test daemon response to TLS client hello requests containing extensions * * @param session * @param exten_t - the type of extension being appended to client hello request * @param ext_count - the number of consecutive extension replicas inserted into request * @param ext_length - the length of each appended extension * @return 0 on successful test completion, -1 otherwise */ static int test_hello_extension (gnutls_session_t session, extensions_t exten_t, int ext_count, int ext_length) { int i, ret = 0, pos = 0; MHD_socket sd; int exten_data_len, ciphersuite_len, datalen; struct sockaddr_in sa; char url[255]; opaque *data = NULL; uint8_t session_id_len = 0; opaque rnd[TLS_RANDOM_SIZE]; opaque extdata[MAX_EXT_DATA_LENGTH]; /* single, null compression */ unsigned char comp[] = { 0x01, 0x00 }; struct CBC cbc; sd = -1; memset (&cbc, 0, sizeof (struct CBC)); if (NULL == (cbc.buf = malloc (sizeof (char) * 256))) { fprintf (stderr, MHD_E_MEM); ret = -1; goto cleanup; } cbc.size = 256; sd = socket (AF_INET, SOCK_STREAM, 0); if (sd == -1) { fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); free (cbc.buf); return -1; } memset (&sa, '\0', sizeof (struct sockaddr_in)); sa.sin_family = AF_INET; sa.sin_port = htons (DEAMON_TEST_PORT); sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); enum MHD_GNUTLS_Protocol hver; /* init hash functions */ session->internals.handshake_mac_handle_md5 = MHD_gtls_hash_init (MHD_GNUTLS_MAC_MD5); session->internals.handshake_mac_handle_sha = MHD_gtls_hash_init (MHD_GNUTLS_MAC_SHA1); /* version = 2 , random = [4 for unix time + 28 for random bytes] */ datalen = 2 /* version */ + TLS_RANDOM_SIZE + (session_id_len + 1); data = MHD_gnutls_malloc (datalen); if (data == NULL) { free (cbc.buf); return -1; } hver = MHD_gtls_version_max (session); data[pos++] = MHD_gtls_version_get_major (hver); data[pos++] = MHD_gtls_version_get_minor (hver); /* Set the version we advertise as maximum (RSA uses it). */ set_adv_version (session, MHD_gtls_version_get_major (hver), MHD_gtls_version_get_minor (hver)); session->security_parameters.version = hver; session->security_parameters.timestamp = time (NULL); /* generate session client random */ memset (session->security_parameters.client_random, 0, TLS_RANDOM_SIZE); gnutls_write_uint32 (time (NULL), rnd); if (GC_OK != MHD_gc_nonce ((char *) &rnd[4], TLS_RANDOM_SIZE - 4)) abort (); memcpy (session->security_parameters.client_random, rnd, TLS_RANDOM_SIZE); memcpy (&data[pos], rnd, TLS_RANDOM_SIZE); pos += TLS_RANDOM_SIZE; /* Copy the Session ID */ data[pos++] = session_id_len; /* * len = ciphersuite data + 2 bytes ciphersuite length \ * 1 byte compression length + 1 byte compression data + \ * 2 bytes extension length, extensions data */ ciphersuite_len = MHD__gnutls_copy_ciphersuites (session, extdata, sizeof (extdata)); exten_data_len = ext_count * (2 + 2 + ext_length); datalen += ciphersuite_len + 2 + 2 + exten_data_len; data = MHD_gtls_realloc_fast (data, datalen); memcpy (&data[pos], extdata, sizeof (ciphersuite_len)); pos += ciphersuite_len; /* set compression */ memcpy (&data[pos], comp, sizeof (comp)); pos += 2; /* set extensions length = 2 type bytes + 2 length bytes + extension length */ gnutls_write_uint16 (exten_data_len, &data[pos]); pos += 2; for (i = 0; i < ext_count; ++i) { /* write extension type */ gnutls_write_uint16 (exten_t, &data[pos]); pos += 2; gnutls_write_uint16 (ext_length, &data[pos]); pos += 2; /* we might want to generate random data here */ memset (&data[pos], 0, ext_length); pos += ext_length; } if (connect (sd, &sa, sizeof (struct sockaddr_in)) < 0) { fprintf (stderr, "%s\n", MHD_E_FAILED_TO_CONNECT); ret = -1; goto cleanup; } gnutls_transport_set_ptr (session, (MHD_gnutls_transport_ptr_t) (long) sd); if (gen_test_file_url (url, DEAMON_TEST_PORT)) { ret = -1; goto cleanup; } /* this should crash the server */ ret = gnutls_send_handshake (session, data, datalen, GNUTLS_HANDSHAKE_CLIENT_HELLO); /* advance to STATE2 */ session->internals.handshake_state = STATE2; ret = gnutls_handshake (session); ret = gnutls_bye (session, GNUTLS_SHUT_WR); gnutls_free (data); /* make sure daemon is still functioning */ if (CURLE_OK != send_curl_req (url, &cbc, "AES128-SHA", MHD_GNUTLS_PROTOCOL_TLS1_2)) { ret = -1; goto cleanup; } cleanup: if (-1 != sd) MHD_socket_close_ (sd); gnutls_free (cbc.buf); return ret; }
/* Read a v2 client hello. Some browsers still use that beast! * However they set their version to 3.0 or 3.1. */ int _gnutls_read_client_hello_v2(gnutls_session_t session, uint8_t * data, unsigned int datalen) { uint16_t session_id_len = 0; int pos = 0; int ret = 0, sret = 0; uint16_t sizeOfSuites; gnutls_protocol_t adv_version; uint8_t rnd[GNUTLS_RANDOM_SIZE]; int len = datalen; uint16_t challenge; uint8_t session_id[GNUTLS_MAX_SESSION_ID_SIZE]; DECR_LEN(len, 2); _gnutls_handshake_log ("HSK[%p]: SSL 2.0 Hello: Client's version: %d.%d\n", session, data[pos], data[pos + 1]); set_adv_version(session, data[pos], data[pos + 1]); adv_version = _gnutls_version_get(data[pos], data[pos + 1]); ret = _gnutls_negotiate_version(session, adv_version); if (ret < 0) { gnutls_assert(); return ret; } pos += 2; /* Read uint16_t cipher_spec_length */ DECR_LEN(len, 2); sizeOfSuites = _gnutls_read_uint16(&data[pos]); pos += 2; /* read session id length */ DECR_LEN(len, 2); session_id_len = _gnutls_read_uint16(&data[pos]); pos += 2; if (session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* read challenge length */ DECR_LEN(len, 2); challenge = _gnutls_read_uint16(&data[pos]); pos += 2; if (challenge < 16 || challenge > GNUTLS_RANDOM_SIZE) { gnutls_assert(); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } /* call the user hello callback */ ret = _gnutls_user_hello_func(session, adv_version); if (ret < 0) { if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { sret = GNUTLS_E_INT_RET_0; } else { gnutls_assert(); return ret; } } /* find an appropriate cipher suite */ DECR_LEN(len, sizeOfSuites); ret = _gnutls_handshake_select_v2_suite(session, &data[pos], sizeOfSuites); pos += sizeOfSuites; if (ret < 0) { gnutls_assert(); return ret; } /* check if the credentials (username, public key etc.) are ok */ if (_gnutls_get_kx_cred (session, _gnutls_cipher_suite_get_kx_algo(session->security_parameters. cipher_suite)) == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } /* set the mod_auth_st to the appropriate struct * according to the KX algorithm. This is needed since all the * handshake functions are read from there; */ session->internals.auth_struct = _gnutls_kx_auth_struct(_gnutls_cipher_suite_get_kx_algo (session->security_parameters. cipher_suite)); if (session->internals.auth_struct == NULL) { _gnutls_handshake_log ("HSK[%p]: SSL 2.0 Hello: Cannot find the appropriate handler for the KX algorithm\n", session); gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } /* read random new values -skip session id for now */ DECR_LEN(len, session_id_len); /* skip session id for now */ memcpy(session_id, &data[pos], session_id_len); pos += session_id_len; DECR_LEN(len, challenge); memset(rnd, 0, GNUTLS_RANDOM_SIZE); memcpy(&rnd[GNUTLS_RANDOM_SIZE - challenge], &data[pos], challenge); ret = _gnutls_set_client_random(session, rnd); if (ret < 0) return gnutls_assert_val(ret); /* generate server random value */ ret = _gnutls_set_server_random(session, NULL); if (ret < 0) return gnutls_assert_val(ret); session->security_parameters.timestamp = gnutls_time(NULL); /* RESUME SESSION */ DECR_LEN(len, session_id_len); ret = _gnutls_server_restore_session(session, session_id, session_id_len); if (ret == 0) { /* resumed! */ /* get the new random values */ memcpy(session->internals.resumed_security_parameters. server_random, session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); memcpy(session->internals.resumed_security_parameters. client_random, session->security_parameters.client_random, GNUTLS_RANDOM_SIZE); session->internals.resumed = RESUME_TRUE; return 0; } else { _gnutls_generate_session_id(session->security_parameters. session_id, &session->security_parameters. session_id_size); session->internals.resumed = RESUME_FALSE; } _gnutls_epoch_set_compression(session, EPOCH_NEXT, GNUTLS_COMP_NULL); session->security_parameters.compression_method = GNUTLS_COMP_NULL; return sret; }
/* Read a v2 client hello. Some browsers still use that beast! * However they set their version to 3.0 or 3.1. */ int _gnutls_read_client_hello_v2 (gnutls_session_t session, opaque * data, int datalen) { uint16_t session_id_len = 0; int pos = 0; int ret = 0; uint16_t sizeOfSuites; gnutls_protocol_t version; opaque rnd[TLS_RANDOM_SIZE]; int len = datalen; int err; uint16_t challenge; opaque session_id[TLS_MAX_SESSION_ID_SIZE]; gnutls_protocol_t ver; /* we only want to get here once - only in client hello */ session->internals.v2_hello = 0; DECR_LEN (len, 2); _gnutls_handshake_log ("HSK[%x]: SSL 2.0 Hello: Client's version: %d.%d\n", session, data[pos], data[pos + 1]); set_adv_version (session, data[pos], data[pos + 1]); version = _gnutls_version_get (data[pos], data[pos + 1]); /* if we do not support that version */ if (_gnutls_version_is_supported (session, version) == 0) { ver = _gnutls_version_lowest (session); } else { ver = version; } _gnutls_set_current_version (session, ver); pos += 2; /* Read uint16_t cipher_spec_length */ DECR_LEN (len, 2); sizeOfSuites = _gnutls_read_uint16 (&data[pos]); pos += 2; /* read session id length */ DECR_LEN (len, 2); session_id_len = _gnutls_read_uint16 (&data[pos]); pos += 2; if (session_id_len > TLS_MAX_SESSION_ID_SIZE) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* read challenge length */ DECR_LEN (len, 2); challenge = _gnutls_read_uint16 (&data[pos]); pos += 2; if (challenge < 16 || challenge > TLS_RANDOM_SIZE) { gnutls_assert (); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } /* find an appropriate cipher suite */ DECR_LEN (len, sizeOfSuites); ret = _gnutls_handshake_select_v2_suite (session, &data[pos], sizeOfSuites); pos += sizeOfSuites; if (ret < 0) { gnutls_assert (); return ret; } /* check if the credentials (username, public key etc.) are ok */ if (_gnutls_get_kx_cred (session, _gnutls_cipher_suite_get_kx_algo (&session->security_parameters. current_cipher_suite), &err) == NULL && err != 0) { gnutls_assert (); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } /* set the mod_auth_st to the appropriate struct * according to the KX algorithm. This is needed since all the * handshake functions are read from there; */ session->internals.auth_struct = _gnutls_kx_auth_struct (_gnutls_cipher_suite_get_kx_algo (&session->security_parameters. current_cipher_suite)); if (session->internals.auth_struct == NULL) { _gnutls_handshake_log ("HSK[%x]: SSL 2.0 Hello: Cannot find the appropriate handler for the KX algorithm\n", session); gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } /* read random new values -skip session id for now */ DECR_LEN (len, session_id_len); /* skip session id for now */ memcpy (session_id, &data[pos], session_id_len); pos += session_id_len; DECR_LEN (len, challenge); memset (rnd, 0, TLS_RANDOM_SIZE); memcpy (&rnd[TLS_RANDOM_SIZE - challenge], &data[pos], challenge); _gnutls_set_client_random (session, rnd); /* generate server random value */ _gnutls_tls_create_random (rnd); _gnutls_set_server_random (session, rnd); session->security_parameters.timestamp = time (NULL); /* RESUME SESSION */ DECR_LEN (len, session_id_len); ret = _gnutls_server_restore_session (session, session_id, session_id_len); if (ret == 0) { /* resumed! */ /* get the new random values */ memcpy (session->internals.resumed_security_parameters. server_random, session->security_parameters.server_random, TLS_RANDOM_SIZE); memcpy (session->internals.resumed_security_parameters. client_random, session->security_parameters.client_random, TLS_RANDOM_SIZE); session->internals.resumed = RESUME_TRUE; return 0; } else { _gnutls_generate_session_id (session->security_parameters. session_id, &session->security_parameters. session_id_size); session->internals.resumed = RESUME_FALSE; } session->internals.compression_method = GNUTLS_COMP_NULL; return 0; }