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; }