[Openvpn-devel,v4,2/2] make tls-auth and tls-crypt per-connection-block options

Message ID 20180707090421.25953-2-a@unstable.cc
State Accepted
Headers show
Series [Openvpn-devel,v4,1/2] crypto: always reload tls-auth/crypt key contexts | expand

Commit Message

Antonio Quartulli July 6, 2018, 11:04 p.m. UTC
Different VPN servers may use different tls-auth/crypt keys.
For this reason it is convenient to make tls-auth/crypt
per-connection-block options so that the user is allowed to
specify one key per remote.

If no tls-auth/crypt option is specified in a given connection
block, the global settings, if any, are used.

Trac: #720
Cc: Steffan Karger <steffan@karger.me>
Signed-off-by: Antonio Quartulli <a@unstable.cc>
---
v2:
- convert tls-auth keyfile to inline key if persist-key was specified
v3:
- squash 2/3 and 3/3 in one patch to prevent temporary features
  breakages
- restyle code introduced in options_postprocess_mutate_ce()
v4:
- rebase on top of latest 1/2

 doc/openvpn.8         |   3 +
 src/openvpn/init.c    |  18 ++---
 src/openvpn/options.c | 173 +++++++++++++++++++++++++++++-------------
 src/openvpn/options.h |   9 +++
 4 files changed, 142 insertions(+), 61 deletions(-)

Comments

Steffan Karger July 7, 2018, 3:22 a.m. UTC | #1
Hi,

On 07-07-18 11:04, Antonio Quartulli wrote:
> Different VPN servers may use different tls-auth/crypt keys.
> For this reason it is convenient to make tls-auth/crypt
> per-connection-block options so that the user is allowed to
> specify one key per remote.
> 
> If no tls-auth/crypt option is specified in a given connection
> block, the global settings, if any, are used.
> 
> Trac: #720
> Cc: Steffan Karger <steffan@karger.me>
> Signed-off-by: Antonio Quartulli <a@unstable.cc>
> ---
> v2:
> - convert tls-auth keyfile to inline key if persist-key was specified
> v3:
> - squash 2/3 and 3/3 in one patch to prevent temporary features
>   breakages
> - restyle code introduced in options_postprocess_mutate_ce()
> v4:
> - rebase on top of latest 1/2
> 
>  doc/openvpn.8         |   3 +
>  src/openvpn/init.c    |  18 ++---
>  src/openvpn/options.c | 173 +++++++++++++++++++++++++++++-------------
>  src/openvpn/options.h |   9 +++
>  4 files changed, 142 insertions(+), 61 deletions(-)
> 
> diff --git a/doc/openvpn.8 b/doc/openvpn.8
> index 46ea58b6..f01b48b6 100644
> --- a/doc/openvpn.8
> +++ b/doc/openvpn.8
> @@ -361,6 +361,7 @@ block:
>  .B fragment,
>  .B http\-proxy,
>  .B http\-proxy\-option,
> +.B key\-direction,
>  .B link\-mtu,
>  .B local,
>  .B lport,
> @@ -372,6 +373,8 @@ block:
>  .B remote,
>  .B rport,
>  .B socks\-proxy,
> +.B tls\-auth,
> +.B tls\-crypt,
>  .B tun\-mtu and
>  .B tun\-mtu\-extra.
>  
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index 1b46b364..14732e34 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -2507,7 +2507,7 @@ do_init_tls_wrap_key(struct context *c)
>      const struct options *options = &c->options;
>  
>      /* TLS handshake authentication (--tls-auth) */
> -    if (options->tls_auth_file)
> +    if (options->ce.tls_auth_file)
>      {
>          /* Initialize key_type for tls-auth with auth only */
>          CLEAR(c->c1.ks.tls_auth_key_type);
> @@ -2525,18 +2525,18 @@ do_init_tls_wrap_key(struct context *c)
>  
>          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,
> +                                options->ce.tls_auth_file,
> +                                options->ce.tls_auth_file_inline,
> +                                options->ce.key_direction,
>                                  "Control Channel Authentication", "tls-auth");
>      }
>  
>      /* TLS handshake encryption+authentication (--tls-crypt) */
> -    if (options->tls_crypt_file)
> +    if (options->ce.tls_crypt_file)
>      {
>          tls_crypt_init_key(&c->c1.ks.tls_wrap_key,
> -                           options->tls_crypt_file,
> -                           options->tls_crypt_inline, options->tls_server);
> +                           options->ce.tls_crypt_file,
> +                           options->ce.tls_crypt_inline, options->tls_server);
>      }
>  }
>  
> @@ -2806,7 +2806,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
>  #endif
>  
>      /* TLS handshake authentication (--tls-auth) */
> -    if (options->tls_auth_file)
> +    if (options->ce.tls_auth_file)
>      {
>          to.tls_wrap.mode = TLS_WRAP_AUTH;
>          to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
> @@ -2817,7 +2817,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
>      }
>  
>      /* TLS handshake encryption (--tls-crypt) */
> -    if (options->tls_crypt_file)
> +    if (options->ce.tls_crypt_file)
>      {
>          to.tls_wrap.mode = TLS_WRAP_CRYPT;
>          to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index b2ca2738..6f1a5382 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -1506,6 +1506,11 @@ show_connection_entry(const struct connection_entry *o)
>  #ifdef ENABLE_OCC
>      SHOW_INT(explicit_exit_notification);
>  #endif
> +
> +    SHOW_STR(tls_auth_file);
> +    SHOW_PARM(key_direction, keydirection2ascii(o->key_direction, false, true),
> +              "%s");
> +    SHOW_STR(tls_crypt_file);
>  }
>  
>  
> @@ -1786,9 +1791,6 @@ show_settings(const struct options *o)
>      SHOW_BOOL(push_peer_info);
>      SHOW_BOOL(tls_exit);
>  
> -    SHOW_STR(tls_auth_file);
> -    SHOW_STR(tls_crypt_file);
> -
>  #ifdef ENABLE_PKCS11
>      {
>          int i;
> @@ -2723,7 +2725,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
>                  notnull(options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
>              }
>          }
> -        if (options->tls_auth_file && options->tls_crypt_file)
> +        if (ce->tls_auth_file && ce->tls_crypt_file)
>          {
>              msg(M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
>          }
> @@ -2869,6 +2871,49 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
>          }
>      }
>  
> +    /*
> +     * Set per-connection block tls-auth/crypt fields if undefined.
> +     *
> +     * At the end only one of the two will be really set because the parser
> +     * logic prevents configurations where both are set.
> +     */
> +    if (!ce->tls_auth_file && !ce->tls_crypt_file)
> +    {
> +        ce->tls_auth_file = o->tls_auth_file;
> +        ce->tls_auth_file_inline = o->tls_auth_file_inline;
> +        ce->key_direction = o->key_direction;
> +
> +        ce->tls_crypt_file = o->tls_crypt_file;
> +        ce->tls_crypt_inline = o->tls_crypt_inline;
> +    }
> +
> +    /* pre-cache tls-auth/crypt key file if persist-key was specified and keys
> +     * were not already embedded in the config file
> +     */
> +    if (o->persist_key)
> +    {
> +        if (ce->tls_auth_file && !ce->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);
> +
> +            ce->tls_auth_file = INLINE_FILE_TAG;
> +            ce->tls_auth_file_inline = (char *)in.data;
> +        }
> +
> +        if (ce->tls_crypt_file && !ce->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);
> +
> +            ce->tls_crypt_file = INLINE_FILE_TAG;
> +            ce->tls_crypt_inline = (char *)in.data;
> +        }
> +    }
>  }
>  
>  #ifdef _WIN32
> @@ -3019,32 +3064,6 @@ 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 */
> @@ -3311,12 +3330,22 @@ options_postprocess_filechecks(struct options *options)
>                                           options->crl_file, R_OK, "--crl-verify");
>      }
>  
> -    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
> -                              options->tls_auth_file, R_OK, "--tls-auth");
> -    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
> -                              options->tls_crypt_file, R_OK, "--tls-crypt");
> +    ASSERT(options->connection_list);
> +    for (int i = 0; i < options->connection_list->len; ++i)
> +    {
> +        struct connection_entry *ce = options->connection_list->array[i];
> +
> +        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
> +                                  ce->tls_auth_file, R_OK, "--tls-auth");
> +
> +        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
> +                                  ce->tls_crypt_file, R_OK, "--tls-crypt");
> +
> +    }
> +
>      errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
>                                options->shared_secret_file, R_OK, "--secret");
> +
>      errs |= check_file_access(CHKACC_DIRPATH|CHKACC_FILEXSTWR,
>                                options->packet_id_file, R_OK|W_OK, "--replay-persist");
>  
> @@ -3673,7 +3702,7 @@ options_string(const struct options *o,
>      {
>          if (TLS_CLIENT || TLS_SERVER)
>          {
> -            if (o->tls_auth_file)
> +            if (o->ce.tls_auth_file)
>              {
>                  buf_printf(&out, ",tls-auth");
>              }
> @@ -7446,10 +7475,19 @@ add_option(struct options *options,
>      {
>          int key_direction;
>  
> +        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
> +
>          key_direction = ascii2keydirection(msglevel, p[1]);
>          if (key_direction >= 0)
>          {
> -            options->key_direction = key_direction;
> +            if (permission_mask & OPT_P_GENERAL)
> +            {
> +                options->key_direction = key_direction;
> +            }
> +            else if (permission_mask & OPT_P_CONNECTION)
> +            {
> +                options->ce.key_direction = key_direction;
> +            }
>          }
>          else
>          {
> @@ -8018,35 +8056,66 @@ add_option(struct options *options,
>      }
>      else if (streq(p[0], "tls-auth") && p[1] && !p[3])
>      {
> -        VERIFY_PERMISSION(OPT_P_GENERAL);
> -        if (streq(p[1], INLINE_FILE_TAG) && p[2])
> +        int key_direction = -1;
> +
> +        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
> +
> +        if (permission_mask & OPT_P_GENERAL)
>          {
> -            options->tls_auth_file_inline = p[2];
> +            if (streq(p[1], INLINE_FILE_TAG) && p[2])
> +            {
> +                options->tls_auth_file_inline = p[2];
> +            }
> +            else if (p[2])
> +            {
> +                key_direction = ascii2keydirection(msglevel, p[2]);
> +                if (key_direction < 0)
> +                {
> +                    goto err;
> +                }
> +                options->key_direction = key_direction;
> +            }
> +            options->tls_auth_file = p[1];
>          }
> -        else if (p[2])
> +        else if (permission_mask & OPT_P_CONNECTION)
>          {
> -            int key_direction;
> -
> -            key_direction = ascii2keydirection(msglevel, p[2]);
> -            if (key_direction >= 0)
> +            options->ce.key_direction = KEY_DIRECTION_BIDIRECTIONAL;
> +            if (streq(p[1], INLINE_FILE_TAG) && p[2])
>              {
> -                options->key_direction = key_direction;
> +                options->ce.tls_auth_file_inline = p[2];
>              }
> -            else
> +            else if (p[2])
>              {
> -                goto err;
> +                key_direction = ascii2keydirection(msglevel, p[2]);
> +                if (key_direction < 0)
> +                {
> +                    goto err;
> +                }
> +                options->ce.key_direction = key_direction;
>              }
> +            options->ce.tls_auth_file = p[1];
>          }
> -        options->tls_auth_file = p[1];
>      }
>      else if (streq(p[0], "tls-crypt") && p[1] && !p[3])
>      {
> -        VERIFY_PERMISSION(OPT_P_GENERAL);
> -        if (streq(p[1], INLINE_FILE_TAG) && p[2])
> +        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
> +        if (permission_mask & OPT_P_GENERAL)
>          {
> -            options->tls_crypt_inline = p[2];
> +            if (streq(p[1], INLINE_FILE_TAG) && p[2])
> +            {
> +                options->tls_crypt_inline = p[2];
> +            }
> +            options->tls_crypt_file = p[1];
> +        }
> +        else if (permission_mask & OPT_P_CONNECTION)
> +        {
> +            if (streq(p[1], INLINE_FILE_TAG) && p[2])
> +            {
> +                options->ce.tls_crypt_inline = p[2];
> +            }
> +            options->ce.tls_crypt_file = p[1];
> +
>          }
> -        options->tls_crypt_file = p[1];
>      }
>      else if (streq(p[0], "key-method") && p[1] && !p[2])
>      {
> diff --git a/src/openvpn/options.h b/src/openvpn/options.h
> index 3a6c33f8..acbd1087 100644
> --- a/src/openvpn/options.h
> +++ b/src/openvpn/options.h
> @@ -130,6 +130,15 @@ struct connection_entry
>  #define CE_MAN_QUERY_REMOTE_MASK   (0x07)
>  #define CE_MAN_QUERY_REMOTE_SHIFT  (2)
>      unsigned int flags;
> +
> +    /* Shared secret used for TLS control channel authentication */
> +    const char *tls_auth_file;
> +    const char *tls_auth_file_inline;
> +    int key_direction;
> +
> +    /* Shared secret used for TLS control channel authenticated encryption */
> +    const char *tls_crypt_file;
> +    const char *tls_crypt_inline;
>  };
>  
>  struct remote_entry
> 

Patch looks good, and I can no longer get this to fail on me, so:

Acked-by: Steffan Karger <steffan@karger.me>
Tested-by; Steffan Karger <steffan@karger.me>

-Steffan

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Gert Doering July 24, 2018, 2:29 a.m. UTC | #2
Your patch has been applied to the master branch.

(I have not reviewed this - trusting Steffan on it - but have run cmocka and 
t_client tests on it, and things keep working)

commit 57d6f103f378b927b9e0054b022b5b8b442973b8
Author: Antonio Quartulli
Date:   Sat Jul 7 17:04:21 2018 +0800

     make tls-auth and tls-crypt per-connection-block options

     Signed-off-by: Antonio Quartulli <a@unstable.cc>
     Acked-by: Steffan Karger <steffan.karger@fox-it.com>
     Message-Id: <20180707090421.25953-2-a@unstable.cc>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17226.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

Patch

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 46ea58b6..f01b48b6 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -361,6 +361,7 @@  block:
 .B fragment,
 .B http\-proxy,
 .B http\-proxy\-option,
+.B key\-direction,
 .B link\-mtu,
 .B local,
 .B lport,
@@ -372,6 +373,8 @@  block:
 .B remote,
 .B rport,
 .B socks\-proxy,
+.B tls\-auth,
+.B tls\-crypt,
 .B tun\-mtu and
 .B tun\-mtu\-extra.
 
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 1b46b364..14732e34 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2507,7 +2507,7 @@  do_init_tls_wrap_key(struct context *c)
     const struct options *options = &c->options;
 
     /* TLS handshake authentication (--tls-auth) */
-    if (options->tls_auth_file)
+    if (options->ce.tls_auth_file)
     {
         /* Initialize key_type for tls-auth with auth only */
         CLEAR(c->c1.ks.tls_auth_key_type);
@@ -2525,18 +2525,18 @@  do_init_tls_wrap_key(struct context *c)
 
         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,
+                                options->ce.tls_auth_file,
+                                options->ce.tls_auth_file_inline,
+                                options->ce.key_direction,
                                 "Control Channel Authentication", "tls-auth");
     }
 
     /* TLS handshake encryption+authentication (--tls-crypt) */
-    if (options->tls_crypt_file)
+    if (options->ce.tls_crypt_file)
     {
         tls_crypt_init_key(&c->c1.ks.tls_wrap_key,
-                           options->tls_crypt_file,
-                           options->tls_crypt_inline, options->tls_server);
+                           options->ce.tls_crypt_file,
+                           options->ce.tls_crypt_inline, options->tls_server);
     }
 }
 
@@ -2806,7 +2806,7 @@  do_init_crypto_tls(struct context *c, const unsigned int flags)
 #endif
 
     /* TLS handshake authentication (--tls-auth) */
-    if (options->tls_auth_file)
+    if (options->ce.tls_auth_file)
     {
         to.tls_wrap.mode = TLS_WRAP_AUTH;
         to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
@@ -2817,7 +2817,7 @@  do_init_crypto_tls(struct context *c, const unsigned int flags)
     }
 
     /* TLS handshake encryption (--tls-crypt) */
-    if (options->tls_crypt_file)
+    if (options->ce.tls_crypt_file)
     {
         to.tls_wrap.mode = TLS_WRAP_CRYPT;
         to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index b2ca2738..6f1a5382 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1506,6 +1506,11 @@  show_connection_entry(const struct connection_entry *o)
 #ifdef ENABLE_OCC
     SHOW_INT(explicit_exit_notification);
 #endif
+
+    SHOW_STR(tls_auth_file);
+    SHOW_PARM(key_direction, keydirection2ascii(o->key_direction, false, true),
+              "%s");
+    SHOW_STR(tls_crypt_file);
 }
 
 
@@ -1786,9 +1791,6 @@  show_settings(const struct options *o)
     SHOW_BOOL(push_peer_info);
     SHOW_BOOL(tls_exit);
 
-    SHOW_STR(tls_auth_file);
-    SHOW_STR(tls_crypt_file);
-
 #ifdef ENABLE_PKCS11
     {
         int i;
@@ -2723,7 +2725,7 @@  options_postprocess_verify_ce(const struct options *options, const struct connec
                 notnull(options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
             }
         }
-        if (options->tls_auth_file && options->tls_crypt_file)
+        if (ce->tls_auth_file && ce->tls_crypt_file)
         {
             msg(M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
         }
@@ -2869,6 +2871,49 @@  options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
         }
     }
 
+    /*
+     * Set per-connection block tls-auth/crypt fields if undefined.
+     *
+     * At the end only one of the two will be really set because the parser
+     * logic prevents configurations where both are set.
+     */
+    if (!ce->tls_auth_file && !ce->tls_crypt_file)
+    {
+        ce->tls_auth_file = o->tls_auth_file;
+        ce->tls_auth_file_inline = o->tls_auth_file_inline;
+        ce->key_direction = o->key_direction;
+
+        ce->tls_crypt_file = o->tls_crypt_file;
+        ce->tls_crypt_inline = o->tls_crypt_inline;
+    }
+
+    /* pre-cache tls-auth/crypt key file if persist-key was specified and keys
+     * were not already embedded in the config file
+     */
+    if (o->persist_key)
+    {
+        if (ce->tls_auth_file && !ce->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);
+
+            ce->tls_auth_file = INLINE_FILE_TAG;
+            ce->tls_auth_file_inline = (char *)in.data;
+        }
+
+        if (ce->tls_crypt_file && !ce->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);
+
+            ce->tls_crypt_file = INLINE_FILE_TAG;
+            ce->tls_crypt_inline = (char *)in.data;
+        }
+    }
 }
 
 #ifdef _WIN32
@@ -3019,32 +3064,6 @@  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 */
@@ -3311,12 +3330,22 @@  options_postprocess_filechecks(struct options *options)
                                          options->crl_file, R_OK, "--crl-verify");
     }
 
-    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
-                              options->tls_auth_file, R_OK, "--tls-auth");
-    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
-                              options->tls_crypt_file, R_OK, "--tls-crypt");
+    ASSERT(options->connection_list);
+    for (int i = 0; i < options->connection_list->len; ++i)
+    {
+        struct connection_entry *ce = options->connection_list->array[i];
+
+        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+                                  ce->tls_auth_file, R_OK, "--tls-auth");
+
+        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
+                                  ce->tls_crypt_file, R_OK, "--tls-crypt");
+
+    }
+
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE,
                               options->shared_secret_file, R_OK, "--secret");
+
     errs |= check_file_access(CHKACC_DIRPATH|CHKACC_FILEXSTWR,
                               options->packet_id_file, R_OK|W_OK, "--replay-persist");
 
@@ -3673,7 +3702,7 @@  options_string(const struct options *o,
     {
         if (TLS_CLIENT || TLS_SERVER)
         {
-            if (o->tls_auth_file)
+            if (o->ce.tls_auth_file)
             {
                 buf_printf(&out, ",tls-auth");
             }
@@ -7446,10 +7475,19 @@  add_option(struct options *options,
     {
         int key_direction;
 
+        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
+
         key_direction = ascii2keydirection(msglevel, p[1]);
         if (key_direction >= 0)
         {
-            options->key_direction = key_direction;
+            if (permission_mask & OPT_P_GENERAL)
+            {
+                options->key_direction = key_direction;
+            }
+            else if (permission_mask & OPT_P_CONNECTION)
+            {
+                options->ce.key_direction = key_direction;
+            }
         }
         else
         {
@@ -8018,35 +8056,66 @@  add_option(struct options *options,
     }
     else if (streq(p[0], "tls-auth") && p[1] && !p[3])
     {
-        VERIFY_PERMISSION(OPT_P_GENERAL);
-        if (streq(p[1], INLINE_FILE_TAG) && p[2])
+        int key_direction = -1;
+
+        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
+
+        if (permission_mask & OPT_P_GENERAL)
         {
-            options->tls_auth_file_inline = p[2];
+            if (streq(p[1], INLINE_FILE_TAG) && p[2])
+            {
+                options->tls_auth_file_inline = p[2];
+            }
+            else if (p[2])
+            {
+                key_direction = ascii2keydirection(msglevel, p[2]);
+                if (key_direction < 0)
+                {
+                    goto err;
+                }
+                options->key_direction = key_direction;
+            }
+            options->tls_auth_file = p[1];
         }
-        else if (p[2])
+        else if (permission_mask & OPT_P_CONNECTION)
         {
-            int key_direction;
-
-            key_direction = ascii2keydirection(msglevel, p[2]);
-            if (key_direction >= 0)
+            options->ce.key_direction = KEY_DIRECTION_BIDIRECTIONAL;
+            if (streq(p[1], INLINE_FILE_TAG) && p[2])
             {
-                options->key_direction = key_direction;
+                options->ce.tls_auth_file_inline = p[2];
             }
-            else
+            else if (p[2])
             {
-                goto err;
+                key_direction = ascii2keydirection(msglevel, p[2]);
+                if (key_direction < 0)
+                {
+                    goto err;
+                }
+                options->ce.key_direction = key_direction;
             }
+            options->ce.tls_auth_file = p[1];
         }
-        options->tls_auth_file = p[1];
     }
     else if (streq(p[0], "tls-crypt") && p[1] && !p[3])
     {
-        VERIFY_PERMISSION(OPT_P_GENERAL);
-        if (streq(p[1], INLINE_FILE_TAG) && p[2])
+        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
+        if (permission_mask & OPT_P_GENERAL)
         {
-            options->tls_crypt_inline = p[2];
+            if (streq(p[1], INLINE_FILE_TAG) && p[2])
+            {
+                options->tls_crypt_inline = p[2];
+            }
+            options->tls_crypt_file = p[1];
+        }
+        else if (permission_mask & OPT_P_CONNECTION)
+        {
+            if (streq(p[1], INLINE_FILE_TAG) && p[2])
+            {
+                options->ce.tls_crypt_inline = p[2];
+            }
+            options->ce.tls_crypt_file = p[1];
+
         }
-        options->tls_crypt_file = p[1];
     }
     else if (streq(p[0], "key-method") && p[1] && !p[2])
     {
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 3a6c33f8..acbd1087 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -130,6 +130,15 @@  struct connection_entry
 #define CE_MAN_QUERY_REMOTE_MASK   (0x07)
 #define CE_MAN_QUERY_REMOTE_SHIFT  (2)
     unsigned int flags;
+
+    /* Shared secret used for TLS control channel authentication */
+    const char *tls_auth_file;
+    const char *tls_auth_file_inline;
+    int key_direction;
+
+    /* Shared secret used for TLS control channel authenticated encryption */
+    const char *tls_crypt_file;
+    const char *tls_crypt_inline;
 };
 
 struct remote_entry