From patchwork Mon Jun 4 22:14:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 346 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.27.255.58]) by backend30.mail.ord1d.rsapps.net (Dovecot) with LMTP id dPkkKa1GFlvYDAAAIUCqbw for ; Tue, 05 Jun 2018 04:15:41 -0400 Received: from proxy7.mail.iad3a.rsapps.net ([172.27.255.58]) by director8.mail.ord1d.rsapps.net (Dovecot) with LMTP id e50NLa1GFlvZYQAAfY0hYg ; Tue, 05 Jun 2018 04:15:41 -0400 Received: from smtp39.gate.iad3a ([172.27.255.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy7.mail.iad3a.rsapps.net with LMTP id cLswK61GFls0WwAAnPvY+A ; Tue, 05 Jun 2018 04:15:41 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp39.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: a2698fb0-6898-11e8-ad12-525400eea4e4-1-1 Received: from [216.105.38.7] ([216.105.38.7:57079] helo=lists.sourceforge.net) by smtp39.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id A9/F9-21742-DA6461B5; Tue, 05 Jun 2018 04:15:41 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1fQ778-0000Kr-Cn; Tue, 05 Jun 2018 08:14:46 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1fQ777-0000KF-24 for openvpn-devel@lists.sourceforge.net; Tue, 05 Jun 2018 08:14:45 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=PJUqimGMStDPPxZ9TE7Rv70wD8NKjJ8PDOL3LgX0TLo=; b=bFP0r+CTT0T/2GTf3aFkJJazEM zNIK47AJ3z1Ca/FIL9yHPmQO1DLJR5NYmXgx9soKPIeZrsBWSA6DOH40a0P41tqdhXqXfpkFiVC7h CE9kdoqAOIwS8/U5WQZc63BnQvIEo2x0HrlVokLr4xYQfVRV/R0TqFKJIDdmN8JySaH4=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:MIME-Version: Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=PJUqimGMStDPPxZ9TE7Rv70wD8NKjJ8PDOL3LgX0TLo=; b=YnJnP55NB7Uk1PUJwBIsGMO62q oSy7pvud40uzVO50qsRXqdjcR7guzHg31C2IK7xE8I3KGovquIB9cLUqRYbgX306nhZs/bjdtL+ld rKPD623CmEO9+EHSVcIDmk8CKjKVbPCvfxbTKr2PiIzAiMwN2TaFi+soEXaeuSfcWDuw=; Received: from s2.neomailbox.net ([5.148.176.60]) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) id 1fQ774-002MqP-Ep for openvpn-devel@lists.sourceforge.net; Tue, 05 Jun 2018 08:14:44 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Tue, 5 Jun 2018 16:14:06 +0800 Message-Id: <20180605081407.6944-1-a@unstable.cc> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [5.148.176.60 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1fQ774-002MqP-Ep Subject: [Openvpn-devel] [PATCH v3 1/2] crypto: always reload tls-auth/crypt key contexts X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Antonio Quartulli MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox In preparation to having tls-auth/crypt keys per connection block, it is important to ensure that such material is always reloaded upon SIGUSR1, no matter if `persist-key` was specified or not. This is required because when moving from one remote to the other the key may change and thus the key context needs to be refreshed. To ensure that the `persist-key` logic will still work as expected, the tls-auth/crypt key is pre-loaded so that the keyfile is not required at runtime. Trac: #720 Cc: Steffan Karger Signed-off-by: Antonio Quartulli --- v2: - introduce this patch v3: - add key per-loading logic to this patch to avoid temporary features breakages src/openvpn/buffer.c | 29 +++++++++++++++ src/openvpn/buffer.h | 13 +++++++ src/openvpn/crypto.c | 20 ++-------- src/openvpn/init.c | 87 ++++++++++++++++++++++++++++--------------- src/openvpn/options.c | 20 ++++++++++ 5 files changed, 122 insertions(+), 47 deletions(-) diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c index becfeb93..cbf969a8 100644 --- a/src/openvpn/buffer.c +++ b/src/openvpn/buffer.c @@ -1332,3 +1332,32 @@ buffer_list_file(const char *fn, int max_line_len) } return bl; } + +struct buffer +keyfile_to_buffer(const char *file, int max_size, struct gc_arena *gc) +{ + size_t size; + struct buffer in = alloc_buf_gc(max_size, gc); + int fd = platform_open(file, O_RDONLY, 0); + if (fd == -1) + { + msg(M_ERR, "Cannot open key file '%s'", file); + } + + size = read(fd, in.data, in.capacity); + if (size < 0) + { + msg(M_FATAL, "Read error on key file ('%s')", file); + } + + if (size == in.capacity) + { + msg(M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, + (int)in.capacity); + } + close(fd); + + in.len = size; + + return in; +} diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h index d848490a..ba9857eb 100644 --- a/src/openvpn/buffer.h +++ b/src/openvpn/buffer.h @@ -1112,4 +1112,17 @@ void buffer_list_aggregate_separator(struct buffer_list *bl, struct buffer_list *buffer_list_file(const char *fn, int max_line_len); +/** + * keyfile_to_buffer - copy the content of a file into a buffer + * + * @param file path to the file to read + * @param max_size maximum size of the buffer to allocate + * @param gc the garbage collector to use when allocating the buffer. It + * passed to alloc_buf_gc() and therefore can be NULL. + * + * @return the buffer storing the file content + */ +struct buffer keyfile_to_buffer(const char *file, int max_size, + struct gc_arena *gc); + #endif /* BUFFER_H */ diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index b59c1f73..f201b533 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -1224,7 +1224,7 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { struct gc_arena gc = gc_new(); struct buffer in; - int fd, size; + int size; uint8_t hex_byte[3] = {0, 0, 0}; const char *error_filename = file; @@ -1268,22 +1268,8 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) } else /* 'file' is a filename which refers to a file containing the ascii key */ { - in = alloc_buf_gc(2048, &gc); - fd = platform_open(file, O_RDONLY, 0); - if (fd == -1) - { - msg(M_ERR, "Cannot open key file '%s'", file); - } - size = read(fd, in.data, in.capacity); - if (size < 0) - { - msg(M_FATAL, "Read error on key file ('%s')", file); - } - if (size == in.capacity) - { - msg(M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity); - } - close(fd); + in = keyfile_to_buffer(file, 2048, &gc); + size = in.len; } cp = (unsigned char *)in.data; diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 36c1a4c4..15fef08d 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2406,7 +2406,6 @@ key_schedule_free(struct key_schedule *ks, bool free_ssl_ctx) if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx) { tls_ctx_free(&ks->ssl_ctx); - free_key_ctx_bi(&ks->tls_wrap_key); } CLEAR(*ks); } @@ -2496,6 +2495,48 @@ do_init_crypto_static(struct context *c, const unsigned int flags) check_replay_consistency(&c->c1.ks.key_type, options->replay); } +/* + * Initialize the tls-auth/crypt key context + */ +static void +do_init_tls_wrap_key(struct context *c) +{ + const struct options *options = &c->options; + + /* TLS handshake authentication (--tls-auth) */ + if (options->tls_auth_file) + { + /* Initialize key_type for tls-auth with auth only */ + CLEAR(c->c1.ks.tls_auth_key_type); + if (!streq(options->authname, "none")) + { + c->c1.ks.tls_auth_key_type.digest = md_kt_get(options->authname); + c->c1.ks.tls_auth_key_type.hmac_length = + md_kt_size(c->c1.ks.tls_auth_key_type.digest); + } + else + { + msg(M_FATAL, "ERROR: tls-auth enabled, but no valid --auth " + "algorithm specified ('%s')", options->authname); + } + + crypto_read_openvpn_key(&c->c1.ks.tls_auth_key_type, + &c->c1.ks.tls_wrap_key, + options->tls_auth_file, + options->tls_auth_file_inline, + options->key_direction, + "Control Channel Authentication", "tls-auth"); + } + + /* TLS handshake encryption+authentication (--tls-crypt) */ + if (options->tls_crypt_file) + { + tls_crypt_init_key(&c->c1.ks.tls_wrap_key, + options->tls_crypt_file, + options->tls_crypt_inline, options->tls_server); + } +} + /* * Initialize the persistent component of OpenVPN's TLS mode, * which is preserved across SIGUSR1 resets. @@ -2545,35 +2586,8 @@ do_init_crypto_tls_c1(struct context *c) /* Initialize PRNG with config-specified digest */ prng_init(options->prng_hash, options->prng_nonce_secret_len); - /* TLS handshake authentication (--tls-auth) */ - if (options->tls_auth_file) - { - /* Initialize key_type for tls-auth with auth only */ - CLEAR(c->c1.ks.tls_auth_key_type); - if (!streq(options->authname, "none")) - { - c->c1.ks.tls_auth_key_type.digest = md_kt_get(options->authname); - c->c1.ks.tls_auth_key_type.hmac_length = - md_kt_size(c->c1.ks.tls_auth_key_type.digest); - } - else - { - msg(M_FATAL, "ERROR: tls-auth enabled, but no valid --auth " - "algorithm specified ('%s')", options->authname); - } - - crypto_read_openvpn_key(&c->c1.ks.tls_auth_key_type, - &c->c1.ks.tls_wrap_key, options->tls_auth_file, - options->tls_auth_file_inline, options->key_direction, - "Control Channel Authentication", "tls-auth"); - } - - /* TLS handshake encryption+authentication (--tls-crypt) */ - if (options->tls_crypt_file) - { - tls_crypt_init_key(&c->c1.ks.tls_wrap_key, options->tls_crypt_file, - options->tls_crypt_inline, options->tls_server); - } + /* initialize tls-auth/crypt key */ + do_init_tls_wrap_key(c); c->c1.ciphername = options->ciphername; c->c1.authname = options->authname; @@ -2595,6 +2609,12 @@ do_init_crypto_tls_c1(struct context *c) c->options.ciphername = c->c1.ciphername; c->options.authname = c->c1.authname; c->options.keysize = c->c1.keysize; + + /* + * tls-auth/crypt key can be configured per connection block, therefore + * we must reload it as it may have changed + */ + do_init_tls_wrap_key(c); } } @@ -3398,6 +3418,13 @@ do_close_tls(struct context *c) static void do_close_free_key_schedule(struct context *c, bool free_ssl_ctx) { + /* + * always free the tls_auth/crypt key. If persist_key is true, the key will + * be reloaded from memory (pre-cached) + */ + free_key_ctx_bi(&c->c1.ks.tls_wrap_key); + CLEAR(c->c1.ks.tls_wrap_key); + if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_key)) { key_schedule_free(&c->c1.ks, free_ssl_ctx); diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 426057ab..730ca6f1 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -3019,6 +3019,26 @@ options_postprocess_mutate(struct options *o) options_postprocess_mutate_ce(o, o->connection_list->array[i]); } + /* pre-cache tls-auth/crypt key file if persist-key was specified */ + if (o->persist_key) + { + if (o->tls_auth_file && !o->tls_auth_file_inline) + { + struct buffer in = keyfile_to_buffer(o->tls_auth_file, 2048, + &o->gc); + o->tls_auth_file = INLINE_FILE_TAG; + o->tls_auth_file_inline = (char *)in.data; + } + + if (o->tls_crypt_file && !o->tls_crypt_inline) + { + struct buffer in = keyfile_to_buffer(o->tls_crypt_file, 2048, + &o->gc); + o->tls_crypt_file = INLINE_FILE_TAG; + o->tls_crypt_inline = (char *)in.data; + } + } + if (o->tls_server) { /* Check that DH file is specified, or explicitly disabled */