aboutsummaryrefslogtreecommitdiff
path: root/ssl.c
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-05-24 23:26:11 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-05-24 23:26:11 +0000
commit344ee9181734dcd5a922b8b2a7ebea4ce818a0b0 (patch)
tree8a4c3724971a0c81debc97d3bba62138aab3a247 /ssl.c
parentDid: (diff)
downloadopenvpn-344ee9181734dcd5a922b8b2a7ebea4ce818a0b0.tar.xz
Support asynchronous/deferred authentication in
OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY plugin handler. See documentation in openvpn-plugin.h and example usage in plugin/defer/simple.c. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2969 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to 'ssl.c')
-rw-r--r--ssl.c201
1 files changed, 157 insertions, 44 deletions
diff --git a/ssl.c b/ssl.c
index 4cc6b76..2e0b3e3 100644
--- a/ssl.c
+++ b/ssl.c
@@ -710,7 +710,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es);
- if (!ret)
+ if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s",
ctx->error_depth, subject);
@@ -847,33 +847,129 @@ tls_lock_common_name (struct tls_multi *multi)
}
/*
- * Return true if at least one valid key state exists
- * which has passed authentication. If we are using
- * username/password authentication, and the authentication
- * failed, we may have a live S_ACTIVE/S_NORMAL key state
- * even though the 'authenticated' var might be false.
- *
- * This is so that we can return an AUTH_FAILED error
- * message to the client over the TLS channel.
- *
- * If 'authenticated' is false, tunnel traffic forwarding
- * is disabled but TLS channel data can still be sent
- * or received.
+ * auth_control_file functions
*/
-bool
-tls_authenticated (struct tls_multi *multi)
+
+static void
+key_state_rm_auth_control_file (struct key_state *ks)
+{
+ if (ks && ks->auth_control_file)
+ {
+ delete_file (ks->auth_control_file);
+ free (ks->auth_control_file);
+ ks->auth_control_file = NULL;
+ }
+}
+
+static void
+key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options *opt)
{
+ struct gc_arena gc = gc_new ();
+ const char *acf;
+
+ key_state_rm_auth_control_file (ks);
+ acf = create_temp_filename (opt->tmp_dir, "acf", &gc);
+ ks->auth_control_file = string_alloc (acf, NULL);
+ setenv_str (opt->es, "auth_control_file", ks->auth_control_file);
+
+ gc_free (&gc);
+}
+
+/* key_state_test_auth_control_file return values */
+#define ACF_SUCCEEDED 0
+#define ACF_FAILED 1
+#define ACF_KILL 2
+#define ACF_UNDEFINED 3
+#define ACF_DISABLED 4
+static int
+key_state_test_auth_control_file (const struct key_state *ks)
+{
+ int ret = ACF_DISABLED;
+ if (ks && ks->auth_control_file)
+ {
+ ret = ACF_UNDEFINED;
+ FILE *fp = fopen (ks->auth_control_file, "r");
+ if (fp)
+ {
+ int c = fgetc (fp);
+ if (c == '1')
+ ret = ACF_SUCCEEDED;
+ else if (c == '0')
+ ret = ACF_FAILED;
+ else if (c == '2')
+ ret = ACF_KILL;
+ fclose (fp);
+ }
+ }
+ return ret;
+}
+
+/*
+ * Return current session authentication state. Return
+ * value is TLS_AUTHENTICATION_x.
+ */
+
+int
+tls_authentication_status (struct tls_multi *multi, const int latency)
+{
+ bool deferred = false;
+ bool success = false;
+ bool kill = false;
+ bool active = false;
+
+ if (latency && multi->tas_last && multi->tas_last + latency >= now)
+ return TLS_AUTHENTICATION_UNDEFINED;
+ multi->tas_last = now;
+
if (multi)
{
int i;
for (i = 0; i < KEY_SCAN_SIZE; ++i)
{
- const struct key_state *ks = multi->key_scan[i];
- if (DECRYPT_KEY_ENABLED (multi, ks) && ks->authenticated)
- return true;
+ struct key_state *ks = multi->key_scan[i];
+ if (DECRYPT_KEY_ENABLED (multi, ks))
+ {
+ active = true;
+ if (ks->authenticated)
+ {
+ switch (key_state_test_auth_control_file (ks))
+ {
+ case ACF_SUCCEEDED:
+ case ACF_DISABLED:
+ success = true;
+ ks->auth_deferred = false;
+ break;
+ case ACF_UNDEFINED:
+ if (now < ks->auth_deferred_expire)
+ deferred = true;
+ break;
+ case ACF_FAILED:
+ ks->authenticated = false;
+ break;
+ case ACF_KILL:
+ kill = true;
+ ks->authenticated = false;
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+ }
}
}
- return false;
+
+#if 0
+ dmsg (D_TLS_ERRORS, "TAS: a=%d k=%d s=%d d=%d", active, kill, success, deferred);
+#endif
+
+ if (kill)
+ return TLS_AUTHENTICATION_FAILED;
+ else if (success)
+ return TLS_AUTHENTICATION_SUCCEEDED;
+ else if (!active || deferred)
+ return TLS_AUTHENTICATION_DEFERRED;
+ else
+ return TLS_AUTHENTICATION_FAILED;
}
void
@@ -1905,6 +2001,8 @@ key_state_free (struct key_state *ks, bool clear)
packet_id_free (&ks->packet_id);
+ key_state_rm_auth_control_file (ks);
+
if (clear)
CLEAR (*ks);
}
@@ -2747,7 +2845,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
{
struct status_output *so;
- tmp_file = create_temp_filename (session->opt->tmp_dir, &gc);
+ tmp_file = create_temp_filename (session->opt->tmp_dir, "up", &gc);
so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
status_printf (so, "%s", up->username);
status_printf (so, "%s", up->password);
@@ -2798,11 +2896,10 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
return ret;
}
-static bool
+static int
verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username)
{
- int retval;
- bool ret = false;
+ int retval = OPENVPN_PLUGIN_FUNC_ERROR;
/* Is username defined? */
if (strlen (up->username))
@@ -2817,11 +2914,15 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
/* setenv client real IP address */
setenv_untrusted (session);
+ /* generate filename for deferred auth control file */
+ key_state_gen_auth_control_file (ks, session->opt);
+
/* call command */
retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
- if (!retval)
- ret = true;
+ /* purge auth control filename (and file itself) for non-deferred returns */
+ if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
+ key_state_rm_auth_control_file (ks);
setenv_del (session->opt->es, "password");
setenv_str (session->opt->es, "username", up->username);
@@ -2831,7 +2932,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
}
- return ret;
+ return retval;
}
/*
@@ -3047,7 +3148,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
if (session->opt->auth_user_pass_verify_script
|| plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
{
- bool s1 = true;
+ int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
bool s2 = true;
char *raw_username;
@@ -3077,12 +3178,15 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
s2 = verify_user_pass_script (session, up);
/* auth succeeded? */
- if (s1 && s2)
+ if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) && s2)
{
ks->authenticated = true;
+ if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
+ ks->auth_deferred = true;
if (session->opt->username_as_common_name)
set_common_name (session, up->username);
- msg (D_HANDSHAKE, "TLS: Username/Password authentication succeeded for username '%s' %s",
+ msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
+ s1 == OPENVPN_PLUGIN_FUNC_SUCCESS ? "succeeded" : "deferred",
up->username,
session->opt->username_as_common_name ? "[CN SET]" : "");
}
@@ -3155,7 +3259,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
*/
if (ks->authenticated && plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL))
{
- if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es))
+ if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
ks->authenticated = false;
}
@@ -3268,7 +3372,7 @@ tls_process (struct tls_multi *multi,
buf = reliable_get_buf_output_sequenced (ks->send_reliable);
if (buf)
{
- ks->must_negotiate = now + session->opt->handshake_window;
+ ks->auth_deferred_expire = ks->must_negotiate = now + session->opt->handshake_window;
/* null buffer */
reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode);
@@ -3593,7 +3697,7 @@ error:
* the active or untrusted sessions.
*/
-bool
+int
tls_multi_process (struct tls_multi *multi,
struct buffer *to_link,
struct link_socket_actual **to_link_addr,
@@ -3602,8 +3706,9 @@ tls_multi_process (struct tls_multi *multi,
{
struct gc_arena gc = gc_new ();
int i;
- bool active = false;
+ int active = TLSMP_INACTIVE;
bool error = false;
+ int tas;
perf_push (PERF_TLS_MULTI_PROCESS);
@@ -3641,7 +3746,7 @@ tls_multi_process (struct tls_multi *multi,
if (tls_process (multi, session, to_link, &tla,
to_link_socket_info, wakeup))
- active = true;
+ active = TLSMP_ACTIVE;
/*
* If tls_process produced an outgoing packet,
@@ -3680,6 +3785,8 @@ tls_multi_process (struct tls_multi *multi,
update_time ();
+ tas = tls_authentication_status (multi, TLS_MULTI_AUTH_STATUS_INTERVAL);
+
/*
* If lame duck session expires, kill it.
*/
@@ -3700,7 +3807,7 @@ tls_multi_process (struct tls_multi *multi,
if (DECRYPT_KEY_ENABLED (multi, &multi->session[TM_UNTRUSTED].key[KS_PRIMARY])) {
move_session (multi, TM_ACTIVE, TM_UNTRUSTED, true);
msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: untrusted session promoted to %strusted",
- tls_authenticated (multi) ? "" : "semi-");
+ tas == TLS_AUTHENTICATION_SUCCEEDED ? "" : "semi-");
}
/*
@@ -3738,7 +3845,8 @@ tls_multi_process (struct tls_multi *multi,
perf_pop ();
gc_free (&gc);
- return active;
+
+ return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active;
}
/*
@@ -3815,6 +3923,7 @@ tls_pre_decrypt (struct tls_multi *multi,
if (DECRYPT_KEY_ENABLED (multi, ks)
&& key_id == ks->key_id
&& ks->authenticated
+ && !ks->auth_deferred
&& link_socket_actual_match (from, &ks->remote_addr))
{
/* return appropriate data channel decrypt key in opt */
@@ -3835,13 +3944,14 @@ tls_pre_decrypt (struct tls_multi *multi,
#if 0 /* keys out of sync? */
else
{
- dmsg (D_TLS_DEBUG, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d match=%d",
- i,
- DECRYPT_KEY_ENABLED (multi, ks),
- key_id,
- ks->key_id,
- ks->authenticated,
- link_socket_actual_match (from, &ks->remote_addr));
+ dmsg (D_TLS_ERRORS, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d def=%d match=%d",
+ i,
+ DECRYPT_KEY_ENABLED (multi, ks),
+ key_id,
+ ks->key_id,
+ ks->authenticated,
+ ks->auth_deferred,
+ link_socket_actual_match (from, &ks->remote_addr));
}
#endif
}
@@ -4331,7 +4441,10 @@ tls_pre_encrypt (struct tls_multi *multi,
for (i = 0; i < KEY_SCAN_SIZE; ++i)
{
struct key_state *ks = multi->key_scan[i];
- if (ks->state >= S_ACTIVE && ks->authenticated)
+ if (ks->state >= S_ACTIVE
+ && ks->authenticated
+ && !ks->auth_deferred
+ && (!ks->key_id || now >= ks->auth_deferred_expire))
{
opt->key_ctx_bi = &ks->key;
opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL;