From: Guus Sliepen Date: Thu, 2 Apr 2009 23:05:23 +0000 (+0200) Subject: Handle UDP packets from different and ports than advertised. X-Git-Tag: release-1.0.10~79 X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=commitdiff_plain;h=3308d13e7e3bf20cfeaf6f2ab17228a9820cea66 Handle UDP packets from different and ports than advertised. Previously, tinc used a fixed address and port for each node for UDP packet exchange. The port was the one advertised by that node as its listening port. However, due to NAT the port might be different. Now, tinc sends a different session key to each node. This way, the sending node can be determined from incoming packets by checking the MAC against all session keys. If a match is found, the address and port for that node are updated. --- diff --git a/src/graph.c b/src/graph.c index e0c48d42..87bb2204 100644 --- a/src/graph.c +++ b/src/graph.c @@ -226,27 +226,8 @@ void sssp_bfs(void) e->to->via = indirect ? n->via : e->to; e->to->options = e->options; - if(sockaddrcmp(&e->to->address, &e->address)) { - node = avl_unlink(node_udp_tree, e->to); - sockaddrfree(&e->to->address); - sockaddrcpy(&e->to->address, &e->address); - - if(e->to->hostname) - free(e->to->hostname); - - e->to->hostname = sockaddr2hostname(&e->to->address); - - if(node) - avl_insert_node(node_udp_tree, node); - - if(e->to->options & OPTION_PMTU_DISCOVERY) { - e->to->mtuprobes = 0; - e->to->minmtu = 0; - e->to->maxmtu = MTU; - if(e->to->status.validkey) - send_mtu_probe(e->to); - } - } + if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN) + update_node_udp(e->to, &e->address); list_insert_tail(todo_list, e->to); } @@ -269,13 +250,13 @@ void sssp_bfs(void) if(n->status.reachable) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"), n->name, n->hostname); - avl_insert(node_udp_tree, n); } else { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"), n->name, n->hostname); - avl_delete(node_udp_tree, n); } + /* TODO: only clear status.validkey if node is unreachable? */ + n->status.validkey = false; n->status.waitingforkey = false; diff --git a/src/net.c b/src/net.c index 0cdc72cc..7f17252d 100644 --- a/src/net.c +++ b/src/net.c @@ -414,11 +414,19 @@ int main_loop(void) /* Should we regenerate our key? */ if(keyexpires < now) { - ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); + avl_node_t *node; + node_t *n; + + ifdebug(STATUS) logger(LOG_INFO, _("Expiring symmetric keys")); + + for(node = node_tree->head; node; node = node->next) { + n = node->data; + if(n->inkey) { + free(n->inkey); + n->inkey = NULL; + } + } - RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); - if(myself->cipher) - EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len); send_key_changed(broadcast, myself); keyexpires = now + keylifetime; } diff --git a/src/net_packet.c b/src/net_packet.c index 544bbde7..1730023d 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -168,6 +168,18 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) route(n, packet); } +static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) +{ + unsigned char hmac[EVP_MAX_MD_SIZE]; + + if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength) + return false; + + HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL); + + return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength); +} + static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { vpn_packet_t pkt1, pkt2; @@ -180,9 +192,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) cp(); + if(!n->inkey) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got packet from %s (%s) but he hasn't got our key yet"), + n->name, n->hostname); + return; + } + /* Check packet length */ - if(inpkt->len < sizeof(inpkt->seqno) + myself->maclength) { + if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"), n->name, n->hostname); return; @@ -190,12 +208,12 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) /* Check the message authentication code */ - if(myself->digest && myself->maclength) { - inpkt->len -= myself->maclength; - HMAC(myself->digest, myself->key, myself->keylength, + if(n->indigest && n->inmaclength) { + inpkt->len -= n->inmaclength; + HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL); - if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) { + if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname); return; @@ -204,13 +222,13 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) /* Decrypt the packet */ - if(myself->cipher) { + if(n->incipher) { outpkt = pkt[nextpkt++]; - if(!EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_DecryptUpdate(&packet_ctx, (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(&packet_ctx, (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; @@ -253,10 +271,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) /* Decompress the packet */ - if(myself->compression) { + if(n->incompression) { outpkt = pkt[nextpkt++]; - if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, myself->compression)) < 0) { + if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) { ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"), n->name, n->hostname); return; @@ -315,7 +333,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) n->name, n->hostname); if(!n->status.waitingforkey) - send_req_key(n->nexthop->connection, myself, n); + send_req_key(n); n->status.waitingforkey = true; @@ -330,6 +348,8 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) n->name, n->hostname); send_tcppacket(n->nexthop->connection, origpkt); + + return; } origlen = inpkt->len; @@ -337,10 +357,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) /* Compress the packet */ - if(n->compression) { + if(n->outcompression) { outpkt = pkt[nextpkt++]; - if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->compression)) < 0) { + if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) { ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while compressing packet to %s (%s)"), n->name, n->hostname); return; @@ -356,13 +376,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) /* Encrypt the packet */ - if(n->cipher) { + if(n->outcipher) { outpkt = pkt[nextpkt++]; - if(!EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_EncryptUpdate(&n->packet_ctx, (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->packet_ctx, (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; @@ -374,10 +394,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) /* Add the message authentication code */ - if(n->digest && n->maclength) { - HMAC(n->digest, n->key, n->keylength, (unsigned char *) &inpkt->seqno, + if(n->outdigest && n->outmaclength) { + HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL); - inpkt->len += n->maclength; + inpkt->len += n->outmaclength; } /* Determine which socket we have to use */ @@ -476,6 +496,30 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) } } +static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) { + avl_node_t *node; + edge_t *e; + node_t *n = NULL; + + for(node = edge_weight_tree->head; node; node = node->next) { + e = node->data; + + if(sockaddrcmp_noport(from, &e->address)) + continue; + + if(!n) + n = e->to; + + if(!try_mac(e->to, pkt)) + continue; + + n = e->to; + break; + } + + return n; +} + void handle_incoming_vpn_data(int sock) { vpn_packet_t pkt; @@ -498,11 +542,15 @@ void handle_incoming_vpn_data(int sock) n = lookup_node_udp(&from); if(!n) { - hostname = sockaddr2hostname(&from); - logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), - hostname); - free(hostname); - return; + n = try_harder(&from, &pkt); + if(n) + update_node_udp(n, &from); + else { + hostname = sockaddr2hostname(&from); + logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname); + free(hostname); + return; + } } receive_udppacket(n, &pkt); diff --git a/src/net_setup.c b/src/net_setup.c index 3eb56441..75267794 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -349,88 +349,72 @@ bool setup_myself(void) if(get_config_string (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) { if(!strcasecmp(cipher, "none")) { - myself->cipher = NULL; + myself->incipher = NULL; } else { - myself->cipher = EVP_get_cipherbyname(cipher); + myself->incipher = EVP_get_cipherbyname(cipher); - if(!myself->cipher) { + if(!myself->incipher) { logger(LOG_ERR, _("Unrecognized cipher type!")); return false; } } } else - myself->cipher = EVP_bf_cbc(); + myself->incipher = EVP_bf_cbc(); - if(myself->cipher) - myself->keylength = myself->cipher->key_len + myself->cipher->iv_len; + if(myself->incipher) + myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len; else - myself->keylength = 1; + myself->inkeylength = 1; myself->connection->outcipher = EVP_bf_ofb(); - myself->key = xmalloc(myself->keylength); - RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); - if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; keyexpires = now + keylifetime; - if(myself->cipher) { - EVP_CIPHER_CTX_init(&packet_ctx); - if(!EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"), - myself->name, myself->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - } - /* Check if we want to use message authentication codes... */ - if(get_config_string - (lookup_config(myself->connection->config_tree, "Digest"), &digest)) { + if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) { if(!strcasecmp(digest, "none")) { - myself->digest = NULL; + myself->indigest = NULL; } else { - myself->digest = EVP_get_digestbyname(digest); + myself->indigest = EVP_get_digestbyname(digest); - if(!myself->digest) { + if(!myself->indigest) { logger(LOG_ERR, _("Unrecognized digest type!")); return false; } } } else - myself->digest = EVP_sha1(); + myself->indigest = EVP_sha1(); myself->connection->outdigest = EVP_sha1(); - if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), - &myself->maclength)) { - if(myself->digest) { - if(myself->maclength > myself->digest->md_size) { + if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength)) { + if(myself->indigest) { + if(myself->inmaclength > myself->indigest->md_size) { logger(LOG_ERR, _("MAC length exceeds size of digest!")); return false; - } else if(myself->maclength < 0) { + } else if(myself->inmaclength < 0) { logger(LOG_ERR, _("Bogus MAC length!")); return false; } } } else - myself->maclength = 4; + myself->inmaclength = 4; myself->connection->outmaclength = 0; /* Compression */ - if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), - &myself->compression)) { - if(myself->compression < 0 || myself->compression > 11) { + if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->incompression)) { + if(myself->incompression < 0 || myself->incompression > 11) { logger(LOG_ERR, _("Bogus compression level!")); return false; } } else - myself->compression = 0; + myself->incompression = 0; myself->connection->outcompression = 0; diff --git a/src/netutl.c b/src/netutl.c index 83e19ed8..20648605 100644 --- a/src/netutl.c +++ b/src/netutl.c @@ -144,6 +144,39 @@ char *sockaddr2hostname(const sockaddr_t *sa) return str; } +int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b) +{ + int result; + + cp(); + + result = a->sa.sa_family - b->sa.sa_family; + + if(result) + return result; + + switch (a->sa.sa_family) { + case AF_UNSPEC: + return 0; + + case AF_UNKNOWN: + return strcmp(a->unknown.address, b->unknown.address); + + case AF_INET: + return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr)); + + case AF_INET6: + return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)); + + default: + logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"), + a->sa.sa_family); + cp_trace(); + raise(SIGFPE); + exit(0); + } +} + int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) { int result; diff --git a/src/node.c b/src/node.c index 4ee9ce72..9d359253 100644 --- a/src/node.c +++ b/src/node.c @@ -42,16 +42,7 @@ static int node_compare(const node_t *a, const node_t *b) static int node_udp_compare(const node_t *a, const node_t *b) { - int result; - - cp(); - - result = sockaddrcmp(&a->address, &b->address); - - if(result) - return result; - - return (a->name && b->name) ? strcmp(a->name, b->name) : 0; + return sockaddrcmp(&a->address, &b->address); } void init_nodes(void) @@ -78,7 +69,8 @@ node_t *new_node(void) n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); - EVP_CIPHER_CTX_init(&n->packet_ctx); + EVP_CIPHER_CTX_init(&n->inctx); + EVP_CIPHER_CTX_init(&n->outctx); n->mtu = MTU; n->maxmtu = MTU; @@ -89,8 +81,11 @@ void free_node(node_t *n) { cp(); - if(n->key) - free(n->key); + if(n->inkey) + free(n->inkey); + + if(n->outkey) + free(n->outkey); if(n->subnet_tree) free_subnet_tree(n->subnet_tree); @@ -100,7 +95,8 @@ void free_node(node_t *n) sockaddrfree(&n->address); - EVP_CIPHER_CTX_cleanup(&n->packet_ctx); + EVP_CIPHER_CTX_cleanup(&n->inctx); + EVP_CIPHER_CTX_cleanup(&n->outctx); if(n->mtuevent) event_del(n->mtuevent); @@ -142,6 +138,7 @@ void node_del(node_t *n) } avl_delete(node_tree, n); + avl_delete(node_udp_tree, n); } node_t *lookup_node(char *name) @@ -167,6 +164,25 @@ node_t *lookup_node_udp(const sockaddr_t *sa) return avl_search(node_udp_tree, &n); } +void update_node_udp(node_t *n, const sockaddr_t *sa) +{ + avl_delete(node_udp_tree, n); + + if(n->hostname) + free(n->hostname); + + if(sa) { + n->address = *sa; + n->hostname = sockaddr2hostname(&n->address); + avl_delete(node_udp_tree, n); + avl_insert(node_udp_tree, n); + logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname); + } else { + memset(&n->address, 0, sizeof n->address); + logger(LOG_DEBUG, "UDP address of %s cleared", n->name); + } +} + void dump_nodes(void) { avl_node_t *node; @@ -179,8 +195,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 %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"), - n->name, n->hostname, n->cipher ? n->cipher->nid : 0, - n->digest ? n->digest->type : 0, n->maclength, n->compression, + n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, + n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, n->options, *(uint32_t *)&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 55a1b530..4321c008 100644 --- a/src/node.h +++ b/src/node.h @@ -51,15 +51,24 @@ typedef struct node_t { node_status_t status; - const EVP_CIPHER *cipher; /* Cipher type for UDP packets */ - char *key; /* Cipher key and iv */ - int keylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX packet_ctx; /* Cipher context */ + 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 */ - const EVP_MD *digest; /* Digest type for MAC */ - int maclength; /* Length of MAC */ + 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 */ + + const EVP_MD *indigest; /* Digest type for MAC of packets received from him */ + int inmaclength; /* Length of MAC */ + + const EVP_MD *outdigest; /* Digest type for MAC of packets sent to him*/ + int outmaclength; /* Length of MAC */ - int compression; /* Compressionlevel, 0 = no compression */ + int incompression; /* Compressionlevel, 0 = no compression */ + int outcompression; /* Compressionlevel, 0 = no compression */ struct node_t *nexthop; /* nearest node from us to him */ struct node_t *via; /* next hop for UDP packets */ @@ -93,6 +102,7 @@ extern void node_add(node_t *); extern void node_del(node_t *); extern node_t *lookup_node(char *); extern node_t *lookup_node_udp(const sockaddr_t *); +extern void update_node_udp(node_t *, const sockaddr_t *); extern void dump_nodes(void); #endif /* __TINC_NODE_H__ */ diff --git a/src/protocol.h b/src/protocol.h index 0664d4cf..ac86198e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -97,9 +97,9 @@ extern bool send_add_subnet(struct connection_t *, const struct subnet_t *); extern bool send_del_subnet(struct connection_t *, const struct subnet_t *); extern bool send_add_edge(struct connection_t *, const struct edge_t *); extern bool send_del_edge(struct connection_t *, const struct edge_t *); -extern bool send_key_changed(struct connection_t *, const struct node_t *); -extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *); -extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *); +extern bool send_key_changed(); +extern bool send_req_key(struct node_t *); +extern bool send_ans_key(struct node_t *); extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *); /* Request handlers */ diff --git a/src/protocol_key.c b/src/protocol_key.c index 06c6d336..5baa5f40 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -24,6 +24,7 @@ #include #include +#include #include "avl_tree.h" #include "connection.h" @@ -37,7 +38,7 @@ bool mykeyused = false; -bool send_key_changed(connection_t *c, const node_t *n) +bool send_key_changed() { cp(); @@ -45,10 +46,10 @@ bool send_key_changed(connection_t *c, const node_t *n) This reduces unnecessary key_changed broadcasts. */ - if(n == myself && !mykeyused) + if(!mykeyused) return true; - return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name); + return send_request(broadcast, "%d %lx %s", KEY_CHANGED, random(), myself->name); } bool key_changed_h(connection_t *c) @@ -86,11 +87,11 @@ bool key_changed_h(connection_t *c) return true; } -bool send_req_key(connection_t *c, const node_t *from, const node_t *to) +bool send_req_key(node_t *to) { cp(); - return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name); + return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name); } bool req_key_h(connection_t *c) @@ -129,7 +130,7 @@ bool req_key_h(connection_t *c) mykeyused = true; from->received_seqno = 0; memset(from->late, 0, sizeof(from->late)); - send_ans_key(c, myself, from); + send_ans_key(from); } else { if(tunnelserver) return false; @@ -140,27 +141,39 @@ bool req_key_h(connection_t *c) return true; } - send_req_key(to->nexthop->connection, from, to); + send_request(to->nexthop->connection, "%s", c->buffer); } return true; } -bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) +bool send_ans_key(node_t *to) { char *key; cp(); - key = alloca(2 * from->keylength + 1); - bin2hex(from->key, key, from->keylength); - key[from->keylength * 2] = '\0'; + if(!to->inkey) { + to->incipher = myself->incipher; + to->inkeylength = myself->inkeylength; + to->indigest = myself->indigest; + to->incompression = myself->incompression; + to->inkey = xmalloc(to->inkeylength); - return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY, - from->name, to->name, key, - from->cipher ? from->cipher->nid : 0, - from->digest ? from->digest->type : 0, from->maclength, - from->compression); + RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength); + if(to->incipher) + EVP_DecryptInit_ex(&packet_ctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len); + } + + key = alloca(2 * to->inkeylength + 1); + bin2hex(to->inkey, key, to->inkeylength); + key[to->outkeylength * 2] = '\0'; + + 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->incompression); } bool ans_key_h(connection_t *c) @@ -214,13 +227,13 @@ bool ans_key_h(connection_t *c) /* Update our copy of the origin's packet key */ - if(from->key) - free(from->key); + if(from->outkey) + free(from->outkey); - from->key = xstrdup(key); - from->keylength = strlen(key) / 2; - hex2bin(from->key, from->key, from->keylength); - from->key[from->keylength] = '\0'; + from->outkey = xstrdup(key); + from->outkeylength = strlen(key) / 2; + hex2bin(from->outkey, from->outkey, from->outkeylength); + from->outkey[from->outkeylength] = '\0'; from->status.validkey = true; from->status.waitingforkey = false; @@ -229,41 +242,41 @@ bool ans_key_h(connection_t *c) /* Check and lookup cipher and digest algorithms */ if(cipher) { - from->cipher = EVP_get_cipherbynid(cipher); + from->outcipher = EVP_get_cipherbynid(cipher); - if(!from->cipher) { + if(!from->outcipher) { logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname); return false; } - if(from->keylength != from->cipher->key_len + from->cipher->iv_len) { + if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) { logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname); return false; } } else { - from->cipher = NULL; + from->outcipher = NULL; } - from->maclength = maclength; + from->outmaclength = maclength; if(digest) { - from->digest = EVP_get_digestbynid(digest); + from->outdigest = EVP_get_digestbynid(digest); - if(!from->digest) { + if(!from->outdigest) { logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname); return false; } - if(from->maclength > from->digest->md_size || from->maclength < 0) { + if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) { logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname); return false; } } else { - from->digest = NULL; + from->outdigest = NULL; } if(compression < 0 || compression > 11) { @@ -271,10 +284,10 @@ bool ans_key_h(connection_t *c) return false; } - from->compression = compression; + from->outcompression = compression; - if(from->cipher) - if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) { + if(from->outcipher) + if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) { logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"), from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); return false;