Hi All, This patch allows for a client reason to be returned from an auth plugin and sent to the connecting client on an auth fail. This change is backwards compatible with existing plugins and hasn't caused issues with existing plugins like the included pam plugin in our testing. The main purpose of this change is to support dynamic challenge/response from plugins, currently this is only possible from the management interface.
Example usage for this change can be found in a new plugin here modified from the included PAM plugin - https://github.com/thesparklabs/openvpn-two-factor-extensions/tree/master/yubikey-u2f-pam-plugin This is version two for this patch and addresses previous inadequacies pointed out. Regards, Eric -- -- Eric Thorpe SparkLabs Developer https://www.sparklabs.com https://twitter.com/sparklabs supp...@sparklabs.com
From 672e0163d5f6ad41e508c41fa6ea88ed07b4f4b9 Mon Sep 17 00:00:00 2001 From: Eric Thorpe <eric+...@sparklabs.com> Date: Wed, 10 Apr 2019 20:59:47 +1000 Subject: [PATCH] Allows a plugin to provide a client_reason for authentication failure --- src/openvpn/ssl.c | 4 +-- src/openvpn/ssl_common.h | 4 +-- src/openvpn/ssl_verify.c | 68 ++++++++++++++++++++++++++++------------ src/openvpn/ssl_verify.h | 9 ++---- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 9696e9bab..884c94a23 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1354,10 +1354,8 @@ tls_multi_free(struct tls_multi *multi, bool clear) ASSERT(multi); -#ifdef MANAGEMENT_DEF_AUTH - man_def_auth_set_client_reason(multi, NULL); + set_client_reason(multi, NULL); -#endif #if P2MP_SERVER free(multi->peer_info); #endif diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index b82a014a0..975fb983a 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -515,13 +515,13 @@ struct tls_multi char *locked_cn; char *locked_username; struct cert_hash_set *locked_cert_hash_set; - -#ifdef ENABLE_DEF_AUTH + /* * An error message to send to client on AUTH_FAILED */ char *client_reason; +#ifdef ENABLE_DEF_AUTH /* Time of last call to tls_authentication_status */ time_t tas_last; #endif diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index fd83cde85..fa0ba28c4 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -837,21 +837,6 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep #endif #ifdef MANAGEMENT_DEF_AUTH -void -man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reason) -{ - if (multi->client_reason) - { - free(multi->client_reason); - multi->client_reason = NULL; - } - if (client_reason && strlen(client_reason)) - { - /* FIXME: Last alloc will never be freed */ - multi->client_reason = string_alloc(client_reason, NULL); - } -} - static inline unsigned int man_def_auth_test(const struct key_state *ks) { @@ -1055,7 +1040,7 @@ tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, con if (multi) { int i; - man_def_auth_set_client_reason(multi, client_reason); + set_client_reason(multi, client_reason); for (i = 0; i < KEY_SCAN_SIZE; ++i) { struct key_state *ks = multi->key_scan[i]; @@ -1081,6 +1066,21 @@ tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, con * this is the place to start. *************************************************************************** */ + +void +set_client_reason(struct tls_multi *multi, const char *client_reason) +{ + if (multi->client_reason) + { + free(multi->client_reason); + multi->client_reason = NULL; + } + if (client_reason && strlen(client_reason)) + { + multi->client_reason = string_alloc(client_reason, NULL); + } +} + /* * Verify the user name and password using a script */ @@ -1166,7 +1166,7 @@ verify_user_pass_script(struct tls_session *session, const struct user_pass *up) * Verify the username and password using a plugin */ static int -verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, const char *raw_username) +verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi, const struct user_pass *up, const char *raw_username) { int retval = OPENVPN_PLUGIN_FUNC_ERROR; #ifdef PLUGIN_DEF_AUTH @@ -1176,6 +1176,9 @@ verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, /* Is username defined? */ if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username)) { + struct plugin_return pr, prfetch; + plugin_return_init(&pr); + /* set username/password in private env space */ setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username)); setenv_str(session->opt->es, "password", up->password); @@ -1197,7 +1200,23 @@ verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, #endif /* call command */ - retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); + retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, &pr, session->opt->es); + + /* Fetch client reason */ + plugin_return_get_column(&pr, &prfetch, "client_reason"); + if (plugin_return_defined(&prfetch)) + { + int i; + for (i = 0; i < prfetch.n; ++i) + { + if (prfetch.list[i] && prfetch.list[i]->value) + { + set_client_reason(multi, prfetch.list[i]->value); + } + } + } + + plugin_return_free(&pr); #ifdef PLUGIN_DEF_AUTH /* purge auth control filename (and file itself) for non-deferred returns */ @@ -1378,7 +1397,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, #endif if (plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) { - s1 = verify_user_pass_plugin(session, up, raw_username); + s1 = verify_user_pass_plugin(session, multi, up, raw_username); } if (session->opt->auth_user_pass_verify_script) { @@ -1462,7 +1481,16 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, if (multi->connection_established) { /* Notify the client */ - send_push_reply_auth_failed(multi, "SESSION:Auth failed"); + const char *client_reason; + if (multi->client_reason != NULL) + { + client_reason = multi->client_reason; + } + else + { + client_reason = "SESSION:Auth failed"; + } + send_push_reply_auth_failed(multi, client_reason); } msg(D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer"); diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h index 3e2267aed..afcfb77be 100644 --- a/src/openvpn/ssl_verify.h +++ b/src/openvpn/ssl_verify.h @@ -171,6 +171,8 @@ tls_common_name_hash(const struct tls_multi *multi, const char **cn, uint32_t *c #endif +void set_client_reason(struct tls_multi *multi, const char *client_reason); + /** * Verify the given username and password, using either an external script, a * plugin, or the management interface. @@ -225,19 +227,12 @@ struct x509_track */ #ifdef MANAGEMENT_DEF_AUTH bool tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason); - -void man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reason); - #endif static inline const char * tls_client_reason(struct tls_multi *multi) { -#ifdef ENABLE_DEF_AUTH return multi->client_reason; -#else - return NULL; -#endif } /** Remove any X509_ env variables from env_set es */
_______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel