@@ -2225,32 +2225,50 @@ multi_client_connect_source_ccd(struct multi_context *m,
return ret;
}
-static inline bool
-cc_check_return(int *cc_succeeded_count,
- enum client_connect_return ret)
+typedef enum client_connect_return (*multi_client_connect_handler)
+ (struct multi_context *m, struct multi_instance *mi,
+ unsigned int *option_types_found);
+
+struct client_connect_handlers
+{
+ multi_client_connect_handler main;
+ multi_client_connect_handler deferred;
+};
+
+static enum client_connect_return
+multi_client_connect_fail(struct multi_context *m, struct multi_instance *mi,
+ unsigned int *option_types_found)
{
- if (ret == CC_RET_SUCCEEDED)
+ /* Called null call-back. This should never happen. */
+ return CC_RET_FAILED;
+}
+
+static const struct client_connect_handlers client_connect_handlers[] = {
{
- (*cc_succeeded_count)++;
- return true;
- }
- else if (ret == CC_RET_FAILED)
+ .main = multi_client_connect_source_ccd,
+ .deferred = multi_client_connect_fail
+ },
{
- return false;
- }
- else if (ret == CC_RET_SKIPPED)
+ .main = multi_client_connect_call_plugin_v1,
+ .deferred = multi_client_connect_fail
+ },
{
- return true;
- }
- else
+ .main = multi_client_connect_call_plugin_v2,
+ .deferred = multi_client_connect_fail
+ },
+ {
+ .main = multi_client_connect_call_script,
+ .deferred = multi_client_connect_fail
+ },
{
- ASSERT(0);
+ .main = multi_client_connect_mda,
+ .deferred = multi_client_connect_fail
+ },
+ {
+ .main = NULL,
+ .deferred = NULL
}
-}
-
-typedef enum client_connect_return (*multi_client_connect_handler)
- (struct multi_context *m, struct multi_instance *mi,
- unsigned int *option_types_found);
+};
/*
* Called as soon as the SSL/TLS connection is authenticated.
@@ -2280,27 +2298,81 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
return;
}
- multi_client_connect_handler handlers[] = {
- multi_client_connect_source_ccd,
- multi_client_connect_call_plugin_v1,
- multi_client_connect_call_plugin_v2,
- multi_client_connect_call_script,
- multi_client_connect_mda,
- NULL
- };
+ /* We are only called for the CAS_PENDING_x states, so we
+ * can ignore other states here */
+ bool from_deferred = (mi->context.c2.context_auth != CAS_PENDING);
- unsigned int option_types_found = 0;
+ struct client_connect_defer_state *defer_state =
+ &(mi->client_connect_defer_state);
- int cc_succeeded = true; /* client connect script status */
- int cc_succeeded_count = 0;
- enum client_connect_return ret;
+ /* We are called for the first time */
+ if (!from_deferred)
+ {
+ defer_state->cur_handler_index = 0;
+ defer_state->option_types_found = 0;
+ /* Initially we have no handler that has returned a result */
+ mi->context.c2.context_auth = CAS_PENDING_DEFERRED;
+
+ multi_client_connect_early_setup(m, mi);
+ }
+
+ const struct client_connect_handlers *handler;
+ bool cc_succeeded = true;
- multi_client_connect_early_setup(m, mi);
+ handler = &client_connect_handlers[defer_state->cur_handler_index];
- for (int i = 0; cc_succeeded && handlers[i]; i++)
+ while (cc_succeeded && handler->main != NULL)
{
- ret = handlers[i](m, mi, &option_types_found);
- cc_succeeded = cc_check_return(&cc_succeeded_count, ret);
+ enum client_connect_return ret;
+ if (from_deferred)
+ {
+ ret = handler->deferred(m, mi, &(defer_state->option_types_found));
+ from_deferred = false;
+ }
+ else
+ {
+ ret = handler->main(m, mi, &(defer_state->option_types_found));
+ }
+
+ if (ret == CC_RET_SUCCEEDED)
+ {
+ /*
+ * Remember that we already had at least one handler
+ * returning a result should go to into deferred state
+ */
+ mi->context.c2.context_auth = CAS_PENDING_DEFERRED_PARTIAL;
+ }
+ else if (ret == CC_RET_SKIPPED)
+ {
+ /*
+ * Move on with the next handler without modifying any
+ * other state
+ */
+ }
+ else if (ret == CC_RET_DEFERRED)
+ {
+ /*
+ * we already set client_connect_status to DEFERRED_RESULT or
+ * DEFERRED_NO_RESULT and increased index. We just return
+ * from the function as having client_connect_status
+ */
+ return;
+ }
+ else if (ret == CC_RET_FAILED)
+ {
+ /*
+ * One handler failed. We abort the chain and set the final
+ * result to failed
+ */
+ cc_succeeded = false;
+ }
+ else
+ {
+ ASSERT(0);
+ }
+
+ handler = &client_connect_handlers[defer_state->cur_handler_index];
+ (defer_state->cur_handler_index)++;
}
/*
@@ -2312,18 +2384,26 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to "
"'disable' directive");
cc_succeeded = false;
- cc_succeeded_count = 0;
}
if (cc_succeeded)
{
- multi_client_connect_late_setup(m, mi, option_types_found);
+ multi_client_connect_late_setup(m, mi,
+ defer_state->option_types_found);
}
else
{
- /* set context-level authentication flag */
- mi->context.c2.context_auth =
- cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
+ /* set context-level authentication flag to failed but remember
+ * if had a handler succeed (for cleanup) */
+ if (mi->context.c2.context_auth == CAS_PENDING_DEFERRED_PARTIAL)
+ {
+ mi->context.c2.context_auth = CAS_PARTIAL;
+ }
+ else
+ {
+ mi->context.c2.context_auth = CAS_FAILED;
+ }
+
}
/* increment number of current authenticated clients */
@@ -2624,7 +2704,7 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns
{
/* connection is "established" when SSL/TLS key negotiation succeeds
* and (if specified) auth user/pass succeeds */
- if (mi->context.c2.context_auth == CAS_PENDING
+ if (is_cas_pending(mi->context.c2.context_auth)
&& CONNECTION_ESTABLISHED(&mi->context))
{
multi_connection_established(m, mi);
@@ -3579,7 +3659,7 @@ management_client_auth(void *arg,
{
if (auth)
{
- if (mi->context.c2.context_auth == CAS_PENDING)
+ if (is_cas_pending(mi->context.c2.context_auth))
{
set_cc_config(mi, cc_config);
cc_config_owned = false;
@@ -3591,7 +3671,7 @@ management_client_auth(void *arg,
{
msg(D_MULTI_LOW, "MULTI: connection rejected: %s, CLI:%s", reason, np(client_reason));
}
- if (mi->context.c2.context_auth != CAS_PENDING)
+ if (!is_cas_pending(mi->context.c2.context_auth))
{
send_auth_failed(&mi->context, client_reason); /* mid-session reauth failed */
multi_schedule_context_wakeup(m, mi);
@@ -62,6 +62,18 @@ struct deferred_signal_schedule_entry
struct timeval wakeup;
};
+/**
+ * Detached client connection state. This is the state that is tracked while
+ * the client connect hooks are executed.
+ */
+struct client_connect_defer_state
+{
+ /* Index of currently executed handler. */
+ int cur_handler_index;
+ /* Remember which option classes where processed for delayed option
+ * handling. */
+ unsigned int option_types_found;
+};
/**
* Server-mode state structure for one single VPN tunnel.
@@ -108,7 +120,7 @@ struct multi_instance {
struct context context; /**< The context structure storing state
* for this VPN tunnel. */
-
+ struct client_connect_defer_state client_connect_defer_state;
#ifdef ENABLE_ASYNC_PUSH
int inotify_watch; /* watch descriptor for acf */
#endif
@@ -195,6 +207,7 @@ enum client_connect_return
{
CC_RET_FAILED,
CC_RET_SUCCEEDED,
+ CC_RET_DEFERRED,
CC_RET_SKIPPED
};
@@ -217,6 +217,8 @@ struct context_1
enum client_connect_status {
CAS_SUCCEEDED=0,
CAS_PENDING,
+ CAS_PENDING_DEFERRED,
+ CAS_PENDING_DEFERRED_PARTIAL, /**< at least handler succeeded, no result yet*/
CAS_FAILED,
CAS_PARTIAL, /**< Variant of CAS_FAILED: at least one
* client-connect script/plugin succeeded
@@ -225,6 +227,13 @@ enum client_connect_status {
*/
};
+static inline bool
+is_cas_pending(enum client_connect_status cas)
+{
+ return cas == CAS_PENDING || cas == CAS_PENDING_DEFERRED
+ || cas == CAS_PENDING_DEFERRED_PARTIAL;
+}
+
/**
* Level 2 %context containing state that is reset on both \c SIGHUP and
* \c SIGUSR1 restarts.