From patchwork Thu Mar 10 00:57:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 2327 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director13.mail.ord1d.rsapps.net ([172.30.191.6]) by backend41.mail.ord1d.rsapps.net with LMTP id UKatH47sKWLmdAAAqwncew (envelope-from ) for ; Thu, 10 Mar 2022 07:18:22 -0500 Received: from proxy14.mail.ord1d.rsapps.net ([172.30.191.6]) by director13.mail.ord1d.rsapps.net with LMTP id 4DZKK47sKWJrFwAA91zNiA (envelope-from ) for ; Thu, 10 Mar 2022 07:18:22 -0500 Received: from smtp29.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy14.mail.ord1d.rsapps.net with LMTPS id EO02MJDsKWIlYgAAtEH5vw (envelope-from ) for ; Thu, 10 Mar 2022 07:18:24 -0500 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp29.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=greenie.muc.de X-Suspicious-Flag: YES X-Classification-ID: 2d1854e8-a06c-11ec-8ce6-525400f257a9-1-1 Received: from [216.105.38.7] ([216.105.38.7:53068] helo=lists.sourceforge.net) by smtp29.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 38/B5-16728-D8CE9226; Thu, 10 Mar 2022 07:18:22 -0500 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.94.2) (envelope-from ) id 1nSHjJ-000185-CB; Thu, 10 Mar 2022 12:17:16 +0000 Received: from [172.30.20.202] (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.94.2) (envelope-from ) id 1nSHjI-00017z-84 for openvpn-devel@lists.sourceforge.net; Thu, 10 Mar 2022 12:17:15 +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: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:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=LCbEAcmziIU+DF+7ALhbNcag4zRUzMUTRjmGvI8do/k=; b=juX1eA+aWwPDFX7nJVqCeLJld5 NLsaMwF5NLVHqTw8Krk2tVRJuDjVBCq+ujtKd38LDaijIVMqQZN9VIhLHFKWH7BTIXTQW06pICHT5 moAS0lc/0e0ID9lnTo2CzbGb80M5oGBM2oEukfPDfV/3mrHzouHCib6ZCbPUGwdVvX40=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version: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:In-Reply-To: References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=LCbEAcmziIU+DF+7ALhbNcag4zRUzMUTRjmGvI8do/k=; b=E XnyfnzQyP4Q9aEsLmYSbnTeG+BpZoMwyWirtgoQ6dz9CtN7Ewxd4NAbqM12dnxLCnQ9yC+PbgdL9J KRr2Hc+RJwpsa8Q6J/AZ1zjhkAMYm5cnvtI86NrG4XhZitNLApG8f2yIc6V3SzDPq4a0zzQGu8FtI XxV+dyhxlruoJa7Q=; Received: from vmail1.greenie.net ([195.30.8.66]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1nSHjB-0000RY-A7 for openvpn-devel@lists.sourceforge.net; Thu, 10 Mar 2022 12:17:14 +0000 Received: from gentoo.ov.greenie.net (gentoo.ov.greenie.net [IPv6:2001:608:0:814:0:0:f000:11]) by vmail1.greenie.net (8.17.1/8.16.1) with SMTP id 22ABvJe1076107; Thu, 10 Mar 2022 12:57:19 +0100 (CET) Received: (nullmailer pid 11199 invoked by uid 1000); Thu, 10 Mar 2022 11:57:19 -0000 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Thu, 10 Mar 2022 12:57:18 +0100 Message-Id: <20220310115718.11188-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.6.4 (vmail1.greenie.net [IPv6:2001:608:1:995a:20c:29ff:feb8:10eb]); Thu, 10 Mar 2022 12:57:19 +0100 (CET) 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: Without this patch, OpenVPN behaviour if more than one plugin wants to do deferred user/password authentication not well-defined, as there is just one set of auth control files and a single plugin sta [...] Content analysis details: (-2.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [195.30.8.66 listed in list.dnswl.org] 0.2 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 SPF_NONE SPF: sender does not publish an SPF Record X-Headers-End: 1nSHjB-0000RY-A7 Subject: [Openvpn-devel] [PATCH] Enable deferred auth for multiple plugins (RFC). 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: Gert Doering Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox Without this patch, OpenVPN behaviour if more than one plugin wants to do deferred user/password authentication not well-defined, as there is just one set of auth control files and a single plugin state. This patch changes "key state -> plugin_auth" from a single struct to an array of MAX_PLUGINS elements that have per-plugin auth-control files and auth status. All direct access is changed to iterating over this array. The actual plugin calls are no longer done with the "do them all" function plugin_call() (or plugin_call_ssl()) but plugin.c/plugin.h is changed to expose the "call one" function plugin_call_item(), and verify_user_pass_plugin() calls this for each loaded plugin, keeping track of "overall" state. key_state_test_plugin_auth() is introduced to do the "key_state_test_auth_control_file()" test for all plugins, and return "FAIL if one fails, PENDING if one is pending, SUCCESS if one was pending and succeeded now". This was tested with the "auth-multi" test plugin, with 5-7 plugins loaded (some deferred, some direct) and "some of them failing" or "all succeeding". Testing included "will it leak files if multiple deferred plugins are ongoing, and one of the earlier ones rejects authentication". This patch is not suitable for production use: - it's full of debug output - it will break compilation without ENABLE_PLUGINS - it stands to argue whether plugin_call_item() should be exposed, or key_state_test_plugin_auth() might be moved to plugin.c instead (with a null function if ENABLE_PLUGINS is not defined) Signed-off-by: Gert Doering Signed-off-by: Gert Doering <gert@greenie.muc.de>
--- src/openvpn/plugin.c | 2 +- src/openvpn/plugin.h | 9 +++ src/openvpn/ssl.c | 5 +- src/openvpn/ssl_common.h | 4 +- src/openvpn/ssl_verify.c | 127 ++++++++++++++++++++++++++++++++------- 5 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/openvpn/plugin.c b/src/openvpn/plugin.c index e3a89293..74b57033 100644 --- a/src/openvpn/plugin.c +++ b/src/openvpn/plugin.c @@ -518,7 +518,7 @@ plugin_open_item(struct plugin *p, } } -static int +int plugin_call_item(const struct plugin *p, void *per_client_context, const int type, diff --git a/src/openvpn/plugin.h b/src/openvpn/plugin.h index c6fa206a..799a646e 100644 --- a/src/openvpn/plugin.h +++ b/src/openvpn/plugin.h @@ -132,6 +132,15 @@ int plugin_call_ssl(const struct plugin_list *pl, int current_cert_depth, openvpn_x509_cert_t *current_cert ); +int plugin_call_item(const struct plugin *p, + void *per_client_context, + const int type, + const struct argv *av, + struct openvpn_plugin_string_list **retlist, + const char **envp, + int certdepth, + openvpn_x509_cert_t *current_cert + ); void plugin_list_close(struct plugin_list *pl); diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 14a943a7..ce6de9a0 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1036,7 +1036,10 @@ key_state_free(struct key_state *ks, bool clear) packet_id_free(&ks->crypto_options.packet_id); - key_state_rm_auth_control_files(&ks->plugin_auth); + for (int i=0; iplugin_auth[i]); + } key_state_rm_auth_control_files(&ks->script_auth); if (clear) diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 8a077c74..0de3290a 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -37,6 +37,8 @@ #include "ssl_backend.h" +#include "plugin.h" + /* passwords */ #define UP_TYPE_AUTH "Auth" #define UP_TYPE_PRIVATE_KEY "Private Key" @@ -239,7 +241,7 @@ struct key_state #endif time_t acf_last_mod; - struct auth_deferred_status plugin_auth; + struct auth_deferred_status plugin_auth[MAX_PLUGINS]; struct auth_deferred_status script_auth; }; diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 3b6b58fa..38c8a830 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -1059,6 +1059,50 @@ key_state_test_auth_control_file(struct auth_deferred_status *ads, bool cached) return ACF_DISABLED; } +/** + * Checks the auth control status for all plugins. + * + * returns: + * - ACF_PENDING if any plugin is still waiting for results. + * - ACF_SUCCEEDED if there were pending plugins AND all succeeded + * - ACF_FAILED if any plugin fails + * - ACF_DISABLED if no plugin is waiting + * + * @param ads array of deferred status control structures + * @param cached Return only cached status + * @return ACF_* as per enum + */ +static enum auth_deferred_result +key_state_test_plugin_auth(struct auth_deferred_status *ads, bool cached) +{ + unsigned int ret = ACF_DISABLED; + + for (int i=0;iauthenticated); if (ks->authenticated == KS_AUTH_FALSE) { return; @@ -1078,7 +1123,7 @@ update_key_auth_status(bool cached, struct key_state *ks) enum auth_deferred_result auth_plugin = ACF_DISABLED; enum auth_deferred_result auth_script = ACF_DISABLED; enum auth_deferred_result auth_man = ACF_DISABLED; - auth_plugin = key_state_test_auth_control_file(&ks->plugin_auth, cached); + auth_plugin = key_state_test_plugin_auth(ks->plugin_auth, cached); auth_script = key_state_test_auth_control_file(&ks->script_auth, cached); #ifdef ENABLE_MANAGEMENT auth_man = man_def_auth_test(ks); @@ -1357,40 +1402,80 @@ static int verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi, const struct user_pass *up) { - int retval = OPENVPN_PLUGIN_FUNC_ERROR; + int retval = OPENVPN_PLUGIN_FUNC_SUCCESS; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ + struct gc_arena gc = gc_new(); + /* set password in private env space */ setenv_str(session->opt->es, "password", up->password); - /* generate filename for deferred auth control file */ - if (!key_state_gen_auth_control_files(&ks->plugin_auth, session->opt)) + /* iterate over list of plugins here, because deferred auth needs + * per-plugin auth control files + */ + const struct plugin_list *pl = session->opt->plugins; + int plugins_active = plugin_n(pl); + ASSERT( plugins_active < SIZE(ks->plugin_auth) ); + msg(M_INFO, "GERT(1): plugins_active=%d, SIZE=%d", plugins_active, (int)SIZE(ks->plugin_auth) ); + + for (int i=0; iplugin_auth[i], session->opt)) + { + msg(D_TLS_ERRORS, "TLS Auth Error (%s): " + "could not create deferred auth control file", __func__); + retval = OPENVPN_PLUGIN_FUNC_ERROR; + break; + } - /* call command */ - retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); + msg(M_INFO, "GERT(4): plugin #%d, acf=%s acp=%s", i, + ks->plugin_auth[i].auth_control_file, ks->plugin_auth[i].auth_pending_file); - if (retval == OPENVPN_PLUGIN_FUNC_DEFERRED) - { - /* Check if the plugin has written the pending auth control - * file and send the pending auth to the client */ - if(!key_state_check_auth_pending_file(&ks->plugin_auth, multi)) + /* call command */ + // retval = plugin_call_item(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); + // pl type av pr es [crt -1, NULL] + + const char **envp = make_env_array(session->opt->es, false, &gc); + const int status = plugin_call_item(&pl->common->plugins[i], + pl->per_client.per_client_context[i], + OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, + NULL, NULL, envp, -1, NULL ); + + if (status == OPENVPN_PLUGIN_FUNC_DEFERRED) + { + /* Check if the plugin has written the pending auth control + * file and send the pending auth to the client */ + if(!key_state_check_auth_pending_file(&(ks->plugin_auth[i]), multi)) + { + retval = OPENVPN_PLUGIN_FUNC_ERROR; + key_state_rm_auth_control_files(&(ks->plugin_auth[i])); + break; + } + else + { + retval = OPENVPN_PLUGIN_FUNC_DEFERRED; + } + } + else + { + /* purge auth control filename (and file itself) for non-deferred returns */ + key_state_rm_auth_control_files(&(ks->plugin_auth[i])); + ks->plugin_auth[i].auth_control_status = ACF_DISABLED; + } + + /* if a single plugin fails, we fail all */ + if (status == OPENVPN_PLUGIN_FUNC_ERROR) { retval = OPENVPN_PLUGIN_FUNC_ERROR; - key_state_rm_auth_control_files(&ks->plugin_auth); + break; } - } - else - { - /* purge auth control filename (and file itself) for non-deferred returns */ - key_state_rm_auth_control_files(&ks->plugin_auth); + + msg(M_INFO, "GERT(3): plugin #%d status=%d -> retval=%d", i, status, retval ); } setenv_del(session->opt->es, "password"); + gc_free(&gc); return retval; }