Browse code

Implement auth-token-user

When not using username and password (i.e. auth-user-pass) it can still
be desirable to provide the client with an auth-token, e.g. for allowing
a session to continue after a reconnect without requiring 2FA again.

However, without --auth-user-pass openvpn does not have a username and will
ignore any pushed auth-token command.

This patch adds support for auth-token-user to set the username that should
be used for auth-token

The spec of using auth-token-user base64-encoded-user are the ones that
OpenVPN3 already implements.

Patch V2: Improve style, fix comments and commit message

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Message-Id: <20210520151148.2565578-2-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg22417.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Arne Schwabe authored on 2021/05/21 00:11:41
Showing 6 changed files
... ...
@@ -50,6 +50,14 @@ configuration.
50 50
   after a failed auth. Older clients will keep using the token value and
51 51
   react according to ``--auth-retry``
52 52
 
53
+--auth-token-user base64username
54
+  Companion option to ``--auth-token``. This options allows to override
55
+  the username used by the client when reauthenticating with the ``auth-token``.
56
+  It also allows to use ``--auth-token`` in setups that normally do not use
57
+  username and password.
58
+
59
+  The username has to be base64 encoded.
60
+
53 61
 --auth-user-pass
54 62
   Authenticate with server using username/password.
55 63
 
... ...
@@ -490,22 +490,49 @@ void
490 490
 set_auth_token(struct user_pass *up, struct user_pass *tk, const char *token)
491 491
 {
492 492
 
493
-    if (strlen(token) && (up->defined || tk->defined))
493
+    if (strlen(token))
494 494
     {
495
-        /* auth-token has no password, so it needs the username
496
-         * either already set or copied from up */
497 495
         strncpynt(tk->password, token, USER_PASS_LEN);
498
-        if (up->defined)
496
+        tk->token_defined = true;
497
+
498
+        /*
499
+         * --auth-token has no username, so it needs the username
500
+         * either already set or copied from up, or later set by
501
+         * --auth-token-user
502
+         *
503
+         * Do not overwrite the username if already set to avoid
504
+         * overwriting an username set by --auth-token-user
505
+         */
506
+        if (up->defined && !tk->defined)
499 507
         {
500 508
             strncpynt(tk->username, up->username, USER_PASS_LEN);
509
+            tk->defined = true;
501 510
         }
502
-        tk->defined = true;
503 511
     }
504 512
 
505 513
     /* Cleans user/pass for nocache */
506 514
     purge_user_pass(up, false);
507 515
 }
508 516
 
517
+void
518
+set_auth_token_user(struct user_pass *tk, const char *username)
519
+{
520
+    if (strlen(username))
521
+    {
522
+        /* Clear the username before decoding to ensure no old material is left
523
+         * and also allow decoding to not use all space to ensure the last byte is
524
+         * always 0 */
525
+        CLEAR(tk->username);
526
+        int len = openvpn_base64_decode(username, tk->username, USER_PASS_LEN - 1);
527
+        tk->defined = len > 0;
528
+        if (!tk->defined)
529
+        {
530
+            msg(D_PUSH, "Error decoding auth-token-username");
531
+        }
532
+    }
533
+}
534
+
535
+
509 536
 /*
510 537
  * Process string received by untrusted peer before
511 538
  * printing to console or log file.
... ...
@@ -56,6 +56,9 @@ const char *hostname_randomize(const char *hostname, struct gc_arena *gc);
56 56
 struct user_pass
57 57
 {
58 58
     bool defined;
59
+    /* For auth-token username and token can be set individually, so we
60
+     * use this second bool to track if the token (password) is defined */
61
+    bool token_defined;
59 62
     bool nocache;
60 63
 
61 64
 /* max length of username/password */
... ...
@@ -138,19 +141,31 @@ void fail_user_pass(const char *prefix,
138 138
 void purge_user_pass(struct user_pass *up, const bool force);
139 139
 
140 140
 /**
141
- * Sets the auth-token to token if a username is available from either
142
- * up or already present in tk. The method will also purge up if
141
+ * Sets the auth-token to token. If a username is available from
142
+ * either up or already present in tk that will be used as default
143
+ * username for the token. The method will also purge up if
143 144
  * the auth-nocache option is active.
144 145
  *
145 146
  * @param up        (non Auth-token) Username/password
146 147
  * @param tk        auth-token userpass to set
147
- * @param token     token to use as password for the
148
+ * @param token     token to use as password for the auth-token
148 149
  *
149 150
  * @note    all parameters to this function must not be null.
150 151
  */
151 152
 void set_auth_token(struct user_pass *up, struct user_pass *tk,
152 153
                     const char *token);
153 154
 
155
+/**
156
+ * Sets the auth-token username by base64 decoding the passed
157
+ * username
158
+ *
159
+ * @param tk        auth-token userpass to set
160
+ * @param username  base64 encoded username to set
161
+ *
162
+ * @note    all parameters to this function must not be null.
163
+ */
164
+void set_auth_token_user(struct user_pass *tk, const char *username);
165
+
154 166
 /*
155 167
  * Process string received by untrusted peer before
156 168
  * printing to console or log file.
... ...
@@ -8311,6 +8311,11 @@ add_option(struct options *options,
8311 8311
         }
8312 8312
 #endif
8313 8313
     }
8314
+    else if (streq(p[0], "auth-token-user") && p[1] && !p[2])
8315
+    {
8316
+        VERIFY_PERMISSION(OPT_P_ECHO);
8317
+        ssl_set_auth_token_user(p[1]);
8318
+    }
8314 8319
     else if (streq(p[0], "single-session") && !p[1])
8315 8320
     {
8316 8321
         VERIFY_PERMISSION(OPT_P_GENERAL);
... ...
@@ -446,6 +446,12 @@ ssl_set_auth_token(const char *token)
446 446
     set_auth_token(&auth_user_pass, &auth_token, token);
447 447
 }
448 448
 
449
+void
450
+ssl_set_auth_token_user(const char *username)
451
+{
452
+    set_auth_token_user(&auth_token, username);
453
+}
454
+
449 455
 /*
450 456
  * Cleans an auth token and checks if it was active
451 457
  */
... ...
@@ -2310,8 +2316,8 @@ key_method_2_write(struct buffer *buf, struct tls_multi *multi, struct tls_sessi
2310 2310
         }
2311 2311
     }
2312 2312
 
2313
-    /* write username/password if specified */
2314
-    if (auth_user_pass_enabled)
2313
+    /* write username/password if specified or we are using a auth-token */
2314
+    if (auth_user_pass_enabled || (auth_token.token_defined && auth_token.defined))
2315 2315
     {
2316 2316
 #ifdef ENABLE_MANAGEMENT
2317 2317
         auth_user_pass_setup(session->opt->auth_user_pass_file, session->opt->sci);
... ...
@@ -2324,7 +2330,7 @@ key_method_2_write(struct buffer *buf, struct tls_multi *multi, struct tls_sessi
2324 2324
          * If we have a valid auth-token, send that instead of real
2325 2325
          * username/password
2326 2326
          */
2327
-        if (auth_token.defined)
2327
+        if (auth_token.token_defined && auth_token.defined)
2328 2328
         {
2329 2329
             up = &auth_token;
2330 2330
         }
... ...
@@ -450,6 +450,8 @@ void ssl_purge_auth(const bool auth_user_pass_only);
450 450
 
451 451
 void ssl_set_auth_token(const char *token);
452 452
 
453
+void ssl_set_auth_token_user(const char *username);
454
+
453 455
 bool ssl_clean_auth_token(void);
454 456
 
455 457
 #ifdef ENABLE_MANAGEMENT