From patchwork Mon Mar 2 20:53:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4790 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:292f:b0:83c:d90d:321 with SMTP id f15csp2039561maw; Mon, 2 Mar 2026 12:53:44 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCU4ugybivGSOaNwRWbVGBHy5CcTJHcM8zWdNReVRHa6Vg5Ve7Iu2pj+1sEY+RNXsoZqeX0TYST+vIU=@openvpn.net X-Received: by 2002:a05:690e:154e:10b0:64c:9f6d:b5f7 with SMTP id 956f58d0204a3-64cc207b9c0mr8614574d50.9.1772484824736; Mon, 02 Mar 2026 12:53:44 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1772484824; cv=none; d=google.com; s=arc-20240605; b=XAEQbStqQN50nbej2M4H8cXqHbbCuMCvawld2uI9Nh8NLx5B5xFFJjRticl2oVejPo Zv5nCZcsTuVUty/UeV90TlaWJYqEVcE1uYmozDX3nG5qN2LW6bNKYAl72IosTq2mbj+s 4Pwcm8pfnbVo/CJOgpOIUUwCZWoDHKWUK/R2VJb8/Gm40R6fpYxb+/PIhRoB632Bc+1s tIbiKHDGpnkQVhQMH4bVveluyDYC2RDpdtGCJPB78rcizxxm0rw8GDvDCksRz9jo65ub oDLPYynF8bNl1iBE4BucIeEcRNeJIxnwIbsi+djWUyy3DYkcJ5jaZqdGmL1IjQUhaoHm Jelw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding: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=lXTOQ0Sckp+MJgcGcmWA0JIriRaaieuEOq8mXP5JgeI=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=PS8Issk/480D3uoHOV1B2vALyXU5qQbQMHu4mnPWrfgc2OAfxorpnX+oAUpDxXz59W su5FL34UEP1KBQKOyoPp6UONKOqK+2gK/4HH6HEiqwVLuPvvaX97LnCB1wUmFAc1kVIC 3XznOuCPsrjBTesR/57nY1dtekuGA7U2AGFPH5jWwyNeorsFZCBLZ7rpSwlwx5wH2lkP o+Fi6MtvrPN08VSRmVBfur4qbImWB2hRTJN2pTABjeih5mI1a3iyqejOhTY30zuJPOgc EJf7JRxRl8eBaqPrnTGH9HACtVrNdwxB7mhhZtJFOdNgsWcGM4CNZgYQWHZGq8VsNvfx ZZFg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=lRdM5QD5; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=ZuNl99sy; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=htutnHu+; 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 956f58d0204a3-64cb765b843si10876354d50.127.2026.03.02.12.53.44 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 02 Mar 2026 12:53: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=pass header.i=@lists.sourceforge.net header.s=beta header.b=lRdM5QD5; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=ZuNl99sy; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=htutnHu+; 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=lXTOQ0Sckp+MJgcGcmWA0JIriRaaieuEOq8mXP5JgeI=; b=lRdM5QD5emeNshFpM6bh+2j96y Xuj0XNlEeMgfb3myFGAiXHpNskXJiqwDjo6iByRKAqQWQoJ2bjuHvoKQxDb0kj9vV3BXlM7C3qxnd sbGdax7q9JDYyk59ufpr5UW7iOKuWnytz1eyy01hISWdFnveGykIQuqYzrW+Subt91K0=; Received: from [127.0.0.1] (helo=sfs-ml-3.v29.lw.sourceforge.com) by sfs-ml-3.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1vxAGr-00046A-Bb; Mon, 02 Mar 2026 20:53:41 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-3.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1vxAGp-000463-Cy for openvpn-devel@lists.sourceforge.net; Mon, 02 Mar 2026 20:53: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:To:From:Sender:Reply-To:Cc: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=lERRPi0wbcr0bpnSWKh1EcNKeRGIu9mWDzabLnMghP8=; b=ZuNl99sy9ljFzrjI1/VQjhu0yd KmNS1L4IAv/piQJLOOMGnDBebtp1rEQuweXt+kgZ5SAS8enb/b2599M8yfofj4Bmnojqo2W3yW+WP Y90r0GGTXUvAW31gj7aoGzwnGFj/6eonvv3/lzSF5jiuS6vpxgm0G5dzlZSebqWxakQg=; 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:To:From:Sender:Reply-To:Cc: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=lERRPi0wbcr0bpnSWKh1EcNKeRGIu9mWDzabLnMghP8=; b=htutnHu+swFezHVdZJikTcFhuu XENw22pcT+s7Ubi6oSKSWxR1r9MsMmvX/nwZC5rpgdYZQMSyl/J4NVnnXQuglMQR/FNb1+kbE2Ymd Cj4sF9x7VlTk1rD17XJHPIUpCQ2VUlrndLZ9u/bW6TyNsQiiWSRo4dh/o0SZ05EFAZHg=; Received: from [193.149.48.129] (helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1vxAGn-0007Wy-Ve for openvpn-devel@lists.sourceforge.net; Mon, 02 Mar 2026 20:53:39 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.18.1/8.18.1) with ESMTP id 622KrQtj010067 for ; Mon, 2 Mar 2026 21:53:26 +0100 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.1/8.18.1/Submit) id 622KrQbM010066 for openvpn-devel@lists.sourceforge.net; Mon, 2 Mar 2026 21:53:26 +0100 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Mon, 2 Mar 2026 21:53:17 +0100 Message-ID: <20260302205324.10025-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.52.0 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-2.hosts.colo.sdot.me", 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: Selva Nair Specify "--auth-user-pass username-only" for openvpn to prompt for only username, not password. Prompt via management interface uses the usual ">PASSWORD 'Auth' " prompt with type "username" instead o [...] Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1vxAGn-0007Wy-Ve Subject: [Openvpn-devel] [PATCH v6] Add an optional username-only flag for auth-user-pass 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: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1858585047931128017?= X-GMAIL-MSGID: =?utf-8?q?1858585047931128017?= From: Selva Nair Specify "--auth-user-pass username-only" for openvpn to prompt for only username, not password. Prompt via management interface uses the usual ">PASSWORD 'Auth' " prompt with type "username" instead of "username/password". Internally, the password gets set as "[[BLANK]]" which is currently used as tag for blank password. Not compatible with --static-challenge or when username and password are inlined or read from a file. In such cases, the user hard-code a dummy password in the file instead. Change-Id: I788f76e6a70a9c20bca3367140d2741bd0551582 Signed-off-by: Selva Nair Acked-by: Arne Schwabe Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1548 --- 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/+/1548 This mail reflects revision 6 of this Change. Acked-by according to Gerrit (reflected above): Arne Schwabe diff --git a/doc/man-sections/client-options.rst b/doc/man-sections/client-options.rst index b9ae7ce..0b7c308 100644 --- a/doc/man-sections/client-options.rst +++ b/doc/man-sections/client-options.rst @@ -68,7 +68,9 @@ auth-user-pass up If ``up`` is present, it must be a file containing username/password on 2 - lines. If the password line is missing, OpenVPN will prompt for one. + lines or a flag named :code:`username-only` to indicate no password + should be prompted for. In the former case, if the password line is missing + in the file, OpenVPN will prompt for one. If ``up`` is omitted, username/password will be prompted from the console. @@ -84,6 +86,20 @@ where password is optional, and will be prompted from the console if missing. + The :code:`username-only` flag is meant to be used with SSO authentication. + In this case the user will be asked for a username but not password. Instead, + a dummy password :code:`[[BLANK]]` is generated internally and submitted to + the server. See management-notes.txt for how this option affects username/password + prompt via the management interface. For the console, it simply eliminates + the password prompt. + + The :code:`username-only` flag cannot be used along with embedding username and/or + password in the config file, or while reading them from an external file. In + such cases, if only username is relevant and no password prompt is desired, a + dummy password like 'no_passsword' should be embedded as well. This flag is also + incompatible with the ``--static-challenge`` option and legacy ``dynamic challenge`` + protocol. + The server configuration must specify an ``--auth-user-pass-verify`` script to verify the username/password provided by the client. diff --git a/doc/management-notes.txt b/doc/management-notes.txt index 86b74f3..500feaf 100644 --- a/doc/management-notes.txt +++ b/doc/management-notes.txt @@ -304,6 +304,19 @@ username "Auth" foo password "Auth" bar + Example 3: + + >PASSWORD:Need 'Auth' username + + OpenVPN needs a --auth-user-pass username. The + management interface client should respond: + + username "Auth" foo + + In this case the user should not be prompted for a password. + Support for such username-only prompting is conditional on the + client announcing a version >= 4. + The username/password itself can be in quotes, and special characters such as double quote or backslash must be escaped, for example, diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 1391aa85..4948b8d 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -655,10 +655,10 @@ enable_auth_user_pass(); #ifdef ENABLE_MANAGEMENT auth_user_pass_setup(c->options.auth_user_pass_file, c->options.auth_user_pass_file_inline, - &c->options.sc_info); + c->options.auth_user_pass_username_only, &c->options.sc_info); #else auth_user_pass_setup(c->options.auth_user_pass_file, c->options.auth_user_pass_file_inline, - NULL); + c->options.auth_user_pass_username_only, NULL); #endif } } @@ -3376,6 +3376,7 @@ } to.auth_user_pass_file = options->auth_user_pass_file; to.auth_user_pass_file_inline = options->auth_user_pass_file_inline; + to.auth_user_pass_username_only = options->auth_user_pass_username_only; to.auth_token_generate = options->auth_token_generate; to.auth_token_lifetime = options->auth_token_lifetime; to.auth_token_renewal = options->auth_token_renewal; diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 03ff5b3..91b5884 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -58,9 +58,6 @@ #define MANAGEMENT_ECHO_FLAGS 0 #endif -/* tag for blank username/password */ -static const char blank_up[] = "[[BLANK]]"; - struct management *management; /* GLOBAL */ /* static forward declarations */ @@ -729,6 +726,13 @@ { switch (man->connection.up_query_mode) { + case UP_QUERY_USERNAME: + if (strlen(man->connection.up_query.username)) + { + man->connection.up_query.defined = true; + } + break; + case UP_QUERY_USER_PASS: if (!strlen(man->connection.up_query.username)) { @@ -783,7 +787,9 @@ man_query_username(struct management *man, const char *type, const char *string) { const bool needed = - ((man->connection.up_query_mode == UP_QUERY_USER_PASS) && man->connection.up_query_type); + ((man->connection.up_query_mode == UP_QUERY_USER_PASS + || man->connection.up_query_mode == UP_QUERY_USERNAME) + && man->connection.up_query_type); man_query_user_pass(man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN); } @@ -3547,6 +3553,12 @@ prefix = "PASSWORD"; alert_type = "password"; } + else if ((man->connection.client_version >= 4) && (flags & GET_USER_PASS_USERNAME_ONLY)) + { + up_query_mode = UP_QUERY_USERNAME; + prefix = "PASSWORD"; + alert_type = "username"; + } else { up_query_mode = UP_QUERY_USER_PASS; diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index 38f437f..e5ad23f 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -264,6 +264,7 @@ #define UP_QUERY_PASS 2 #define UP_QUERY_NEED_OK 3 #define UP_QUERY_NEED_STR 4 +#define UP_QUERY_USERNAME 5 /* states */ #define MS_INITIAL 0 /* all sockets are closed */ diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index 9ff281c..8f687a8 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -216,6 +216,12 @@ msg(M_WARN, "Note: previous '%s' credentials failed", prefix); } + /* Use tag for blank password if we are not prompting for one */ + if (flags & GET_USER_PASS_USERNAME_ONLY) + { + strncpy(up->password, blank_up, sizeof(up->password)); + } + #ifdef ENABLE_MANAGEMENT /* * Get username/password from management interface? @@ -389,7 +395,7 @@ query_user_add(BSTR(&user_prompt), up->username, USER_PASS_LEN, true); } - if (password_from_stdin) + if (password_from_stdin && !(flags & GET_USER_PASS_USERNAME_ONLY)) { query_user_add(BSTR(&pass_prompt), up->password, USER_PASS_LEN, false); } diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h index e9cfadb..76bcac3 100644 --- a/src/openvpn/misc.h +++ b/src/openvpn/misc.h @@ -48,6 +48,9 @@ * Get and store a username/password */ +/* tag for blank username/password */ +static const char blank_up[] = "[[BLANK]]"; + struct user_pass { bool defined; @@ -123,6 +126,8 @@ #define GET_USER_PASS_INLINE_CREDS (1 << 10) /** indicates password and response should be concatenated */ #define GET_USER_PASS_STATIC_CHALLENGE_CONCAT (1 << 11) +/** indicate that only username should be prompted for auth-user-pass */ +#define GET_USER_PASS_USERNAME_ONLY (1 << 12) /** * Retrieves the user credentials from various sources depending on the flags. diff --git a/src/openvpn/options.c b/src/openvpn/options.c index fdbc678..d266c3e 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -510,7 +510,8 @@ " up is a file containing the username on the first line,\n" " and a password on the second. If either the password or both\n" " the username and the password are omitted OpenVPN will prompt\n" - " for them from console.\n" + " for them from console. If [up] is 'username-only', only username\n" + " will be prompted for from console or management interface.\n" "--pull : Accept certain config file options from the peer as if they\n" " were part of the local config file. Must be specified\n" " when connecting to a '--mode server' remote host.\n" @@ -3909,6 +3910,12 @@ { o->auth_token_renewal = o->renegotiate_seconds; } +#if ENABLE_MANAGEMENT + if (o->auth_user_pass_username_only && o->sc_info.challenge_text) + { + msg(M_USAGE, "'auth-user-pass username-only' cannot be used with static challenge"); + } +#endif pre_connect_save(o); } @@ -7705,7 +7712,13 @@ else if (streq(p[0], "auth-user-pass") && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL | OPT_P_INLINE); - if (p[1]) + options->auth_user_pass_username_only = false; + if (p[1] && streq(p[1], "username-only")) + { + options->auth_user_pass_username_only = true; + options->auth_user_pass_file = "stdin"; + } + else if (p[1]) { options->auth_user_pass_file = p[1]; options->auth_user_pass_file_inline = is_inline; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 3d8b505..f4a36fc 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -557,6 +557,7 @@ unsigned int push_update_options_found; /* tracks which option types have been reset in current PUSH_UPDATE sequence */ const char *auth_user_pass_file; bool auth_user_pass_file_inline; + bool auth_user_pass_username_only; struct options_pre_connect *pre_connect; int scheduled_exit_interval; diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 69d0e4e..962f5df 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -290,7 +290,8 @@ } void -auth_user_pass_setup(const char *auth_file, bool is_inline, const struct static_challenge_info *sci) +auth_user_pass_setup(const char *auth_file, bool is_inline, bool username_only, + const struct static_challenge_info *sci) { unsigned int flags = GET_USER_PASS_MANAGEMENT; @@ -298,6 +299,10 @@ { flags |= GET_USER_PASS_INLINE_CREDS; } + if (username_only) + { + flags |= GET_USER_PASS_USERNAME_ONLY; + } if (!auth_user_pass.defined && !auth_token.defined) { @@ -2099,10 +2104,12 @@ { #ifdef ENABLE_MANAGEMENT auth_user_pass_setup(session->opt->auth_user_pass_file, - session->opt->auth_user_pass_file_inline, session->opt->sci); + session->opt->auth_user_pass_file_inline, + session->opt->auth_user_pass_username_only, session->opt->sci); #else auth_user_pass_setup(session->opt->auth_user_pass_file, - session->opt->auth_user_pass_file_inline, NULL); + session->opt->auth_user_pass_file_inline, + session->opt->auth_user_pass_username_only, NULL); #endif struct user_pass *up = &auth_user_pass; diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 28a3b78..5822336 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -389,7 +389,7 @@ * credentials stored in the file, however, if is_inline is true then auth_file * contains the username/password inline. */ -void auth_user_pass_setup(const char *auth_file, bool is_inline, +void auth_user_pass_setup(const char *auth_file, bool is_inline, bool username_only, const struct static_challenge_info *sc_info); /* diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index fba01bb..6f310a5 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -396,6 +396,7 @@ const char *export_peer_cert_dir; const char *auth_user_pass_file; bool auth_user_pass_file_inline; + bool auth_user_pass_username_only; bool auth_token_generate; /**< Generate auth-tokens on successful * user/pass auth,seet via