From patchwork Thu Nov 12 21:40:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 5682 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:6022:529c:b0:11a:a05e:cb97 with SMTP id x28csp2524734lax; Thu, 12 Nov 2026 13:40:42 -0800 (PST) X-Forwarded-Encrypted: i=2; AL+hErEETsTVRTBeTFHI5LOoYw4C5o6+PuWpbxbIolfyLtmCTSeoztmtHTCYRmRCL6PdO2UU8t4efWOLLfI=@openvpn.net X-Received: by 2002:a05:7300:828e:b0:356:7ddb:2e54 with SMTP id 5a478bee46e88-37966a5ce93mr352264eec.29.1794519642443; Thu, 12 Nov 2026 13:40:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1794519642; cv=none; d=google.com; s=arc-20260327; b=THv0GOkhitoVLoi2s+uzgVCU5AH0BImq1jsOgmT6IK3ww7q/b0aGtN2qMcTZAbuf1y rOZH5n7mAmz5SgOV63yqguUi2tYmVxOyIodKVwm1IOY2+BPLePsgculby9dAfAraizkJ +a+BgzaX9VX71SoTnsQAIfMqDntx8xTg26nb5izkyQh3tx9epc4BdeYF4IwzzPLC32gC qkNxup8eAgTrdAwKMcDhkwZKmkKY0HyFUXlUe7hDt8dWthyI8V2jxxN8/yBHxEXKA1M+ F8ixMwo5qSrqG/eh+BMhx8THz7yZPJbaztLoVC62pumh/abqQKQpAXNxrYCNjjJ44zZO J0TQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; 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=tGnAVYrNI5Nfu6tUOOv+gIUm9b/srmkBxU5IMvpPEwk=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=laWaCYaFqEhfJaDdb+jldsB9QveNj3raICl38nobZImhZA0LFAiJMQ+T9VPWIW8MRy D8iET6LavfeBsIAkOCKXxi7Mzw8XPqBmZoyeZbFxqsA0LjWsGtFjDbcyzkZWmkBDKhVh 77G3+d6oBUoVkhiN4p1h4In4ZXtObdatlGThbSRLSk4p2HraGmXRfhO9mnD2AxPL5Rf5 u07sVc5DpULQrfDvbojMw7eQQaK6KsjRx+mQJtG+a8/gT2MYbVOv1+o6jkd+u/cefk3B cp2xo9Pe3+idv9/C/SQGNfvuRKtg4+0t+OBf1ciSbCVU5Rj5OwJY1K8hlXlA6ss7QsW/ /NZQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b="Z/tEv1Xk"; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=jQDWDwet; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=k6Q2DO7j; 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 5a478bee46e88-379566cfb46si1154451eec.6.2026.11.12.13.40.42 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 12 Nov 2026 13:40:42 -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="Z/tEv1Xk"; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=jQDWDwet; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=k6Q2DO7j; 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-Unsubscribe-Post: List-Owner; bh=tGnAVYrNI5Nfu6tUOOv+gIUm9b/srmkBxU5IMvpPEwk=; b=Z/tEv1Xk3uZksb FpKe4Yvt/iAixD9kWYN7n3FC4c+gxwn3mn7qJDFp2r2LCTrGclEZHw54++egBpFPCVpqa89E65sy4 dyG/wW3fqD68Eq0xeqcR4Fs2uAn56EgiNbIMWcWvnnJuG9pTYqnXTxNDlLHLDWA9KMNXIVt4noJ8M xRs=; 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.100.1) (envelope-from ) id 1xRcX8-000000002rD-0QeM; Thu, 12 Nov 2026 21:40:38 +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.100.1) (envelope-from ) id 1xRcX6-000000002r6-3vMw for openvpn-devel@lists.sourceforge.net; Thu, 12 Nov 2026 21:40:36 +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-Unsubscribe-Post:List-Subscribe:List-Post:List-Owner:List-Archive; bh=MdR2X8xFxiO8KhYrhktQePWxkN8vp5RbiAJXb+0W+yk=; b=jQDWDwet4l/I/NWWUBWA9243qH qyBXOcjpY2HyeWGm55MCKegHCKqbJHTvq2QEAqAVrgQLBdQX8l0jVffPrfxNGt1yo3nF50STh3qW2 3Pb36vAFfGCx9vyEeHuVqdJhQdLgxwA+0Lx+YoL3Fn4FFOXuxXvvZW2ZnhLe3lihWrmE=; 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-Unsubscribe-Post: List-Subscribe:List-Post:List-Owner:List-Archive; bh=MdR2X8xFxiO8KhYrhktQePWxkN8vp5RbiAJXb+0W+yk=; b=k6Q2DO7jCQsYtJ1AbJKm05PrZm k3NFeF+MkKhngz6c1P8Baj4u+nG0WfTFNUFZnkWQf9mvj0p6X8UkEk+UXncnwNJFluTYXx8kAtta2 1dEEvEMhIQ4/8ymF8915f9N+xTwLoKbhpfZg0lCIkC9ePtiUub1MduWLvn+Bt3T9NhIk=; 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.100.1) id 1xRcX4-000000005JE-3spO for openvpn-devel@lists.sourceforge.net; Thu, 12 Nov 2026 21:40:36 +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 6ACLeSxG013485 for ; Thu, 12 Nov 2026 22:40:28 +0100 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.2/8.18.1/Submit) id 6ACLeSfd013484 for openvpn-devel@lists.sourceforge.net; Thu, 12 Nov 2026 22:40:28 +0100 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Thu, 12 Nov 2026 22:40:21 +0100 Message-ID: <20261112214027.13465-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.55.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: Razvan Cojocaru openvpn.exe takes a lone argument as the config file only when there are no other arguments, but the service adds --msg-channel. Since c139e82d openvpn.exe takes the config file as an extra parameter [...] 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: 1xRcX4-000000005JE-3spO Subject: [Openvpn-devel] [PATCH v7] openvpnserv: reject a config file passed without --config 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: 1881677477256134874 X-GMAIL-MSGID: 1881690228381482693 From: Razvan Cojocaru openvpn.exe takes a lone argument as the config file only when there are no other arguments, but the service adds --msg-channel. Since c139e82d openvpn.exe takes the config file as an extra parameter of --msg-channel and ignores it. ValidateOptions() treated it as the config file, but only ever returned FALSE for it, with an empty error message if the path was fine. Return an error to all users when the options don't start with an option, and drop the lone argument special case in ValidateOptions(), so that a lone option is checked against the white list. Change-Id: I57469ca50c551ad475510690ecfa2d5e7cf9cfcf Signed-off-by: Razvan Cojocaru Acked-by: Heiko Hund Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/2159 --- 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/+/2159 This mail reflects revision 7 of this Change. Acked-by according to Gerrit (reflected above): Heiko Hund diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c index 81205d6..9de747e 100644 --- a/src/openvpnserv/interactive.c +++ b/src/openvpnserv/interactive.c @@ -354,6 +354,29 @@ ReturnError(pipe, GetLastError(), func, 1, &exit_event); } +/** + * Check whether the user options start with a non-option + * + * openvpn.exe takes a lone argument as the config file only when there are + * no other arguments, and takes non-options after the --msg-channel handle + * as its extra parameters. + * + * @param cmdline openvpn.exe command line built by the service + * + * @return BOOL to indicate if the first user argument is not an option + */ +static BOOL +StartsWithNonOption(const WCHAR *cmdline) +{ + int argc; + WCHAR **argv = CommandLineToArgvW(cmdline, &argc); + /* "openvpn --msg-channel ..." */ + BOOL ret = argv && argc > 3 && !IsOption(argv[3]); + + LocalFree(argv); + return ret; +} + /* * Validate options against a white list. Also check the config_file is * inside the config_dir. The white list is defined in validate.c @@ -396,20 +419,6 @@ goto out; } - /* - * If only one argument, it is the config file - */ - if (argc == first + 1) - { - WCHAR *argv_tmp[2] = { L"--config", argv[first] }; - - if (!CheckOption(workdir, 2, argv_tmp, &settings)) - { - swprintf(errmsg, capacity, msg1, argv[first], workdir, settings.ovpn_admin_group); - } - goto out; - } - for (i = first; i < argc; ++i) { if (!IsOption(argv[i])) @@ -3839,6 +3848,14 @@ swprintf(cmdline, cmdline_size, L"openvpn --msg-channel %" PRIuPTR L" %ls", (uintptr_t)svc_pipe, sud.options); + if (StartsWithNonOption(cmdline)) + { + ReturnError(pipe, ERROR_STARTUP_DATA, + L"Options must start with an option, pass the config file with --config", 1, + &exit_event); + goto out; + } + /* * Only authorized users are allowed to use any command line options or * have the config file in locations other than the global config directory. diff --git a/tests/unit_tests/openvpnserv/test_openvpnserv.c b/tests/unit_tests/openvpnserv/test_openvpnserv.c index 20fb1af..7db61f7 100644 --- a/tests/unit_tests/openvpnserv/test_openvpnserv.c +++ b/tests/unit_tests/openvpnserv/test_openvpnserv.c @@ -301,6 +301,33 @@ assert_merge_result(L"", L"", L""); } +static void +test_starts_with_non_option(void **state) +{ + assert_true(StartsWithNonOption(L"openvpn --msg-channel 1 foo.ovpn")); + assert_true(StartsWithNonOption(L"openvpn --msg-channel 1 \"my vpn.ovpn\"")); + assert_true(StartsWithNonOption(L"openvpn --msg-channel 1 foo.ovpn --verb 3")); + assert_true(StartsWithNonOption(L"openvpn --msg-channel 1 foo.ovpn bar")); + assert_true(StartsWithNonOption(L"openvpn --msg-channel 1 \"\"")); + + assert_false(StartsWithNonOption(L"openvpn --msg-channel 1 ")); + assert_false(StartsWithNonOption(L"openvpn --msg-channel 1 --persist-tun")); + assert_false(StartsWithNonOption(L"openvpn --msg-channel 1 --config foo.ovpn")); +} + +static void +test_validate_lone_option(void **state) +{ + WCHAR errmsg[512]; + + assert_true(ValidateOptions(NULL, L"C:\\OpenVPN\\config\\", + L"openvpn --msg-channel 1 --script-security", errmsg, + _countof(errmsg))); + assert_false(ValidateOptions(NULL, L"C:\\OpenVPN\\config\\", + L"openvpn --msg-channel 1 --persist-tun", errmsg, + _countof(errmsg))); +} + int wmain(void) { @@ -311,6 +338,8 @@ cmocka_unit_test(test_append_search_list), cmocka_unit_test(test_remove_search_list_tokens), cmocka_unit_test(test_merge_openvpn_environment), + cmocka_unit_test(test_starts_with_non_option), + cmocka_unit_test(test_validate_lone_option), }; int ret = cmocka_run_group_tests_name("openvpnserv tests", tests, NULL, NULL);