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

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

Commit Message

Antonio Quartulli June 4, 2018, 10:14 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()


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

Patch

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 0e5d467a..274c7412 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 15fef08d..99b5b12a 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2504,7 +2504,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);
@@ -2522,18 +2522,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);
     }
 }
 
@@ -2803,7 +2803,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;
@@ -2814,7 +2814,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 730ca6f1..aa069cfd 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,43 @@  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 = keyfile_to_buffer(ce->tls_auth_file, 2048,
+                                                 &o->gc);
+            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 = keyfile_to_buffer(ce->tls_crypt_file, 2048,
+                                                 &o->gc);
+            ce->tls_crypt_file = INLINE_FILE_TAG;
+            ce->tls_crypt_inline = (char *)in.data;
+        }
+    }
 }
 
 #ifdef _WIN32
@@ -3019,26 +3058,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 = 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 */
@@ -3305,12 +3324,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");
 
@@ -3667,7 +3696,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");
             }
@@ -7440,10 +7469,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
         {
@@ -8012,35 +8050,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 f7d0145a..2c0902a9 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