fr_trunk_conf_t *trunk_conf; //!< Module trunk config
fr_trunk_conf_t *bind_trunk_conf; //!< Trunk config for bind auth trunk
fr_event_list_t *el; //!< Thread event list for callbacks / timeouts
- fr_connection_t *conn; //!< LDAP connection used for bind auths
fr_ldap_thread_trunk_t *bind_trunk; //!< LDAP trunk used for bind auths
fr_rb_tree_t *binds; //!< Tree of outstanding bind auths
} fr_ldap_thread_t;
typedef struct {
fr_rb_node_t node; //!< Entry in the tree of outstanding bind requests.
fr_ldap_thread_t *thread; //!< This bind is being run by.
+ fr_trunk_request_t *treq; //!< Trunk request this bind is associated with.
int msgid; //!< libldap msgid for this bind.
request_t *request; //!< this bind relates to.
fr_ldap_bind_type_t type; //!< type of bind.
return 0;
}
-/** Submit an async LDAP auth bind
+/** Yield interpreter after queueing LDAP bind
*
*/
-static unlang_action_t ldap_async_auth_bind_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
+static unlang_action_t ldap_async_auth_bind_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority,
+ UNUSED request_t *request, UNUSED void *uctx)
{
- fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
- fr_ldap_bind_ctx_t *bind_ctx = bind_auth_ctx->bind_ctx;
-
- int ret;
- struct berval cred;
-
- RDEBUG2("Starting bind auth operation as %s", bind_ctx->bind_dn);
-
- if (bind_ctx->password) {
- memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
- cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
- } else {
- cred.bv_val = NULL;
- cred.bv_len = 0;
- }
-
- ret = ldap_sasl_bind(bind_auth_ctx->bind_ctx->c->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE,
- &cred, NULL, NULL, &bind_auth_ctx->msgid);
-
- switch (ret) {
- case LDAP_SUCCESS:
- fr_rb_insert(bind_auth_ctx->thread->binds, bind_auth_ctx);
- return UNLANG_ACTION_YIELD;
-
- default:
- return UNLANG_ACTION_FAIL;
- }
+ return UNLANG_ACTION_YIELD;
}
/** Handle the return code from parsed LDAP results to set the module rcode
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
fr_ldap_bind_ctx_t *bind_ctx = bind_auth_ctx->bind_ctx;
+ rlm_rcode_t rcode = RLM_MODULE_OK;
switch (bind_auth_ctx->ret) {
case LDAP_PROC_SUCCESS:
RDEBUG2("Bind as user \"%s\" was successful", bind_ctx->bind_dn);
- RETURN_MODULE_OK;
+ break;
case LDAP_PROC_NOT_PERMITTED:
RDEBUG2("Bind as user \"%s\" not permitted", bind_ctx->bind_dn);
- RETURN_MODULE_DISALLOW;
+ rcode = RLM_MODULE_DISALLOW;
+ break;
case LDAP_PROC_REJECT:
RDEBUG2("Bind as user \"%s\" rejected", bind_ctx->bind_dn);
- RETURN_MODULE_REJECT;
+ rcode = RLM_MODULE_REJECT;
+ break;
case LDAP_PROC_BAD_DN:
- RETURN_MODULE_INVALID;
+ rcode = RLM_MODULE_INVALID;
+ break;
case LDAP_PROC_NO_RESULT:
- RETURN_MODULE_NOTFOUND;
+ rcode = RLM_MODULE_NOTFOUND;
+ break;
default:
- RETURN_MODULE_FAIL;
-
+ rcode = RLM_MODULE_FAIL;
+ break;
}
- talloc_free(bind_auth_ctx);
+ /*
+ * Bind auth ctx is freed by trunk request free.
+ */
+ fr_trunk_request_signal_complete(bind_auth_ctx->treq);
+
+ RETURN_MODULE_RCODE(rcode);
}
/** Signal an outstanding LDAP bind request to cancel
*
*/
-static void ldap_async_auth_bind_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
+static void ldap_async_auth_bind_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
- ldap_abandon_ext(bind_auth_ctx->bind_ctx->c->handle, bind_auth_ctx->msgid, NULL, NULL);
- fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
-
- talloc_free(bind_auth_ctx);
+ RWARN("Cancelling bind auth");
+ if (bind_auth_ctx->msgid > 0) fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
+ fr_trunk_request_signal_cancel(bind_auth_ctx->treq);
}
/** Initiate an async LDAP bind for authentication
int fr_ldap_bind_auth_async(request_t *request, fr_ldap_thread_t *thread, char const *bind_dn, char const *password)
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx;
- fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
+ fr_trunk_request_t *treq;
+ fr_ldap_thread_trunk_t *ttrunk = fr_thread_ldap_bind_trunk_get(thread);
+ fr_trunk_enqueue_t ret;
- if (ldap_conn->state != FR_LDAP_STATE_RUN) {
- connection_fault:
- fr_connection_signal_reconnect(ldap_conn->conn, FR_CONNECTION_FAILED);
+ if (!ttrunk) {
+ ERROR("Failed to get trunk connection for LDAP bind");
return -1;
}
- if (ldap_conn->fd < 0) goto connection_fault;
+ treq = fr_trunk_request_alloc(ttrunk->trunk, request);
+ if (!treq) {
+ ERROR ("Failed to allocate trunk request for LDAP bind");
+ return -1;
+ }
+
+ MEM(bind_auth_ctx = talloc(treq, fr_ldap_bind_auth_ctx_t));
+ *bind_auth_ctx = (fr_ldap_bind_auth_ctx_t) {
+ .treq = treq,
+ .request = request,
+ .thread = thread,
+ .ret = LDAP_PROC_NO_RESULT
+ };
+
+ MEM(bind_auth_ctx->bind_ctx = talloc(bind_auth_ctx, fr_ldap_bind_ctx_t));
+ *bind_auth_ctx->bind_ctx = (fr_ldap_bind_ctx_t) {
+ .bind_dn = bind_dn,
+ .password = password
+ };
+
+ ret = fr_trunk_request_enqueue(&bind_auth_ctx->treq, ttrunk->trunk, request, bind_auth_ctx, NULL);
+
+ switch (ret) {
+ case FR_TRUNK_ENQUEUE_OK:
+ case FR_TRUNK_ENQUEUE_IN_BACKLOG:
+ break;
- MEM(bind_auth_ctx = talloc_zero(request, fr_ldap_bind_auth_ctx_t));
- MEM(bind_auth_ctx->bind_ctx = talloc_zero(bind_auth_ctx, fr_ldap_bind_ctx_t));
- bind_auth_ctx->bind_ctx->c = ldap_conn;
- bind_auth_ctx->bind_ctx->bind_dn = bind_dn;
- bind_auth_ctx->bind_ctx->password = password;
- bind_auth_ctx->request = request;
- bind_auth_ctx->thread = thread;
- bind_auth_ctx->ret = LDAP_PROC_NO_RESULT;
+ default:
+ ERROR("Failed to enqueue bind request");
+ fr_trunk_request_free(&treq);
+ return -1;
+ }
return unlang_function_push(request,
ldap_async_auth_bind_start,
ldap_async_auth_bind_results,
ldap_async_auth_bind_cancel,
- ~FR_SIGNAL_CANCEL, UNLANG_TOP_FRAME, bind_auth_ctx);
+ ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME,
+ bind_auth_ctx) == UNLANG_ACTION_PUSHED_CHILD ? 0 : -1;
}
return (found) ? found->trunk->state : FR_TRUNK_STATE_MAX;
}
+/** Free LDAP bind auth ctx when trunk request is "freed" with fr_trunk_request_free()
+ *
+ */
+static void ldap_trunk_bind_auth_free(UNUSED request_t *request, void *preq_to_free, UNUSED void *uctx)
+{
+ fr_ldap_bind_auth_ctx_t *bind = talloc_get_type_abort(preq_to_free, fr_ldap_bind_auth_ctx_t);
+
+ talloc_free(bind);
+}
+
+/** Take pending LDAP bind auths from the queue and send them.
+ *
+ * @param[in] el Event list for timers.
+ * @param[in] tconn Trunk handle.
+ * @param[in] conn on which to send the queries
+ * @param[in] uctx User context passed to fr_trunk_alloc
+ */
+static void ldap_trunk_bind_auth_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn,
+ fr_connection_t *conn, void *uctx)
+{
+ fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
+ fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
+ fr_ldap_thread_t *thread = ttrunk->t;
+ fr_trunk_request_t *treq;
+
+ fr_ldap_bind_auth_ctx_t *bind = NULL;
+ int ret = 0;
+ struct berval cred;
+ request_t *request;
+
+ if (fr_trunk_connection_pop_request(&treq, tconn) != 0) return;
+
+ /* Pacify clang scan */
+ if (!treq) return;
+
+ bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
+ request = bind->request;
+
+ switch (bind->type) {
+ case LDAP_BIND_SIMPLE:
+ {
+ fr_ldap_bind_ctx_t *bind_ctx = bind->bind_ctx;
+
+ RDEBUG2("Starting bind auth operation as %s", bind_ctx->bind_dn);
+
+ if (bind_ctx->password) {
+ memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
+ cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
+ } else {
+ cred.bv_val = NULL;
+ cred.bv_len = 0;
+ }
+
+ ret = ldap_sasl_bind(ldap_conn->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE,
+ &cred, NULL, NULL, &bind->msgid);
+
+ switch (ret) {
+ case LDAP_SUCCESS:
+ fr_rb_insert(thread->binds, bind);
+ RDEBUG3("Bind auth sent as LDAP msgid %d", bind->msgid);
+ break;
+
+ default:
+ bind->ret = LDAP_PROC_ERROR;
+ unlang_interpret_mark_runnable(treq->request);
+ RERROR("Failed to send bind auth");
+ break;
+ }
+ }
+ break;
+
+ case LDAP_BIND_SASL:
+ {
+ fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
+
+ RDEBUG2("%s SASL bind auth operation as %s", sasl_ctx->rmech ? "Continuing" : "Starting",
+ sasl_ctx->identity);
+
+ ret = fr_ldap_sasl_bind_auth_send(sasl_ctx, &bind->msgid, ldap_conn);
+
+ switch (ret) {
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ /*
+ * Add the bind to the list of pending binds.
+ */
+ fr_rb_insert(thread->binds, bind);
+ RDEBUG3("SASL bind auth sent as LDAP msgid %d", bind->msgid);
+ break;
+
+ case LDAP_SUCCESS:
+ bind->ret = LDAP_PROC_SUCCESS;
+ unlang_interpret_mark_runnable(treq->request);
+ break;
+
+ default:
+ bind->ret = LDAP_PROC_ERROR;
+ unlang_interpret_mark_runnable(treq->request);
+ RERROR("Failed to send SASL bind auth");
+ break;
+ }
+ }
+ break;
+ }
+ /*
+ * The request is marked as sent, to remove from the pending list.
+ * This is regardless of whether the sending was successful or not as
+ * the different states are handled by the resume function which then
+ * marks the request as complete triggering the tidy up.
+ */
+ fr_trunk_request_signal_sent(treq);
+}
+
+/** Read LDAP bind auth responses
+ *
+ * @param[in] el To insert timers into.
+ * @param[in] tconn Trunk connection associated with these results.
+ * @param[in] conn Connection handle for these results.
+ * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
+ */
+static void ldap_trunk_bind_auth_demux(UNUSED fr_event_list_t *el, UNUSED fr_trunk_connection_t *tconn,
+ fr_connection_t *conn, void *uctx)
+{
+ fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
+ fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
+ fr_ldap_thread_t *thread = ttrunk->t;
+ fr_ldap_bind_auth_ctx_t find = { .msgid = -1 }, *bind = NULL;
+
+ int ret = 0;
+ LDAPMessage *result = NULL;
+ request_t *request;
+ bool really_no_result = false;
+
+ do {
+ /*
+ * The first time ldap_result is called when there's pending network
+ * data, it may read the data, but actually return a timeout.
+ *
+ * In order to fix the spurious debugging messages and overhead,
+ * if this is the first iteration through the loop and fr_ldap_result
+ * returns a timeout, we call it again.
+ */
+ ret = fr_ldap_result(&result, NULL, ldap_conn, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(10));
+ if (ret == LDAP_PROC_TIMEOUT) {
+ if (really_no_result) return;
+ really_no_result = true;
+ continue;
+ }
+
+ if (!result) return;
+
+ really_no_result = true;
+ find.msgid = ldap_msgid(result);
+ bind = fr_rb_find(thread->binds, &find);
+
+ if (!bind) {
+ WARN("Ignoring bind result msgid %i - doesn't match any outstanding binds", find.msgid);
+ ldap_msgfree(result);
+ result = NULL;
+ continue;
+ }
+ } while (!bind);
+
+ /*
+ * There will only ever be one bind in flight at a time on a given
+ * connection - so having got a result, no need to loop.
+ */
+
+ fr_rb_remove(thread->binds, bind);
+ request = bind->request;
+ bind->ret = ret;
+
+ switch (ret) {
+ /*
+ * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
+ */
+ case LDAP_PROC_NOT_PERMITTED:
+ case LDAP_PROC_REJECT:
+ case LDAP_PROC_BAD_DN:
+ case LDAP_PROC_NO_RESULT:
+ break;
+
+ case LDAP_PROC_SUCCESS:
+ if (bind->type == LDAP_BIND_SIMPLE) break;
+
+ /*
+ * With SASL binds, we will be here after ldap_sasl_interactive_bind
+ * returned LDAP_SASL_BIND_IN_PROGRESS. That always requires a further
+ * call of ldap_sasl_interactive_bind to get the final result.
+ */
+ bind->ret = LDAP_PROC_CONTINUE;
+ FALL_THROUGH;
+
+ case LDAP_PROC_CONTINUE:
+ {
+ fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
+ struct berval *srv_cred;
+
+ /*
+ * Free any previous result and track the new one.
+ */
+ if (sasl_ctx->result) ldap_msgfree(sasl_ctx->result);
+ sasl_ctx->result = result;
+ result = NULL;
+
+ ret = ldap_parse_sasl_bind_result(ldap_conn->handle, sasl_ctx->result, &srv_cred, 0);
+ if (ret != LDAP_SUCCESS) {
+ RERROR("SASL decode failed (bind failed): %s", ldap_err2string(ret));
+ break;
+ }
+
+ if (srv_cred) {
+ RDEBUG3("SASL response : %pV",
+ fr_box_strvalue_len(srv_cred->bv_val, srv_cred->bv_len));
+ ber_bvfree(srv_cred);
+ }
+
+ if (sasl_ctx->rmech) RDEBUG3("Continuing SASL mech %s...", sasl_ctx->rmech);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ldap_msgfree(result);
+ unlang_interpret_mark_runnable(request);
+}
+
+/** Callback to cancel LDAP bind auth
+ *
+ * Inform the remote LDAP server that we no longer want responses to specific bind.
+ *
+ * @param[in] el For timer management.
+ * @param[in] tconn The trunk connection handle
+ * @param[in] conn The specific connection binds will be cancelled on
+ * @param[in] uctx Context provided to fr_trunk_alloc
+ */
+static void ldap_bind_auth_cancel_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn,
+ fr_connection_t *conn, UNUSED void *uctx)
+{
+ fr_trunk_request_t *treq;
+ fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
+ fr_ldap_bind_auth_ctx_t *bind;
+
+ while ((fr_trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
+ bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
+ if (bind->type == LDAP_BIND_SASL) {
+ /*
+ * With SASL binds, abandoning the bind part way through
+ * seems to leave the connection in an unpredictable state
+ * so safer to restart.
+ */
+ fr_trunk_connection_signal_reconnect(tconn, FR_CONNECTION_FAILED);
+ } else {
+ ldap_abandon_ext(ldap_conn->handle, bind->msgid, NULL, NULL);
+ }
+ fr_trunk_request_signal_cancel_complete(treq);
+ }
+}
+
/** Find the thread specific trunk to use for LDAP bind auths
*
* If there is no current trunk then a new one is created.
&(fr_trunk_io_funcs_t){
.connection_alloc = ldap_trunk_connection_alloc,
.connection_notify = ldap_trunk_connection_notify,
+ .request_mux = ldap_trunk_bind_auth_mux,
+ .request_demux = ldap_trunk_bind_auth_demux,
+ .request_cancel_mux = ldap_bind_auth_cancel_mux,
+ .request_free = ldap_trunk_bind_auth_free
},
thread->bind_trunk_conf,
"rlm_ldap bind auth", ttrunk, false);
return 0;
}
-
/** Send a SASL LDAP auth bind
*
* Shares the same callback as SASL admin binds
&sasl_ctx->rmech, msgid);
}
-/** Submit an async SASL LDAP auth bind
+/** Yield interpreter after enqueueing sasl auth bind
*
- * @param[in] p_result Unused.
- * @param[in] priority Unused.
- * @param[in] request being processed.
- * @param[in] uctx bind auth ctx.
- * @return unlang action.
*/
-static unlang_action_t ldap_async_sasl_auth_bind_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority,
- request_t *request, void *uctx)
+static unlang_action_t ldap_async_sasl_bind_auth_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority,
+ UNUSED request_t *request, UNUSED void *uctx)
{
- fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
- fr_ldap_sasl_ctx_t *sasl_ctx = bind_auth_ctx->sasl_ctx;
- int ret;
-
- RDEBUG2("%s SASL bind auth operation as %s", sasl_ctx->rmech ? "Continuing" : "Starting", sasl_ctx->dn);
-
- ret = ldap_sasl_interactive_bind(sasl_ctx->c->handle, NULL, sasl_ctx->mechs,
- NULL, NULL, LDAP_SASL_AUTOMATIC,
- _sasl_interact, sasl_ctx, sasl_ctx->result,
- &sasl_ctx->rmech, &bind_auth_ctx->msgid);
-
- switch (ret) {
- case LDAP_SUCCESS:
- bind_auth_ctx->ret = LDAP_PROC_SUCCESS;
- return UNLANG_ACTION_CALCULATE_RESULT;
-
- case LDAP_SASL_BIND_IN_PROGRESS:
- fr_rb_insert(bind_auth_ctx->thread->binds, bind_auth_ctx);
- return UNLANG_ACTION_YIELD;
-
- default:
- return UNLANG_ACTION_FAIL;
- }
+ return UNLANG_ACTION_YIELD;
}
/** Signal an outstanding SASL LDAP bind to cancel
* @param[in] action Signal to handle.
* @param[in] uctx bind auth ctx.
*/
-static void ldap_async_sasl_auth_bind_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
+static void ldap_async_sasl_bind_auth_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
- ldap_abandon_ext(bind_auth_ctx->sasl_ctx->c->handle, bind_auth_ctx->msgid, NULL, NULL);
- fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
-
- talloc_free(bind_auth_ctx);
+ RWARN("Cancelling SASL bind auth");
+ if (bind_auth_ctx->msgid > 0) fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
+ fr_trunk_request_signal_cancel(bind_auth_ctx->treq);
}
/** Handle the return code from parsed LDAP results to set the module rcode
* @param[in] uctx bind auth ctx.
* @return unlang action.
*/
-static unlang_action_t ldap_async_sasl_auth_bind_results(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
+static unlang_action_t ldap_async_sasl_bind_auth_results(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
fr_ldap_sasl_ctx_t *sasl_ctx = bind_auth_ctx->sasl_ctx;
fr_ldap_rcode_t ret = bind_auth_ctx->ret;
+ fr_ldap_connection_t *ldap_conn = NULL;
switch (bind_auth_ctx->ret) {
case LDAP_PROC_SUCCESS:
break;
case LDAP_PROC_CONTINUE:
- return unlang_function_push(request, ldap_async_sasl_auth_bind_start, ldap_async_sasl_auth_bind_results,
- ldap_async_sasl_auth_bind_cancel, ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, bind_auth_ctx);
+ if (fr_trunk_request_requeue(bind_auth_ctx->treq) != FR_TRUNK_ENQUEUE_OK) {
+ ret = LDAP_PROC_ERROR;
+ break;
+ }
+
+ /*
+ * Once the next SASL exchange has completed repeat this function to
+ * process the results
+ */
+ if (unlang_function_repeat_set(request, ldap_async_sasl_bind_auth_results) < 0) {
+ /*
+ * Not strictly an LDAP error but if this happens we will want to reset
+ * the connection to get a known state.
+ */
+ ret = LDAP_PROC_ERROR;
+ break;
+ }
+ return UNLANG_ACTION_YIELD;
default:
break;
}
- talloc_free(bind_auth_ctx);
+ if (bind_auth_ctx->treq->tconn) ldap_conn = talloc_get_type_abort(bind_auth_ctx->treq->tconn->conn->h,
+ fr_ldap_connection_t);
+
+ /*
+ * Will free bind_auth_ctx
+ */
+ fr_trunk_request_signal_complete(bind_auth_ctx->treq);
switch (ret) {
case LDAP_PROC_SUCCESS:
RETURN_MODULE_NOTFOUND;
default:
+ if (ldap_conn) {
+ RPERROR("LDAP connection returned an error - restarting the connection");
+ fr_ldap_state_error(ldap_conn);
+ }
RETURN_MODULE_FAIL;
}
}
char const *identity, char const *password, char const *proxy, char const *realm)
{
fr_ldap_bind_auth_ctx_t *bind_auth_ctx;
- fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
+ fr_trunk_request_t *treq;
+ fr_ldap_thread_trunk_t *ttrunk = fr_thread_ldap_bind_trunk_get(thread);
+ fr_trunk_enqueue_t ret;
- if ((ldap_conn->state != FR_LDAP_STATE_RUN) || (ldap_conn->fd < 0)) {
- fr_connection_signal_reconnect(ldap_conn->conn, FR_CONNECTION_FAILED);
+ if (!ttrunk) {
+ ERROR("Failed to get trunk connection for LDAP bind");
return -1;
}
- MEM(bind_auth_ctx = talloc_zero(request, fr_ldap_bind_auth_ctx_t));
+ treq = fr_trunk_request_alloc(ttrunk->trunk, request);
+ if (!treq) {
+ ERROR("Failed to allocate trunk request for LDAP bind");
+ return -1;
+ }
+
+ MEM(bind_auth_ctx = talloc_zero(treq, fr_ldap_bind_auth_ctx_t));
+ *bind_auth_ctx = (fr_ldap_bind_auth_ctx_t) {
+ .treq = treq,
+ .request = request,
+ .thread = thread,
+ .ret = LDAP_PROC_NO_RESULT,
+ .type = LDAP_BIND_SASL
+ };
+
MEM(bind_auth_ctx->sasl_ctx = talloc(bind_auth_ctx, fr_ldap_sasl_ctx_t));
talloc_set_destructor(bind_auth_ctx->sasl_ctx, _sasl_ctx_free);
*bind_auth_ctx->sasl_ctx = (fr_ldap_sasl_ctx_t) {
- .c = ldap_conn,
.mechs = mechs,
.dn = dn,
.identity = identity,
.proxy = proxy,
.realm = realm,
};
- bind_auth_ctx->request = request;
- bind_auth_ctx->thread = thread;
- bind_auth_ctx->ret = LDAP_PROC_NO_RESULT;
- bind_auth_ctx->type = LDAP_BIND_SASL;
- return unlang_function_push(request, ldap_async_sasl_auth_bind_start, ldap_async_sasl_auth_bind_results,
- ldap_async_sasl_auth_bind_cancel, ~FR_SIGNAL_CANCEL, UNLANG_TOP_FRAME, bind_auth_ctx);
+ ret = fr_trunk_request_enqueue(&bind_auth_ctx->treq, ttrunk->trunk, request, bind_auth_ctx, NULL);
+
+ switch (ret) {
+ case FR_TRUNK_ENQUEUE_OK:
+ case FR_TRUNK_ENQUEUE_IN_BACKLOG:
+ break;
+
+ default:
+ ERROR("Failed to enqueue bind request");
+ fr_trunk_request_free(&treq);
+ return -1;
+ }
+
+ return unlang_function_push(request,
+ ldap_async_sasl_bind_auth_start,
+ ldap_async_sasl_bind_auth_results,
+ ldap_async_sasl_bind_auth_cancel,
+ ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME,
+ bind_auth_ctx) == UNLANG_ACTION_PUSHED_CHILD ? 0 : -1;
}
return 0;
}
-/** Error reading from or writing to the file descriptor
- *
- * @param[in] el the event occurred in.
- * @param[in] fd the event occurred on.
- * @param[in] flags from kevent.
- * @param[in] fd_errno The error that ocurred.
- * @param[in] uctx LDAP thread the connection that faulted relates to.
- */
-static void _ldap_bind_auth_io_error(UNUSED fr_event_list_t *el, UNUSED int fd,
- UNUSED int flags, UNUSED int fd_errno, void *uctx)
-{
- fr_ldap_thread_t *thread = talloc_get_type_abort(uctx, fr_ldap_thread_t);
- fr_ldap_connection_t *c = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
-
- fr_ldap_state_error(c); /* Restart the connection state machine */
-}
-
-/** Callback used to process LDAP bind auth results
- *
- * @param[in] el the read event occurred in.
- * @param[in] fd the read event occurred on.
- * @param[in] flags from kevent.
- * @param[in] uctx LDAP thread associated with the event.
- */
-static void _ldap_bind_auth_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
-{
- fr_ldap_thread_t *thread = talloc_get_type_abort(uctx, fr_ldap_thread_t);
- fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
- fr_ldap_bind_auth_ctx_t find = { .msgid = -1 }, *bind_auth_ctx;
- LDAPMessage *result = NULL;
-
- int ret;
-
- do {
- /*
- * Fetch the next LDAP result which has been fully received
- */
- ret = fr_ldap_result(&result, NULL, ldap_conn, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(10));
-
- /*
- * Timeout in this case really means no results to read - we've
- * handled everything that was available
- */
- if (ret == LDAP_PROC_TIMEOUT) return;
-
- /*
- * If there is no result don't try to process one
- */
- if (!result) return;
-
- find.msgid = ldap_msgid(result);
- bind_auth_ctx = fr_rb_find(thread->binds, &find);
-
- if (!bind_auth_ctx) {
- WARN("Ignoring bind result msgid %i - doesn't match any outstanidng binds", find.msgid);
- ldap_msgfree(result);
- continue;
- }
-
- /*
- * Remove from the list of pending bind requests
- */
- fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
-
- bind_auth_ctx->ret = ret;
-
- switch (ret) {
- /*
- * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
- */
- case LDAP_PROC_NOT_PERMITTED:
- case LDAP_PROC_REJECT:
- case LDAP_PROC_BAD_DN:
- case LDAP_PROC_NO_RESULT:
- break;
-
- case LDAP_PROC_SUCCESS:
- if (bind_auth_ctx->type == LDAP_BIND_SIMPLE) break;
-
- /*
- * With SASL binds, we will be here after ldap_sasl_interactive_bind
- * returned LDAP_SASL_BIND_IN_PROGRESS. That always requires a further
- * call of ldap_sasl_interactive_bind to get the final result.
- */
- bind_auth_ctx->ret = LDAP_PROC_CONTINUE;
- FALL_THROUGH;
-
- case LDAP_PROC_CONTINUE:
- {
- fr_ldap_sasl_ctx_t *sasl_ctx = bind_auth_ctx->sasl_ctx;
- struct berval *srv_cred;
-
- /*
- * Free any previous result and track the new one.
- */
- if (sasl_ctx->result) ldap_msgfree(sasl_ctx->result);
- sasl_ctx->result = result;
- result = NULL;
-
- ret = ldap_parse_sasl_bind_result(ldap_conn->handle, sasl_ctx->result, &srv_cred, 0);
- if (ret != LDAP_SUCCESS) {
- ERROR("SASL decode failed (bind failed): %s", ldap_err2string(ret));
- break;
- }
-
- if (srv_cred) {
- DEBUG3("SASL response : %pV",
- fr_box_strvalue_len(srv_cred->bv_val, srv_cred->bv_len));
- ber_bvfree(srv_cred);
- }
-
- if (sasl_ctx->rmech) DEBUG3("Continuing SASL mech %s...", sasl_ctx->rmech);
- }
- break;
-
- default:
- PERROR("LDAP connection returned an error - restarting the connection");
- fr_ldap_state_error(bind_auth_ctx->bind_ctx->c); /* Restart the connection state machine */
- break;
- }
- unlang_interpret_mark_runnable(bind_auth_ctx->request);
-
- /*
- * Clear up the libldap results if they are not being tracked.
- */
- if (result) ldap_msgfree(result);
-
- } while (1);
-}
-
-/** Watch callback to add fd read callback to LDAP connection
- *
- * To add "bind" specific callbacks to LDAP conneciton being used
- * for bind auths, when the connection becomes connected.
- *
- * @param[in] conn to watch.
- * @param[in] prev connection state.
- * @param[in] state the connection is now in.
- * @param[in] uctx LDAP thread this connection relates to.
- */
-static void _ldap_async_bind_auth_watch(fr_connection_t *conn, UNUSED fr_connection_state_t prev,
- UNUSED fr_connection_state_t state, void *uctx)
-{
- fr_ldap_thread_t *thread = talloc_get_type_abort(uctx, fr_ldap_thread_t);
- fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
-
- if (ldap_conn->fd < 0) {
- connection_failed:
- fr_connection_signal_reconnect(conn, FR_CONNECTION_FAILED);
- return;
- }
- if (fr_event_fd_insert(conn, conn->el, ldap_conn->fd,
- _ldap_bind_auth_io_read,
- NULL,
- _ldap_bind_auth_io_error,
- thread) < 0) {
- goto connection_failed;
- };
-}
-
/** Perform a search and map the result of the search to server attributes
*
* Unlike LDAP xlat, this can be used to process attributes from multiple entries.
}
/*
- * Attempt a bind using the thread specific connection for bind auths
+ * Attempt a bind using the thread specific trunk for bind auths
*/
if (auth_ctx->mod_env->user_sasl_mech.type == FR_TYPE_STRING) {
#ifdef WITH_SASL
} else {
if (fr_ldap_bind_auth_async(request, auth_ctx->thread, auth_ctx->dn, auth_ctx->password) < 0) goto fail;
}
-
- RETURN_MODULE_RCODE(unlang_interpret_synchronous(unlang_interpret_event_list(request), request));
+ return UNLANG_ACTION_PUSHED_CHILD;
}
static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
}
/*
- * Set up a per-thread LDAP connection to use for bind auths
+ * Set up a per-thread LDAP trunk to use for bind auths
*/
- t->conn = fr_ldap_connection_state_alloc(t, mctx->el, t->config, mctx->inst->name);
- fr_connection_add_watch_post(t->conn, FR_CONNECTION_STATE_CONNECTED, _ldap_async_bind_auth_watch, false, t);
- fr_connection_signal_init(t->conn);
+ t->bind_trunk = fr_thread_ldap_bind_trunk_get(t);
MEM(t->binds = fr_rb_inline_talloc_alloc(t, fr_ldap_bind_auth_ctx_t, node, fr_ldap_bind_auth_cmp, NULL));