From: Guus Sliepen Date: Sun, 30 Oct 2016 14:19:12 +0000 (+0100) Subject: Enforce maximum amount of bytes sent/received on meta-connections. X-Git-Tag: release-1.1pre15~53 X-Git-Url: https://git.tinc-vpn.org/git/browse?a=commitdiff_plain;h=979acc48aded5bb04f1574128d174d56550be302;p=tinc Enforce maximum amount of bytes sent/received on meta-connections. This is 2^{block_length_in_bits / 2 - 1}. --- diff --git a/src/cipher.h b/src/cipher.h index 41335ec4..44db40f8 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -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__)); diff --git a/src/connection.h b/src/connection.h index 7fa769f8..acd77bc2 100644 --- a/src/connection.h +++ b/src/connection.h @@ -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 */ diff --git a/src/meta.c b/src/meta.c index 260cb005..4b357982 100644 --- a/src/meta.c +++ b/src/meta.c @@ -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) { diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c index 17ad408b..e362325a 100644 --- a/src/openssl/cipher.c +++ b/src/openssl/cipher.c @@ -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; diff --git a/src/protocol_auth.c b/src/protocol_auth.c index aee5736a..224b6d84 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -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);