From 40b21d3ff4b50f7688e49651d1237987ce45128d Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Wed, 15 Jun 2016 11:12:09 +0200 Subject: [PATCH] Ensure compatibility with OpenSSL 1.1.0. --- m4/openssl.m4 | 4 ++- src/net_packet.c | 16 +++++----- src/net_setup.c | 48 +++++++++++++++++++++++------ src/node.c | 16 +++++----- src/node.h | 4 +-- src/protocol_auth.c | 74 ++++++++++++++++++++++++++++----------------- src/protocol_key.c | 12 ++++---- src/tincd.c | 37 +++++++++++++++++++++-- 8 files changed, 149 insertions(+), 62 deletions(-) diff --git a/m4/openssl.m4 b/m4/openssl.m4 index 49257fc7..bb1f1465 100644 --- a/m4/openssl.m4 +++ b/m4/openssl.m4 @@ -45,7 +45,7 @@ AC_DEFUN([tinc_OPENSSL], [AC_MSG_ERROR([LibreSSL/OpenSSL libraries not found.])] ) - AC_CHECK_FUNCS([RAND_pseudo_bytes EVP_EncryptInit_ex], , + AC_CHECK_FUNCS([RAND_bytes EVP_EncryptInit_ex EVP_CIPHER_CTX_new], , [AC_MSG_ERROR([Missing LibreSSL/OpenSSL functionality, make sure you have installed the latest version.]); break], ) @@ -53,4 +53,6 @@ AC_DEFUN([tinc_OPENSSL], [AC_MSG_ERROR([Missing LibreSSL/OpenSSL functionality, make sure you have installed the latest version.]); break], [#include ] ) + + AC_CHECK_FUNCS([BN_GENCB_new ERR_remove_state RSA_set0_key], , , [#include ]) ]) diff --git a/src/net_packet.c b/src/net_packet.c index 5f6d9d50..e0781760 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -1,7 +1,7 @@ /* net_packet.c -- Handles in- and outgoing VPN packets Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2015 Guus Sliepen + 2000-2016 Guus Sliepen 2010 Timothy Redaelli 2010 Brandon Black @@ -145,7 +145,7 @@ void send_mtu_probe(node_t *n) { len = 64; memset(packet.data, 0, 14); - RAND_pseudo_bytes(packet.data + 14, len - 14); + RAND_bytes(packet.data + 14, len - 14); packet.len = len; if(i >= 4 && n->mtuprobes <= 10) packet.priority = -1; @@ -314,10 +314,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if(n->incipher) { outpkt = pkt[nextpkt++]; - if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL) - || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen, + if(!EVP_DecryptInit_ex(n->inctx, NULL, NULL, NULL, NULL) + || !EVP_DecryptUpdate(n->inctx, (unsigned char *) &outpkt->seqno, &outlen, (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { + || !EVP_DecryptFinal_ex(n->inctx, (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; @@ -479,10 +479,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { if(n->outcipher) { outpkt = pkt[nextpkt++]; - if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL) - || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen, + if(!EVP_EncryptInit_ex(n->outctx, NULL, NULL, NULL, NULL) + || !EVP_EncryptUpdate(n->outctx, (unsigned char *) &outpkt->seqno, &outlen, (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { + || !EVP_EncryptFinal_ex(n->outctx, (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; diff --git a/src/net_setup.c b/src/net_setup.c index 50d5680a..6c50f9d8 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -1,7 +1,7 @@ /* net_setup.c -- Setup. Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2015 Guus Sliepen + 2000-2016 Guus Sliepen 2006 Scott Lamb 2010 Brandon Black @@ -48,11 +48,22 @@ char *myport; devops_t devops; +#ifndef HAVE_RSA_SET0_KEY +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { + BN_free(r->n); r->n = n; + BN_free(r->e); r->e = e; + BN_free(r->d); r->d = d; + return 1; +} +#endif + bool read_rsa_public_key(connection_t *c) { FILE *fp; char *pubname; char *hcfname; char *key; + BIGNUM *n = NULL; + BIGNUM *e = NULL; if(!c->rsa_key) { c->rsa_key = RSA_new(); @@ -62,12 +73,19 @@ bool read_rsa_public_key(connection_t *c) { /* First, check for simple PublicKey statement */ if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) { - if(BN_hex2bn(&c->rsa_key->n, key) != strlen(key)) { + if(BN_hex2bn(&n, key) != strlen(key)) { + free(key); logger(LOG_ERR, "Invalid PublicKey for %s!", c->name); return false; } - BN_hex2bn(&c->rsa_key->e, "FFFF"); free(key); + BN_hex2bn(&e, "FFFF"); + if(!n || !e || RSA_set0_key(c->rsa_key, n, e, NULL) != 1) { + BN_free(e); + BN_free(n); + logger(LOG_ERR, "RSA_set0_key() failed with PublicKey for %s!", c->name); + return false; + } return true; } @@ -158,27 +176,39 @@ bool read_rsa_public_key(connection_t *c) { static bool read_rsa_private_key(void) { FILE *fp; char *fname, *key, *pubkey; + BIGNUM *n = NULL; + BIGNUM *e = NULL; + BIGNUM *d = NULL; if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { myself->connection->rsa_key = RSA_new(); // RSA_blinding_on(myself->connection->rsa_key, NULL); - if(BN_hex2bn(&myself->connection->rsa_key->d, key) != strlen(key)) { + if(BN_hex2bn(&d, key) != strlen(key)) { logger(LOG_ERR, "Invalid PrivateKey for myself!"); free(key); return false; } free(key); if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) { + BN_free(d); logger(LOG_ERR, "PrivateKey used but no PublicKey found!"); return false; } - if(BN_hex2bn(&myself->connection->rsa_key->n, pubkey) != strlen(pubkey)) { - logger(LOG_ERR, "Invalid PublicKey for myself!"); + if(BN_hex2bn(&n, pubkey) != strlen(pubkey)) { free(pubkey); + BN_free(d); + logger(LOG_ERR, "Invalid PublicKey for myself!"); return false; } free(pubkey); - BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); + BN_hex2bn(&e, "FFFF"); + if(!n || !e || !d || RSA_set0_key(myself->connection->rsa_key, n, e, d) != 1) { + BN_free(d); + BN_free(e); + BN_free(n); + logger(LOG_ERR, "RSA_set0_key() failed with PrivateKey for myself!"); + return false; + } return true; } @@ -623,7 +653,7 @@ static bool setup_myself(void) { myself->incipher = EVP_bf_cbc(); if(myself->incipher) - myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len; + myself->inkeylength = EVP_CIPHER_key_length(myself->incipher) + EVP_CIPHER_iv_length(myself->incipher); else myself->inkeylength = 1; @@ -657,7 +687,7 @@ static bool setup_myself(void) { if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) { if(myself->indigest) { - if(myself->inmaclength > myself->indigest->md_size) { + if(myself->inmaclength > EVP_MD_size(myself->indigest)) { logger(LOG_ERR, "MAC length exceeds size of digest!"); return false; } else if(myself->inmaclength < 0) { diff --git a/src/node.c b/src/node.c index cf70f838..19f37306 100644 --- a/src/node.c +++ b/src/node.c @@ -1,6 +1,6 @@ /* node.c -- node tree management - Copyright (C) 2001-2011 Guus Sliepen , + Copyright (C) 2001-2016 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@ -57,8 +57,10 @@ node_t *new_node(void) { if(replaywin) n->late = xmalloc_and_zero(replaywin); n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); - EVP_CIPHER_CTX_init(&n->inctx); - EVP_CIPHER_CTX_init(&n->outctx); + n->inctx = EVP_CIPHER_CTX_new(); + n->outctx = EVP_CIPHER_CTX_new(); + if(!n->inctx || !n->outctx) + abort(); n->mtu = MTU; n->maxmtu = MTU; @@ -80,8 +82,8 @@ void free_node(node_t *n) { sockaddrfree(&n->address); - EVP_CIPHER_CTX_cleanup(&n->inctx); - EVP_CIPHER_CTX_cleanup(&n->outctx); + EVP_CIPHER_CTX_free(n->outctx); + EVP_CIPHER_CTX_free(n->inctx); if(n->mtuevent) event_del(n->mtuevent); @@ -172,8 +174,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 %x status %04x nexthop %s via %s pmtu %d (min %d max %d)", - n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, - n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, + n->name, n->hostname, n->outcipher ? EVP_CIPHER_nid(n->outcipher) : 0, + n->outdigest ? EVP_MD_type(n->outdigest) : 0, n->outmaclength, n->outcompression, n->options, bitfield_to_int(&n->status, sizeof 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 f9ef3c15..c83610e8 100644 --- a/src/node.h +++ b/src/node.h @@ -50,12 +50,12 @@ typedef struct node_t { const EVP_CIPHER *incipher; /* Cipher type for UDP packets received from him */ char *inkey; /* Cipher key and iv */ int inkeylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX inctx; /* Cipher context */ + EVP_CIPHER_CTX *inctx; /* Cipher context */ const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/ char *outkey; /* Cipher key and iv */ int outkeylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX outctx; /* Cipher context */ + EVP_CIPHER_CTX *outctx; /* Cipher context */ const EVP_MD *indigest; /* Digest type for MAC of packets received from him */ int inmaclength; /* Length of MAC */ diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 091629a1..13dae1d3 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -1,7 +1,7 @@ /* protocol_auth.c -- handle the meta-protocol, authentication Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2015 Guus Sliepen + 2000-2016 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -125,8 +125,11 @@ bool send_metakey(connection_t *c) { c->outkey = xrealloc(c->outkey, len); - if(!c->outctx) - c->outctx = xmalloc_and_zero(sizeof(*c->outctx)); + if(!c->outctx) { + c->outctx = EVP_CIPHER_CTX_new(); + if(!c->outctx) + abort(); + } /* Copy random data to the buffer */ @@ -177,17 +180,17 @@ bool send_metakey(connection_t *c) { /* Send the meta key */ 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->outcipher ? EVP_CIPHER_nid(c->outcipher) : 0, + c->outdigest ? EVP_MD_type(c->outdigest) : 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)) { + (unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher), + (unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher) - + EVP_CIPHER_iv_length(c->outcipher))) { 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; @@ -223,8 +226,11 @@ bool metakey_h(connection_t *c) { c->inkey = xrealloc(c->inkey, len); - if(!c->inctx) - c->inctx = xmalloc_and_zero(sizeof(*c->inctx)); + if(!c->inctx) { + c->inctx = EVP_CIPHER_CTX_new(); + if(!c->inctx) + abort(); + } /* Convert the challenge from hexadecimal back to binary */ @@ -260,9 +266,9 @@ bool metakey_h(connection_t *c) { } 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)) { + (unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher), + (unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher) - + EVP_CIPHER_iv_length(c->incipher))) { 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; @@ -283,7 +289,7 @@ bool metakey_h(connection_t *c) { return false; } - if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) { + if(c->inmaclength > EVP_MD_size(c->indigest) || c->inmaclength < 0) { logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname); return false; } @@ -367,22 +373,29 @@ bool challenge_h(connection_t *c) { bool send_chal_reply(connection_t *c) { char hash[EVP_MAX_MD_SIZE * 2 + 1]; - EVP_MD_CTX ctx; + EVP_MD_CTX *ctx; /* Calculate the hash from the challenge we received */ - if(!EVP_DigestInit(&ctx, c->indigest) - || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) - || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) { + ctx = EVP_MD_CTX_create(); + if(!ctx) + abort(); + + if(!EVP_DigestInit(ctx, c->indigest) + || !EVP_DigestUpdate(ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) + || !EVP_DigestFinal(ctx, (unsigned char *)hash, NULL)) { + EVP_MD_CTX_destroy(ctx); 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; } + EVP_MD_CTX_destroy(ctx); + /* Convert the hash to a hexadecimal formatted string */ - bin2hex(hash, hash, c->indigest->md_size); - hash[c->indigest->md_size * 2] = '\0'; + bin2hex(hash, hash, EVP_MD_size(c->indigest)); + hash[EVP_MD_size(c->indigest) * 2] = '\0'; /* Send the reply */ @@ -392,7 +405,7 @@ bool send_chal_reply(connection_t *c) { bool chal_reply_h(connection_t *c) { char hishash[MAX_STRING_SIZE]; char myhash[EVP_MAX_MD_SIZE]; - EVP_MD_CTX ctx; + EVP_MD_CTX *ctx; if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) { logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name, @@ -402,7 +415,7 @@ bool chal_reply_h(connection_t *c) { /* Check if the length of the hash is all right */ - if(strlen(hishash) != c->outdigest->md_size * 2) { + if(strlen(hishash) != EVP_MD_size(c->outdigest) * 2) { logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length"); return false; @@ -410,24 +423,31 @@ bool chal_reply_h(connection_t *c) { /* Convert the hash to binary format */ - if(!hex2bin(hishash, hishash, c->outdigest->md_size)) { + if(!hex2bin(hishash, hishash, EVP_MD_size(c->outdigest))) { logger(LOG_ERR, "Got bad %s from %s(%s): %s", "CHAL_REPLY", c->name, c->hostname, "invalid hash"); return false; } /* 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)) { + ctx = EVP_MD_CTX_create(); + if(!ctx) + abort(); + + if(!EVP_DigestInit(ctx, c->outdigest) + || !EVP_DigestUpdate(ctx, c->hischallenge, RSA_size(c->rsa_key)) + || !EVP_DigestFinal(ctx, (unsigned char *)myhash, NULL)) { + EVP_MD_CTX_destroy(ctx); 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; } + EVP_MD_CTX_destroy(ctx); + /* Verify the incoming hash with the calculated hash */ - if(memcmp(hishash, myhash, c->outdigest->md_size)) { + if(memcmp(hishash, myhash, EVP_MD_size(c->outdigest))) { logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply"); diff --git a/src/protocol_key.c b/src/protocol_key.c index 4f1e1b76..5f71d1ca 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -164,7 +164,7 @@ bool send_ans_key(node_t *to) { } if(to->incipher) - EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len); + EVP_DecryptInit_ex(to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + EVP_CIPHER_key_length(to->incipher)); // Reset sequence number and late packet window mykeyused = true; @@ -178,8 +178,8 @@ bool send_ans_key(node_t *to) { return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY, myself->name, to->name, key, - to->incipher ? to->incipher->nid : 0, - to->indigest ? to->indigest->type : 0, to->inmaclength, + to->incipher ? EVP_CIPHER_nid(to->incipher) : 0, + to->indigest ? EVP_MD_type(to->indigest) : 0, to->inmaclength, to->incompression); } @@ -268,7 +268,7 @@ bool ans_key_h(connection_t *c) { return true; } - if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) { + if(from->outkeylength != EVP_CIPHER_key_length(from->outcipher) + EVP_CIPHER_iv_length(from->outcipher)) { logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname); return true; @@ -288,7 +288,7 @@ bool ans_key_h(connection_t *c) { return true; } - if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) { + if(from->outmaclength > EVP_MD_size(from->outdigest) || from->outmaclength < 0) { logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname); return true; @@ -305,7 +305,7 @@ bool ans_key_h(connection_t *c) { from->outcompression = compression; if(from->outcipher) - if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) { + if(!EVP_EncryptInit_ex(from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + EVP_CIPHER_key_length(from->outcipher))) { logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s", from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); return true; diff --git a/src/tincd.c b/src/tincd.c index f106f5f6..ccd79dcb 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -336,7 +336,7 @@ static bool parse_options(int argc, char **argv) { /* This function prettyprints the key generation process */ -static void indicator(int a, int b, void *p) { +static int indicator(int a, int b, BN_GENCB *cb) { switch (a) { case 0: fprintf(stderr, "."); @@ -368,19 +368,48 @@ static void indicator(int a, int b, void *p) { default: fprintf(stderr, "?"); } + + return 1; +} + +#ifndef HAVE_BN_GENCB_NEW +BN_GENCB *BN_GENCB_new(void) { + return xmalloc_and_zero(sizeof(BN_GENCB)); } +void BN_GENCB_free(BN_GENCB *cb) { + free(cb); +} +#endif + /* Generate a public/private RSA keypair, and ask for a file to store them in. */ static bool keygen(int bits) { + BIGNUM *e = NULL; RSA *rsa_key; FILE *f; char *pubname, *privname; + BN_GENCB *cb; + int result; fprintf(stderr, "Generating %d bits keys:\n", bits); - rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL); + + cb = BN_GENCB_new(); + if(!cb) + abort(); + BN_GENCB_set(cb, indicator, NULL); + + rsa_key = RSA_new(); + BN_hex2bn(&e, "10001"); + if(!rsa_key || !e) + abort(); + + result = RSA_generate_key_ex(rsa_key, bits, e, cb); + + BN_free(e); + BN_GENCB_free(cb); if(!rsa_key) { fprintf(stderr, "Error during key generation!\n"); @@ -702,7 +731,11 @@ end: EVP_cleanup(); ENGINE_cleanup(); CRYPTO_cleanup_all_ex_data(); +#ifdef HAVE_ERR_REMOVE_STATE + // OpenSSL claims this function was deprecated in 1.0.0, + // but valgrind's leak detector shows you still need to call it to make sure OpenSSL cleans up properly. ERR_remove_state(0); +#endif ERR_free_strings(); exit_configuration(&config_tree); -- 2.20.1