Enforce maximum amount of bytes sent/received on meta-connections.
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 30 Oct 2016 14:19:12 +0000 (15:19 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 30 Oct 2016 14:19:12 +0000 (15:19 +0100)
This is 2^{block_length_in_bits / 2 - 1}.

src/cipher.h
src/connection.h
src/meta.c
src/openssl/cipher.c
src/protocol_auth.c

index 41335ec..44db40f 100644 (file)
@@ -33,6 +33,7 @@ extern cipher_t *cipher_open_by_nid(int) __attribute__ ((__malloc__));
 extern void cipher_close(cipher_t *);
 extern size_t cipher_keylength(const cipher_t *);
 extern size_t cipher_blocksize(const cipher_t *);
+extern uint64_t cipher_budget(const cipher_t *);
 extern void cipher_get_key(const cipher_t *, void *);
 extern bool cipher_set_key(cipher_t *, void *, bool) __attribute__ ((__warn_unused_result__));
 extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool) __attribute__ ((__warn_unused_result__));
index 7fa769f..acd77bc 100644 (file)
@@ -81,6 +81,8 @@ typedef struct connection_t {
        cipher_t *outcipher;            /* Cipher we will use to send data to him */
        digest_t *indigest;
        digest_t *outdigest;
+       uint64_t inbudget;
+       uint64_t outbudget;
 #endif
 
        ecdsa_t *ecdsa;                 /* his public ECDSA key */
index 260cb00..4b35798 100644 (file)
@@ -65,6 +65,13 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
 #ifdef DISABLE_LEGACY
                return false;
 #else
+               if(length > c->outbudget) {
+                       logger(DEBUG_META, LOG_ERR, "Byte limit exceeded for encryption to %s (%s)", c->name, c->hostname);
+                       return false;
+               } else {
+                       c->outbudget -= length;
+               }
+
                size_t outlen = length;
 
                if(!cipher_encrypt(c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
@@ -220,6 +227,13 @@ bool receive_meta(connection_t *c) {
 #ifdef DISABLE_LEGACY
                        return false;
 #else
+                       if(inlen > c->inbudget) {
+                               logger(DEBUG_META, LOG_ERR, "yte limit exceeded for decryption from %s (%s)", c->name, c->hostname);
+                               return false;
+                       } else {
+                               c->inbudget -= inlen;
+                       }
+
                        size_t outlen = inlen;
 
                        if(!cipher_decrypt(c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
index 17ad408..e362325 100644 (file)
@@ -77,6 +77,24 @@ size_t cipher_keylength(const cipher_t *cipher) {
        return EVP_CIPHER_key_length(cipher->cipher) + EVP_CIPHER_iv_length(cipher->cipher);
 }
 
+uint64_t cipher_budget(const cipher_t *cipher) {
+       /* Hopefully some failsafe way to calculate the maximum amount of bytes to
+          send/receive with a given cipher before we might run into birthday paradox
+          attacks. Because we might use different modes, the block size of the mode
+          might be 1 byte. In that case, use the IV length. Ensure the whole thing
+          is limited to what can be represented with a 64 bits integer.
+        */
+
+       if(!cipher || !cipher->cipher)
+               return UINT64_MAX; // NULL cipher
+
+       int ivlen = EVP_CIPHER_iv_length(cipher->cipher);
+       int blklen = EVP_CIPHER_block_size(cipher->cipher);
+       int len = blklen > 1 ? blklen : ivlen > 1 ? ivlen : 8;
+       int bits = len * 4 - 1;
+       return bits < 64 ? UINT64_C(1) << bits : UINT64_MAX;
+}
+
 size_t cipher_blocksize(const cipher_t *cipher) {
        if(!cipher || !cipher->cipher)
                return 1;
index aee5736..224b6d8 100644 (file)
@@ -436,6 +436,8 @@ bool send_metakey(connection_t *c) {
        if(!c)
                return false;
 
+       c->outbudget = cipher_budget(c->outcipher);
+
        if(!(c->outdigest = digest_open_by_name("sha256", -1)))
                return false;
 
@@ -548,6 +550,8 @@ bool metakey_h(connection_t *c, const char *request) {
                c->incipher = NULL;
        }
 
+       c->inbudget = cipher_budget(c->incipher);
+
        if(digest) {
                if(!(c->indigest = digest_open_by_nid(digest, -1))) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);