Fix ans_key exchange in recent changes
[tinc] / src / protocol_key.c
index 421e2a3..64225fd 100644 (file)
@@ -1,7 +1,7 @@
 /*
     protocol_key.c -- handle the meta-protocol, key exchange
-    Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
-                  2000-2003 Guus Sliepen <guus@sliepen.eu.org>
+    Copyright (C) 1999-2005 Ivo Timmermans,
+                  2000-2009 Guus Sliepen <guus@tinc-vpn.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id: protocol_key.c,v 1.1.4.21 2003/07/23 22:17:31 guus Exp $
+    $Id$
 */
 
 #include "system.h"
 
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
 #include "avl_tree.h"
 #include "connection.h"
 #include "logger.h"
@@ -34,7 +38,7 @@
 
 bool mykeyused = false;
 
-bool send_key_changed(connection_t *c, node_t *n)
+bool send_key_changed()
 {
        cp();
 
@@ -42,10 +46,10 @@ bool send_key_changed(connection_t *c, 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)
@@ -77,16 +81,17 @@ bool key_changed_h(connection_t *c)
 
        /* Tell the others */
 
-       forward_request(c);
+       if(!tunnelserver)
+               forward_request(c);
 
        return true;
 }
 
-bool send_req_key(connection_t *c, node_t *from, 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)
@@ -122,31 +127,58 @@ bool req_key_h(connection_t *c)
        /* Check if this key request is for us */
 
        if(to == myself) {                      /* Yes, send our own key back */
-               mykeyused = true;
-               from->received_seqno = 0;
-               memset(from->late, 0, sizeof(from->late));
-               send_ans_key(c, myself, from);
+               send_ans_key(from);
        } else {
-               send_req_key(to->nexthop->connection, from, to);
+               if(tunnelserver)
+                       return false;
+
+               if(!to->status.reachable) {
+                       logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
+                               "REQ_KEY", c->name, c->hostname, to_name);
+                       return true;
+               }
+
+               send_request(to->nexthop->connection, "%s", c->buffer);
        }
 
        return true;
 }
 
-bool send_ans_key(connection_t *c, node_t *from, node_t *to)
+bool send_ans_key(node_t *to)
 {
-       char key[MAX_STRING_SIZE];
+       char *key;
 
        cp();
 
-       bin2hex(from->key, key, from->keylength);
-       key[from->keylength * 2] = '\0';
-
-       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);
+       // Set key parameters
+       to->incipher = myself->incipher;
+       to->inkeylength = myself->inkeylength;
+       to->indigest = myself->indigest;
+       to->incompression = myself->incompression;
+
+       // Allocate memory for key
+       to->inkey = xrealloc(to->inkey, to->inkeylength);
+
+       // Create a new key
+       RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
+       if(to->incipher)
+               EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
+
+       // Reset sequence number and late packet window
+       mykeyused = true;
+       to->received_seqno = 0;
+       memset(to->late, 0, sizeof(to->late));
+
+       // Convert to hexadecimal and send
+       key = alloca(2 * to->inkeylength + 1);
+       bin2hex(to->inkey, key, to->inkeylength);
+       key[to->inkeylength * 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)
@@ -186,61 +218,64 @@ bool ans_key_h(connection_t *c)
        /* Forward it if necessary */
 
        if(to != myself) {
+               if(tunnelserver)
+                       return false;
+
+               if(!to->status.reachable) {
+                       logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
+                               "ANS_KEY", c->name, c->hostname, to_name);
+                       return true;
+               }
+
                return send_request(to->nexthop->connection, "%s", c->buffer);
        }
 
        /* Update our copy of the origin's packet key */
+       from->outkey = xrealloc(from->outkey, strlen(key) / 2);
 
-       if(from->key)
-               free(from->key);
-
-       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(key, from->outkey, from->outkeylength);
 
-       from->status.validkey = true;
        from->status.waitingforkey = false;
-       from->sent_seqno = 0;
-
        /* 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) {
@@ -248,12 +283,20 @@ bool ans_key_h(connection_t *c)
                return false;
        }
        
-       from->compression = compression;
+       from->outcompression = compression;
 
-       if(from->cipher)
-               EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, from->key, 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;
+               }
+
+       from->status.validkey = true;
+       from->sent_seqno = 0;
 
-       flush_queue(from);
+       if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
+               send_mtu_probe(from);
 
        return true;
 }