]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added protection against the newly discovered CBC attacks against TLS.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 8 Mar 2002 18:04:58 +0000 (18:04 +0000)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 8 Mar 2002 18:04:58 +0000 (18:04 +0000)
Experimental code.

lib/gnutls_buffers.c
lib/gnutls_buffers.h
lib/gnutls_cipher.c
lib/gnutls_cipher.h
lib/gnutls_openpgp.c
lib/gnutls_record.c
lib/gnutls_state.h

index 63ec136bfd3492b4684cff8063b694b657352d17..a81c28e18bc053a7e5884b74fe717815085ec757 100644 (file)
@@ -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.
index 604535fd392ee1600a752781f753b80d17d8b192..2e31040548627b52ec965ef49b258e6fc7b03ab9 100644 (file)
@@ -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);
index 462ed6029c13b6b3712c4271871ea1cf8cb4e47f..15c4cad969400d34ef09507ce4c1eaa51d2a6f52 100644 (file)
 
 /* 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;
index 7720e0ed1bddbabc97f77aba3dfc73547e735057..bb3b22479ef8a455001460c2e553260c722c4f1c 100644 (file)
@@ -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);
index 6eba07f616770751f6db13e94df6cdaed7900df6..f2626f101f7ca089bff970cf1e402685a50df2c9 100644 (file)
@@ -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);
index bfc5e7b2f604d32bea4c3d2f2fd9ffb563dc9356..6c23e0faf9b263ee5d07a76254df434b8d145479 100644 (file)
@@ -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;
 }
 
index 929a85d68f79b6886f215d24a5683d1b6faf50a4..9e1a6b5fb9dccde847332b8760658be211f38c7c 100644 (file)
@@ -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) { \