if (status != LDAP_PROC_SUCCESS) break;
}
- if (*result_p && ((status < 0) || !result)) {
+ if (*result_p && (!result)) {
ldap_msgfree(*result_p);
*result_p = NULL;
}
/** Free any libldap structures when an fr_ldap_query_t is freed
*
+ * It is also possible that the connection used for this query is now closed,
+ * in that instance we free it here.
*/
static int _ldap_query_free(fr_ldap_query_t *query)
{
int i;
+ /*
+ * Remove the query from the tree of outstanding queries
+ */
+ if (query->ldap_conn) fr_rb_remove(query->ldap_conn->queries, query);
+
/*
* Free any results which were retrieved
*/
fr_dlist_talloc_free(&query->referrals);
+ /*
+ * If the connection this query was using has no pending queries and
+ * is no-longer associated with a fr_connection_t then free it
+ */
+ if ((query->ldap_conn) && (query->ldap_conn->conn == NULL) &&
+ (fr_rb_num_elements(query->ldap_conn->queries) == 0)) {
+ talloc_free(query->ldap_conn);
+ }
+
return 0;
}
RETURN_MODULE_DISALLOW;
case LDAP_PROC_REJECT:
+ RDEBUG2("Bind as user \"%s\" rejected", bind_ctx->bind_dn);
RETURN_MODULE_REJECT;
case LDAP_PROC_BAD_DN:
RETURN_MODULE_FAIL;
}
+
+ talloc_free(bind_auth_ctx);
}
/** Signal an outstanding LDAP bind request to cancel
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);
}
/** Initiate an async LDAP bind for authentication
*/
static int _ldap_connection_free(fr_ldap_connection_t *c)
{
+ /*
+ * If there are any pending queries, don't free
+ */
+ if ((c->queries) && (fr_rb_num_elements(c->queries) > 0)) return -1;
+
talloc_free_children(c); /* Force inverted free order */
fr_ldap_control_clear(c);
return c;
}
+/** Watcher for LDAP connections being closed
+ *
+ * If there are any outstanding queries on the connection then
+ * re-parent the connection to the NULL ctx so that it remains
+ * until all the queries have been dealt with.
+ */
+static void _ldap_connection_close_watch(fr_connection_t *conn, UNUSED fr_connection_state_t prev,
+ UNUSED fr_connection_state_t state, void *uctx)
+{
+ fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(uctx, fr_ldap_connection_t);
+
+ if (fr_rb_num_elements(ldap_conn->queries) == 0) return;
+
+ talloc_reparent(conn, NULL, ldap_conn);
+ ldap_conn->conn = NULL;
+}
+
/** (Re-)Initialises the libldap side of the connection handle
*
* The first ldap state transition is either:
c = fr_ldap_connection_alloc(conn);
c->conn = conn;
+ fr_connection_add_watch_pre(conn, FR_CONNECTION_STATE_CLOSED, _ldap_connection_close_watch, false, c);
+
/*
* Configure/allocate the libldap handle
*/
while ((fr_trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
query = treq->preq;
ldap_abandon_ext(ldap_conn->handle, query->msgid, NULL, NULL);
- fr_rb_remove(ldap_conn->queries, query);
fr_trunk_request_signal_cancel_complete(treq);
if (!query) {
WARN("Ignoring msgid %i - doesn't match any outstanding queries (it may have been cancelled)",
find.msgid);
+ ldap_msgfree(result);
+ continue;
+ }
+
+ /*
+ * This really shouldn't happen - as we only retrieve complete sets of results -
+ * but as the query data structure will last until its results are fully handled
+ * better to have this safety check here.
+ */
+ if (query->ret != LDAP_RESULT_PENDING) {
+ WARN("Received results for msgid %i which has already been handled - ignoring", find.msgid);
+ ldap_msgfree(result);
continue;
}
switch (rcode) {
case LDAP_PROC_SUCCESS:
- query->ret = ((!query->mods) && (ldap_count_entries(ldap_conn->handle, result) == 0)) ?
+ query->ret = ((query->type == LDAP_REQUEST_SEARCH) &&
+ (ldap_count_entries(ldap_conn->handle, result) == 0)) ?
LDAP_RESULT_NO_RESULT : LDAP_RESULT_SUCCESS;
break;
break;
case LDAP_PROC_BAD_DN:
- ROPTIONAL(RDEBUG2, DEBUG2, "DN %s does not exist", query->ldap_url->lud_dn);
+ ROPTIONAL(RDEBUG2, DEBUG2, "DN %s does not exist", query->dn);
query->ret = LDAP_RESULT_BAD_DN;
break;
if (query->parser) query->parser(query, result);
/*
- * Remove the query from the outstanding list and tidy up
+ * Mark the trunk request as complete and set the request as runnable
*/
- fr_rb_remove(ldap_conn->queries, query);
fr_trunk_request_signal_complete(query->treq);
if (query->request) unlang_interpret_mark_runnable(query->request);
int ret;
-next_result:
- /*
- * 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));
+ 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;
+ /*
+ * Timeout in this case really means no results to read - we've
+ * handled everything that was available
+ */
+ if (ret == LDAP_PROC_TIMEOUT) return;
- find.msgid = ldap_msgid(result);
- bind_auth_ctx = fr_rb_find(thread->binds, &find);
+ /*
+ * If there is no result don't try to process one
+ */
+ if (!result) return;
- if (!bind_auth_ctx) {
- WARN("Ignoring bind result msgid %i - doesn't match any outstanidng binds", find.msgid);
- goto next_result;
- }
+ find.msgid = ldap_msgid(result);
+ bind_auth_ctx = fr_rb_find(thread->binds, &find);
- /*
- * Remove from the list of pending bind requests
- */
- fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
+ 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;
+ bind_auth_ctx->ret = ret;
- switch (ret) {
- case LDAP_PROC_SUCCESS:
- case LDAP_PROC_NOT_PERMITTED:
- break;
+ switch (ret) {
+ /*
+ * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
+ */
+ case LDAP_PROC_SUCCESS:
+ case LDAP_PROC_NOT_PERMITTED:
+ case LDAP_PROC_REJECT:
+ break;
- default:
- fr_ldap_state_error(bind_auth_ctx->bind_ctx->c); /* Restart the connection state machine */
- break;
- }
- unlang_interpret_mark_runnable(bind_auth_ctx->request);
+ default:
+ ERROR("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
+ */
+ ldap_msgfree(result);
- goto next_result;
+ } while (1);
}
/** Watch callback to add fd read callback to LDAP connection