From 30ef2a981e1d62692b3a2363e0b3a0e8711d9604 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 10 Jul 2011 22:34:17 +0200 Subject: [PATCH] Automatically exchange ECDSA keys and upgrade to new authentication protocol. 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 | 18 +++++++++++++ src/conf.h | 1 + src/openssl/ecdsa.c | 16 +++++++++-- src/openssl/ecdsa.h | 1 + src/protocol_auth.c | 65 ++++++++++++++++++++++++++++++++++++++------- 5 files changed, 90 insertions(+), 11 deletions(-) diff --git a/src/conf.c b/src/conf.c index 099b77da..0bbee092 100644 --- a/src/conf.c +++ b/src/conf.c @@ -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; diff --git a/src/conf.h b/src/conf.h index cc57e82b..20fc95e6 100644 --- a/src/conf.h +++ b/src/conf.h @@ -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 *); diff --git a/src/openssl/ecdsa.c b/src/openssl/ecdsa.c index a4f0f30d..43464d88 100644 --- a/src/openssl/ecdsa.c +++ b/src/openssl/ecdsa.c @@ -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) { diff --git a/src/openssl/ecdsa.h b/src/openssl/ecdsa.h index cc0e7b02..04f9eb98 100644 --- a/src/openssl/ecdsa.h +++ b/src/openssl/ecdsa.h @@ -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); diff --git a/src/protocol_auth.c b/src/protocol_auth.c index d38814c6..70062f3f 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -43,8 +43,12 @@ 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; -- 2.39.5