diff --git a/debian/changelog b/debian/changelog index 85365a9..f8c1df3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +tinc (1.0.24-2.1+deb8u1) jessie-security; urgency=medium + + * Prevent oracle attacks (CVE-2018-16737, CVE-2018-16738). + * Prevent a MITM from forcing a NULL cipher for UDP (CVE-2018-16758). + + -- Guus Sliepen Mon, 08 Oct 2018 20:14:24 +0200 + tinc (1.0.24-2.1) unstable; urgency=medium * NMU after getting go ahead from guus on #tinc diff --git a/debian/patches/security-fixes b/debian/patches/security-fixes new file mode 100644 index 0000000..9f04f0b --- /dev/null +++ b/debian/patches/security-fixes @@ -0,0 +1,840 @@ +diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in +index 0548628f..a2a635fb 100644 +--- a/doc/tinc.conf.5.in ++++ b/doc/tinc.conf.5.in +@@ -464,7 +464,7 @@ Multiple + .Va Address + variables can be specified, in which case each address will be tried until a working + connection has been established. +-.It Va Cipher Li = Ar cipher Pq blowfish ++.It Va Cipher Li = Ar cipher Pq aes-256-cbc + The symmetric cipher algorithm used to encrypt UDP packets. + Any cipher supported by OpenSSL is recognised. + Furthermore, specifying +@@ -479,7 +479,7 @@ Fragmentation Needed or Packet too Big messages are dropped by firewalls. + This option sets the level of compression used for UDP packets. + Possible values are 0 (off), 1 (fast zlib) and any integer up to 9 (best zlib), + 10 (fast lzo) and 11 (best lzo). +-.It Va Digest Li = Ar digest Pq sha1 ++.It Va Digest Li = Ar digest Pq sha256 + The digest algorithm used to authenticate UDP packets. + Any digest supported by OpenSSL is recognised. + Furthermore, specifying +diff --git a/doc/tinc.texi b/doc/tinc.texi +index 85790028..6e422cfe 100644 +--- a/doc/tinc.texi ++++ b/doc/tinc.texi +@@ -1143,7 +1143,7 @@ Multiple Address variables can be specified, in which case each address will be + tried until a working connection has been established. + + @cindex Cipher +-@item Cipher = <@var{cipher}> (blowfish) ++@item Cipher = <@var{cipher}> (aes-256-cbc) + The symmetric cipher algorithm used to encrypt UDP packets. + Any cipher supported by OpenSSL is recognized. + Furthermore, specifying "none" will turn off packet encryption. +@@ -1162,7 +1162,7 @@ Possible values are 0 (off), 1 (fast zlib) and any integer up to 9 (best zlib), + 10 (fast lzo) and 11 (best lzo). + + @cindex Digest +-@item Digest = <@var{digest}> (sha1) ++@item Digest = <@var{digest}> (sha256) + The digest algorithm used to authenticate UDP packets. + Any digest supported by OpenSSL is recognized. + Furthermore, specifying "none" will turn off packet authentication. +diff --git a/m4/openssl.m4 b/m4/openssl.m4 +index 254ea4ff..77b3a4a8 100644 +--- a/m4/openssl.m4 ++++ b/m4/openssl.m4 +@@ -45,12 +45,14 @@ AC_DEFUN([tinc_OPENSSL], + [AC_MSG_ERROR([OpenSSL libraries not found.])] + ) + +- AC_CHECK_FUNCS([RAND_pseudo_bytes EVP_EncryptInit_ex], , +- [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break], ++ AC_CHECK_FUNCS([RAND_bytes EVP_EncryptInit_ex EVP_CIPHER_CTX_new], , ++ [AC_MSG_ERROR([Missing LibreSSL/OpenSSL functionality, make sure you have installed the latest version.]); break], + ) + +- AC_CHECK_DECL([OpenSSL_add_all_algorithms], , +- [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break], ++ AC_CHECK_DECLS([OpenSSL_add_all_algorithms, EVP_aes_256_cfb], , ++ [AC_MSG_ERROR([Missing LibreSSL/OpenSSL functionality, make sure you have installed the latest version.]); break], + [#include ] + ) ++ ++ AC_CHECK_FUNCS([BN_GENCB_new ERR_remove_state RSA_set0_key], , , [#include ]) + ]) +diff --git a/src/connection.h b/src/connection.h +index 877601fe..9e4dc663 100644 +--- a/src/connection.h ++++ b/src/connection.h +@@ -41,7 +41,9 @@ typedef struct connection_status_t { + unsigned int encryptout:1; /* 1 if we can encrypt outgoing traffic */ + unsigned int decryptin:1; /* 1 if we have to decrypt incoming traffic */ + unsigned int mst:1; /* 1 if this connection is part of a minimum spanning tree */ +- unsigned int unused:23; ++ unsigned int proxy_passed:1; /* 1 if we are connecting via a proxy and we have finished talking with it */ ++ unsigned int tarpit:1; /* 1 if the connection should be added to the tarpit */ ++ unsigned int unused:21; + } connection_status_t; + + #include "edge.h" +diff --git a/src/net.c b/src/net.c +index 8d0a0cf4..4e53d920 100644 +--- a/src/net.c ++++ b/src/net.c +@@ -166,6 +166,22 @@ static int build_fdset(fd_set *readset, fd_set *writeset) { + return max; + } + ++/* Put a misbehaving connection in the tarpit */ ++void tarpit(int fd) { ++ static int pits[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ++ static int next_pit = 0; ++ ++ if(pits[next_pit] != -1) { ++ closesocket(pits[next_pit]); ++ } ++ ++ pits[next_pit++] = fd; ++ ++ if(next_pit >= sizeof pits / sizeof pits[0]) { ++ next_pit = 0; ++ } ++} ++ + /* + Terminate a connection: + - Close the socket +@@ -186,8 +202,13 @@ void terminate_connection(connection_t *c, bool report) { + if(c->node) + c->node->connection = NULL; + +- if(c->socket) +- closesocket(c->socket); ++ if(c->socket) { ++ if(c->status.tarpit) { ++ tarpit(c->socket); ++ } else { ++ closesocket(c->socket); ++ } ++ } + + if(c->edge) { + if(!c->node) { +@@ -274,6 +295,7 @@ static void check_dead_connections(void) { + closesocket(c->socket); + do_outgoing_connection(c); + } else { ++ c->status.tarpit = true; + terminate_connection(c, false); + } + } +@@ -353,6 +375,7 @@ static void check_network_activity(fd_set * readset, fd_set * writeset) { + + if(FD_ISSET(c->socket, readset)) { + if(!receive_meta(c)) { ++ c->status.tarpit = true; + terminate_connection(c, c->status.active); + continue; + } +diff --git a/src/net.h b/src/net.h +index 2b50c5a2..5ae8d344 100644 +--- a/src/net.h ++++ b/src/net.h +@@ -163,6 +163,7 @@ extern void flush_queue(struct node_t *); + extern bool read_rsa_public_key(struct connection_t *); + extern void send_mtu_probe(struct node_t *); + extern void load_all_subnets(void); ++extern void tarpit(int fd); + + #ifndef HAVE_MINGW + #define closesocket(s) close(s) +diff --git a/src/net_packet.c b/src/net_packet.c +index d11d58ad..5d7c212d 100644 +--- a/src/net_packet.c ++++ b/src/net_packet.c +@@ -145,7 +145,7 @@ void send_mtu_probe(node_t *n) { + len = 64; + + memset(packet.data, 0, 14); +- RAND_pseudo_bytes(packet.data + 14, len - 14); ++ RAND_bytes(packet.data + 14, len - 14); + packet.len = len; + if(i >= 4 && n->mtuprobes <= 10) + packet.priority = -1; +@@ -314,10 +314,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { + if(n->incipher) { + outpkt = pkt[nextpkt++]; + +- if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL) +- || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen, ++ if(!EVP_DecryptInit_ex(n->inctx, NULL, NULL, NULL, NULL) ++ || !EVP_DecryptUpdate(n->inctx, (unsigned char *) &outpkt->seqno, &outlen, + (unsigned char *) &inpkt->seqno, inpkt->len) +- || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { ++ || !EVP_DecryptFinal_ex(n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s): %s", + n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + return; +@@ -482,10 +482,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { + if(n->outcipher) { + outpkt = pkt[nextpkt++]; + +- if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL) +- || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen, ++ if(!EVP_EncryptInit_ex(n->outctx, NULL, NULL, NULL, NULL) ++ || !EVP_EncryptUpdate(n->outctx, (unsigned char *) &outpkt->seqno, &outlen, + (unsigned char *) &inpkt->seqno, inpkt->len) +- || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { ++ || !EVP_EncryptFinal_ex(n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { + ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s): %s", + n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + goto end; +diff --git a/src/net_setup.c b/src/net_setup.c +index fa4e9867..ea2a3b78 100644 +--- a/src/net_setup.c ++++ b/src/net_setup.c +@@ -53,11 +53,22 @@ char *proxyuser; + char *proxypass; + proxytype_t proxytype; + ++#ifndef HAVE_RSA_SET0_KEY ++int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { ++ BN_free(r->n); r->n = n; ++ BN_free(r->e); r->e = e; ++ BN_free(r->d); r->d = d; ++ return 1; ++} ++#endif ++ + bool read_rsa_public_key(connection_t *c) { + FILE *fp; + char *pubname; + char *hcfname; + char *key; ++ BIGNUM *n = NULL; ++ BIGNUM *e = NULL; + + if(!c->rsa_key) { + c->rsa_key = RSA_new(); +@@ -67,12 +78,19 @@ bool read_rsa_public_key(connection_t *c) { + /* First, check for simple PublicKey statement */ + + if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) { +- if(BN_hex2bn(&c->rsa_key->n, key) != strlen(key)) { ++ if(BN_hex2bn(&n, key) != strlen(key)) { ++ free(key); + logger(LOG_ERR, "Invalid PublicKey for %s!", c->name); + return false; + } +- BN_hex2bn(&c->rsa_key->e, "FFFF"); + free(key); ++ BN_hex2bn(&e, "FFFF"); ++ if(!n || !e || RSA_set0_key(c->rsa_key, n, e, NULL) != 1) { ++ BN_free(e); ++ BN_free(n); ++ logger(LOG_ERR, "RSA_set0_key() failed with PublicKey for %s!", c->name); ++ return false; ++ } + return true; + } + +@@ -163,27 +181,39 @@ bool read_rsa_public_key(connection_t *c) { + static bool read_rsa_private_key(void) { + FILE *fp; + char *fname, *key, *pubkey; ++ BIGNUM *n = NULL; ++ BIGNUM *e = NULL; ++ BIGNUM *d = NULL; + + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { + myself->connection->rsa_key = RSA_new(); + // RSA_blinding_on(myself->connection->rsa_key, NULL); +- if(BN_hex2bn(&myself->connection->rsa_key->d, key) != strlen(key)) { ++ if(BN_hex2bn(&d, key) != strlen(key)) { + logger(LOG_ERR, "Invalid PrivateKey for myself!"); + free(key); + return false; + } + free(key); + if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) { ++ BN_free(d); + logger(LOG_ERR, "PrivateKey used but no PublicKey found!"); + return false; + } +- if(BN_hex2bn(&myself->connection->rsa_key->n, pubkey) != strlen(pubkey)) { +- logger(LOG_ERR, "Invalid PublicKey for myself!"); ++ if(BN_hex2bn(&n, pubkey) != strlen(pubkey)) { + free(pubkey); ++ BN_free(d); ++ logger(LOG_ERR, "Invalid PublicKey for myself!"); + return false; + } + free(pubkey); +- BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); ++ BN_hex2bn(&e, "FFFF"); ++ if(!n || !e || !d || RSA_set0_key(myself->connection->rsa_key, n, e, d) != 1) { ++ BN_free(d); ++ BN_free(e); ++ BN_free(n); ++ logger(LOG_ERR, "RSA_set0_key() failed with PrivateKey for myself!"); ++ return false; ++ } + return true; + } + +@@ -608,14 +638,25 @@ static bool setup_myself(void) { + } + free(cipher); + } else +- myself->incipher = EVP_bf_cbc(); ++ myself->incipher = EVP_aes_256_cbc(); + + if(myself->incipher) +- myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len; ++ myself->inkeylength = EVP_CIPHER_key_length(myself->incipher) + EVP_CIPHER_iv_length(myself->incipher); + else + myself->inkeylength = 1; + +- myself->connection->outcipher = EVP_bf_ofb(); ++ /* We need to use a stream mode for the meta protocol. Use AES for this, ++ but try to match the key size with the one from the cipher selected ++ by Cipher. ++ */ ++ ++ int keylen = EVP_CIPHER_key_length(myself->incipher); ++ if(keylen <= 16) ++ myself->connection->outcipher = EVP_aes_128_cfb(); ++ else if(keylen <= 24) ++ myself->connection->outcipher = EVP_aes_192_cfb(); ++ else ++ myself->connection->outcipher = EVP_aes_256_cfb(); + + if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) + keylifetime = 3600; +@@ -639,13 +680,13 @@ static bool setup_myself(void) { + + free(digest); + } else +- myself->indigest = EVP_sha1(); ++ myself->indigest = EVP_sha256(); + +- myself->connection->outdigest = EVP_sha1(); ++ myself->connection->outdigest = EVP_sha256(); + + if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) { + if(myself->indigest) { +- if(myself->inmaclength > myself->indigest->md_size) { ++ if(myself->inmaclength > EVP_MD_size(myself->indigest)) { + logger(LOG_ERR, "MAC length exceeds size of digest!"); + return false; + } else if(myself->inmaclength < 0) { +diff --git a/src/net_socket.c b/src/net_socket.c +index 9a67bb3c..479b595d 100644 +--- a/src/net_socket.c ++++ b/src/net_socket.c +@@ -513,6 +513,9 @@ void setup_outgoing_connection(outgoing_t *outgoing) { + new connection + */ + bool handle_new_meta_connection(int sock) { ++ static const int max_accept_burst = 10; ++ static int last_accept_burst; ++ static int last_accept_time; + connection_t *c; + sockaddr_t sa; + int fd; +@@ -525,6 +528,22 @@ bool handle_new_meta_connection(int sock) { + return false; + } + ++ if(last_accept_time == now) { ++ last_accept_burst++; ++ ++ if(last_accept_burst >= max_accept_burst) { ++ if(last_accept_burst == max_accept_burst) { ++ ifdebug(CONNECTIONS) logger(LOG_WARNING, "Throttling incoming connections"); ++ } ++ ++ tarpit(fd); ++ return false; ++ } ++ } else { ++ last_accept_burst = 0; ++ last_accept_time = now; ++ } ++ + sockaddrunmap(&sa); + + c = new_connection(); +@@ -546,7 +565,6 @@ bool handle_new_meta_connection(int sock) { + connection_add(c); + + c->allow_request = ID; +- send_id(c); + + return true; + } +diff --git a/src/node.c b/src/node.c +index cf70f838..19f37306 100644 +--- a/src/node.c ++++ b/src/node.c +@@ -1,6 +1,6 @@ + /* + node.c -- node tree management +- Copyright (C) 2001-2011 Guus Sliepen , ++ Copyright (C) 2001-2016 Guus Sliepen , + 2001-2005 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify +@@ -57,8 +57,10 @@ node_t *new_node(void) { + if(replaywin) n->late = xmalloc_and_zero(replaywin); + n->subnet_tree = new_subnet_tree(); + n->edge_tree = new_edge_tree(); +- EVP_CIPHER_CTX_init(&n->inctx); +- EVP_CIPHER_CTX_init(&n->outctx); ++ n->inctx = EVP_CIPHER_CTX_new(); ++ n->outctx = EVP_CIPHER_CTX_new(); ++ if(!n->inctx || !n->outctx) ++ abort(); + n->mtu = MTU; + n->maxmtu = MTU; + +@@ -80,8 +82,8 @@ void free_node(node_t *n) { + + sockaddrfree(&n->address); + +- EVP_CIPHER_CTX_cleanup(&n->inctx); +- EVP_CIPHER_CTX_cleanup(&n->outctx); ++ EVP_CIPHER_CTX_free(n->outctx); ++ EVP_CIPHER_CTX_free(n->inctx); + + if(n->mtuevent) + event_del(n->mtuevent); +@@ -172,8 +174,8 @@ void dump_nodes(void) { + for(node = node_tree->head; node; node = node->next) { + n = node->data; + logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)", +- n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, +- n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, ++ n->name, n->hostname, n->outcipher ? EVP_CIPHER_nid(n->outcipher) : 0, ++ n->outdigest ? EVP_MD_type(n->outdigest) : 0, n->outmaclength, n->outcompression, + n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-", + n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); + } +diff --git a/src/node.h b/src/node.h +index f9ef3c15..c83610e8 100644 +--- a/src/node.h ++++ b/src/node.h +@@ -50,12 +50,12 @@ typedef struct node_t { + const EVP_CIPHER *incipher; /* Cipher type for UDP packets received from him */ + char *inkey; /* Cipher key and iv */ + int inkeylength; /* Cipher key and iv length */ +- EVP_CIPHER_CTX inctx; /* Cipher context */ ++ EVP_CIPHER_CTX *inctx; /* Cipher context */ + + const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/ + char *outkey; /* Cipher key and iv */ + int outkeylength; /* Cipher key and iv length */ +- EVP_CIPHER_CTX outctx; /* Cipher context */ ++ EVP_CIPHER_CTX *outctx; /* Cipher context */ + + const EVP_MD *indigest; /* Digest type for MAC of packets received from him */ + int inmaclength; /* Length of MAC */ +diff --git a/src/protocol_auth.c b/src/protocol_auth.c +index 971341f7..5834fcdf 100644 +--- a/src/protocol_auth.c ++++ b/src/protocol_auth.c +@@ -145,7 +145,7 @@ bool id_h(connection_t *c) { + + /* Check if identity is a valid name */ + +- if(!check_id(name)) { ++ if(!check_id(name) || !strcmp(name, myself->name)) { + logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name, + c->hostname, "invalid name"); + return false; +@@ -177,6 +177,11 @@ bool id_h(connection_t *c) { + if(!c->config_tree) + init_configuration(&c->config_tree); + c->allow_request = ACK; ++ ++ if(!c->outgoing) { ++ send_id(c); ++ } ++ + return send_ack(c); + } + +@@ -196,6 +201,10 @@ bool id_h(connection_t *c) { + + c->allow_request = METAKEY; + ++ if(!c->outgoing) { ++ send_id(c); ++ } ++ + return send_metakey(c); + } + +@@ -210,8 +219,11 @@ bool send_metakey(connection_t *c) { + + c->outkey = xrealloc(c->outkey, len); + +- if(!c->outctx) +- c->outctx = xmalloc_and_zero(sizeof(*c->outctx)); ++ if(!c->outctx) { ++ c->outctx = EVP_CIPHER_CTX_new(); ++ if(!c->outctx) ++ abort(); ++ } + + /* Copy random data to the buffer */ + +@@ -262,17 +274,17 @@ bool send_metakey(connection_t *c) { + /* Send the meta key */ + + x = send_request(c, "%d %d %d %d %d %s", METAKEY, +- c->outcipher ? c->outcipher->nid : 0, +- c->outdigest ? c->outdigest->type : 0, c->outmaclength, ++ c->outcipher ? EVP_CIPHER_nid(c->outcipher) : 0, ++ c->outdigest ? EVP_MD_type(c->outdigest) : 0, c->outmaclength, + c->outcompression, buffer); + + /* Further outgoing requests are encrypted with the key we just generated */ + + if(c->outcipher) { + if(!EVP_EncryptInit(c->outctx, c->outcipher, +- (unsigned char *)c->outkey + len - c->outcipher->key_len, +- (unsigned char *)c->outkey + len - c->outcipher->key_len - +- c->outcipher->iv_len)) { ++ (unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher), ++ (unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher) - ++ EVP_CIPHER_iv_length(c->outcipher))) { + logger(LOG_ERR, "Error during initialisation of cipher for %s (%s): %s", + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + return false; +@@ -308,8 +320,11 @@ bool metakey_h(connection_t *c) { + + c->inkey = xrealloc(c->inkey, len); + +- if(!c->inctx) +- c->inctx = xmalloc_and_zero(sizeof(*c->inctx)); ++ if(!c->inctx) { ++ c->inctx = EVP_CIPHER_CTX_new(); ++ if(!c->inctx) ++ abort(); ++ } + + /* Convert the challenge from hexadecimal back to binary */ + +@@ -345,9 +360,9 @@ bool metakey_h(connection_t *c) { + } + + if(!EVP_DecryptInit(c->inctx, c->incipher, +- (unsigned char *)c->inkey + len - c->incipher->key_len, +- (unsigned char *)c->inkey + len - c->incipher->key_len - +- c->incipher->iv_len)) { ++ (unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher), ++ (unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher) - ++ EVP_CIPHER_iv_length(c->incipher))) { + logger(LOG_ERR, "Error during initialisation of cipher from %s (%s): %s", + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + return false; +@@ -355,7 +370,8 @@ bool metakey_h(connection_t *c) { + + c->status.decryptin = true; + } else { +- c->incipher = NULL; ++ logger(LOG_ERR, "%s (%s) uses null cipher!", c->name, c->hostname); ++ return false; + } + + c->inmaclength = maclength; +@@ -368,12 +384,13 @@ bool metakey_h(connection_t *c) { + return false; + } + +- if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) { ++ if(c->inmaclength > EVP_MD_size(c->indigest) || c->inmaclength < 0) { + logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname); + return false; + } + } else { +- c->indigest = NULL; ++ logger(LOG_ERR, "%s (%s) uses null digest!", c->name, c->hostname); ++ return false; + } + + c->incompression = compression; +@@ -447,27 +464,38 @@ bool challenge_h(connection_t *c) { + + /* Rest is done by send_chal_reply() */ + +- return send_chal_reply(c); ++ if(c->outgoing) { ++ return send_chal_reply(c); ++ } else { ++ return true; ++ } + } + + bool send_chal_reply(connection_t *c) { + char hash[EVP_MAX_MD_SIZE * 2 + 1]; +- EVP_MD_CTX ctx; ++ EVP_MD_CTX *ctx; + + /* Calculate the hash from the challenge we received */ + +- if(!EVP_DigestInit(&ctx, c->indigest) +- || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) +- || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) { ++ ctx = EVP_MD_CTX_create(); ++ if(!ctx) ++ abort(); ++ ++ if(!EVP_DigestInit(ctx, c->indigest) ++ || !EVP_DigestUpdate(ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) ++ || !EVP_DigestFinal(ctx, (unsigned char *)hash, NULL)) { ++ EVP_MD_CTX_destroy(ctx); + logger(LOG_ERR, "Error during calculation of response for %s (%s): %s", + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + return false; + } + ++ EVP_MD_CTX_destroy(ctx); ++ + /* Convert the hash to a hexadecimal formatted string */ + +- bin2hex(hash, hash, c->indigest->md_size); +- hash[c->indigest->md_size * 2] = '\0'; ++ bin2hex(hash, hash, EVP_MD_size(c->indigest)); ++ hash[EVP_MD_size(c->indigest) * 2] = '\0'; + + /* Send the reply */ + +@@ -477,7 +505,7 @@ bool send_chal_reply(connection_t *c) { + bool chal_reply_h(connection_t *c) { + char hishash[MAX_STRING_SIZE]; + char myhash[EVP_MAX_MD_SIZE]; +- EVP_MD_CTX ctx; ++ EVP_MD_CTX *ctx; + + if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) { + logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name, +@@ -487,7 +515,7 @@ bool chal_reply_h(connection_t *c) { + + /* Check if the length of the hash is all right */ + +- if(strlen(hishash) != c->outdigest->md_size * 2) { ++ if(strlen(hishash) != EVP_MD_size(c->outdigest) * 2) { + logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, + c->hostname, "wrong challenge reply length"); + return false; +@@ -495,24 +523,31 @@ bool chal_reply_h(connection_t *c) { + + /* Convert the hash to binary format */ + +- if(!hex2bin(hishash, hishash, c->outdigest->md_size)) { ++ if(!hex2bin(hishash, hishash, EVP_MD_size(c->outdigest))) { + logger(LOG_ERR, "Got bad %s from %s(%s): %s", "CHAL_REPLY", c->name, c->hostname, "invalid hash"); + return false; + } + + /* Calculate the hash from the challenge we sent */ + +- if(!EVP_DigestInit(&ctx, c->outdigest) +- || !EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key)) +- || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) { ++ ctx = EVP_MD_CTX_create(); ++ if(!ctx) ++ abort(); ++ ++ if(!EVP_DigestInit(ctx, c->outdigest) ++ || !EVP_DigestUpdate(ctx, c->hischallenge, RSA_size(c->rsa_key)) ++ || !EVP_DigestFinal(ctx, (unsigned char *)myhash, NULL)) { ++ EVP_MD_CTX_destroy(ctx); + logger(LOG_ERR, "Error during calculation of response from %s (%s): %s", + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + return false; + } + ++ EVP_MD_CTX_destroy(ctx); ++ + /* Verify the incoming hash with the calculated hash */ + +- if(memcmp(hishash, myhash, c->outdigest->md_size)) { ++ if(memcmp(hishash, myhash, EVP_MD_size(c->outdigest))) { + logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, + c->hostname, "wrong challenge reply"); + +@@ -531,6 +566,10 @@ bool chal_reply_h(connection_t *c) { + + c->allow_request = ACK; + ++ if(!c->outgoing) { ++ send_chal_reply(c); ++ } ++ + return send_ack(c); + } + +diff --git a/src/protocol_edge.c b/src/protocol_edge.c +index 3dfff05e..fa57494e 100644 +--- a/src/protocol_edge.c ++++ b/src/protocol_edge.c +@@ -70,7 +70,7 @@ bool add_edge_h(connection_t *c) { + + /* Check if names are valid */ + +- if(!check_id(from_name) || !check_id(to_name)) { ++ if(!check_id(from_name) || !check_id(to_name) || !strcmp(from_name, to_name)) { + logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name, + c->hostname, "invalid name"); + return false; +@@ -181,7 +181,7 @@ bool del_edge_h(connection_t *c) { + + /* Check if names are valid */ + +- if(!check_id(from_name) || !check_id(to_name)) { ++ if(!check_id(from_name) || !check_id(to_name) || !strcmp(from_name, to_name)) { + logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name, + c->hostname, "invalid name"); + return false; +diff --git a/src/protocol_key.c b/src/protocol_key.c +index 301ead5d..1b96597f 100644 +--- a/src/protocol_key.c ++++ b/src/protocol_key.c +@@ -164,7 +164,7 @@ bool send_ans_key(node_t *to) { + } + + if(to->incipher) +- EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len); ++ EVP_DecryptInit_ex(to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + EVP_CIPHER_key_length(to->incipher)); + + // Reset sequence number and late packet window + mykeyused = true; +@@ -178,8 +178,8 @@ bool send_ans_key(node_t *to) { + + return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY, + myself->name, to->name, key, +- to->incipher ? to->incipher->nid : 0, +- to->indigest ? to->indigest->type : 0, to->inmaclength, ++ to->incipher ? EVP_CIPHER_nid(to->incipher) : 0, ++ to->indigest ? EVP_MD_type(to->indigest) : 0, to->inmaclength, + to->incompression); + } + +@@ -268,12 +268,17 @@ bool ans_key_h(connection_t *c) { + return true; + } + +- if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) { ++ if(from->outkeylength != EVP_CIPHER_key_length(from->outcipher) + EVP_CIPHER_iv_length(from->outcipher)) { + logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, + from->hostname); + return true; + } + } else { ++ if(from->outkeylength != 1) { ++ logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname); ++ return true; ++ } ++ + from->outcipher = NULL; + } + +@@ -288,7 +293,7 @@ bool ans_key_h(connection_t *c) { + return true; + } + +- if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) { ++ if(from->outmaclength > EVP_MD_size(from->outdigest) || from->outmaclength < 0) { + logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", + from->name, from->hostname); + return true; +@@ -305,7 +310,7 @@ bool ans_key_h(connection_t *c) { + from->outcompression = compression; + + if(from->outcipher) +- if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) { ++ if(!EVP_EncryptInit_ex(from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + EVP_CIPHER_key_length(from->outcipher))) { + logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s", + from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); + return true; +diff --git a/src/tincd.c b/src/tincd.c +index b6a6c29c..19f2e174 100644 +--- a/src/tincd.c ++++ b/src/tincd.c +@@ -330,7 +330,7 @@ static bool parse_options(int argc, char **argv) { + + /* This function prettyprints the key generation process */ + +-static void indicator(int a, int b, void *p) { ++static int indicator(int a, int b, BN_GENCB *cb) { + switch (a) { + case 0: + fprintf(stderr, "."); +@@ -362,19 +362,48 @@ static void indicator(int a, int b, void *p) { + default: + fprintf(stderr, "?"); + } ++ ++ return 1; ++} ++ ++#ifndef HAVE_BN_GENCB_NEW ++BN_GENCB *BN_GENCB_new(void) { ++ return xmalloc_and_zero(sizeof(BN_GENCB)); + } + ++void BN_GENCB_free(BN_GENCB *cb) { ++ free(cb); ++} ++#endif ++ + /* + Generate a public/private RSA keypair, and ask for a file to store + them in. + */ + static bool keygen(int bits) { ++ BIGNUM *e = NULL; + RSA *rsa_key; + FILE *f; + char *pubname, *privname; ++ BN_GENCB *cb; ++ int result; + + fprintf(stderr, "Generating %d bits keys:\n", bits); +- rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL); ++ ++ cb = BN_GENCB_new(); ++ if(!cb) ++ abort(); ++ BN_GENCB_set(cb, indicator, NULL); ++ ++ rsa_key = RSA_new(); ++ BN_hex2bn(&e, "10001"); ++ if(!rsa_key || !e) ++ abort(); ++ ++ result = RSA_generate_key_ex(rsa_key, bits, e, cb); ++ ++ BN_free(e); ++ BN_GENCB_free(cb); + + if(!rsa_key) { + fprintf(stderr, "Error during key generation!\n"); +@@ -697,7 +726,11 @@ end: + EVP_cleanup(); + ENGINE_cleanup(); + CRYPTO_cleanup_all_ex_data(); ++#ifdef HAVE_ERR_REMOVE_STATE ++ // OpenSSL claims this function was deprecated in 1.0.0, ++ // but valgrind's leak detector shows you still need to call it to make sure OpenSSL cleans up properly. + ERR_remove_state(0); ++#endif + ERR_free_strings(); + + exit_configuration(&config_tree); diff --git a/debian/patches/series b/debian/patches/series index e69de29..a9c233b 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -0,0 +1 @@ +security-fixes