diff options
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | doc/TODO | 1 | ||||
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/gnutls.c | 61 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 1253 | ||||
-rw-r--r-- | lib/gnutls_int.h | 7 | ||||
-rw-r--r-- | lib/gnutls_v2_compat.c | 260 | ||||
-rw-r--r-- | lib/gnutls_v2_compat.h | 1 | ||||
-rw-r--r-- | src/cli.c | 3 |
9 files changed, 1050 insertions, 547 deletions
@@ -1,7 +1,8 @@ -Version 0.0.8 (28/01/2001) +Version 0.1.0 (?) +- Added SSL 2.0 client hello support - GNUTLS is a gnu library -- Added some support for TLS extensions. -- Added some support for SRP +- Added support for TLS extensions. +- Added support for SRP Version 0.0.7 (11/01/2001) - Added server side session resuming (using gdbm) @@ -2,4 +2,3 @@ * x509 Certificate API (we need an ASN.1 parser or something) * Add Kerberos support * OpenPGP certificate support (through libgcrypt) -* Add support for v2.0 init packet (why do that anyway ???) diff --git a/lib/Makefile.am b/lib/Makefile.am index 269b3f1d6a..5e6f6ed319 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = debug.h gnutls_compress.h defines.h gnutls_plaintext.h \ gnutls_compress_int.h gnutls_session.h gnutls_priority.h gnutls_auth.h \ auth_anon.h auth_dhe_dss.h gnutls_extensions.h ext_srp.h \ gnutls_auth_int.h crypt_bcrypt.h gnutls_random.h crypt_srpsha1.h \ - cert_b64.h gnutls_srp.h auth_srp.h auth_srp_passwd.h + cert_b64.h gnutls_srp.h auth_srp.h auth_srp_passwd.h gnutls_v2_compat.h lib_LTLIBRARIES = libgnutls.la libgnutls_la_SOURCES = gnutls.c gnutls_compress.c debug.c gnutls_plaintext.c \ gnutls_cipher.c gnutls_buffers.c gnutls_handshake.c gnutls_num.c \ @@ -15,5 +15,5 @@ libgnutls_la_SOURCES = gnutls.c gnutls_compress.c debug.c gnutls_plaintext.c \ gnutls_compress_int.c gnutls_session.c gnutls_db.c cert_b64.c \ auth_anon.c auth_dhe_dss.c gnutls_extensions.c ext_srp.c gnutls_auth.c \ crypt_bcrypt.c crypt.c gnutls_random.c crypt_srpsha1.c gnutls_srp.c \ - auth_srp.c auth_srp_passwd.c + auth_srp.c auth_srp_passwd.c gnutls_v2_compat.c libgnutls_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/lib/gnutls.c b/lib/gnutls.c index 641037b5f7..4e873f8c8b 100644 --- a/lib/gnutls.c +++ b/lib/gnutls.c @@ -92,6 +92,8 @@ int gnutls_init(GNUTLS_STATE * state, ConnectionEnd con_end) (*state)->cipher_specs.server_write_key = NULL; (*state)->cipher_specs.client_write_key = NULL; + (*state)->gnutls_internals.v2_hello = 0; + (*state)->gnutls_internals.buffer = NULL; /* SSL3 stuff */ (*state)->gnutls_internals.hash_buffer = NULL; @@ -579,6 +581,7 @@ char peekdata; * flags is the sockets flags to use. Currently only MSG_DONTWAIT is * supported. */ +#define SSL2_HSIZE ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data, size_t sizeofdata, int flags) { uint8 *tmpdata; @@ -589,7 +592,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data uint16 length; uint8 *ciphertext; int ret = 0; - + int header_size = HEADER_SIZE; /* If we have enough data in the cache do not bother receiving * a new packet. (in order to flush the cache) */ @@ -621,37 +624,57 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } - memcpy( &recv_type, &headers[0], 1); - version = _gnutls_version_get( headers[1], headers[2]); + /* Read the first two bytes to determine if this is a + * version 2 message + */ + if ( headers[0] > 127 && type==GNUTLS_HANDSHAKE) { + + /* if msb set and expecting handshake message + * it should be SSL 2 hello + */ + version = GNUTLS_SSL3; /* assume ssl 3.0 */ + length = (((headers[0] & 0x7f) << 8)) | headers[1]; + header_size = 2; + recv_type = GNUTLS_HANDSHAKE; /* only v2 client hello we accept */ + state->gnutls_internals.v2_hello = length; +#ifdef DEBUG + fprintf(stderr, "Record: V2 packet received. Length: %d\n", length); +#endif - memcpy( &length, &headers[3], 2); + } else { + /* version 3.x + */ + memcpy( &recv_type, &headers[0], 1); + version = _gnutls_version_get( headers[1], headers[2]); + + memcpy( &length, &headers[3], 2); #ifndef WORDS_BIGENDIAN - length = byteswap16(length); + length = byteswap16(length); #endif + } + - if (_gnutls_version_is_supported(state, version) == 0) { + if (type != GNUTLS_HANDSHAKE && gnutls_get_current_version(state) != version) { #ifdef DEBUG - fprintf(stderr, "INVALID VERSION PACKET: %d.%d\n", headers[1], headers[2]); + fprintf(stderr, "Record: INVALID VERSION PACKET: (%d) %d.%d\n", headers[0], headers[1], headers[2]); #endif _gnutls_send_alert(cd, state, GNUTLS_FATAL, GNUTLS_PROTOCOL_VERSION); state->gnutls_internals.resumable = RESUME_FALSE; gnutls_assert(); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; - } else { - gnutls_set_current_version(state, version); } #ifdef HARD_DEBUG - fprintf(stderr, "Expected Packet[%d] %s(%d) with length: %d\n", + fprintf(stderr, "Record: Expected Packet[%d] %s(%d) with length: %d\n", (int) state->connection_state.read_sequence_number, _gnutls_packet2str(type), type, sizeofdata); - fprintf(stderr, "Received Packet[%d] %s(%d) with length: %d\n", + fprintf(stderr, "Record: Received Packet[%d] %s(%d) with length: %d\n", (int) state->connection_state.read_sequence_number, _gnutls_packet2str(recv_type), recv_type, length); #endif if (length > MAX_RECV_SIZE) { #ifdef DEBUG - fprintf(stderr, "FATAL ERROR: Received packet with length: %d\n", length); + fprintf(stderr, "Record: FATAL ERROR: Received packet with length: %d\n", length); #endif _gnutls_send_alert(cd, state, GNUTLS_FATAL, GNUTLS_RECORD_OVERFLOW); state->gnutls_internals.valid_connection = VALID_FALSE; @@ -660,12 +683,12 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } - ciphertext = gnutls_malloc(length+HEADER_SIZE); + ciphertext = gnutls_malloc(length+header_size); /* check if we have that data into buffer. This seems to be * expensive - but this is the only way to handle Non Blocking IO. */ - if ( _gnutls_Read(cd, ciphertext, HEADER_SIZE+length, MSG_PEEK|flags) != length+HEADER_SIZE) { + if ( _gnutls_Read(cd, ciphertext, header_size+length, MSG_PEEK|flags) != length+header_size) { gnutls_free(ciphertext); if (errno==EAGAIN) return GNUTLS_E_AGAIN; @@ -677,7 +700,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data /* ok now we are sure that we can read all the data - so * move on ! */ - if (_gnutls_Read(cd, headers, HEADER_SIZE, 0)!=HEADER_SIZE) { /* read and clear the headers - again! */ + if (_gnutls_Read(cd, headers, header_size, 0)!=header_size) { /* read and clear the headers - again! */ state->gnutls_internals.valid_connection = VALID_FALSE; state->gnutls_internals.resumable = RESUME_FALSE; gnutls_assert(); @@ -699,7 +722,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data */ if (ret != length) { #ifdef DEBUG - fprintf(stderr, "Received packet with length: %d\nExpected %d\n", ret, length); + fprintf(stderr, "Record: Received packet with length: %d\nExpected %d\n", ret, length); #endif gnutls_free(ciphertext); state->gnutls_internals.valid_connection = VALID_FALSE; @@ -710,7 +733,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data if (type == GNUTLS_CHANGE_CIPHER_SPEC && recv_type == GNUTLS_CHANGE_CIPHER_SPEC) { #ifdef HARD_DEBUG - fprintf(stderr, "ChangeCipherSpec Packet was received\n"); + fprintf(stderr, "Record: ChangeCipherSpec Packet was received\n"); #endif if (length!=1) { gnutls_assert(); @@ -748,7 +771,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data switch (recv_type) { case GNUTLS_ALERT: #ifdef DEBUG - fprintf(stderr, "Alert[%d|%d] - %s - was received\n", tmpdata[0], tmpdata[1], _gnutls_alert2str((int)tmpdata[1])); + fprintf(stderr, "Record: Alert[%d|%d] - %s - was received\n", tmpdata[0], tmpdata[1], _gnutls_alert2str((int)tmpdata[1])); #endif state->gnutls_internals.last_alert = tmpdata[1]; @@ -782,7 +805,7 @@ ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char *data break; default: #ifdef DEBUG - fprintf(stderr, "Received Unknown packet %d expecting %d\n", recv_type, type); + fprintf(stderr, "Record: Received Unknown packet %d expecting %d\n", recv_type, type); #endif gnutls_assert(); return GNUTLS_E_UNKNOWN_ERROR; diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index af3867a8f2..6661172398 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -36,6 +36,7 @@ #include "gnutls_extensions.h" #include "gnutls_random.h" #include "gnutls_auth_int.h" +#include "gnutls_v2_compat.h" #ifdef DEBUG #define ERR(x, y) fprintf(stderr, "GNUTLS Error: %s (%d)\n", x,y) @@ -46,30 +47,39 @@ #define TRUE 1 #define FALSE 0 +static int SelectSuite(GNUTLS_STATE state, opaque ret[2], char *data, int datalen); +static int SelectCompMethod(GNUTLS_STATE state, CompressionMethod * ret, opaque * data, int datalen); + /* Calculate The SSL3 Finished message */ #define SSL3_CLIENT_MSG "CLNT" #define SSL3_SERVER_MSG "SRVR" -void* _gnutls_ssl3_finished( GNUTLS_STATE state, int type, int skip) { +void *_gnutls_ssl3_finished(GNUTLS_STATE state, int type, int skip) +{ int siz; GNUTLS_MAC_HANDLE td; GNUTLS_MAC_HANDLE td2; - char* data; - char* concat=gnutls_malloc(36); + char *data; + char *concat = gnutls_malloc(36); char *mesg; - - td = gnutls_mac_init_ssl3_handshake( GNUTLS_MAC_MD5, state->security_parameters.master_secret, 48); - td2 = gnutls_mac_init_ssl3_handshake( GNUTLS_MAC_SHA, state->security_parameters.master_secret, 48); - - siz = gnutls_getHashDataBufferSize( state) - skip; - data = gnutls_malloc( siz); - gnutls_readHashDataFromBuffer( state, data, siz); + td = gnutls_mac_init_ssl3_handshake(GNUTLS_MAC_MD5, + state->security_parameters. + master_secret, 48); + td2 = + gnutls_mac_init_ssl3_handshake(GNUTLS_MAC_SHA, + state->security_parameters. + master_secret, 48); + + siz = gnutls_getHashDataBufferSize(state) - skip; + data = gnutls_malloc(siz); + + gnutls_readHashDataFromBuffer(state, data, siz); gnutls_mac_ssl3(td, data, siz); gnutls_mac_ssl3(td2, data, siz); gnutls_free(data); - - if (type==GNUTLS_SERVER) { + + if (type == GNUTLS_SERVER) { mesg = SSL3_SERVER_MSG; } else { mesg = SSL3_CLIENT_MSG; @@ -79,12 +89,12 @@ void* _gnutls_ssl3_finished( GNUTLS_STATE state, int type, int skip) { gnutls_mac_ssl3(td2, mesg, siz); data = gnutls_mac_deinit_ssl3_handshake(td); - memcpy( concat, data, 16); + memcpy(concat, data, 16); gnutls_free(data); - + data = gnutls_mac_deinit_ssl3_handshake(td2); - memcpy( &concat[16], data, 20); + memcpy(&concat[16], data, 20); gnutls_free(data); return concat; } @@ -92,49 +102,212 @@ void* _gnutls_ssl3_finished( GNUTLS_STATE state, int type, int skip) { /* Hash the handshake messages as required by TLS 1.0 */ #define SERVER_MSG "server finished" #define CLIENT_MSG "client finished" -void* _gnutls_finished( GNUTLS_STATE state, int type, int skip) { +void *_gnutls_finished(GNUTLS_STATE state, int type, int skip) +{ int siz; GNUTLS_MAC_HANDLE td; GNUTLS_MAC_HANDLE td2; - char* data; + char *data; char concat[36]; char *mesg; - - td = gnutls_hash_init( GNUTLS_MAC_MD5); - td2 = gnutls_hash_init( GNUTLS_MAC_SHA); - - siz = gnutls_getHashDataBufferSize( state) - skip; - data = gnutls_malloc( siz); - gnutls_readHashDataFromBuffer( state, data, siz); + td = gnutls_hash_init(GNUTLS_MAC_MD5); + td2 = gnutls_hash_init(GNUTLS_MAC_SHA); + + siz = gnutls_getHashDataBufferSize(state) - skip; + data = gnutls_malloc(siz); + + gnutls_readHashDataFromBuffer(state, data, siz); gnutls_hash(td, data, siz); gnutls_hash(td2, data, siz); - + gnutls_free(data); - + data = gnutls_hash_deinit(td); - memcpy( concat, data, 16); + memcpy(concat, data, 16); gnutls_free(data); - + data = gnutls_hash_deinit(td2); - memcpy( &concat[16], data, 20); + memcpy(&concat[16], data, 20); gnutls_free(data); - if (type==GNUTLS_SERVER) { - mesg=SERVER_MSG; + if (type == GNUTLS_SERVER) { + mesg = SERVER_MSG; } else { - mesg=CLIENT_MSG; + mesg = CLIENT_MSG; } data = - gnutls_PRF( state->security_parameters.master_secret, - 48, mesg, strlen(mesg), concat, - 36, 12); + gnutls_PRF(state->security_parameters.master_secret, + 48, mesg, strlen(mesg), concat, 36, 12); return data; } +/* Read a client hello + * client hello must be a known version client hello + * or version 2.0 client hello (only for compatibility) + * version 2.0 is not supported. + */ + +#define DECR_LEN(len, x) len-=x; if (len<0) {gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;} + +int _gnutls_read_client_hello(GNUTLS_STATE state, opaque * data, + int datalen) +{ + uint8 session_id_len = 0, z; + int pos = 0; + int ret = 0; + uint16 sizeOfSuites; + GNUTLS_Version version; + time_t cur_time; + char *rand; + int len = datalen; + int err; + + if (state->gnutls_internals.v2_hello!=0) { /* version 2.0 */ + return _gnutls_read_client_hello_v2(state, data, datalen); + } + + DECR_LEN(len, 2); + +#ifdef DEBUG + fprintf(stderr, "Client's version: %d.%d\n", data[pos], + data[pos + 1]); +#endif + + version = _gnutls_version_get(data[pos], data[pos + 1]); + + /* if we do not support that version */ + if (_gnutls_version_is_supported(state, version) == 0) { + gnutls_assert(); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } else { + gnutls_set_current_version(state, version); + } + + pos += 2; + + DECR_LEN(len, 32); + memmove(state->security_parameters.client_random, &data[pos], 32); + pos += 32; + + /* generate server random value */ +#ifdef WORDS_BIGENDIAN + cur_time = time(NULL); +#else + cur_time = byteswap32(time(NULL)); +#endif + memmove(state->security_parameters.server_random, &cur_time, 4); + rand = _gnutls_get_random(28, GNUTLS_STRONG_RANDOM); + memmove(&state->security_parameters.server_random[4], rand, 28); + _gnutls_free_rand(rand); + state->security_parameters.timestamp = time(NULL); + + DECR_LEN(len, 1); + memmove(&session_id_len, &data[pos++], 1); + + /* RESUME SESSION */ + if (session_id_len > 32) + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + + DECR_LEN(len, session_id_len); + ret = + _gnutls_server_restore_session(state, &data[pos], + session_id_len); + pos += session_id_len; + + if (ret == 0) { /* resumed! */ + /* get the new random values */ + memcpy(state->gnutls_internals.resumed_security_parameters. + server_random, + state->security_parameters.server_random, 32); + memcpy(state->gnutls_internals.resumed_security_parameters. + client_random, + state->security_parameters.client_random, 32); + + state->gnutls_internals.resumed = RESUME_TRUE; + return 0; + } else { + _gnutls_generate_session_id(state->security_parameters. + session_id, + &state->security_parameters. + session_id_size); + state->gnutls_internals.resumed = RESUME_FALSE; + } + + /* Select a ciphersuite */ + DECR_LEN(len, 2); + memmove(&sizeOfSuites, &data[pos], 2); + pos += 2; + +#ifndef WORDS_BIGENDIAN + sizeOfSuites = byteswap16(sizeOfSuites); +#endif + + DECR_LEN(len, sizeOfSuites); + ret = SelectSuite(state, state->gnutls_internals. + current_cipher_suite.CipherSuite, &data[pos], + sizeOfSuites); + + pos += sizeOfSuites; + if (ret < 0) + return ret; + + + /* check if the credentials (username, public key etc. are ok) + */ + if (_gnutls_get_kx_cred + (state->gnutls_key, + _gnutls_cipher_suite_get_kx_algo(state->gnutls_internals. + current_cipher_suite), + &err) == NULL && err != 0) { + gnutls_assert(); + return GNUTLS_E_INSUFICIENT_CRED; + } + + /* set the MOD_AUTH_STRUCT to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + state->gnutls_internals.auth_struct = + _gnutls_kx_auth_struct(_gnutls_cipher_suite_get_kx_algo + (state->gnutls_internals. + current_cipher_suite)); + if (state->gnutls_internals.auth_struct == NULL) { +#ifdef DEBUG + fprintf(stderr, + "Cannot find the appropriate handler for the KX algorithm\n"); +#endif + gnutls_assert(); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + + DECR_LEN(len, 1); + memmove(&z, &data[pos++], 1); /* z is the number of compression methods */ + + DECR_LEN(len, z); + ret = SelectCompMethod(state, &state-> + gnutls_internals.compression_method, + &data[pos], z); + pos += z; + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_parse_extensions(state, &data[pos], len); /* len is the rest of the parsed length */ + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return ret; +} + + /* This is to be called after sending CHANGE CIPHER SPEC packet * and initializing encryption. This is the first encrypted message * we send. @@ -146,14 +319,21 @@ int _gnutls_send_finished(int cd, GNUTLS_STATE state) int data_size; if (_gnutls_version_ssl3(state->connection_state.version) == 0) { - data = _gnutls_ssl3_finished( state, state->security_parameters.entity, 0); + data = + _gnutls_ssl3_finished(state, + state->security_parameters. + entity, 0); data_size = 36; - } else { /* TLS 1.0 */ - data = _gnutls_finished( state, state->security_parameters.entity, 0); - data_size=12; + } else { /* TLS 1.0 */ + data = + _gnutls_finished(state, + state->security_parameters.entity, 0); + data_size = 12; } - ret = _gnutls_send_handshake(cd, state, data, data_size, GNUTLS_FINISHED); + ret = + _gnutls_send_handshake(cd, state, data, data_size, + GNUTLS_FINISHED); gnutls_free(data); return ret; @@ -172,18 +352,20 @@ int _gnutls_recv_finished(int cd, GNUTLS_STATE state) ret = 0; - ret = _gnutls_recv_handshake(cd, state, &vrfy, &vrfysize, GNUTLS_FINISHED); + ret = + _gnutls_recv_handshake(cd, state, &vrfy, &vrfysize, + GNUTLS_FINISHED); if (ret < 0) { ERR("recv finished int", ret); return ret; } if (_gnutls_version_ssl3(state->connection_state.version) == 0) { - data_size=36; + data_size = 36; } else { - data_size=12; + data_size = 12; } - + if (vrfysize != data_size) { gnutls_assert(); return GNUTLS_E_ERROR_IN_FINISHED_PACKET; @@ -191,9 +373,18 @@ int _gnutls_recv_finished(int cd, GNUTLS_STATE state) if (_gnutls_version_ssl3(state->connection_state.version) == 0) { /* skip the bytes from the last message */ - data = _gnutls_ssl3_finished( state, (state->security_parameters.entity+1)%2, vrfysize + HANDSHAKE_HEADERS_SIZE); - } else { /* TLS 1.0 */ - data = _gnutls_finished( state, (state->security_parameters.entity+1)%2, vrfysize + HANDSHAKE_HEADERS_SIZE); + data = + _gnutls_ssl3_finished(state, + (state->security_parameters. + entity + 1) % 2, + vrfysize + + HANDSHAKE_HEADERS_SIZE); + } else { /* TLS 1.0 */ + data = + _gnutls_finished(state, + (state->security_parameters.entity + + 1) % 2, + vrfysize + HANDSHAKE_HEADERS_SIZE); } if (memcmp(vrfy, data, data_size) != 0) { @@ -203,13 +394,15 @@ int _gnutls_recv_finished(int cd, GNUTLS_STATE state) gnutls_free(data); gnutls_free(vrfy); - + return ret; } + /* This selects the best supported ciphersuite from the ones provided */ -static int SelectSuite(GNUTLS_STATE state, opaque ret[2], char *data, int datalen) +static int SelectSuite(GNUTLS_STATE state, opaque ret[2], char *data, + int datalen) { int x, i, j; GNUTLS_CipherSuite *ciphers; @@ -217,22 +410,31 @@ static int SelectSuite(GNUTLS_STATE state, opaque ret[2], char *data, int datale x = _gnutls_supported_ciphersuites(state, &ciphers); #ifdef HARD_DEBUG fprintf(stderr, "Requested cipher suites: \n"); - for (j=0;j<datalen;j+=2) fprintf(stderr, "\t%s\n", _gnutls_cipher_suite_get_name( *((GNUTLS_CipherSuite*)&data[j]) )); + for (j = 0; j < datalen; j += 2) + fprintf(stderr, "\t%s\n", + _gnutls_cipher_suite_get_name(* + ((GNUTLS_CipherSuite + *) & data[j]))); fprintf(stderr, "Supported cipher suites: \n"); - for (j=0;j<x;j++) fprintf(stderr, "\t%s\n", _gnutls_cipher_suite_get_name(ciphers[j])); + for (j = 0; j < x; j++) + fprintf(stderr, "\t%s\n", + _gnutls_cipher_suite_get_name(ciphers[j])); #endif memset(ret, '\0', sizeof(GNUTLS_CipherSuite)); for (j = 0; j < datalen; j += 2) { for (i = 0; i < x; i++) { - if (memcmp(&ciphers[i].CipherSuite, &data[j], 2) == 0) { + if (memcmp(&ciphers[i].CipherSuite, &data[j], 2) == + 0) { #ifdef HARD_DEBUG fprintf(stderr, "Selected cipher suite: "); - fprintf(stderr, "%s\n", _gnutls_cipher_suite_get_name( *((GNUTLS_CipherSuite*)&data[j]) )); + fprintf(stderr, "%s\n", + _gnutls_cipher_suite_get_name(* + ((GNUTLS_CipherSuite *) & data[j]))); #endif memmove(ret, &ciphers[i].CipherSuite, 2); gnutls_free(ciphers); - + return 0; } } @@ -245,8 +447,10 @@ static int SelectSuite(GNUTLS_STATE state, opaque ret[2], char *data, int datale } + /* This selects the best supported compression method from the ones provided */ -static int SelectCompMethod(GNUTLS_STATE state, CompressionMethod * ret, opaque *data, int datalen) +static int SelectCompMethod(GNUTLS_STATE state, CompressionMethod * ret, + opaque * data, int datalen) { int x, i, j; uint8 *ciphers; @@ -256,7 +460,7 @@ static int SelectCompMethod(GNUTLS_STATE state, CompressionMethod * ret, opaque for (j = 0; j < datalen; j++) { for (i = 0; i < x; i++) { - if ( ciphers[i] == data[j]) { + if (ciphers[i] == data[j]) { *ret = ciphers[i]; gnutls_free(ciphers); return 0; @@ -302,16 +506,18 @@ int _gnutls_send_handshake(int cd, GNUTLS_STATE state, void *i_data, memmove(&data[pos], i_data, i_datasize - 4); #ifdef HANDSHAKE_DEBUG - fprintf(stderr, "Handshake: %s was send [%ld bytes]\n", _gnutls_handshake2str(type), i_datasize); + fprintf(stderr, "Handshake: %s was send [%ld bytes]\n", + _gnutls_handshake2str(type), i_datasize); #endif /* Here we keep the handshake messages in order to hash them later! */ - if (type!=GNUTLS_HELLO_REQUEST) - gnutls_insertHashDataBuffer( state, data, i_datasize); + if (type != GNUTLS_HELLO_REQUEST) + gnutls_insertHashDataBuffer(state, data, i_datasize); ret = - _gnutls_Send_int(cd, state, GNUTLS_HANDSHAKE, data, i_datasize); + _gnutls_Send_int(cd, state, GNUTLS_HANDSHAKE, data, + i_datasize); gnutls_free(data); return ret; @@ -323,70 +529,111 @@ int _gnutls_send_handshake(int cd, GNUTLS_STATE state, void *i_data, * Eg. for the SERVER_HELLO message (if it is expected), it will be * send to _gnutls_recv_hello(). */ -int _gnutls_recv_handshake(int cd, GNUTLS_STATE state, uint8 **data, - int* datalen, HandshakeType type) +#define SSL2_HEADERS 1 +int _gnutls_recv_handshake(int cd, GNUTLS_STATE state, uint8 ** data, + int *datalen, HandshakeType type) { int ret; - uint32 length32 = 0, sum=0; - uint8 *dataptr; + uint32 length32 = 0, sum = 0; + uint8 *dataptr=NULL; /* for realloc */ uint24 num; + int handshake_headers = HANDSHAKE_HEADERS_SIZE; + int recv_type; - - if (type==GNUTLS_CERTIFICATE) { - /* If the ciphersuite does not support certificate just quit */ + if (type == GNUTLS_CERTIFICATE) { + /* If the ciphersuite does not support certificate just quit */ if (state->security_parameters.entity == GNUTLS_CLIENT) { - if ( _gnutls_kx_server_certificate( - _gnutls_cipher_suite_get_kx_algo(state->gnutls_internals.current_cipher_suite)) ==0 ) - return 0; - } else { /* server */ - if (_gnutls_kx_client_certificate( _gnutls_cipher_suite_get_kx_algo(state->gnutls_internals.current_cipher_suite)==0)) - return 0; + if (_gnutls_kx_server_certificate( _gnutls_cipher_suite_get_kx_algo( state->gnutls_internals.current_cipher_suite) ) == 0) + return 0; + } else { /* server */ + if (_gnutls_kx_client_certificate( _gnutls_cipher_suite_get_kx_algo( state->gnutls_internals.current_cipher_suite) ) == 0) + return 0; } } dataptr = gnutls_malloc(HANDSHAKE_HEADERS_SIZE); - ret = _gnutls_Recv_int(cd, state, GNUTLS_HANDSHAKE, dataptr, HANDSHAKE_HEADERS_SIZE); + ret = + _gnutls_Recv_int(cd, state, GNUTLS_HANDSHAKE, dataptr, SSL2_HEADERS); if (ret < 0) { gnutls_free(dataptr); return ret; } - if (ret!=HANDSHAKE_HEADERS_SIZE) { + if (ret!=SSL2_HEADERS) { gnutls_assert(); gnutls_free(dataptr); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } - if (dataptr[0]!=type) { - gnutls_assert(); - gnutls_free(dataptr); - return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; - } + if ( state->gnutls_internals.v2_hello == 0 || type != GNUTLS_CLIENT_HELLO) { + + ret = + _gnutls_Recv_int(cd, state, GNUTLS_HANDSHAKE, &dataptr[SSL2_HEADERS], + HANDSHAKE_HEADERS_SIZE-SSL2_HEADERS); + if (ret < 0) { + gnutls_free(dataptr); + return ret; + } + if (ret != HANDSHAKE_HEADERS_SIZE - SSL2_HEADERS) { + gnutls_assert(); + gnutls_free(dataptr); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + recv_type = dataptr[0]; + + if (recv_type != type) { + gnutls_assert(); + gnutls_free(dataptr); + return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + } - num.pint[0] = dataptr[1]; - num.pint[1] = dataptr[2]; - num.pint[2] = dataptr[3]; - length32 = uint24touint32(num); + num.pint[0] = dataptr[1]; + num.pint[1] = dataptr[2]; + num.pint[2] = dataptr[3]; + length32 = uint24touint32(num); #ifndef WORDS_BIGENDIAN - length32 = byteswap32(length32); + length32 = byteswap32(length32); #endif #ifdef HANDSHAKE_DEBUG - fprintf(stderr, "Handshake: %s was received [%ld bytes]\n", _gnutls_handshake2str(dataptr[0]), length32+HANDSHAKE_HEADERS_SIZE); + fprintf(stderr, "Handshake: %s was received [%ld bytes]\n", + _gnutls_handshake2str(dataptr[0]), + length32 + HANDSHAKE_HEADERS_SIZE); #endif - dataptr = gnutls_realloc( dataptr, length32+HANDSHAKE_HEADERS_SIZE); - if (length32>0 && data!=NULL) - *data = gnutls_malloc( length32); - if (datalen!=NULL) *datalen = length32; + } else { /* v2 hello */ + length32 = state->gnutls_internals.v2_hello - 1; /* we've read the first byte */ + + handshake_headers = 0; /* no headers in v2 */ - sum=HANDSHAKE_HEADERS_SIZE; +#ifdef HANDSHAKE_DEBUG + fprintf(stderr, "Handshake: %s(v2) was received [%ld bytes]\n", + _gnutls_handshake2str(dataptr[0]), + length32 + handshake_headers); +#endif + recv_type = dataptr[0]; + if (dataptr[0] != GNUTLS_CLIENT_HELLO) /* it should be one or nothing */ + return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + } + + dataptr = + gnutls_realloc(dataptr, length32 + handshake_headers); + if (length32 > 0 && data != NULL) + *data = gnutls_malloc(length32); + + if (datalen != NULL) + *datalen = length32; + + sum = handshake_headers; do { - ret = _gnutls_Recv_int(cd, state, GNUTLS_HANDSHAKE, &dataptr[sum], length32); + ret = + _gnutls_Recv_int(cd, state, GNUTLS_HANDSHAKE, + &dataptr[sum], length32); sum += ret; - } while( ( (sum-HANDSHAKE_HEADERS_SIZE) < length32) && (ret > 0) ); - + } while (((sum - handshake_headers) < length32) && (ret > 0)); + if (ret < 0) { gnutls_assert(); gnutls_free(dataptr); @@ -394,21 +641,26 @@ int _gnutls_recv_handshake(int cd, GNUTLS_STATE state, uint8 **data, } ret = GNUTLS_E_UNKNOWN_ERROR; - if (length32 > 0 && data!=NULL) - memmove( *data, &dataptr[HANDSHAKE_HEADERS_SIZE], length32); + if (length32 > 0 && data != NULL) + memmove(*data, &dataptr[handshake_headers], length32); /* here we buffer the handshake messages - needed at Finished message */ - gnutls_insertHashDataBuffer( state, dataptr, length32+HANDSHAKE_HEADERS_SIZE); + gnutls_insertHashDataBuffer(state, dataptr, length32 + handshake_headers); - switch (dataptr[0]) { + switch (recv_type) { case GNUTLS_CLIENT_HELLO: case GNUTLS_SERVER_HELLO: - ret = _gnutls_recv_hello(cd, state, &dataptr[HANDSHAKE_HEADERS_SIZE], - length32); + ret = + _gnutls_recv_hello(cd, state, + &dataptr[handshake_headers], + length32); break; case GNUTLS_CERTIFICATE: - ret = _gnutls_recv_certificate(cd, state, &dataptr[HANDSHAKE_HEADERS_SIZE], - length32); + ret = + _gnutls_recv_certificate(cd, state, + &dataptr + [HANDSHAKE_HEADERS_SIZE], + length32); break; case GNUTLS_SERVER_HELLO_DONE: ret = 0; @@ -426,9 +678,9 @@ int _gnutls_recv_handshake(int cd, GNUTLS_STATE state, uint8 **data, #ifdef HARD_DEBUG fprintf(stderr, "Requested Client Certificate!\n"); #endif - /* FIXME: just ignore that message for the time being - * we have to parse it and the store the needed information - */ + /* FIXME: just ignore that message for the time being + * we have to parse it and the store the needed information + */ state->gnutls_internals.certificate_requested = 1; ret = length32; break; @@ -450,8 +702,9 @@ int _gnutls_send_client_certificate(int cd, GNUTLS_STATE state) { char data[1]; int ret; - - if (state->gnutls_internals.certificate_requested==0) return 0; + + if (state->gnutls_internals.certificate_requested == 0) + return 0; /* we do not have that functionality yet */ state->gnutls_internals.certificate_verify_needed = 0; @@ -464,11 +717,177 @@ int _gnutls_send_client_certificate(int cd, GNUTLS_STATE state) */ data[0] = 0; ret = _gnutls_send_handshake(cd, state, &data, 1, - GNUTLS_CERTIFICATE); - + GNUTLS_CERTIFICATE); + return ret; } +static int _gnutls_read_server_hello( GNUTLS_STATE state, char *data, int datalen) +{ + uint8 session_id_len = 0, z; + int pos = 0; + GNUTLS_CipherSuite cipher_suite, *cipher_suites; + uint8 compression_method, *compression_methods; + int i, ret = 0; + uint16 x; + GNUTLS_Version version; + int len = datalen; + int err; + + if (datalen < 38) { + gnutls_assert(); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } +#ifdef DEBUG + fprintf(stderr, "Server's version: %d.%d\n", data[pos], + data[pos + 1]); +#endif + DECR_LEN(len, 2); + version = _gnutls_version_get(data[pos], data[pos + 1]); + if (_gnutls_version_is_supported(state, version) == 0) { + gnutls_assert(); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } else { + gnutls_set_current_version(state, version); + } + pos += 2; + + DECR_LEN(len, 32); + memmove(state->security_parameters.server_random, + &data[pos], 32); + pos += 32; + + DECR_LEN(len, 1); + memmove(&session_id_len, &data[pos++], 1); + + if (len < session_id_len) { + gnutls_assert(); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + + DECR_LEN(len, session_id_len); + +#ifdef HARD_DEBUG + fprintf(stderr, "SessionID length: %d\n", session_id_len); + fprintf(stderr, "SessionID: %s\n", + _gnutls_bin2hex(&data[pos], session_id_len)); +#endif + if ((state->gnutls_internals.resumed_security_parameters. + session_id_size > 0) + && memcmp(&data[pos], + state->gnutls_internals. + resumed_security_parameters.session_id, + session_id_len) == 0) { + /* resume session */ + memcpy(state->gnutls_internals. + resumed_security_parameters.server_random, + state->security_parameters.server_random, + 32); + memcpy(state->gnutls_internals. + resumed_security_parameters.client_random, + state->security_parameters.client_random, + 32); + state->gnutls_internals.resumed = RESUME_TRUE; /* we are resuming */ + return 0; + } else { + /* keep the session id */ + state->gnutls_internals.resumed = RESUME_FALSE; /* we are not resuming */ + state->security_parameters.session_id_size = + session_id_len; + memcpy(state->security_parameters.session_id, + &data[pos], session_id_len); + } + pos += session_id_len; + DECR_LEN(len, 2); + memmove(&cipher_suite.CipherSuite, &data[pos], 2); + pos += 2; + + z = 1; + x = _gnutls_supported_ciphersuites(state, &cipher_suites); + for (i = 0; i < x; i++) { + if (memcmp + (&cipher_suites[i], cipher_suite.CipherSuite, + 2) == 0) { + z = 0; + } + } + if (z != 0) { + gnutls_assert(); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + + memmove(state->gnutls_internals. + current_cipher_suite.CipherSuite, + cipher_suite.CipherSuite, 2); + +#ifdef HARD_DEBUG + fprintf(stderr, "Selected cipher suite: "); + fprintf(stderr, "%s\n", + _gnutls_cipher_suite_get_name(state-> + gnutls_internals. + current_cipher_suite)); +#endif + + /* check if the credentials (username, public key etc. are ok - actually check if they exist) + */ + if (_gnutls_get_kx_cred + (state->gnutls_key, + _gnutls_cipher_suite_get_kx_algo(state-> + gnutls_internals. + current_cipher_suite), + &err) == NULL && err != 0) { + gnutls_assert(); + return GNUTLS_E_INSUFICIENT_CRED; + } + + /* set the MOD_AUTH_STRUCT to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + state->gnutls_internals.auth_struct = + _gnutls_kx_auth_struct(_gnutls_cipher_suite_get_kx_algo + (cipher_suite)); + if (state->gnutls_internals.auth_struct == NULL) { +#ifdef DEBUG + fprintf(stderr, + "Cannot find the appropriate handler for the KX algorithm\n"); +#endif + gnutls_assert(); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + + + /* move to compression */ + z = 1; + DECR_LEN(len, 1); + memmove(&compression_method, &data[pos++], 1); + + z = _gnutls_supported_compression_methods + (state, &compression_methods); + for (i = 0; i < z; i++) { + if (memcmp + (&compression_methods[i], &compression_method, + 1) == 0) { + z = 0; + } + } + + if (z != 0) + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + memmove(&state->gnutls_internals.compression_method, + &compression_method, 1); + + gnutls_free(cipher_suites); + gnutls_free(compression_methods); + + ret = _gnutls_parse_extensions(state, &data[pos], len); /* len is the rest of the parsed length */ + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return ret; +} int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, uint8 SessionIDLen) @@ -498,8 +917,12 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, */ data = gnutls_malloc(datalen); - data[pos++] = _gnutls_version_get_major(state->connection_state.version); - data[pos++] = _gnutls_version_get_minor(state->connection_state.version); + data[pos++] = + _gnutls_version_get_major(state->connection_state. + version); + data[pos++] = + _gnutls_version_get_minor(state->connection_state. + version); #ifdef WORDS_BIGENDIAN cur_time = time(NULL); #else @@ -512,10 +935,11 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, memmove(&state->security_parameters.client_random[4], rand, 28); _gnutls_free_rand(rand); - + state->security_parameters.timestamp = time(0); - memmove(&data[pos], state->security_parameters.client_random, 32); + memmove(&data[pos], + state->security_parameters.client_random, 32); pos += 32; memmove(&data[pos++], &session_id_len, 1); @@ -525,8 +949,9 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, } pos += session_id_len; - x = _gnutls_supported_ciphersuites_sorted(state, &cipher_suites); - x *= sizeof(uint16); /* in order to get bytes */ + x = _gnutls_supported_ciphersuites_sorted(state, + &cipher_suites); + x *= sizeof(uint16); /* in order to get bytes */ #ifdef WORDS_BIGENDIAN memmove(&data[pos], &x, sizeof(uint16)); #else @@ -539,7 +964,7 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, datalen += x; data = gnutls_realloc(data, datalen); - for (i = 0; i < x/2; i++) { + for (i = 0; i < x / 2; i++) { memmove(&data[pos], &cipher_suites[i].CipherSuite, 2); pos += 2; @@ -549,22 +974,22 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, z = _gnutls_supported_compression_methods (state, &compression_methods); - memmove(&data[pos++], &z, 1); /* put the number of compression methods */ + memmove(&data[pos++], &z, 1); /* put the number of compression methods */ - datalen += z; + datalen += z; data = gnutls_realloc(data, datalen); - + for (i = 0; i < z; i++) { memmove(&data[pos++], &compression_methods[i], 1); } gnutls_free(compression_methods); - extdatalen = _gnutls_gen_extensions( state, &extdata); + extdatalen = _gnutls_gen_extensions(state, &extdata); if (extdatalen > 0) { - datalen+=extdatalen; + datalen += extdatalen; data = gnutls_realloc(data, datalen); - memcpy( &data[pos], extdata, extdatalen); + memcpy(&data[pos], extdata, extdatalen); gnutls_free(extdata); } @@ -578,10 +1003,15 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, datalen = 2 + session_id_len + 1 + 32; data = gnutls_malloc(datalen); - data[pos++] = _gnutls_version_get_major(state->connection_state.version); - data[pos++] = _gnutls_version_get_minor(state->connection_state.version); + data[pos++] = + _gnutls_version_get_major(state->connection_state. + version); + data[pos++] = + _gnutls_version_get_minor(state->connection_state. + version); - memmove( &data[pos], state->security_parameters.server_random, 32); + memmove(&data[pos], + state->security_parameters.server_random, 32); pos += 32; memmove(&data[pos++], &session_id_len, sizeof(uint8)); @@ -601,12 +1031,12 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, data = gnutls_realloc(data, datalen); memmove(&data[pos++], &state->gnutls_internals.compression_method, 1); - - extdatalen = _gnutls_gen_extensions( state, &extdata); + + extdatalen = _gnutls_gen_extensions(state, &extdata); if (extdatalen > 0) { - datalen+=extdatalen; + datalen += extdatalen; data = gnutls_realloc(data, datalen); - memcpy( &data[pos], extdata, extdatalen); + memcpy(&data[pos], extdata, extdatalen); gnutls_free(extdata); } @@ -620,310 +1050,69 @@ int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque * SessionID, return ret; } -#define DECR_LEN(len, x) len-=x; if (len<0) {gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;} - /* RECEIVE A HELLO MESSAGE. This should be called from gnutls_recv_handshake_int only if a * hello message is expected. It uses the gnutls_internals.current_cipher_suite * and gnutls_internals.compression_method. */ int _gnutls_recv_hello(int cd, GNUTLS_STATE state, char *data, int datalen) { - uint8 session_id_len = 0, z; - int pos = 0; - GNUTLS_CipherSuite cipher_suite, *cipher_suites; - uint8 compression_method, *compression_methods; - int i, ret=0; - uint16 x, sizeOfSuites; - GNUTLS_Version version; - time_t cur_time; - char* rand; - int len = datalen; - int err; - - if (state->security_parameters.entity == GNUTLS_CLIENT) { - if (datalen < 38) { - gnutls_assert(); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - -#ifdef DEBUG - fprintf(stderr, "Server's version: %d.%d\n", data[pos], data[pos+1]); -#endif - version = _gnutls_version_get( data[pos], data[pos+1]); - if ( _gnutls_version_is_supported( state, version) == 0) { - gnutls_assert(); - return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; - } else { - gnutls_set_current_version(state, version); - } - pos+=2; - DECR_LEN(len, 2); - - memmove(state->security_parameters.server_random, - &data[pos], 32); - pos += 32; - DECR_LEN(len, 32); - - memmove(&session_id_len, &data[pos++], 1); - DECR_LEN(len, 1); - - if (len < session_id_len) { - gnutls_assert(); - return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; - } -#ifdef HARD_DEBUG - fprintf(stderr, "SessionID length: %d\n", session_id_len); - fprintf(stderr, "SessionID: %s\n", - _gnutls_bin2hex(&data[pos], session_id_len)); -#endif - if ( (state->gnutls_internals.resumed_security_parameters.session_id_size > 0) - && memcmp(&data[pos], state->gnutls_internals.resumed_security_parameters.session_id, session_id_len)==0) { - /* resume session */ - memcpy( state->gnutls_internals.resumed_security_parameters.server_random, state->security_parameters.server_random, 32); - memcpy( state->gnutls_internals.resumed_security_parameters.client_random, state->security_parameters.client_random, 32); - - state->gnutls_internals.resumed=RESUME_TRUE; /* we are resuming */ - return 0; - } else { - /* keep the session id */ - state->gnutls_internals.resumed=RESUME_FALSE; /* we are not resuming */ - state->security_parameters.session_id_size = session_id_len; - memcpy( state->security_parameters.session_id, &data[pos], session_id_len); - - } - - pos += session_id_len; - DECR_LEN(len, session_id_len); - - memmove(&cipher_suite.CipherSuite, &data[pos], 2); - pos += 2; - DECR_LEN(len, 2); - - z = 1; - x = _gnutls_supported_ciphersuites(state, &cipher_suites); - for (i = 0; i < x; i++) { - if (memcmp (&cipher_suites[i], cipher_suite.CipherSuite, 2) == 0) { - z = 0; - } - } - if (z != 0) { - gnutls_assert(); - return GNUTLS_E_UNKNOWN_CIPHER_TYPE; - } - - memmove(state->gnutls_internals. - current_cipher_suite.CipherSuite, - cipher_suite.CipherSuite, 2); -#ifdef HARD_DEBUG - fprintf(stderr, "Selected cipher suite: "); - fprintf(stderr, "%s\n", _gnutls_cipher_suite_get_name(state->gnutls_internals.current_cipher_suite ) ); -#endif - - /* check if the credentials (username, public key etc. are ok - actually check if they exist) - */ - if ( _gnutls_get_kx_cred( state->gnutls_key, _gnutls_cipher_suite_get_kx_algo( state->gnutls_internals.current_cipher_suite), &err) == NULL && err!=0) { - gnutls_assert(); - return GNUTLS_E_INSUFICIENT_CRED; - } +int ret; - /* set the MOD_AUTH_STRUCT to the appropriate struct - * according to the KX algorithm. This is needed since all the - * handshake functions are read from there; - */ - state->gnutls_internals.auth_struct = _gnutls_kx_auth_struct( _gnutls_cipher_suite_get_kx_algo( cipher_suite)); - if (state->gnutls_internals.auth_struct==NULL) { -#ifdef DEBUG - fprintf(stderr, "Cannot find the appropriate handler for the KX algorithm\n"); -#endif - gnutls_assert(); - return GNUTLS_E_UNKNOWN_CIPHER_TYPE; - } - - - /* move to compression */ - z = 1; - memmove(&compression_method, &data[pos++], 1); - DECR_LEN(len, 1); - - z = - _gnutls_supported_compression_methods - (state, &compression_methods); - for (i = 0; i < z; i++) { - if (memcmp - (&compression_methods[i], &compression_method, - 1) == 0) { - z = 0; - - } - } - if (z != 0) - return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; - memmove(&state->gnutls_internals.compression_method, - &compression_method, 1); - - gnutls_free(cipher_suites); - gnutls_free(compression_methods); - - ret = _gnutls_parse_extensions( state, &data[pos], len); /* len is the rest of the parsed length */ + if (state->security_parameters.entity == GNUTLS_CLIENT) { + ret = _gnutls_read_server_hello(state, data, datalen); if (ret < 0) { + _gnutls_send_alert( cd, state, GNUTLS_FATAL, GNUTLS_HANDSHAKE_FAILURE); /* send handshake failure */ gnutls_assert(); return ret; } - } else { /* Server side reading a client hello */ - if (datalen < 35) { - gnutls_assert(); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - -#ifdef DEBUG - fprintf(stderr, "Client's version: %d.%d\n", data[pos], data[pos+1]); -#endif - version = _gnutls_version_get( data[pos], data[pos+1]); - if ( _gnutls_version_is_supported( state, version) == 0) { - gnutls_assert(); - return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; - } else { - gnutls_set_current_version(state, version); - } - pos+=2; - DECR_LEN(len, 2); - - memmove(state->security_parameters.client_random, - &data[pos], 32); - pos+=32; - DECR_LEN(len, 32); - - /* generate server random value */ -#ifdef WORDS_BIGENDIAN - cur_time = time(NULL); -#else - cur_time = byteswap32(time(NULL)); -#endif - memmove(state->security_parameters.server_random, - &cur_time, 4); - rand = _gnutls_get_random(28, GNUTLS_STRONG_RANDOM); - memmove(&state->security_parameters.server_random[4], rand, - 28); - _gnutls_free_rand(rand); - state->security_parameters.timestamp = time(NULL); - memmove(&session_id_len, &data[pos++], 1); - DECR_LEN(len, 1); - - /* RESUME SESSION */ - if (session_id_len > 32) return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - - ret = _gnutls_server_restore_session( state, &data[pos], session_id_len); - pos += session_id_len; - DECR_LEN(len, session_id_len); - - if (ret==0) { /* resumed! */ - /* get the new random values */ - memcpy( state->gnutls_internals.resumed_security_parameters.server_random, state->security_parameters.server_random, 32); - memcpy( state->gnutls_internals.resumed_security_parameters.client_random, state->security_parameters.client_random, 32); - - state->gnutls_internals.resumed = RESUME_TRUE; - return 0; - } else { - _gnutls_generate_session_id(state->security_parameters.session_id, &state->security_parameters.session_id_size); - state->gnutls_internals.resumed = RESUME_FALSE; - } - - /* Select a ciphersuite */ - memmove(&sizeOfSuites, &data[pos], 2); - pos += 2; - DECR_LEN(len, 2); -#ifndef WORDS_BIGENDIAN - sizeOfSuites = byteswap16(sizeOfSuites); -#endif - ret = SelectSuite(state, state->gnutls_internals. - current_cipher_suite.CipherSuite, &data[pos], - sizeOfSuites); - - if (ret<0) return ret; - - - /* check if the credentials (username, public key etc. are ok) - */ - if ( _gnutls_get_kx_cred( state->gnutls_key, _gnutls_cipher_suite_get_kx_algo( state->gnutls_internals.current_cipher_suite), &err) == NULL && err!=0) { - gnutls_assert(); - return GNUTLS_E_INSUFICIENT_CRED; - } - - /* set the MOD_AUTH_STRUCT to the appropriate struct - * according to the KX algorithm. This is needed since all the - * handshake functions are read from there; - */ - state->gnutls_internals.auth_struct = _gnutls_kx_auth_struct( _gnutls_cipher_suite_get_kx_algo( state->gnutls_internals.current_cipher_suite)); - if (state->gnutls_internals.auth_struct==NULL) { -#ifdef DEBUG - fprintf(stderr, "Cannot find the appropriate handler for the KX algorithm\n"); -#endif - gnutls_assert(); - return GNUTLS_E_UNKNOWN_CIPHER_TYPE; - } - - pos += sizeOfSuites; - DECR_LEN(len, sizeOfSuites); - memmove(&z, &data[pos++], 1); /* z is the number of compression methods */ - DECR_LEN(len, 1); - ret = SelectCompMethod(state, &state-> - gnutls_internals.compression_method, - &data[pos], z); - pos+=z; - DECR_LEN(len, z); - - if (ret < 0) { - gnutls_assert(); - return ret; - } - - ret = _gnutls_parse_extensions( state, &data[pos], len); /* len is the rest of the parsed length */ + ret = _gnutls_read_client_hello(state, data, datalen); if (ret < 0) { + _gnutls_send_alert( cd, state, GNUTLS_FATAL, GNUTLS_HANDSHAKE_FAILURE); /* send handshake failure */ gnutls_assert(); return ret; } - } return ret; } -int _gnutls_recv_certificate(int cd, GNUTLS_STATE state, char *data, int datalen) +int _gnutls_recv_certificate(int cd, GNUTLS_STATE state, char *data, + int datalen) { int pos = 0; - char* certificate_list; - int ret=0; + char *certificate_list; + int ret = 0; uint32 sizeOfCert; uint24 num; - + if (state->security_parameters.entity == GNUTLS_CLIENT) { if (datalen < 2) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } - num.pint[0] = data[pos]; - num.pint[1] = data[pos+1]; - num.pint[2] = data[pos+2]; + num.pint[0] = data[pos]; + num.pint[1] = data[pos + 1]; + num.pint[2] = data[pos + 2]; sizeOfCert = uint24touint32(num); - - pos+=3; + + pos += 3; #ifndef WORDS_BIGENDIAN - sizeOfCert=byteswap32(sizeOfCert); + sizeOfCert = byteswap32(sizeOfCert); #endif if (sizeOfCert > MAX24) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } certificate_list = gnutls_malloc(sizeOfCert); - - memmove( certificate_list, &data[pos], sizeOfCert); - + + memmove(certificate_list, &data[pos], sizeOfCert); + /* Verify certificates !!! */ - - gnutls_free(certificate_list); /* oooops! */ + + gnutls_free(certificate_list); /* oooops! */ } else { /* Server side reading a client certificate */ /* actually this is not complete */ @@ -942,13 +1131,15 @@ int _gnutls_recv_certificate(int cd, GNUTLS_STATE state, char *data, int datalen /* This is the main function in the handshake protocol. This does actually * everything. (exchange hello messages etc). */ -int gnutls_handshake(int cd, GNUTLS_STATE state) { +int gnutls_handshake(int cd, GNUTLS_STATE state) +{ int ret; - - ret = gnutls_handshake_begin( cd, state); + + ret = gnutls_handshake_begin(cd, state); /* FIXME: check certificate */ - if (ret==0) ret = gnutls_handshake_finish( cd, state); - + if (ret == 0) + ret = gnutls_handshake_finish(cd, state); + return ret; } @@ -962,32 +1153,48 @@ int gnutls_handshake_begin(int cd, GNUTLS_STATE state) if (state->security_parameters.entity == GNUTLS_CLIENT) { #ifdef HARD_DEBUG - if (state->gnutls_internals.resumed_security_parameters.session_id_size>0) - fprintf(stderr, "Ask to resume: %s\n", _gnutls_bin2hex(state->gnutls_internals.resumed_security_parameters.session_id, state->gnutls_internals.resumed_security_parameters.session_id_size)); + if (state->gnutls_internals.resumed_security_parameters. + session_id_size > 0) + fprintf(stderr, "Ask to resume: %s\n", + _gnutls_bin2hex(state->gnutls_internals. + resumed_security_parameters. + session_id, + state->gnutls_internals. + resumed_security_parameters. + session_id_size)); #endif - ret = _gnutls_send_hello(cd, state, state->gnutls_internals.resumed_security_parameters.session_id, state->gnutls_internals.resumed_security_parameters.session_id_size); + ret = + _gnutls_send_hello(cd, state, + state->gnutls_internals. + resumed_security_parameters. + session_id, + state->gnutls_internals. + resumed_security_parameters. + session_id_size); if (ret < 0) { ERR("send hello", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* receive the server hello */ ret = - _gnutls_recv_handshake(cd, state, NULL, NULL, GNUTLS_SERVER_HELLO); + _gnutls_recv_handshake(cd, state, NULL, NULL, + GNUTLS_SERVER_HELLO); if (ret < 0) { ERR("recv hello", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* RECV CERTIFICATE */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = - _gnutls_recv_handshake(cd, state, NULL, NULL, GNUTLS_CERTIFICATE); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_recv_handshake(cd, state, NULL, NULL, + GNUTLS_CERTIFICATE); if (ret < 0) { ERR("recv server certificate", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } return 0; @@ -998,49 +1205,52 @@ int gnutls_handshake_begin(int cd, GNUTLS_STATE state) GNUTLS_CLIENT_HELLO); if (ret < 0) { ERR("recv hello", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } ret = - _gnutls_send_hello(cd, state, state->security_parameters.session_id, - state->security_parameters.session_id_size); + _gnutls_send_hello(cd, state, + state->security_parameters. + session_id, + state->security_parameters. + session_id_size); if (ret < 0) { ERR("send hello", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* FIXME: send our certificate - if required */ /* NOTE: these should not be send if we are resuming */ - + /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */ /* send server key exchange (A) */ - if (state->gnutls_internals.resumed==RESUME_FALSE) - ret = _gnutls_send_server_kx_message(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) + ret = _gnutls_send_server_kx_message(cd, state); if (ret < 0) { ERR("send server kx", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* Added for SRP which uses a different handshake */ /* receive the client key exchange message */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_recv_client_kx_message0(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_kx_message0(cd, state); if (ret < 0) { ERR("recv client kx0", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* send server key exchange (B) */ - if (state->gnutls_internals.resumed==RESUME_FALSE) - ret = _gnutls_send_server_kx_message2(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) + ret = _gnutls_send_server_kx_message2(cd, state); if (ret < 0) { ERR("send server kx2", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } @@ -1050,67 +1260,71 @@ int gnutls_handshake_begin(int cd, GNUTLS_STATE state) } /* This function sends the final handshake packets and initializes connection */ -static int _gnutls_send_handshake_final( int cd, GNUTLS_STATE state, int init) { -int ret=0; - - /* Send the CHANGE CIPHER SPEC PACKET */ - ret = _gnutls_send_change_cipher_spec(cd, state); - if (ret < 0) { - ERR("send ChangeCipherSpec", ret); - gnutls_clearHashDataBuffer( state); - return ret; - } +static int _gnutls_send_handshake_final(int cd, GNUTLS_STATE state, + int init) +{ + int ret = 0; - /* Initialize the connection state (start encryption) - in case of client */ - if (init == TRUE) { - ret = _gnutls_connection_state_init(state); - if (ret<0) { - gnutls_assert(); - gnutls_clearHashDataBuffer( state); - return ret; - } - } - /* send the finished message */ + /* Send the CHANGE CIPHER SPEC PACKET */ + ret = _gnutls_send_change_cipher_spec(cd, state); + if (ret < 0) { + ERR("send ChangeCipherSpec", ret); + gnutls_clearHashDataBuffer(state); + return ret; + } - ret = _gnutls_send_finished(cd, state); + /* Initialize the connection state (start encryption) - in case of client */ + if (init == TRUE) { + ret = _gnutls_connection_state_init(state); if (ret < 0) { - ERR("send Finished", ret); - gnutls_clearHashDataBuffer( state); + gnutls_assert(); + gnutls_clearHashDataBuffer(state); return ret; } + } + /* send the finished message */ + + ret = _gnutls_send_finished(cd, state); + if (ret < 0) { + ERR("send Finished", ret); + gnutls_clearHashDataBuffer(state); return ret; + } + return ret; } /* This function receives the final handshake packets */ -static int _gnutls_recv_handshake_final( int cd, GNUTLS_STATE state, int init) { -int ret=0; - - ret = - gnutls_recv_int(cd, state, GNUTLS_CHANGE_CIPHER_SPEC, - NULL, 0, 0); - if (ret < 0) { - ERR("recv ChangeCipherSpec", ret); - gnutls_clearHashDataBuffer( state); - return ret; - } +static int _gnutls_recv_handshake_final(int cd, GNUTLS_STATE state, + int init) +{ + int ret = 0; - /* Initialize the connection state (start encryption) - in case of server */ - if (init == TRUE) { - ret = _gnutls_connection_state_init(state); - if (ret<0) { - gnutls_assert(); - gnutls_clearHashDataBuffer( state); - return ret; - } - } + ret = + gnutls_recv_int(cd, state, GNUTLS_CHANGE_CIPHER_SPEC, + NULL, 0, 0); + if (ret < 0) { + ERR("recv ChangeCipherSpec", ret); + gnutls_clearHashDataBuffer(state); + return ret; + } - ret = _gnutls_recv_finished(cd, state); + /* Initialize the connection state (start encryption) - in case of server */ + if (init == TRUE) { + ret = _gnutls_connection_state_init(state); if (ret < 0) { - ERR("recv finished", ret); - gnutls_clearHashDataBuffer( state); + gnutls_assert(); + gnutls_clearHashDataBuffer(state); return ret; } + } + + ret = _gnutls_recv_finished(cd, state); + if (ret < 0) { + ERR("recv finished", ret); + gnutls_clearHashDataBuffer(state); return ret; + } + return ret; } /* in this function we finish the handshake procedure @@ -1118,37 +1332,37 @@ int ret=0; */ int gnutls_handshake_finish(int cd, GNUTLS_STATE state) { - int ret=0; + int ret = 0; if (state->security_parameters.entity == GNUTLS_CLIENT) { /* receive the server key exchange */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_recv_server_kx_message(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_server_kx_message(cd, state); if (ret < 0) { ERR("recv server kx message", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* Added for SRP */ - + /* send the client key exchange for SRP */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_send_client_kx_message0(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_send_client_kx_message0(cd, state); if (ret < 0) { ERR("send client kx0", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - + /* receive the server key exchange (B) (SRP only) */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_recv_server_kx_message2(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_server_kx_message2(cd, state); if (ret < 0) { ERR("recv server kx message2", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } @@ -1156,63 +1370,65 @@ int gnutls_handshake_finish(int cd, GNUTLS_STATE state) /* FIXME: receive certificate request */ /* receive the server hello done */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = - _gnutls_recv_handshake(cd, state, NULL, NULL, - GNUTLS_SERVER_HELLO_DONE); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_recv_handshake(cd, state, NULL, NULL, + GNUTLS_SERVER_HELLO_DONE); if (ret < 0) { ERR("recv server hello done", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* send our certificate - if any */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_send_client_certificate(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_send_client_certificate(cd, state); if (ret < 0) { ERR("send client certificate", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_send_client_kx_message(cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_send_client_kx_message(cd, state); if (ret < 0) { ERR("send client kx", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* send client certificate verify */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_send_client_certificate_verify( cd, state); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_send_client_certificate_verify(cd, + state); if (ret < 0) { ERR("send client certificate verify", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - } else { /* SERVER SIDE */ + } else { /* SERVER SIDE */ /* send the server hello done */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = - _gnutls_send_handshake(cd, state, NULL, 0, - GNUTLS_SERVER_HELLO_DONE); + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_send_handshake(cd, state, NULL, 0, + GNUTLS_SERVER_HELLO_DONE); if (ret < 0) { ERR("send server hello done", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */ - /* receive the client key exchange message */ - if (state->gnutls_internals.resumed==RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_recv_client_kx_message(cd, state); + /* receive the client key exchange message */ + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_kx_message(cd, state); if (ret < 0) { ERR("recv client kx", ret); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } @@ -1220,47 +1436,49 @@ int gnutls_handshake_finish(int cd, GNUTLS_STATE state) } /* send and recv the change cipher spec and finished messages */ - if ((state->gnutls_internals.resumed==RESUME_TRUE && state->security_parameters.entity == GNUTLS_CLIENT) - || (state->gnutls_internals.resumed==RESUME_FALSE && state->security_parameters.entity == GNUTLS_SERVER)) { + if ((state->gnutls_internals.resumed == RESUME_TRUE + && state->security_parameters.entity == GNUTLS_CLIENT) + || (state->gnutls_internals.resumed == RESUME_FALSE + && state->security_parameters.entity == GNUTLS_SERVER)) { /* if we are a client resuming - or we are a server not resuming */ - ret = _gnutls_recv_handshake_final( cd, state, TRUE); + ret = _gnutls_recv_handshake_final(cd, state, TRUE); if (ret < 0) { gnutls_assert(); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - ret = _gnutls_send_handshake_final( cd, state, FALSE); + ret = _gnutls_send_handshake_final(cd, state, FALSE); if (ret < 0) { gnutls_assert(); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - } else { /* if we are a client not resuming - or we are a server resuming */ + } else { /* if we are a client not resuming - or we are a server resuming */ - ret = _gnutls_send_handshake_final( cd, state, TRUE); + ret = _gnutls_send_handshake_final(cd, state, TRUE); if (ret < 0) { gnutls_assert(); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } - ret = _gnutls_recv_handshake_final( cd, state, FALSE); + ret = _gnutls_recv_handshake_final(cd, state, FALSE); if (ret < 0) { gnutls_assert(); - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } } if (state->security_parameters.entity == GNUTLS_SERVER) { /* in order to support session resuming */ - _gnutls_server_register_current_session( state); + _gnutls_server_register_current_session(state); } /* clear handshake buffer */ - gnutls_clearHashDataBuffer( state); + gnutls_clearHashDataBuffer(state); return ret; } @@ -1275,7 +1493,8 @@ int _gnutls_generate_session_id(char *session_id, uint8 * len) *len = 32; #ifdef HARD_DEBUG - fprintf(stderr, "SessionID: %s\n", _gnutls_bin2hex(session_id, 32)); + fprintf(stderr, "SessionID: %s\n", + _gnutls_bin2hex(session_id, 32)); #endif return 0; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index fbac103cb6..1b79c838e2 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -23,14 +23,13 @@ #define GNUTLS_INT_H /* -#define HANDSHAKE_DEBUG #define READ_DEBUG #define WRITE_DEBUG #define BUFFERS_DEBUG #define HARD_DEBUG -*/ +#define HANDSHAKE_DEBUG #define DEBUG - +*/ #define MAX32 4294967295 #define MAX24 16777215 @@ -266,7 +265,7 @@ typedef struct { char* db_name; int expire_time; MOD_AUTH_STRUCT* auth_struct; /* used in handshake packets and KX algorithms */ - + int v2_hello; /* set 0 normally - 1 if v2 hello was received - server side only */ } GNUTLS_INTERNALS; typedef struct { diff --git a/lib/gnutls_v2_compat.c b/lib/gnutls_v2_compat.c new file mode 100644 index 0000000000..bc4190ac7a --- /dev/null +++ b/lib/gnutls_v2_compat.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2001 Nikos Mavroyanopoulos + * + * This file is part of GNUTLS. + * + * GNUTLS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GNUTLS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <defines.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "debug.h" +#include "gnutls_algorithms.h" +#include "gnutls_compress.h" +#include "gnutls_plaintext.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_kx.h" +#include "gnutls_handshake.h" +#include "gnutls_num.h" +#include "gnutls_hash_int.h" +#include "gnutls_db.h" +#include "gnutls_extensions.h" +#include "gnutls_random.h" +#include "gnutls_auth_int.h" + + +/* This selects the best supported ciphersuite from the ones provided */ +static int SelectSuite_v2(GNUTLS_STATE state, opaque ret[2], char *data, + int datalen) +{ + int x, i, j; + GNUTLS_CipherSuite *ciphers; + + x = _gnutls_supported_ciphersuites(state, &ciphers); +#ifdef HARD_DEBUG + fprintf(stderr, "Requested cipher suites: \n"); + for (j = 0; j < datalen; j += 2) { + if (data[j] == 0) { /* only print if in v2 compat mode */ + j++; + fprintf(stderr, "\t%s\n", + _gnutls_cipher_suite_get_name(* + ((GNUTLS_CipherSuite *) & data[j]))); + } + } + fprintf(stderr, "Supported cipher suites: \n"); + for (j = 0; j < x; j++) + fprintf(stderr, "\t%s\n", + _gnutls_cipher_suite_get_name(ciphers[j])); +#endif + memset(ret, '\0', sizeof(GNUTLS_CipherSuite)); + + for (j = 0; j < datalen; j += 2) { + for (i = 0; i < x; i++) { + if (data[j++] == 0) + if (memcmp + (&ciphers[i].CipherSuite, &data[j], + 2) == 0) { +#ifdef HARD_DEBUG + fprintf(stderr, + "Selected cipher suite: "); + fprintf(stderr, "%s\n", + _gnutls_cipher_suite_get_name + (* + ((GNUTLS_CipherSuite *) & + data[j]))); +#endif + memmove(ret, + &ciphers[i].CipherSuite, + 2); + gnutls_free(ciphers); + + return 0; + } + } + } + + + gnutls_free(ciphers); + gnutls_assert(); + return GNUTLS_E_UNKNOWN_CIPHER_SUITE; + +} + + +#define DECR_LEN(len, x) len-=x; if (len<0) {gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;} + +/* Read a v2 client hello + */ +int _gnutls_read_client_hello_v2(GNUTLS_STATE state, opaque * data, + int datalen) +{ + uint16 session_id_len = 0; + int pos = 0; + int ret = 0; + uint16 sizeOfSuites; + GNUTLS_Version version; + time_t cur_time; + char *rand; + int len = datalen; + int err; + uint16 challenge; + + + /* we only want to get here once - only in client hello */ + state->gnutls_internals.v2_hello = 0; + + + DECR_LEN(len, 2); +#ifdef DEBUG + fprintf(stderr, "Client's version: %d.%d\n", data[pos], + data[pos + 1]); +#endif + + version = _gnutls_version_get(data[pos], data[pos + 1]); + /* fallback to SSL 3.0 */ + gnutls_set_current_version(state, GNUTLS_SSL3); + + pos += 2; + + + /* Read uint16 cipher_spec_length */ + DECR_LEN(len, 2); + memmove(&sizeOfSuites, &data[pos], 2); + pos += 2; + +#ifndef WORDS_BIGENDIAN + sizeOfSuites = byteswap16(sizeOfSuites); +#endif + + /* read session id length */ + DECR_LEN(len, 2); + memmove(&session_id_len, &data[pos], 2); +#ifndef WORDS_BIGENDIAN + session_id_len = byteswap16(session_id_len); +#endif + pos += 2; + + if (session_id_len > 32) + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + + + /* read challenge length */ + DECR_LEN(len, 2); + memmove(&challenge, &data[pos], 2); +#ifndef WORDS_BIGENDIAN + challenge = byteswap16(challenge); +#endif + pos += 2; + + if (challenge < 10) { /* wow that's not random */ + gnutls_assert(); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + + + /* find an appropriate cipher suite */ + + DECR_LEN(len, sizeOfSuites); + ret = SelectSuite_v2(state, state->gnutls_internals. + current_cipher_suite.CipherSuite, + &data[pos], sizeOfSuites); + + pos += sizeOfSuites; + if (ret < 0) + return ret; + + + /* check if the credentials (username, public key etc. are ok) + */ + if (_gnutls_get_kx_cred( state->gnutls_key, _gnutls_cipher_suite_get_kx_algo(state->gnutls_internals.current_cipher_suite), &err) == NULL && err != 0) { + gnutls_assert(); + return GNUTLS_E_INSUFICIENT_CRED; + } + + /* set the MOD_AUTH_STRUCT to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + state->gnutls_internals.auth_struct = + _gnutls_kx_auth_struct(_gnutls_cipher_suite_get_kx_algo + (state->gnutls_internals. + current_cipher_suite)); + if (state->gnutls_internals.auth_struct == NULL) { +#ifdef DEBUG + fprintf(stderr, + "Cannot find the appropriate handler for the KX algorithm\n"); +#endif + gnutls_assert(); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + + + + /* read random new values -skip session id for now */ + DECR_LEN(len, session_id_len); /* skip session id for now */ + pos+=session_id_len; + + DECR_LEN(len, challenge); + memset( state->security_parameters.client_random, 0, 32); + + /* read the last 32 bytes */ + memcpy( state->security_parameters.client_random, &data[challenge > 32 ? (pos+challenge-32) : pos], challenge < 32 ? challenge : 32); + + /* generate server random value */ +#ifdef WORDS_BIGENDIAN + cur_time = time(NULL); +#else + cur_time = byteswap32(time(NULL)); +#endif + memmove(state->security_parameters.server_random, &cur_time, 4); + rand = _gnutls_get_random(28, GNUTLS_STRONG_RANDOM); + memmove(&state->security_parameters.server_random[4], rand, 28); + _gnutls_free_rand(rand); + state->security_parameters.timestamp = time(NULL); + + + /* RESUME SESSION */ + + len += session_id_len; /* back to session_id */ + pos -= session_id_len; + + + DECR_LEN(len, session_id_len); + ret = _gnutls_server_restore_session(state, &data[pos], session_id_len); + + pos += session_id_len; + + if (ret == 0) { /* resumed! */ + /* get the new random values */ + memcpy(state->gnutls_internals.resumed_security_parameters.server_random, + state->security_parameters.server_random, 32); + memcpy(state->gnutls_internals.resumed_security_parameters.client_random, + state->security_parameters.client_random, 32); + + state->gnutls_internals.resumed = RESUME_TRUE; + return 0; + } else { + _gnutls_generate_session_id(state->security_parameters. + session_id, + &state->security_parameters. + session_id_size); + state->gnutls_internals.resumed = RESUME_FALSE; + } + + return ret; +} diff --git a/lib/gnutls_v2_compat.h b/lib/gnutls_v2_compat.h new file mode 100644 index 0000000000..595d04d60b --- /dev/null +++ b/lib/gnutls_v2_compat.h @@ -0,0 +1 @@ +int _gnutls_read_client_hello_v2(GNUTLS_STATE state, opaque * data, int datalen); @@ -82,7 +82,8 @@ int main() gnutls_set_cipher_priority( state, 3, GNUTLS_3DES, GNUTLS_ARCFOUR, GNUTLS_RIJNDAEL); gnutls_set_compression_priority( state, 2, GNUTLS_ZLIB, GNUTLS_NULL_COMPRESSION); - gnutls_set_kx_priority( state, 2, GNUTLS_KX_SRP, GNUTLS_KX_ANON_DH); +// gnutls_set_kx_priority( state, 2, GNUTLS_KX_SRP, GNUTLS_KX_ANON_DH); + gnutls_set_kx_priority( state, 1, GNUTLS_KX_ANON_DH); gnutls_set_kx_cred( state, GNUTLS_KX_ANON_DH, NULL, 0); gnutls_set_kx_cred( state, GNUTLS_KX_SRP, &cred, sizeof(cred)); |