Automatically exchange ECDSA keys and upgrade to new authentication protocol.
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 10 Jul 2011 20:34:17 +0000 (22:34 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 10 Jul 2011 20:34:17 +0000 (22:34 +0200)
If we don't have ECDSA keys for the node we connect to, set protocol_minor
to 1, to indicate this to the other end. This will first complete the
old way of authentication with RSA keys, and will then exchange ECDSA keys.
The connection will be terminated right afterwards, and the next attempt
will use ECDSA keys.

src/conf.c
src/conf.h
src/openssl/ecdsa.c
src/openssl/ecdsa.h
src/protocol_auth.c

index 099b77d..0bbee09 100644 (file)
@@ -400,6 +400,24 @@ bool read_connection_config(connection_t *c) {
        return x;
 }
 
+bool append_connection_config(const connection_t *c, const char *key, const char *value) {
+       char *fname;
+       xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
+
+       FILE *fp = fopen(fname, "a");
+
+       if(!fp) {
+               logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
+       } else {
+               fprintf(fp, "\n# The following line was automatically added by tinc\n%s = %s\n", key, value);
+               fclose(fp);
+       }
+
+       free(fname);
+
+       return fp;
+}
+
 bool disable_old_keys(FILE *f) {
        char buf[100];
        long pos;
index cc57e82..20fc95e 100644 (file)
@@ -61,6 +61,7 @@ extern bool read_config_file(splay_tree_t *, const char *);
 extern void read_config_options(splay_tree_t *, const char *);
 extern bool read_server_config(void);
 extern bool read_connection_config(struct connection_t *);
+extern bool append_connection_config(const struct connection_t *, const char *, const char *);
 extern FILE *ask_and_open(const char *, const char *, const char *);
 extern bool is_safe_path(const char *);
 extern bool disable_old_keys(FILE *);
index a4f0f30..43464d8 100644 (file)
@@ -26,8 +26,8 @@
 #include "ecdsa.h"
 #include "utils.h"
 
-// Set ECDSA keys
-
+// Get and set ECDSA keys
+//
 bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
        *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
 
@@ -44,6 +44,18 @@ bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
        return true;
 }
 
+char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
+       unsigned char *pubkey = NULL;
+       int len = i2o_ECPublicKey(*ecdsa, &pubkey);
+
+       char *base64 = malloc(len * 4 / 3 + 5);
+       b64encode(pubkey, base64, len);
+
+       free(pubkey);
+
+       return base64;
+}
+
 // Read PEM ECDSA keys
 
 bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
index cc0e7b0..04f9eb9 100644 (file)
@@ -25,6 +25,7 @@
 typedef EC_KEY *ecdsa_t;
 
 extern bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p);
+extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
 extern bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp);
 extern bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp);
 extern size_t ecdsa_size(ecdsa_t *ecdsa);
index d38814c..70062f3 100644 (file)
 bool send_id(connection_t *c) {
        gettimeofday(&c->start, NULL);
 
+       int minor = myself->connection->protocol_minor;
+       if(c->config_tree && !read_ecdsa_public_key(c))
+               minor = 1;
+
        return send_request(c, "%d %s %d.%d", ID, myself->connection->name,
-                                               myself->connection->protocol_major, myself->connection->protocol_minor);
+                                               myself->connection->protocol_major, minor);
 }
 
 bool id_h(connection_t *c, char *request) {
@@ -110,6 +114,13 @@ bool id_h(connection_t *c, char *request) {
                                   c->name);
                        return false;
                }
+
+               if(c->protocol_minor >= 2)
+                       if(!read_ecdsa_public_key(c))
+                               return false;
+       } else {
+               if(!ecdsa_active(&c->ecdsa))
+                       c->protocol_minor = 1;
        }
 
        c->allow_request = METAKEY;
@@ -121,9 +132,6 @@ bool id_h(connection_t *c, char *request) {
 }
 
 bool send_metakey_ec(connection_t *c) {
-       if(!read_ecdsa_public_key(c))
-               return false;
-
        logger(LOG_DEBUG, "Sending ECDH metakey to %s", c->name);
 
        size_t siglen = ecdsa_size(&myself->connection->ecdsa);
@@ -149,11 +157,6 @@ bool send_metakey_ec(connection_t *c) {
 }
 
 bool send_metakey(connection_t *c) {
-       size_t len = rsa_size(&c->rsa);
-       char key[len];
-       char enckey[len];
-       char hexkey[2 * len + 1];
-
        if(!read_rsa_public_key(c))
                return false;
 
@@ -163,6 +166,11 @@ bool send_metakey(connection_t *c) {
        if(!digest_open_sha1(&c->outdigest, -1))
                return false;
 
+       size_t len = rsa_size(&c->rsa);
+       char key[len];
+       char enckey[len];
+       char hexkey[2 * len + 1];
+
        /* Create a random key */
 
        randomize(key, len);
@@ -451,7 +459,24 @@ bool chal_reply_h(connection_t *c, char *request) {
        return send_ack(c);
 }
 
+static bool send_upgrade(connection_t *c) {
+       /* Special case when protocol_minor is 1: the other end is ECDSA capable,
+        * but doesn't know our key yet. So send it now. */
+
+       char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
+
+       if(!pubkey)
+               return false;
+
+       bool result = send_request(c, "%d %s", ACK, pubkey);
+       free(pubkey);
+       return result;
+}
+
 bool send_ack(connection_t *c) {
+       if(c->protocol_minor == 1)
+               return send_upgrade(c);
+
        /* ACK message contains rest of the information the other end needs
           to create node_t and edge_t structures. */
 
@@ -516,7 +541,29 @@ static void send_everything(connection_t *c) {
        }
 }
 
+static bool upgrade_h(connection_t *c, char *request) {
+       char pubkey[MAX_STRING_SIZE];
+
+       if(sscanf(request, "%*d " MAX_STRING, pubkey) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
+               return false;
+       }
+
+       if(ecdsa_active(&c->ecdsa) || read_ecdsa_public_key(c)) {
+               logger(LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
+               return false;
+       }
+
+       logger(LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
+       append_connection_config(c, "ECDSAPublicKey", pubkey);
+       c->allow_request = TERMREQ;
+       return send_termreq(c);
+}
+
 bool ack_h(connection_t *c, char *request) {
+       if(c->protocol_minor == 1)
+               return upgrade_h(c, request);
+
        char hisport[MAX_STRING_SIZE];
        char *hisaddress;
        int weight, mtu;