From 40731d030fef793c6b6405efd9b3e64c26c00045 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Wed, 7 Nov 2007 02:47:05 +0000 Subject: [PATCH] Temporarily revert to old crypto code (The new code is still segfaulting for me, and I'd like to proceed with other work.) This largely rolls back to the revision 1545 state of the existing code (new crypto layer is still there with no callers), though I reintroduced the segfault fix of revision 1562. --- src/connection.c | 11 +- src/connection.h | 27 +++-- src/meta.c | 37 ++++--- src/net_packet.c | 65 ++++++++---- src/net_setup.c | 249 ++++++++++++++++++++++++++++++------------- src/node.c | 11 +- src/node.h | 12 ++- src/protocol_auth.c | 253 ++++++++++++++++++++++++++++++++------------ src/protocol_key.c | 91 ++++++++++------ src/tincd.c | 18 +++- 10 files changed, 535 insertions(+), 239 deletions(-) diff --git a/src/connection.c b/src/connection.c index a369cb83..21cb6aa9 100644 --- a/src/connection.c +++ b/src/connection.c @@ -23,7 +23,6 @@ #include "system.h" #include "splay_tree.h" -#include "cipher.h" #include "conf.h" #include "list.h" #include "logger.h" @@ -74,8 +73,14 @@ void free_connection(connection_t *c) { if(c->hostname) free(c->hostname); - cipher_close(&c->incipher); - cipher_close(&c->outcipher); + if(c->inkey) + free(c->inkey); + + if(c->outkey) + free(c->outkey); + + if(c->mychallenge) + free(c->mychallenge); if(c->hischallenge) free(c->hischallenge); diff --git a/src/connection.h b/src/connection.h index 54caa261..2426a220 100644 --- a/src/connection.h +++ b/src/connection.h @@ -23,11 +23,11 @@ #ifndef __TINC_CONNECTION_H__ #define __TINC_CONNECTION_H__ +#include +#include + #include -#include "cipher.h" -#include "digest.h" -#include "rsa.h" #include "splay_tree.h" #define OPTION_INDIRECT 0x0001 @@ -72,18 +72,23 @@ typedef struct connection_t { struct node_t *node; /* node associated with the other end */ struct edge_t *edge; /* edge associated with this connection */ - rsa_t rsa; /* his public/private key */ - cipher_t incipher; /* Cipher he will use to send data to us */ - cipher_t outcipher; /* Cipher we will use to send data to him */ - digest_t indigest; - digest_t outdigest; - + RSA *rsa_key; /* his public/private key */ + const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */ + const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */ + EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */ + EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */ + char *inkey; /* His symmetric meta key + iv */ + char *outkey; /* Our symmetric meta key + iv */ + int inkeylength; /* Length of his key + iv */ + int outkeylength; /* Length of our key + iv */ + const EVP_MD *indigest; + const EVP_MD *outdigest; int inmaclength; int outmaclength; int incompression; int outcompression; - - char *hischallenge; /* The challenge we sent to him */ + char *mychallenge; /* challenge we received from him */ + char *hischallenge; /* challenge we sent to him */ struct bufferevent *buffer; /* buffer events on this metadata connection */ struct event inevent; /* input event on this metadata connection */ diff --git a/src/meta.c b/src/meta.c index 141a1a1d..524a6ce0 100644 --- a/src/meta.c +++ b/src/meta.c @@ -22,8 +22,10 @@ #include "system.h" +#include +#include + #include "splay_tree.h" -#include "cipher.h" #include "connection.h" #include "logger.h" #include "meta.h" @@ -33,23 +35,31 @@ #include "xalloc.h" bool send_meta(connection_t *c, const char *buffer, int length) { + int outlen; + int result; cp(); - ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length, c->name, c->hostname); + ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length, + c->name, c->hostname); /* Add our data to buffer */ if(c->status.encryptout) { char outbuf[length]; - size_t outlen = length; - if(!cipher_encrypt(&c->outcipher, buffer, length, outbuf, &outlen, false) || outlen != length) { - logger(LOG_ERR, _("Error while encrypting metadata to %s (%s)"), c->name, c->hostname); + result = EVP_EncryptUpdate(c->outctx, (unsigned char *)outbuf, &outlen, (unsigned char *)buffer, length); + if(!result || outlen != length) { + logger(LOG_ERR, _("Error while encrypting metadata to %s (%s): %s"), + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); return false; } + logger(LOG_DEBUG, _("Encrypted write %p %p %p %d"), c, c->buffer, outbuf, length); bufferevent_write(c->buffer, (void *)outbuf, length); + logger(LOG_DEBUG, _("Done.")); } else { + logger(LOG_DEBUG, _("Unencrypted write %p %p %p %d"), c, c->buffer, buffer, length); bufferevent_write(c->buffer, (void *)buffer, length); + logger(LOG_DEBUG, _("Done.")); } return true; @@ -70,7 +80,7 @@ void broadcast_meta(connection_t *from, const char *buffer, int length) { } bool receive_meta(connection_t *c) { - size_t inlen; + int result, inlen, outlen; char inbuf[MAXBUFSIZE]; char *bufp = inbuf, *endp; @@ -105,15 +115,16 @@ bool receive_meta(connection_t *c) { inlen -= endp - bufp; bufp = endp; } else { - size_t outlen = inlen; + logger(LOG_DEBUG, _("Received encrypted %d bytes"), inlen); evbuffer_expand(c->buffer->input, c->buffer->input->off + inlen); - - if(!cipher_decrypt(&c->incipher, bufp, inlen, c->buffer->input->buffer + c->buffer->input->off, &outlen, false) || inlen != outlen) { - logger(LOG_ERR, _("Error while decrypting metadata from %s (%s)"), c->name, c->hostname); + result = EVP_DecryptUpdate(c->inctx, (unsigned char *)c->buffer->input->buffer + c->buffer->input->off, &outlen, (unsigned char *)bufp, inlen); + if(!result || outlen != inlen) { + logger(LOG_ERR, _("Error while decrypting metadata from %s (%s): %s"), + c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); return false; } - c->buffer->input->off += inlen; + inlen = 0; } @@ -135,10 +146,8 @@ bool receive_meta(connection_t *c) { char *request = evbuffer_readline(c->buffer->input); if(request) { - bool result = receive_request(c, request); + receive_request(c, request); free(request); - if(!result) - return false; continue; } else { break; diff --git a/src/net_packet.c b/src/net_packet.c index eee3972e..2a86d657 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -22,15 +22,18 @@ #include "system.h" +#include +#include +#include +#include +#include + #include #include LZO1X_H #include "splay_tree.h" -#include "cipher.h" #include "conf.h" #include "connection.h" -#include "crypto.h" -#include "digest.h" #include "device.h" #include "ethernet.h" #include "graph.h" @@ -49,6 +52,7 @@ #endif int keylifetime = 0; +EVP_CIPHER_CTX packet_ctx; static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS]; static void send_udppacket(node_t *, vpn_packet_t *); @@ -81,7 +85,7 @@ static void send_mtu_probe_handler(int fd, short events, void *data) { len = 64; memset(packet.data, 0, 14); - randomize(packet.data + 14, len - 14); + RAND_pseudo_bytes(packet.data + 14, len - 14); packet.len = len; ifdebug(TRAFFIC) logger(LOG_INFO, _("Sending MTU probe length %d to %s (%s)"), len, n->name, n->hostname); @@ -164,14 +168,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; int nextpkt = 0; vpn_packet_t *outpkt = pkt[0]; - size_t outlen; + int outlen, outpad; + unsigned char hmac[EVP_MAX_MD_SIZE]; int i; cp(); /* Check packet length */ - if(inpkt->len < sizeof(inpkt->seqno) + digest_length(&myself->digest)) { + if(inpkt->len < sizeof(inpkt->seqno) + myself->maclength) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"), n->name, n->hostname); return; @@ -179,23 +184,33 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { /* Check the message authentication code */ - if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname); - return; + if(myself->digest && myself->maclength) { + inpkt->len -= myself->maclength; + HMAC(myself->digest, myself->key, myself->keylength, + (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL); + + if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), + n->name, n->hostname); + return; + } } /* Decrypt the packet */ - if(cipher_active(&myself->cipher)) { + if(myself->cipher) { outpkt = pkt[nextpkt++]; - outlen = MAXSIZE; - if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname); + if(!EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL) + || !EVP_DecryptUpdate(&packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, + (unsigned char *) &inpkt->seqno, inpkt->len) + || !EVP_DecryptFinal_ex(&packet_ctx, (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; } - outpkt->len = outlen; + outpkt->len = outlen + outpad; inpkt = outpkt; } @@ -268,7 +283,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { int nextpkt = 0; vpn_packet_t *outpkt; int origlen; - size_t outlen; + int outlen, outpad; vpn_packet_t *copy; static int priority = 0; int origpriority; @@ -324,24 +339,28 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { /* Encrypt the packet */ - if(cipher_active(&n->cipher)) { + if(n->cipher) { outpkt = pkt[nextpkt++]; - outlen = MAXSIZE; - if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { - ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname); + if(!EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL) + || !EVP_EncryptUpdate(&n->packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, + (unsigned char *) &inpkt->seqno, inpkt->len) + || !EVP_EncryptFinal_ex(&n->packet_ctx, (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; } - outpkt->len = outlen; + outpkt->len = outlen + outpad; inpkt = outpkt; } /* Add the message authentication code */ - if(digest_active(&n->digest)) { - digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len); - inpkt->len += digest_length(&n->digest); + if(n->digest && n->maclength) { + HMAC(n->digest, n->key, n->keylength, (unsigned char *) &inpkt->seqno, + inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL); + inpkt->len += n->maclength; } /* Determine which socket we have to use */ diff --git a/src/net_setup.c b/src/net_setup.c index 033bf376..b8fb4f02 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -22,13 +22,17 @@ #include "system.h" +#include +#include +#include +#include +#include + #include "splay_tree.h" -#include "cipher.h" #include "conf.h" #include "connection.h" #include "control.h" #include "device.h" -#include "digest.h" #include "graph.h" #include "logger.h" #include "net.h" @@ -36,7 +40,6 @@ #include "process.h" #include "protocol.h" #include "route.h" -#include "rsa.h" #include "subnet.h" #include "utils.h" #include "xalloc.h" @@ -47,66 +50,125 @@ static struct event device_ev; bool read_rsa_public_key(connection_t *c) { FILE *fp; char *fname; - char *n; - bool result; + char *key; cp(); + if(!c->rsa_key) { + c->rsa_key = RSA_new(); +// RSA_blinding_on(c->rsa_key, NULL); + } + /* First, check for simple PublicKey statement */ - if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) { - result = rsa_set_hex_public_key(&c->rsa, n, "FFFF"); - free(n); - return result; + if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) { + BN_hex2bn(&c->rsa_key->n, key); + BN_hex2bn(&c->rsa_key->e, "FFFF"); + free(key); + return true; } /* Else, check for PublicKeyFile statement and read it */ - if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) - asprintf(&fname, "%s/hosts/%s", confbase, c->name); + if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) { + fp = fopen(fname, "r"); - fp = fopen(fname, "r"); + if(!fp) { + logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), + fname, strerror(errno)); + free(fname); + return false; + } - if(!fp) { - logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), - fname, strerror(errno)); free(fname); + c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); + fclose(fp); + + if(c->rsa_key) + return true; /* Woohoo. */ + + /* If it fails, try PEM_read_RSA_PUBKEY. */ + fp = fopen(fname, "r"); + + if(!fp) { + logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), + fname, strerror(errno)); + free(fname); + return false; + } + + free(fname); + c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); + fclose(fp); + + if(c->rsa_key) { +// RSA_blinding_on(c->rsa_key, NULL); + return true; + } + + logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), + fname, strerror(errno)); return false; } - result = rsa_read_pem_public_key(&c->rsa, fp); - fclose(fp); + /* Else, check if a harnessed public key is in the config file */ + + asprintf(&fname, "%s/hosts/%s", confbase, c->name); + fp = fopen(fname, "r"); + + if(fp) { + c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); + fclose(fp); + } - if(!result) - logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), fname, strerror(errno)); free(fname); - return result; + + if(c->rsa_key) + return true; + + /* Try again with PEM_read_RSA_PUBKEY. */ + + asprintf(&fname, "%s/hosts/%s", confbase, c->name); + fp = fopen(fname, "r"); + + if(fp) { + c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); +// RSA_blinding_on(c->rsa_key, NULL); + fclose(fp); + } + + free(fname); + + if(c->rsa_key) + return true; + + logger(LOG_ERR, _("No public key for %s specified!"), c->name); + + return false; } -bool read_rsa_private_key() { +bool read_rsa_private_key(void) { FILE *fp; - char *fname; - char *n, *d; - bool result; + char *fname, *key, *pubkey; + struct stat s; cp(); - /* First, check for simple PrivateKey statement */ - - if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) { - if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) { + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { + if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &pubkey)) { logger(LOG_ERR, _("PrivateKey used but no PublicKey found!")); - free(d); return false; } - result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d); - free(n); - free(d); + myself->connection->rsa_key = RSA_new(); +// RSA_blinding_on(myself->connection->rsa_key, NULL); + BN_hex2bn(&myself->connection->rsa_key->d, key); + BN_hex2bn(&myself->connection->rsa_key->n, pubkey); + BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); + free(key); + free(pubkey); return true; } - /* Else, check for PrivateKeyFile statement and read it */ - if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname)) asprintf(&fname, "%s/rsa_key.priv", confbase); @@ -120,10 +182,9 @@ bool read_rsa_private_key() { } #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) - struct stat s; - if(fstat(fileno(fp), &s)) { - logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), fname, strerror(errno)); + logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), + fname, strerror(errno)); free(fname); return false; } @@ -132,13 +193,18 @@ bool read_rsa_private_key() { logger(LOG_WARNING, _("Warning: insecure file permissions for RSA private key file `%s'!"), fname); #endif - result = rsa_read_pem_private_key(&myself->connection->rsa, fp); + myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); fclose(fp); - if(!result) - logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), fname, strerror(errno)); + if(!myself->connection->rsa_key) { + logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), + fname, strerror(errno)); + free(fname); + return false; + } + free(fname); - return result; + return true; } static struct event keyexpire_event; @@ -148,14 +214,10 @@ static void keyexpire_handler(int fd, short events, void *data) { } void regenerate_key() { - ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); - - if(!cipher_regenerate_key(&myself->cipher, true)) { - logger(LOG_ERR, _("Error regenerating key!")); - abort(); - } + RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); if(timeout_initialized(&keyexpire_event)) { + ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); event_del(&keyexpire_event); send_key_changed(broadcast, myself); } else { @@ -163,6 +225,16 @@ void regenerate_key() { } event_add(&keyexpire_event, &(struct timeval){keylifetime, 0}); + + 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)); + abort(); + } + + } } /* @@ -301,44 +373,73 @@ bool setup_myself(void) { /* Generate packet encryption key */ - if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) - cipher = xstrdup("blowfish"); + if(get_config_string + (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) { + if(!strcasecmp(cipher, "none")) { + myself->cipher = NULL; + } else { + myself->cipher = EVP_get_cipherbyname(cipher); - if(!cipher_open_by_name(&myself->cipher, cipher)) { - logger(LOG_ERR, _("Unrecognized cipher type!")); - return false; - } + if(!myself->cipher) { + logger(LOG_ERR, _("Unrecognized cipher type!")); + return false; + } + } + } else + myself->cipher = EVP_bf_cbc(); + + if(myself->cipher) + myself->keylength = myself->cipher->key_len + myself->cipher->iv_len; + else + myself->keylength = 1; + + myself->connection->outcipher = EVP_bf_ofb(); + + myself->key = xmalloc(myself->keylength); if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; regenerate_key(); - /* Check if we want to use message authentication codes... */ - if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) - digest = xstrdup("sha1"); + if(get_config_string + (lookup_config(myself->connection->config_tree, "Digest"), &digest)) { + if(!strcasecmp(digest, "none")) { + myself->digest = NULL; + } else { + myself->digest = EVP_get_digestbyname(digest); - if(!digest_open_by_name(&myself->digest, digest)) { - logger(LOG_ERR, _("Unrecognized digest type!")); - return false; - } - - if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength)) - - if(digest_active(&myself->digest)) { - if(myself->maclength > digest_length(&myself->digest)) { - logger(LOG_ERR, _("MAC length exceeds size of digest!")); - return false; - } else if(myself->maclength < 0) { - logger(LOG_ERR, _("Bogus MAC length!")); - return false; + if(!myself->digest) { + logger(LOG_ERR, _("Unrecognized digest type!")); + return false; + } } - } + } else + myself->digest = 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) { + logger(LOG_ERR, _("MAC length exceeds size of digest!")); + return false; + } else if(myself->maclength < 0) { + logger(LOG_ERR, _("Bogus MAC length!")); + return false; + } + } + } else + myself->maclength = 4; + + myself->connection->outmaclength = 0; /* Compression */ - if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression)) { + if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), + &myself->compression)) { if(myself->compression < 0 || myself->compression > 11) { logger(LOG_ERR, _("Bogus compression level!")); return false; @@ -362,8 +463,8 @@ bool setup_myself(void) { if(!setup_device()) return false; - event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL); - + event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, + handle_device_data, NULL); if (event_add(&device_ev, NULL) < 0) { logger(LOG_ERR, _("event_add failed: %s"), strerror(errno)); close_device(); diff --git a/src/node.c b/src/node.c index 280a70cb..cbafe712 100644 --- a/src/node.c +++ b/src/node.c @@ -74,6 +74,7 @@ node_t *new_node(void) { n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); n->queue = list_alloc((list_action_t) free); + EVP_CIPHER_CTX_init(&n->packet_ctx); n->mtu = MTU; n->maxmtu = MTU; @@ -86,6 +87,9 @@ void free_node(node_t *n) { if(n->queue) list_delete_list(n->queue); + if(n->key) + free(n->key); + if(n->subnet_tree) free_subnet_tree(n->subnet_tree); @@ -94,8 +98,7 @@ void free_node(node_t *n) { sockaddrfree(&n->address); - cipher_close(&n->cipher); - digest_close(&n->digest); + EVP_CIPHER_CTX_cleanup(&n->packet_ctx); event_del(&n->mtuevent); @@ -168,8 +171,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, cipher_get_nid(&n->cipher), - digest_get_nid(&n->digest), n->maclength, n->compression, + n->name, n->hostname, n->cipher ? n->cipher->nid : 0, + n->digest ? n->digest->type : 0, n->maclength, n->compression, 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 4186d061..83af5e3c 100644 --- a/src/node.h +++ b/src/node.h @@ -24,9 +24,7 @@ #define __TINC_NODE_H__ #include "splay_tree.h" -#include "cipher.h" #include "connection.h" -#include "digest.h" #include "list.h" #include "subnet.h" @@ -52,9 +50,13 @@ typedef struct node_t { node_status_t status; - cipher_t cipher; /* Cipher for UDP packets */ - digest_t digest; /* Digest for UDP packets */ - int maclength; /* Portion of digest to use */ + 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_MD *digest; /* Digest type for MAC */ + int maclength; /* Length of MAC */ int compression; /* Compressionlevel, 0 = no compression */ diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 0471932a..49dfd281 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -22,10 +22,14 @@ #include "system.h" +#include +#include +#include +#include + #include "splay_tree.h" #include "conf.h" #include "connection.h" -#include "crypto.h" #include "edge.h" #include "graph.h" #include "logger.h" @@ -33,7 +37,6 @@ #include "netutl.h" #include "node.h" #include "protocol.h" -#include "rsa.h" #include "utils.h" #include "xalloc.h" @@ -114,22 +117,27 @@ bool id_h(connection_t *c, char *request) { } bool send_metakey(connection_t *c) { - size_t len = rsa_size(&c->rsa); - char key[len]; - char enckey[len]; - char hexkey[2 * len + 1]; + char *buffer; + int len; + bool x; cp(); - if(!cipher_open_blowfish_ofb(&c->outcipher)) - return false; + len = RSA_size(c->rsa_key); + + /* Allocate buffers for the meta key */ + + buffer = alloca(2 * len + 1); - if(!digest_open_sha1(&c->outdigest)) - return false; + if(!c->outkey) + c->outkey = xmalloc(len); - /* Create a random key */ + if(!c->outctx) + c->outctx = xmalloc_and_zero(sizeof(*c->outctx)); + cp(); + /* Copy random data to the buffer */ - randomize(key, len); + RAND_pseudo_bytes((unsigned char *)c->outkey, len); /* The message we send must be smaller than the modulus of the RSA key. By definition, for a key of k bits, the following formula holds: @@ -141,14 +149,13 @@ bool send_metakey(connection_t *c) { This can be done by setting the most significant bit to zero. */ - key[0] &= 0x7F; - - cipher_set_key_from_rsa(&c->outcipher, key, len, true); + c->outkey[0] &= 0x7F; ifdebug(SCARY_THINGS) { - bin2hex(key, hexkey, len); - hexkey[len * 2] = '\0'; - logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), hexkey); + bin2hex(c->outkey, buffer, len); + buffer[len * 2] = '\0'; + logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), + buffer); } /* Encrypt the random data @@ -158,78 +165,135 @@ bool send_metakey(connection_t *c) { with a length equal to that of the modulus of the RSA key. */ - if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) { - logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname); + if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) { + logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), + c->name, c->hostname); return false; } /* Convert the encrypted random data to a hexadecimal formatted string */ - bin2hex(enckey, hexkey, len); - hexkey[len * 2] = '\0'; + bin2hex(buffer, buffer, len); + buffer[len * 2] = '\0'; /* Send the meta key */ - bool result = send_request(c, "%d %d %d %d %d %s", METAKEY, - cipher_get_nid(&c->outcipher), - digest_get_nid(&c->outdigest), c->outmaclength, - c->outcompression, hexkey); - - c->status.encryptout = true; - return result; + 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->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)) { + 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; + } + + c->status.encryptout = true; + } + + return x; } bool metakey_h(connection_t *c, char *request) { - char hexkey[MAX_STRING_SIZE]; + char buffer[MAX_STRING_SIZE]; int cipher, digest, maclength, compression; - size_t len = rsa_size(&myself->connection->rsa); - char enckey[len]; - char key[len]; + int len; cp(); - if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname); + if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) { + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, + c->hostname); return false; } + len = RSA_size(myself->connection->rsa_key); + /* Check if the length of the meta key is all right */ - if(strlen(hexkey) != len * 2) { + if(strlen(buffer) != len * 2) { logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength"); return false; } + /* Allocate buffers for the meta key */ + + if(!c->inkey) + c->inkey = xmalloc(len); + + if(!c->inctx) + c->inctx = xmalloc_and_zero(sizeof(*c->inctx)); + /* Convert the challenge from hexadecimal back to binary */ - hex2bin(hexkey, enckey, len); + hex2bin(buffer, buffer, len); /* Decrypt the meta key */ - if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) { - logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname); + if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) { /* See challenge() */ + logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), + c->name, c->hostname); return false; } ifdebug(SCARY_THINGS) { - bin2hex(key, hexkey, len); - hexkey[len * 2] = '\0'; - logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), hexkey); + bin2hex(c->inkey, buffer, len); + buffer[len * 2] = '\0'; + logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer); } + /* All incoming requests will now be encrypted. */ + /* Check and lookup cipher and digest algorithms */ - if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) { - logger(LOG_ERR, _("Error during initialisation of cipher from %s (%s)"), c->name, c->hostname); - return false; + if(cipher) { + c->incipher = EVP_get_cipherbynid(cipher); + + if(!c->incipher) { + logger(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname); + return false; + } + + 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)) { + 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; + } + + c->status.decryptin = true; + } else { + c->incipher = NULL; } - if(!digest_open_by_nid(&c->indigest, digest)) { - logger(LOG_ERR, _("Error during initialisation of digest from %s (%s)"), c->name, c->hostname); - return false; + c->inmaclength = maclength; + + if(digest) { + c->indigest = EVP_get_digestbynid(digest); + + if(!c->indigest) { + logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname); + return false; + } + + if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) { + logger(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname); + return false; + } + } else { + c->indigest = NULL; } - c->status.decryptin = true; + c->incompression = compression; c->allow_request = CHALLENGE; @@ -237,17 +301,25 @@ bool metakey_h(connection_t *c, char *request) { } bool send_challenge(connection_t *c) { - size_t len = rsa_size(&c->rsa); - char buffer[len * 2 + 1]; + char *buffer; + int len; cp(); + /* CHECKME: what is most reasonable value for len? */ + + len = RSA_size(c->rsa_key); + + /* Allocate buffers for the challenge */ + + buffer = alloca(2 * len + 1); + if(!c->hischallenge) c->hischallenge = xmalloc(len); /* Copy random data to the buffer */ - randomize(c->hischallenge, len); + RAND_pseudo_bytes((unsigned char *)c->hischallenge, len); /* Convert to hex */ @@ -261,48 +333,72 @@ bool send_challenge(connection_t *c) { bool challenge_h(connection_t *c, char *request) { char buffer[MAX_STRING_SIZE]; - size_t len = rsa_size(&myself->connection->rsa); - size_t digestlen = digest_length(&c->outdigest); - char digest[digestlen]; + int len; cp(); if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname); + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, + c->hostname); return false; } + len = RSA_size(myself->connection->rsa_key); + /* Check if the length of the challenge is all right */ if(strlen(buffer) != len * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length"); + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, + c->hostname, "wrong challenge length"); return false; } + /* Allocate buffers for the challenge */ + + if(!c->mychallenge) + c->mychallenge = xmalloc(len); + /* Convert the challenge from hexadecimal back to binary */ - hex2bin(buffer, buffer, len); + hex2bin(buffer, c->mychallenge, len); c->allow_request = CHAL_REPLY; + /* Rest is done by send_chal_reply() */ + + return send_chal_reply(c); +} + +bool send_chal_reply(connection_t *c) { + char hash[EVP_MAX_MD_SIZE * 2 + 1]; + EVP_MD_CTX ctx; + cp(); /* Calculate the hash from the challenge we received */ - digest_create(&c->indigest, buffer, len, digest); + if(!EVP_DigestInit(&ctx, c->indigest) + || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) + || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) { + 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; + } /* Convert the hash to a hexadecimal formatted string */ - bin2hex(digest, buffer, digestlen); - buffer[digestlen * 2] = '\0'; + bin2hex(hash, hash, c->indigest->md_size); + hash[c->indigest->md_size * 2] = '\0'; /* Send the reply */ - return send_request(c, "%d %s", CHAL_REPLY, buffer); + return send_request(c, "%d %s", CHAL_REPLY, hash); } bool chal_reply_h(connection_t *c, char *request) { char hishash[MAX_STRING_SIZE]; + char myhash[EVP_MAX_MD_SIZE]; + EVP_MD_CTX ctx; cp(); @@ -314,19 +410,38 @@ bool chal_reply_h(connection_t *c, char *request) { /* Check if the length of the hash is all right */ - if(strlen(hishash) != digest_length(&c->outdigest) * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length")); + if(strlen(hishash) != c->outdigest->md_size * 2) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, + c->hostname, _("wrong challenge reply length")); return false; } /* Convert the hash to binary format */ - hex2bin(hishash, hishash, digest_length(&c->outdigest)); + hex2bin(hishash, hishash, c->outdigest->md_size); - /* Verify the hash */ + /* 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)) { + 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; + } + + /* Verify the incoming hash with the calculated hash */ + + if(memcmp(hishash, myhash, c->outdigest->md_size)) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, + c->hostname, _("wrong challenge reply")); + + ifdebug(SCARY_THINGS) { + bin2hex(myhash, hishash, SHA_DIGEST_LENGTH); + hishash[SHA_DIGEST_LENGTH * 2] = '\0'; + logger(LOG_DEBUG, _("Expected challenge reply: %s"), hishash); + } - if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply")); return false; } @@ -334,8 +449,6 @@ bool chal_reply_h(connection_t *c, char *request) { Send an acknowledgement with the rest of the information needed. */ - free(c->hischallenge); - c->hischallenge = NULL; c->allow_request = ACK; return send_ack(c); diff --git a/src/protocol_key.c b/src/protocol_key.c index 66ffb115..05bc97ba 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -22,8 +22,10 @@ #include "system.h" +#include +#include + #include "splay_tree.h" -#include "cipher.h" #include "connection.h" #include "logger.h" #include "net.h" @@ -33,7 +35,7 @@ #include "utils.h" #include "xalloc.h" -static bool mykeyused = false; +bool mykeyused = false; bool send_key_changed(connection_t *c, const node_t *n) { cp(); @@ -135,19 +137,18 @@ bool req_key_h(connection_t *c, char *request) { } bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) { - size_t keylen = cipher_keylength(&from->cipher); - char key[keylen]; + char *key; cp(); - cipher_get_key(&from->cipher, key); - bin2hex(key, key, keylen); - key[keylen * 2] = '\0'; + key = alloca(2 * from->keylength + 1); + 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, - cipher_get_nid(&from->cipher), - digest_get_nid(&from->digest), from->maclength, + from->cipher ? from->cipher->nid : 0, + from->digest ? from->digest->type : 0, from->maclength, from->compression); } @@ -193,28 +194,58 @@ bool ans_key_h(connection_t *c, char *request) { return send_request(to->nexthop->connection, "%s", request); } + /* Update our copy of the origin's packet key */ + + 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->status.validkey = true; + from->status.waitingforkey = false; + from->sent_seqno = 0; + /* Check and lookup cipher and digest algorithms */ - if(!cipher_open_by_nid(&from->cipher, cipher)) { - logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname); - return false; - } + if(cipher) { + from->cipher = EVP_get_cipherbynid(cipher); - if(strlen(key) / 2 != cipher_keylength(&from->cipher)) { - logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname); - return false; + if(!from->cipher) { + 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) { + logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, + from->hostname); + return false; + } + } else { + from->cipher = NULL; } from->maclength = maclength; - if(!digest_open_by_nid(&from->digest, digest)) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname); - return false; - } + if(digest) { + from->digest = EVP_get_digestbynid(digest); - if(from->maclength > digest_length(&from->digest) || from->maclength < 0) { - logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname); - return false; + if(!from->digest) { + 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) { + logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), + from->name, from->hostname); + return false; + } + } else { + from->digest = NULL; } if(compression < 0 || compression > 11) { @@ -224,14 +255,12 @@ bool ans_key_h(connection_t *c, char *request) { from->compression = compression; - /* Update our copy of the origin's packet key */ - - hex2bin(key, key, cipher_keylength(&from->cipher)); - cipher_set_key(&from->cipher, key, false); - - from->status.validkey = true; - from->status.waitingforkey = false; - from->sent_seqno = 0; + 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)) { + 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; + } if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes) send_mtu_probe(from); diff --git a/src/tincd.c b/src/tincd.c index cb86cd8b..c0be975e 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -31,13 +31,18 @@ #include #endif +#include +#include +#include +#include +#include + #include LZO1X_H #include #include "conf.h" #include "control.h" -#include "crypto.h" #include "device.h" #include "logger.h" #include "net.h" @@ -290,7 +295,12 @@ int main(int argc, char **argv) /* Slllluuuuuuurrrrp! */ srand(time(NULL)); - crypto_init(); + RAND_load_file("/dev/urandom", 1024); + + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + + OpenSSL_add_all_algorithms(); if(!read_server_config()) return 1; @@ -343,7 +353,7 @@ end: exit_control(); #endif - crypto_exit(); - + EVP_cleanup(); + return status; }