diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2002-03-08 18:04:58 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2002-03-08 18:04:58 +0000 |
commit | 428fa3916f5b724543a199337d0c6fefcb5fc4c8 (patch) | |
tree | 31c504aa284911f9ee1214dc8e85f62b7d601d1a | |
parent | 4b3a09767a06a325556dadf3bfb27ab12f7eaa95 (diff) | |
download | gnutls-428fa3916f5b724543a199337d0c6fefcb5fc4c8.tar.gz |
Added protection against the newly discovered CBC attacks against TLS.
Experimental code.
-rw-r--r-- | lib/gnutls_buffers.c | 21 | ||||
-rw-r--r-- | lib/gnutls_buffers.h | 4 | ||||
-rw-r--r-- | lib/gnutls_cipher.c | 16 | ||||
-rw-r--r-- | lib/gnutls_cipher.h | 4 | ||||
-rw-r--r-- | lib/gnutls_openpgp.c | 6 | ||||
-rw-r--r-- | lib/gnutls_record.c | 88 | ||||
-rw-r--r-- | lib/gnutls_state.h | 1 |
7 files changed, 122 insertions, 18 deletions
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 63ec136bfd..a81c28e18b 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -511,6 +511,7 @@ static int _gnutls_buffer_get( gnutls_datum * buffer, const opaque ** ptr, size_ return 0; } + /* This function is like write. But it does not return -1 on error. * It does return gnutls_errno instead. * @@ -618,6 +619,26 @@ ssize_t _gnutls_io_write_buffered( GNUTLS_STATE state, const void *iptr, size_t } +/* This is exactly like write_buffered, but will use two buffers to read + * from. + */ +ssize_t _gnutls_io_write_buffered2( GNUTLS_STATE state, const void *iptr, size_t n, const void* iptr2, size_t n2) +{ +opaque* sptr; + + sptr = gnutls_malloc( n+n2); + if (sptr==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy( sptr, iptr, n); + memcpy( &sptr[n], iptr2, n2); + + return _gnutls_io_write_buffered( state, sptr, n+n2); +} + + /* This function writes the data that are left in the * TLS write buffer (ie. because the previous write was * interrupted. diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h index 604535fd39..2e31040548 100644 --- a/lib/gnutls_buffers.h +++ b/lib/gnutls_buffers.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Nikos Mavroyanopoulos + * Copyright (C) 2000,2001,2002 Nikos Mavroyanopoulos * * This file is part of GNUTLS. * @@ -26,6 +26,8 @@ void _gnutls_io_clear_read_buffer( GNUTLS_STATE); int _gnutls_io_clear_peeked_data( GNUTLS_STATE state); ssize_t _gnutls_io_write_buffered( GNUTLS_STATE, const void *iptr, size_t n ); +ssize_t _gnutls_io_write_buffered2( GNUTLS_STATE, const void *iptr, size_t n, + const void* iptr2, size_t n2); int _gnutls_handshake_buffer_get_size( GNUTLS_STATE state); int _gnutls_handshake_buffer_peek( GNUTLS_STATE state, char *data, int length); diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c index 462ed6029c..15c4cad969 100644 --- a/lib/gnutls_cipher.c +++ b/lib/gnutls_cipher.c @@ -35,25 +35,24 @@ /* returns ciphertext which contains the headers too. This also * calculates the size in the header field. + * + * If random pad != 0 then the random pad data will be appended. */ int _gnutls_encrypt(GNUTLS_STATE state, const char* headers, int headers_size, const char *data, size_t data_size, - uint8 ** ciphertext, ContentType type) + uint8 ** ciphertext, ContentType type, int random_pad) { gnutls_datum plain = { (char*)data, data_size }; gnutls_datum comp, ciph; int err; - if (data_size == 0) - return 0; - err = _gnutls_plaintext2TLSCompressed(state, &comp, plain); if (err < 0) { gnutls_assert(); return err; } - err = _gnutls_compressed2TLSCiphertext(state, &ciph, comp, type, headers_size); + err = _gnutls_compressed2TLSCiphertext(state, &ciph, comp, type, headers_size, random_pad); if (err < 0) { gnutls_assert(); return err; @@ -115,7 +114,9 @@ int _gnutls_decrypt(GNUTLS_STATE state, char *ciphertext, int _gnutls_compressed2TLSCiphertext(GNUTLS_STATE state, gnutls_datum* cipher, - gnutls_datum compressed, ContentType _type, int headers_size) + gnutls_datum compressed, ContentType _type, + int headers_size, + int random_pad) { uint8 MAC[MAX_HASH_SIZE]; uint16 c_length; @@ -199,7 +200,8 @@ int _gnutls_compressed2TLSCiphertext(GNUTLS_STATE state, } /* make rand a multiple of blocksize */ - if ( state->security_parameters.version == GNUTLS_SSL3) { + if ( state->security_parameters.version == GNUTLS_SSL3 || + random_pad==0) { rand = 0; } else { rand = (rand / blocksize) * blocksize; diff --git a/lib/gnutls_cipher.h b/lib/gnutls_cipher.h index 7720e0ed1b..bb3b22479e 100644 --- a/lib/gnutls_cipher.h +++ b/lib/gnutls_cipher.h @@ -18,9 +18,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -int _gnutls_encrypt( GNUTLS_STATE state, const char* headers, int headers_size, const char* data, size_t data_size, uint8** ciphertext, ContentType type); +int _gnutls_encrypt( GNUTLS_STATE state, const char* headers, int headers_size, const char* data, size_t data_size, uint8** ciphertext, ContentType type, int random_pad); int _gnutls_decrypt(GNUTLS_STATE state, char *ciphertext, size_t ciphertext_size, uint8 ** data, ContentType type); -int _gnutls_compressed2TLSCiphertext(GNUTLS_STATE state, gnutls_datum* cipher, gnutls_datum compressed, ContentType _type, int headers_size); +int _gnutls_compressed2TLSCiphertext(GNUTLS_STATE state, gnutls_datum* cipher, gnutls_datum compressed, ContentType _type, int headers_size, int random_pad); int _gnutls_ciphertext2TLSCompressed(GNUTLS_STATE state, gnutls_datum * compress, gnutls_datum ciphertext, uint8 type); diff --git a/lib/gnutls_openpgp.c b/lib/gnutls_openpgp.c index 6eba07f616..f2626f101f 100644 --- a/lib/gnutls_openpgp.c +++ b/lib/gnutls_openpgp.c @@ -826,6 +826,7 @@ gnutls_certificate_set_openpgp_key_file(GNUTLS_CERTIFICATE_CREDENTIALS res, gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } + res->cert_list_length = gnutls_realloc(res->cert_list_length, (1+res->ncerts)*sizeof(int)); if (res->cert_list_length == NULL) @@ -898,14 +899,15 @@ gnutls_certificate_set_openpgp_key_file(GNUTLS_CERTIFICATE_CREDENTIALS res, iobuf_to_datum(buf, &raw); cdk_iobuf_close(buf); - res->ncerts++; res->pkey = gnutls_realloc(res->pkey, - (res->ncerts)*sizeof(gnutls_private_key)); + (res->ncerts+1)*sizeof(gnutls_private_key)); if (res->pkey == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } + + res->ncerts++; /* ncerts has been incremented before */ rc =_gnutls_openpgp_key2gnutls_key( &res->pkey[res->ncerts-1], raw); diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index bfc5e7b2f6..6c23e0faf9 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -35,6 +35,7 @@ #include "gnutls_record.h" #include "gnutls_datum.h" #include "ext_max_record.h" +#include <gnutls_state.h> #include <gnutls_alert.h> #include <gnutls_dh.h> @@ -318,6 +319,58 @@ static int _gnutls_session_is_valid( GNUTLS_STATE state) { return 0; } +static +ssize_t gnutls_create_empty_record( GNUTLS_STATE state, ContentType type, + opaque** erecord) +{ + int cipher_size; + int retval; + int data2send; + uint8 headers[5]; + GNUTLS_Version lver; + + *erecord = NULL; + if (type!=GNUTLS_APPLICATION_DATA || + _gnutls_cipher_is_block( gnutls_cipher_get(state))!=CIPHER_BLOCK) + /* alert messages and stream ciphers + * do not need this protection + */ + return 0; + + headers[0]=type; + + lver = gnutls_protocol_get_version(state); + if (lver==GNUTLS_VERSION_UNKNOWN) { + gnutls_assert(); + return GNUTLS_E_INTERNAL; + } + + headers[1]=_gnutls_version_get_major( lver); + headers[2]=_gnutls_version_get_minor( lver); + + data2send = 0; + + cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, NULL, 0, erecord, type, 0); + if (cipher_size <= 0) { + gnutls_assert(); + if (cipher_size==0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; + return cipher_size; /* error */ + } + + retval = cipher_size; + + /* increase sequence number + */ + if (uint64pp( &state->connection_state.write_sequence_number) != 0) { + _gnutls_session_invalidate( state); + gnutls_assert(); + return GNUTLS_E_RECORD_LIMIT_REACHED; + } + + return retval; +} + + /* This function behave exactly like write(). The only difference is * that it accepts, the gnutls_state and the ContentType of data to * send (if called by the user the Content is specific) @@ -338,6 +391,8 @@ ssize_t gnutls_send_int( GNUTLS_STATE state, ContentType type, HandshakeType hty uint8 headers[5]; const uint8 *data=_data; GNUTLS_Version lver; + int erecord_size = 0; + opaque* erecord; if (sizeofdata == 0 || _data==NULL) { gnutls_assert(); @@ -349,6 +404,8 @@ ssize_t gnutls_send_int( GNUTLS_STATE state, ContentType type, HandshakeType hty return GNUTLS_E_INVALID_SESSION; } + + headers[0]=type; lver = gnutls_protocol_get_version(state); @@ -380,10 +437,24 @@ ssize_t gnutls_send_int( GNUTLS_STATE state, ContentType type, HandshakeType hty retval = state->gnutls_internals.record_send_buffer_user_size; } else { - cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, data, data2send, &cipher, type); + + /* Prepend our packet with an empty record. This is to + * avoid the recent CBC attacks. + */ + erecord_size = + gnutls_create_empty_record( state, type, &erecord); + if (erecord_size < 0) { + gnutls_assert(); + return erecord_size; + } + + /* now proceed to packet encryption + */ + cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, data, data2send, &cipher, type, 1); if (cipher_size <= 0) { gnutls_assert(); if (cipher_size==0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; + gnutls_free( erecord); return cipher_size; /* error */ } @@ -397,14 +468,16 @@ ssize_t gnutls_send_int( GNUTLS_STATE state, ContentType type, HandshakeType hty gnutls_assert(); /* FIXME: Somebody has to do rehandshake before that. */ + gnutls_free( erecord); return GNUTLS_E_RECORD_LIMIT_REACHED; } - ret = _gnutls_io_write_buffered( state, cipher, cipher_size); + ret = _gnutls_io_write_buffered2( state, erecord, erecord_size, cipher, cipher_size); + gnutls_free( erecord); + gnutls_free( cipher); } - if ( ret != cipher_size) { - gnutls_free( cipher); + if ( ret != cipher_size + erecord_size) { if ( ret < 0 && gnutls_error_is_fatal(ret)==0) { /* If we have sent any data then return * that value. @@ -426,8 +499,6 @@ ssize_t gnutls_send_int( GNUTLS_STATE state, ContentType type, HandshakeType hty state->gnutls_internals.record_send_buffer_user_size = 0; - gnutls_free(cipher); - _gnutls_record_log( "REC: Sent Packet[%d] %s(%d) with length: %d\n", (int) uint64touint32(&state->connection_state.write_sequence_number), _gnutls_packet2str(type), type, cipher_size); @@ -669,6 +740,7 @@ ssize_t gnutls_recv_int( GNUTLS_STATE state, ContentType type, HandshakeType hty int ret, ret2; uint16 header_size; + begin: /* default headers for TLS 1.0 */ header_size = RECORD_HEADER_SIZE; @@ -831,6 +903,10 @@ ssize_t gnutls_recv_int( GNUTLS_STATE state, ContentType type, HandshakeType hty */ } + /* TLS 1.0 CBC protection. Read the next fragment. + */ + if (ret==0) goto begin; + return ret; } diff --git a/lib/gnutls_state.h b/lib/gnutls_state.h index 929a85d68f..9e1a6b5fb9 100644 --- a/lib/gnutls_state.h +++ b/lib/gnutls_state.h @@ -7,6 +7,7 @@ void _gnutls_record_set_default_version(GNUTLS_STATE state, GNUTLS_Version versi void _gnutls_state_cert_type_set( GNUTLS_STATE state, CertificateType); KXAlgorithm gnutls_kx_get( GNUTLS_STATE state); +GNUTLS_BulkCipherAlgorithm gnutls_cipher_get( GNUTLS_STATE state); CertificateType gnutls_cert_type_get( GNUTLS_STATE state); #define CHECK_AUTH(auth, ret) if (gnutls_auth_get_type(state) != auth) { \ |