From patchwork Wed Mar 5 12:21:21 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Lichtenheld X-Patchwork-Id: 4162 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:b342:b0:5e7:b9eb:58e8 with SMTP id w2csp713670maz; Wed, 5 Mar 2025 04:21:45 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCUu/0C/D4Bcv8/A/vi3TPO29n20VF0WZgF/V/NZupMf+ZjB+FHyhwIYi53RDEPsgRYnMz3SW1SyyTI=@openvpn.net X-Google-Smtp-Source: AGHT+IGbVC1VVLTtQobweTp3kAntbDbTYT0re0Y69DK/YeM4VPt5kaRETZfu0G6bBkfYx/qk3/QR X-Received: by 2002:a05:6830:3152:b0:727:42aa:e888 with SMTP id 46e09a7af769-72a1fd00abfmr1498007a34.27.1741177305184; Wed, 05 Mar 2025 04:21:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1741177305; cv=none; d=google.com; s=arc-20240605; b=A+kW/4vE5AEll5Y9puFGYIy4ue6hpw6oLQKvakCO9HRHiau1GRLppHea51jDQGbfqu B0xmr3hVS1NtNLenudWwcWqmdJlUw6emweHoAxO06FEhtz6mDrER9AJqlih18jXGioAg EMGMHSFZG7r5M4wvWAp4f9+y+InCwfT/V6GsDmK6uXF1XwplSmj0BkREH3stcvyfH8QP acnY/d7iLfkBmc6gFlvOQe7D2ALl5hzxgpy2ep9Csy3tYUf6t1E6Z1Lo+B3nnUgvBBXv 9cORwTPDEApJ+Qx+k43PA2RgEGObSJJRW6Xx0j7vs4k0poowUfI9eNgwQN6C5KNU6R5U 8fLg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:cc:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature; bh=HR9jiL1U9m4tGh6N0U9cGUHyCzMkuahIhRW3ogmkagc=; fh=H6mFs2pVe2/avHEN3ZXlblBRMUbfPBnqHVZOzcm8npI=; b=CHPWF37RD9yejaomEhXnhd3AiqmwNiDtVT2TUB5OLGQOM0iLqOPBy2p8p9kRX7x7M+ r6Ugua1vxY/Q2zyrg5eWpmNbq/9Zeq1kT9KTr6jBYhyc1lA8SFj3bOTgHx6NGuDeggZy mLtJEOX2kVYV+SS0vuELtbgRSStRLWGvJ503ZbUsgr37/8jpQWtWElmgBNWDq4ZYf1jf EImDToPpNXmdoFW5OqDMjLAqlM4/rdAcylQKXViioHNQj05vF+4VXiY9JATOviwsgf2c 62FYm7/a6lBxFfEMhbrNjGcQVPtjievaLBc/lhkWVUBPC5K8JYUBdaHZnMEihZmo+QCG RIUQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=J8zlINHR; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=XAALu4M7; dkim=neutral (body hash did not verify) header.i=@lichtenheld.com header.s=MBO0001 header.b=uzBXKH2o; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 46e09a7af769-72a138df270si3264084a34.136.2025.03.05.04.21.44 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 05 Mar 2025 04:21:44 -0800 (PST) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=J8zlINHR; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=XAALu4M7; dkim=neutral (body hash did not verify) header.i=@lichtenheld.com header.s=MBO0001 header.b=uzBXKH2o; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1tpnkq-00068i-Mb; Wed, 05 Mar 2025 12:21:40 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1tpnkn-00068a-Sz for openvpn-devel@lists.sourceforge.net; Wed, 05 Mar 2025 12:21:39 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=wszD8pYGqWZggHS2wkoTBr/FfNiG969JOTy7phax9Zs=; b=J8zlINHRgugoLsrqWvS+mUAp3C OmgHIAlNblHsUKi5mw7DID4a66zJ77FJYwiQtfHkgKgzMm5nHOlhECOqTLFuo60XqokyFjYDXJhlz wu0F9C4FjRwN+OiJ0jPIIUygugz7d/bAbhTdYuKuB33jtRRRxqEEPOfqFdAvSV0RFDsM=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=wszD8pYGqWZggHS2wkoTBr/FfNiG969JOTy7phax9Zs=; b=XAALu4M7BtE4WzLlOiGQg5RhDu 5M5qW6fQhHti/KN/flFfgP8gyEVRfNBLKt9p29UPdl/Y34O2nIgrg80IxEKcisF/gEVHf2meS2IZ6 yM57JVVEUWCXrZ3KRp2oXVsC6u4DwP9Wtlaz6Kbm8FAGg9kJFDISjKM7mQZ8Uf2xsRiM=; Received: from mout-p-201.mailbox.org ([80.241.56.171]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1tpnki-0004DZ-0y for openvpn-devel@lists.sourceforge.net; Wed, 05 Mar 2025 12:21:37 +0000 Received: from smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-201.mailbox.org (Postfix) with ESMTPS id 4Z7BVW3pJGz9tm6; Wed, 5 Mar 2025 13:21:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lichtenheld.com; s=MBO0001; t=1741177283; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wszD8pYGqWZggHS2wkoTBr/FfNiG969JOTy7phax9Zs=; b=uzBXKH2oX/lW0vCgLzz/6YAXjU17FD86cIfXuIBK9iIokWWradsVkD7o6/fkBfTES00xrA 1iC76BTy6gkN7OwIlxTmdtgUglXCs7WlXynCykXcPUE4wBFipNRead7VO3AnUZd/No0949 8Y7A3/NkOkVAyT/1AGnSS/KkIRwnXE7LZyuGRWxwKsfLgrloLrohromBzn2kRFSZ3N1mbo dQSOFoHYPuUO7wXlNPDhjw7Jn2jcLIyZakLs94bz7nke1s7qabzNgnGg+d6ZBE2X9UwAB4 EDc1b5A3pOE5OVAz0FmmXIVJwilx3nFfhcRc+I7uDJuyS9OHA6I+sGF2+/0Yvg== From: Frank Lichtenheld To: openvpn-devel@lists.sourceforge.net Date: Wed, 5 Mar 2025 13:21:21 +0100 Message-Id: <20250305122121.185535-1-frank@lichtenheld.com> In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: -0.9 (/) X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Arne Schwabe 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. Content analysis details: (-0.9 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/, low trust [80.241.56.171 listed in list.dnswl.org] 0.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [80.241.56.171 listed in sa-accredit.habeas.com] 0.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [80.241.56.171 listed in bl.score.senderscore.com] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1tpnki-0004DZ-0y Subject: [Openvpn-devel] [PATCH v8] Implement override-username X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Heiko Hund Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1825756733912488540?= X-GMAIL-MSGID: =?utf-8?q?1825756733912488540?= From: Arne Schwabe 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. Change-Id: Ia4095518d5e4447992a2974e0d7a159d79ba6b6f Signed-off-by: Arne Schwabe Acked-by: Heiko Hund --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/872 This mail reflects revision 8 of this Change. Acked-by according to Gerrit (reflected above): Heiko Hund diff --git a/Changes.rst b/Changes.rst index e011811..d6a0ba6 100644 --- a/Changes.rst +++ b/Changes.rst @@ -49,6 +49,12 @@ - IV constructed with XOR instead of concatenation to not have (parts) of the real IV on the wire +Allow overriding username with ``--override-username`` + This is intended to allow using auth-gen-token in scenarios where the + clients use certificates and multi-factor authentication. This will + also generate a 'push "auth-token-user newusername"' directives in + push replies. + Deprecated features ------------------- ``secret`` support has been removed by default. diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst index 3fe9862..efc921d 100644 --- a/doc/man-sections/server-options.rst +++ b/doc/man-sections/server-options.rst @@ -89,6 +89,12 @@ will lead to authentication bypass (as does returning success on a wrong password from a script). + **Note:** the username for ``--auth-gen-token`` can be overridden by + ``--override-user``. In this case the client will be pushed also the + ``--auth-token-user`` option and an auth token that is valid for that + username instead of the original username that the client authenticated + with. + --auth-gen-token-secret file Specifies a file that holds a secret for the HMAC used in ``--auth-gen-token`` If ``file`` is not present OpenVPN will generate a @@ -412,6 +418,32 @@ This option requires that ``--disable-occ`` NOT be used. +--override-username + Sets the username of a connection to the specified username. This username + will also be used by ``--auth-gen-token``. However, the overridden + username comes only into effect *after* the ``--client-config-dir`` has been + read and the ``--auth-user-pass-verify`` and ``--client-connect`` scripts + have been run. + + Also ``username-as-common-name`` will use the client provided username + as common-name. It is recommended to avoid the use of the + ``--override-username`` option if the option ``--username-as-common-name`` + is being used. + + The changed username will be picked up by the status output and also by + the ``--auth-gen-token`` option. It will also be pushed to the client + using ``--auth-token-user``. + + Special care should be taken that both the initial username of the client + and the overridden username are handled correctly when using + ``--override-username`` and the related options to avoid + authentication/authorisation bypasses. + + This option is mainly intended for use cases that use certificates and + multi factor authentication and therefore do not provide a username that + can be used for ``--auth-gen-token`` to allow providing a username in + these scenarios. + --port-share args Share OpenVPN TCP with another service diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index f76dad8..8163f42 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -42,7 +42,9 @@ #include "ssl_verify.h" #include "ssl_ncp.h" #include "vlan.h" +#include "auth_token.h" #include +#include #include "memdbg.h" @@ -2675,6 +2677,50 @@ NULL, }; +/** + * Overrides the locked username with the username of --override-username + * @param mi the multi instance that should be modified. + */ +static void +override_locked_username(struct multi_instance *mi) +{ + struct tls_multi *multi = mi->context.c2.tls_multi; + struct options *options = &mi->context.options; + struct tls_session *session = &multi->session[TM_ACTIVE]; + + if (!multi->locked_original_username + && strcmp(multi->locked_username, options->override_username) != 0) + { + multi->locked_original_username = multi->locked_username; + multi->locked_username = strdup(options->override_username); + + /* Override also the common name if username should be set as common + * name */ + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)) + { + set_common_name(session, multi->locked_username); + free(multi->locked_cn); + multi->locked_cn = NULL; + tls_lock_common_name(multi); + } + + /* Regenerate the auth-token if enabled */ + if (multi->auth_token_initial) + { + struct user_pass up; + CLEAR(up); + strncpynt(up.username, multi->locked_username, + sizeof(up.username)); + + generate_auth_token(&up, multi); + } + + msg(D_MULTI_LOW, "MULTI: Note, override-username changes username " + "from '%s' to '%s'", + multi->locked_original_username, + multi->locked_username); + } +} /* * Called as soon as the SSL/TLS connection is authenticated. * @@ -2778,6 +2824,11 @@ (*cur_handler_index)++; } + if (mi->context.options.override_username) + { + override_locked_username(mi); + } + /* Check if we have forbidding options in the current mode */ if (dco_enabled(&mi->context.options) && !dco_check_option(D_MULTI_ERRORS, &mi->context.options)) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 3ae44db..5b8d121 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -452,6 +452,8 @@ " Only valid in a client-specific config file.\n" "--disable : Client is disabled.\n" " Only valid in a client-specific config file.\n" + "--override-username: Overrides the client-specific username to be used.\n" + " Only valid in a client-specific config file.\n" "--verify-client-cert [none|optional|require] : perform no, optional or\n" " mandatory client certificate verification.\n" " Default is to require the client to supply a certificate.\n" @@ -7955,6 +7957,23 @@ VERIFY_PERMISSION(OPT_P_INSTANCE); options->disable = true; } + else if (streq(p[0], "override-username") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_INSTANCE); + if (strlen(p[1]) > TLS_USERNAME_LEN) + { + msg(msglevel, "override-username exceeds the maximum length of %d " + "characters", TLS_USERNAME_LEN); + + /* disable the connection since ignoring the request to + * set another username might cause serious problems */ + options->disable = true; + } + else + { + options->override_username = p[1]; + } + } else if (streq(p[0], "tcp-nodelay") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 3f4cb90..0b2a9f3 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -504,6 +504,7 @@ const char *client_config_dir; bool ccd_exclusive; bool disable; + const char *override_username; int n_bcast_buf; int tcp_queue_limit; struct iroute *iroutes; diff --git a/src/openvpn/push.c b/src/openvpn/push.c index a7cd3bf..894d85b 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -595,9 +595,19 @@ */ if (tls_multi->auth_token) { - push_option_fmt(gc, push_list, M_USAGE, - "auth-token %s", + push_option_fmt(gc, push_list, M_USAGE, "auth-token %s", tls_multi->auth_token); + + char *base64user = NULL; + int ret = openvpn_base64_encode(tls_multi->locked_username, + (int)strlen(tls_multi->locked_username), + &base64user); + if (ret < USER_PASS_LEN && ret > 0) + { + push_option_fmt(gc, push_list, M_USAGE, "auth-token-user %s", + base64user); + } + free(base64user); } } diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 439ce79..16fa4cd 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1262,6 +1262,7 @@ free(multi->peer_info); free(multi->locked_cn); free(multi->locked_username); + free(multi->locked_original_username); cert_hash_free(multi->locked_cert_hash_set); diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 9625a99..411b64f 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -628,7 +628,16 @@ * Our locked common name, username, and cert hashes (cannot change during the life of this tls_multi object) */ char *locked_cn; + + /** The locked username is the username we assume the client is using. + * Normally the username used for initial authentication unless + * overridden by --override-username */ char *locked_username; + + /** The username that client initially used before being overridden + * by --override-user */ + char *locked_original_username; + struct cert_hash_set *locked_cert_hash_set; /** Time of last when we updated the cached state of diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index e7d7ed6..dd230e8 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -48,9 +48,6 @@ #include "push.h" #include "ssl_util.h" -/** Maximum length of common name */ -#define TLS_USERNAME_LEN 64 - static void string_mod_remap_name(char *str) { @@ -85,10 +82,7 @@ } } -/* - * Set the given session's common_name - */ -static void +void set_common_name(struct tls_session *session, const char *common_name) { if (session->common_name) @@ -153,7 +147,10 @@ { if (multi->locked_username) { - if (strcmp(username, multi->locked_username) != 0) + /* If the username has been overridden, we accept both the original + * username and the changed username */ + if (strcmp(username, multi->locked_username) != 0 + && (!multi->locked_original_username || strcmp(username, multi->locked_original_username) != 0)) { msg(D_TLS_ERRORS, "TLS Auth Error: username attempted to change from '%s' to '%s' -- tunnel disabled", multi->locked_username, @@ -1613,6 +1610,17 @@ */ bool skip_auth = false; + /* Replace username early if override-username is in effect but only + * if client is sending the original username */ + if (multi->locked_original_username + && strncmp(up->username, multi->locked_original_username, sizeof(up->username)) == 0) + { + msg(D_MULTI_LOW, "TLS: Replacing client provided username '%s' with " + "username from override-user '%s'", up->username, + multi->locked_username); + strncpy(up->username, multi->locked_username, sizeof(up->username)); + } + /* * If server is configured with --auth-gen-token and the client sends * something that looks like an authentication token, this diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h index cd2ec24..eba3832 100644 --- a/src/openvpn/ssl_verify.h +++ b/src/openvpn/ssl_verify.h @@ -51,6 +51,9 @@ /** Maximum certificate depth we will allow */ #define MAX_CERT_DEPTH 16 +/** Maximum length of common name */ +#define TLS_USERNAME_LEN 64 + /** Structure containing the hash for a single certificate */ struct cert_hash { unsigned char sha256_hash[256/8]; @@ -146,6 +149,16 @@ */ const char *tls_common_name(const struct tls_multi *multi, const bool null); + +/** + * Sets the common name field for the given tunnel + * + * @param multi The tunnel to set the common name for + * @param common_name The name to set the common name to + */ +void +set_common_name(struct tls_session *session, const char *common_name); + /** * Returns the username field for the given tunnel *