summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2002-03-08 18:04:58 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2002-03-08 18:04:58 +0000
commit428fa3916f5b724543a199337d0c6fefcb5fc4c8 (patch)
tree31c504aa284911f9ee1214dc8e85f62b7d601d1a
parent4b3a09767a06a325556dadf3bfb27ab12f7eaa95 (diff)
downloadgnutls-428fa3916f5b724543a199337d0c6fefcb5fc4c8.tar.gz
Added protection against the newly discovered CBC attacks against TLS.
Experimental code.
-rw-r--r--lib/gnutls_buffers.c21
-rw-r--r--lib/gnutls_buffers.h4
-rw-r--r--lib/gnutls_cipher.c16
-rw-r--r--lib/gnutls_cipher.h4
-rw-r--r--lib/gnutls_openpgp.c6
-rw-r--r--lib/gnutls_record.c88
-rw-r--r--lib/gnutls_state.h1
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) { \