Hi,
On 07-07-18 11:04, Antonio Quartulli wrote:
> 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 <[email protected]>
> Signed-off-by: Antonio Quartulli <[email protected]>
> ---
> v2:
> - introduce this patch
> v3:
> - add key per-loading logic to this patch to avoid temporary features
> breakages
> v4:
> - rebase on top of master
> - use buffer_read_from_file() implementation from "tls-crypt-v2: generate
> client keys". Minor change: in case of error, free buffer and remove
> it from gc (with new free_buf_gc()), if provided.
> - don't go FATAL inside buffer_read_from_file() in case of error, but
> let external logic decide
>
> src/openvpn/buffer.c | 61 ++++++++++++++++++++++++++++++
> src/openvpn/buffer.h | 12 ++++++
> src/openvpn/crypto.c | 21 +++--------
> src/openvpn/init.c | 87 ++++++++++++++++++++++++++++---------------
> src/openvpn/options.c | 26 +++++++++++++
> 5 files changed, 161 insertions(+), 46 deletions(-)
>
> diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
> index becfeb93..7d4860db 100644
> --- a/src/openvpn/buffer.c
> +++ b/src/openvpn/buffer.c
> @@ -189,6 +189,34 @@ free_buf(struct buffer *buf)
> CLEAR(*buf);
> }
>
> +static void
> +free_buf_gc(struct buffer *buf, struct gc_arena *gc)
> +{
> + if (gc)
> + {
> + struct gc_entry **e = &gc->list;
> +
> + while (*e)
> + {
> + /* check if this object is the one we want to delete */
> + if ((uint8_t *)((*e) + sizeof(*e)) == buf->data)
I think this should be sizeof(**e).
> + {
> + struct gc_entry *to_delete = *e;
> +
> + /* remove element from linked list and free it */
> + *e = (*e)->next;
> + free(to_delete);
> +
> + break;
> + }
> +
> + e = &(*e)->next;
> + }
> + }
> +
> + free_buf(buf);
> +}
> +
> /*
> * Return a buffer for write that is a subset of another buffer
> */
> @@ -1332,3 +1360,36 @@ buffer_list_file(const char *fn, int max_line_len)
> }
> return bl;
> }
> +
> +struct buffer
> +buffer_read_from_file(const char *filename, struct gc_arena *gc)
> +{
> + struct buffer ret = { 0 };
> +
> + platform_stat_t file_stat = {0};
> + if (platform_stat(filename, &file_stat) < 0)
> + {
> + return ret;
> + }
> +
> + FILE *fp = platform_fopen(filename, "r");
> + if (!fp)
> + {
> + return ret;
> + }
> +
> + const size_t size = file_stat.st_size;
> + ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
> + ssize_t read_size = fread(BPTR(&ret), 1, size, fp);
> + if (read_size < 0)
> + {
> + free_buf_gc(&ret, gc);
> + goto cleanup;
> + }
> + ASSERT(buf_inc_len(&ret, read_size));
> + buf_null_terminate(&ret);
> +
> +cleanup:
> + fclose(fp);
> + return ret;
> +}
> diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
> index d848490a..a11c8bd9 100644
> --- a/src/openvpn/buffer.h
> +++ b/src/openvpn/buffer.h
> @@ -1112,4 +1112,16 @@ void buffer_list_aggregate_separator(struct
> buffer_list *bl,
>
> struct buffer_list *buffer_list_file(const char *fn, int max_line_len);
>
> +/**
> + * buffer_read_from_file - copy the content of a file into a buffer
> + *
> + * @param file path to the file to read
> + * @param gc the garbage collector to use when allocating the buffer.
> It
> + * passed to alloc_buf_gc() and therefore can be NULL.
It *is* passed ?
> + *
> + * @return the buffer storing the file content or an invalid buffer in case
> of
> + * error
> + */
> +struct buffer buffer_read_from_file(const char *filename, struct gc_arena
> *gc);
> +
> #endif /* BUFFER_H */
> diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
> index b59c1f73..5381ef2a 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,11 @@ 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)
> - {
> + in = buffer_read_from_file(file, &gc);
> + if (!buf_valid(&in))
> 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);
> +
> + size = in.len;
> }
>
> cp = (unsigned char *)in.data;
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index b748357d..1b46b364 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -2409,7 +2409,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);
> }
> @@ -2499,6 +2498,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.
> @@ -2548,35 +2589,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;
> @@ -2598,6 +2612,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);
> }
> }
>
> @@ -3401,6 +3421,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..b2ca2738 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -3019,6 +3019,32 @@ 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 = buffer_read_from_file(o->tls_auth_file,
> &o->gc);
> + if (!buf_valid(&in))
> + msg(M_FATAL, "Cannot pre-load tls-auth keyfile (%s)",
> + o->tls_auth_file);
> +
> + 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 = buffer_read_from_file(o->tls_crypt_file,
> &o->gc);
> + if (!buf_valid(&in))
> + msg(M_FATAL, "Cannot pre-load tls-crypt keyfile (%s)",
> + o->tls_auth_file);
> +
> + 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 */
>
Otherwise, this looks good.
-Steffan
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel