Browse code

Implement override-username

This allow the server to set and override the username that is assumed
for the client for interaction with the client after the authentication.

This is especially intended to allow the of use auth-gen-token in
scenarios where the clients use certificates and multi-factor
authentication.

It allows a client to successfully roam to a different server and have
a correct username and auth-token that can be accepted by that server as
fully authenticated user without requiring MFA again.

The scenario that this feature is probably most useful
when --management-client-auth is in use as in this mode the OpenVPN
server can accept clients without username/password but still use
--auth-gen-token with username and password to accept auth-token as
alternative authentication. A client without a username will also not
use the pushed auth-token. So setting/pushing an auth-token-user
will ensure that the client has a username.

Github: OpenVPN/openvpn#299

Change-Id: Ia4095518d5e4447992a2974e0d7a159d79ba6b6f
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20250311155904.4446-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg31091.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Arne Schwabe authored on 2025/03/12 00:59:04
Showing 10 changed files
... ...
@@ -49,6 +49,12 @@ Epoch data keys and packet format
49 49
     - IV constructed with XOR instead of concatenation to not have (parts) of
50 50
       the real IV on the wire
51 51
 
52
+Allow overriding username with ``--override-username``
53
+    This is intended to allow using auth-gen-token in scenarios where the
54
+    clients use certificates and multi-factor authentication.  This will
55
+    also generate a 'push "auth-token-user newusername"' directives in
56
+    push replies.
57
+
52 58
 Deprecated features
53 59
 -------------------
54 60
 ``secret`` support has been removed by default.
... ...
@@ -89,6 +89,12 @@ fast hardware. SSL/TLS authentication must be used in this mode.
89 89
   will lead to authentication bypass (as does returning success on a wrong
90 90
   password from a script).
91 91
 
92
+  **Note:** the username for ``--auth-gen-token`` can be overridden by
93
+  ``--override-user``. In this case the client will be pushed also the
94
+  ``--auth-token-user`` option and an auth token that is valid for that
95
+  username instead of the original username that the client authenticated
96
+  with.
97
+
92 98
 --auth-gen-token-secret file
93 99
   Specifies a file that holds a secret for the HMAC used in
94 100
   ``--auth-gen-token`` If ``file`` is not present OpenVPN will generate a
... ...
@@ -412,6 +418,32 @@ fast hardware. SSL/TLS authentication must be used in this mode.
412 412
 
413 413
   This option requires that ``--disable-occ`` NOT be used.
414 414
 
415
+--override-username username
416
+  Sets the username of a connection to the specified username.  This username
417
+  will also be used by ``--auth-gen-token``. However, the overridden
418
+  username comes only into effect *after* the ``--client-config-dir`` has been
419
+  read and the ``--auth-user-pass-verify`` and ``--client-connect`` scripts
420
+  have been run.
421
+
422
+  Also ``--username-as-common-name`` will use the client provided username
423
+  as common-name. It is recommended to avoid the use of the
424
+  ``--override-username`` option if the option ``--username-as-common-name``
425
+  is being used.
426
+
427
+  The changed username will be picked up by the status output and also by
428
+  the ``--auth-gen-token`` option. It will also be pushed to the client
429
+  using ``--auth-token-user``.
430
+
431
+  Special care should be taken that both the initial username of the client
432
+  and the overridden username are handled correctly when using
433
+  ``--override-username`` and the related options to avoid
434
+  authentication/authorisation bypasses.
435
+
436
+  This option is mainly intended for use cases that use certificates and
437
+  multi factor authentication and therefore do not provide a username that
438
+  can be used for ``--auth-gen-token`` to allow providing a username in
439
+  these scenarios.
440
+
415 441
 --port-share args
416 442
   Share OpenVPN TCP with another service
417 443
 
... ...
@@ -42,7 +42,9 @@
42 42
 #include "ssl_verify.h"
43 43
 #include "ssl_ncp.h"
44 44
 #include "vlan.h"
45
+#include "auth_token.h"
45 46
 #include <inttypes.h>
47
+#include <string.h>
46 48
 
47 49
 #include "memdbg.h"
48 50
 
... ...
@@ -2680,6 +2682,60 @@ static const multi_client_connect_handler client_connect_handlers[] = {
2680 2680
     NULL,
2681 2681
 };
2682 2682
 
2683
+/**
2684
+ * Overrides the locked username with the username of --override-username
2685
+ * @param mi the multi instance that should be modified.
2686
+ */
2687
+static bool
2688
+override_locked_username(struct multi_instance *mi)
2689
+{
2690
+    struct tls_multi *multi = mi->context.c2.tls_multi;
2691
+    struct options *options = &mi->context.options;
2692
+    struct tls_session *session = &multi->session[TM_ACTIVE];
2693
+
2694
+    if (!multi->locked_username)
2695
+    {
2696
+        msg(D_MULTI_ERRORS, "MULTI: Ignoring override-username as no "
2697
+            "user/password method is enabled. Enable "
2698
+            "--management-client-auth, --auth-user-pass-verify, or a "
2699
+            "plugin with user/password verify capability.");
2700
+        return false;
2701
+    }
2702
+
2703
+    if (!multi->locked_original_username
2704
+        && strcmp(multi->locked_username, options->override_username) != 0)
2705
+    {
2706
+        multi->locked_original_username = multi->locked_username;
2707
+        multi->locked_username = strdup(options->override_username);
2708
+
2709
+        /* Override also the common name if username should be set as common
2710
+         * name */
2711
+        if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME))
2712
+        {
2713
+            set_common_name(session, multi->locked_username);
2714
+            free(multi->locked_cn);
2715
+            multi->locked_cn = NULL;
2716
+            tls_lock_common_name(multi);
2717
+        }
2718
+
2719
+        /* Regenerate the auth-token if enabled */
2720
+        if (multi->auth_token_initial)
2721
+        {
2722
+            struct user_pass up;
2723
+            CLEAR(up);
2724
+            strncpynt(up.username, multi->locked_username,
2725
+                      sizeof(up.username));
2726
+
2727
+            generate_auth_token(&up, multi);
2728
+        }
2729
+
2730
+        msg(D_MULTI_LOW, "MULTI: Note, override-username changes username "
2731
+            "from '%s' to '%s'",
2732
+            multi->locked_original_username,
2733
+            multi->locked_username);
2734
+    }
2735
+    return true;
2736
+}
2683 2737
 /*
2684 2738
  * Called as soon as the SSL/TLS connection is authenticated.
2685 2739
  *
... ...
@@ -2783,6 +2839,14 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
2783 2783
         (*cur_handler_index)++;
2784 2784
     }
2785 2785
 
2786
+    if (mi->context.options.override_username)
2787
+    {
2788
+        if (!override_locked_username(mi))
2789
+        {
2790
+            cc_succeeded = false;
2791
+        }
2792
+    }
2793
+
2786 2794
     /* Check if we have forbidding options in the current mode */
2787 2795
     if (dco_enabled(&mi->context.options)
2788 2796
         && !dco_check_option(D_MULTI_ERRORS, &mi->context.options))
... ...
@@ -452,6 +452,8 @@ static const char usage_message[] =
452 452
     "                  Only valid in a client-specific config file.\n"
453 453
     "--disable       : Client is disabled.\n"
454 454
     "                  Only valid in a client-specific config file.\n"
455
+    "--override-username: Overrides the client-specific username to be used.\n"
456
+    "                  Only valid in a client-specific config file.\n"
455 457
     "--verify-client-cert [none|optional|require] : perform no, optional or\n"
456 458
     "                  mandatory client certificate verification.\n"
457 459
     "                  Default is to require the client to supply a certificate.\n"
... ...
@@ -8000,6 +8002,23 @@ add_option(struct options *options,
8000 8000
         VERIFY_PERMISSION(OPT_P_INSTANCE);
8001 8001
         options->disable = true;
8002 8002
     }
8003
+    else if (streq(p[0], "override-username") && p[1] && !p[2])
8004
+    {
8005
+        VERIFY_PERMISSION(OPT_P_INSTANCE);
8006
+        if (strlen(p[1]) > TLS_USERNAME_LEN)
8007
+        {
8008
+            msg(msglevel, "override-username exceeds the maximum length of %d "
8009
+                "characters", TLS_USERNAME_LEN);
8010
+
8011
+            /* disable the connection since ignoring the request to
8012
+             * set another username might cause serious problems */
8013
+            options->disable = true;
8014
+        }
8015
+        else
8016
+        {
8017
+            options->override_username = p[1];
8018
+        }
8019
+    }
8003 8020
     else if (streq(p[0], "tcp-nodelay") && !p[1])
8004 8021
     {
8005 8022
         VERIFY_PERMISSION(OPT_P_GENERAL);
... ...
@@ -505,6 +505,7 @@ struct options
505 505
     const char *client_config_dir;
506 506
     bool ccd_exclusive;
507 507
     bool disable;
508
+    const char *override_username;
508 509
     int n_bcast_buf;
509 510
     int tcp_queue_limit;
510 511
     struct iroute *iroutes;
... ...
@@ -595,9 +595,19 @@ prepare_auth_token_push_reply(struct tls_multi *tls_multi, struct gc_arena *gc,
595 595
      */
596 596
     if (tls_multi->auth_token)
597 597
     {
598
-        push_option_fmt(gc, push_list, M_USAGE,
599
-                        "auth-token %s",
598
+        push_option_fmt(gc, push_list, M_USAGE, "auth-token %s",
600 599
                         tls_multi->auth_token);
600
+
601
+        char *base64user = NULL;
602
+        int ret = openvpn_base64_encode(tls_multi->locked_username,
603
+                                        (int)strlen(tls_multi->locked_username),
604
+                                        &base64user);
605
+        if (ret < USER_PASS_LEN && ret > 0)
606
+        {
607
+            push_option_fmt(gc, push_list, M_USAGE, "auth-token-user %s",
608
+                            base64user);
609
+        }
610
+        free(base64user);
601 611
     }
602 612
 }
603 613
 
... ...
@@ -1262,6 +1262,7 @@ tls_multi_free(struct tls_multi *multi, bool clear)
1262 1262
     free(multi->peer_info);
1263 1263
     free(multi->locked_cn);
1264 1264
     free(multi->locked_username);
1265
+    free(multi->locked_original_username);
1265 1266
 
1266 1267
     cert_hash_free(multi->locked_cert_hash_set);
1267 1268
 
... ...
@@ -627,7 +627,16 @@ struct tls_multi
627 627
      * Our locked common name, username, and cert hashes (cannot change during the life of this tls_multi object)
628 628
      */
629 629
     char *locked_cn;
630
+
631
+    /** The locked username is the username we assume the client is using.
632
+     * Normally the username used for initial authentication unless
633
+     * overridden by --override-username */
630 634
     char *locked_username;
635
+
636
+    /** The username that client initially used before being overridden
637
+     * by --override-user */
638
+    char *locked_original_username;
639
+
631 640
     struct cert_hash_set *locked_cert_hash_set;
632 641
 
633 642
     /** Time of last when we updated the cached state of
... ...
@@ -48,9 +48,6 @@
48 48
 #include "push.h"
49 49
 #include "ssl_util.h"
50 50
 
51
-/** Maximum length of common name */
52
-#define TLS_USERNAME_LEN 64
53
-
54 51
 static void
55 52
 string_mod_remap_name(char *str)
56 53
 {
... ...
@@ -85,10 +82,7 @@ tls_deauthenticate(struct tls_multi *multi)
85 85
     }
86 86
 }
87 87
 
88
-/*
89
- * Set the given session's common_name
90
- */
91
-static void
88
+void
92 89
 set_common_name(struct tls_session *session, const char *common_name)
93 90
 {
94 91
     if (session->common_name)
... ...
@@ -153,7 +147,10 @@ tls_lock_username(struct tls_multi *multi, const char *username)
153 153
 {
154 154
     if (multi->locked_username)
155 155
     {
156
-        if (strcmp(username, multi->locked_username) != 0)
156
+        /* If the username has been overridden, we accept both the original
157
+         * username and the changed username */
158
+        if (strcmp(username, multi->locked_username) != 0
159
+            &&  (!multi->locked_original_username || strcmp(username, multi->locked_original_username) != 0))
157 160
         {
158 161
             msg(D_TLS_ERRORS, "TLS Auth Error: username attempted to change from '%s' to '%s' -- tunnel disabled",
159 162
                 multi->locked_username,
... ...
@@ -1604,6 +1601,17 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
1604 1604
      */
1605 1605
     bool skip_auth = false;
1606 1606
 
1607
+    /* Replace username early if override-username is in effect but only
1608
+     * if client is sending the original username */
1609
+    if (multi->locked_original_username
1610
+        && strncmp(up->username, multi->locked_original_username, sizeof(up->username)) == 0)
1611
+    {
1612
+        msg(D_MULTI_LOW, "TLS: Replacing client provided username '%s' with "
1613
+            "username from override-user '%s'", up->username,
1614
+            multi->locked_username);
1615
+        strncpy(up->username, multi->locked_username, sizeof(up->username));
1616
+    }
1617
+
1607 1618
     /*
1608 1619
      * If server is configured with --auth-gen-token and the client sends
1609 1620
      * something that looks like an authentication token, this
... ...
@@ -51,6 +51,9 @@
51 51
 /** Maximum certificate depth we will allow */
52 52
 #define MAX_CERT_DEPTH 16
53 53
 
54
+/** Maximum length of common name */
55
+#define TLS_USERNAME_LEN 64
56
+
54 57
 /** Structure containing the hash for a single certificate */
55 58
 struct cert_hash {
56 59
     unsigned char sha256_hash[256/8];
... ...
@@ -146,6 +149,16 @@ void tls_lock_common_name(struct tls_multi *multi);
146 146
  */
147 147
 const char *tls_common_name(const struct tls_multi *multi, const bool null);
148 148
 
149
+
150
+/**
151
+ * Sets the common name field for the given tunnel
152
+ *
153
+ * @param multi         The tunnel to set the common name for
154
+ * @param common_name   The name to set the common name to
155
+ */
156
+void
157
+set_common_name(struct tls_session *session, const char *common_name);
158
+
149 159
 /**
150 160
  * Returns the username field for the given tunnel
151 161
  *