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);