sdap_async.c revision 6f6e4408cedaebbfcef61e5adb78ba75abe5839d
/*
SSSD
Async LDAP Helper routines
Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include "providers/ldap/sdap_async_private.h"
#define REALM_SEPARATOR '@'
#define REPLY_REALLOC_INCREMENT 10
void make_realm_upper_case(const char *upn)
{
char *c;
if (c == NULL) {
return;
}
while(*(++c) != '\0') {
c[0] = toupper(*c);
}
return;
}
/* ==LDAP-Memory-Handling================================================= */
static int lmsg_destructor(void *mem)
{
return 0;
}
{
void *h;
if (!h) return ENOMEM;
return EOK;
}
/* ==sdap-hanlde-utility-functions======================================== */
static int sdap_handle_destructor(void *mem);
{
struct sdap_handle *sh;
return sh;
}
static int sdap_handle_destructor(void *mem)
{
/* if the structure is currently locked, then mark it to be released
* and prevent talloc from freeing the memory */
if (sh->destructor_lock) {
sh->release_memory = true;
return -1;
}
return 0;
}
{
"destructor_lock[%d], release_memory[%d]\n",
if (sh->destructor_lock) return;
sh->destructor_lock = true;
/* make sure nobody tries to reuse this connection from now on */
/* calling the callback may result in freeing the op */
/* check if it is still the same or avoid freeing */
}
}
/* ok, we have done the job, unlock now */
sh->destructor_lock = false;
/* finally if a destructor was ever called, free sh before
* exiting */
if (sh->release_memory) {
/* neutralize the destructor as we already handled
* all was needed to be released */
}
}
/* ==Parse-Results-And-Handle-Disconnections============================== */
struct tevent_timer *te,
{
}
struct tevent_timer *te,
{
}
{
struct timeval no_timeout = {0, 0};
struct tevent_timer *te;
int ret;
return;
}
if (ret == 0) {
/* this almost always means we have reached the end of
* the list of received messages */
return;
}
if (ret == -1) {
return;
}
/* We don't know if this will be the last result.
*
* important: we must do this before actually processing the message
* because the message processing might even free the sdap_handler
* so it must be the last operation.
* FIXME: use tevent_immediate/tevent_queues, when avilable */
if (!te) {
}
/* now process this message */
}
static const char *sdap_ldap_result_str(int msgtype)
{
switch (msgtype) {
case LDAP_RES_BIND:
return "LDAP_RES_BIND";
case LDAP_RES_SEARCH_ENTRY:
return "LDAP_RES_SEARCH_ENTRY";
return "LDAP_RES_SEARCH_REFERENCE";
case LDAP_RES_SEARCH_RESULT:
return "LDAP_RES_SEARCH_RESULT";
case LDAP_RES_MODIFY:
return "LDAP_RES_MODIFY";
case LDAP_RES_ADD:
return "LDAP_RES_ADD";
case LDAP_RES_DELETE:
return "LDAP_RES_DELETE";
case LDAP_RES_MODDN:
/* These are the same result
case LDAP_RES_MODRDN:
case LDAP_RES_RENAME:
*/
return "LDAP_RES_RENAME";
case LDAP_RES_COMPARE:
return "LDAP_RES_COMPARE";
case LDAP_RES_EXTENDED:
return "LDAP_RES_EXTENDED";
case LDAP_RES_INTERMEDIATE:
return "LDAP_RES_INTERMEDIATE";
case LDAP_RES_ANY:
return "LDAP_RES_ANY";
case LDAP_RES_UNSOLICITED:
return "LDAP_RES_UNSOLICITED";
default:
/* Unmatched, fall through */
break;
}
/* Unknown result type */
return "Unknown result type!";
}
/* process a messgae calling the right operation callback.
* msg is completely taken care of (including freeeing it)
* NOTE: this function may even end up freeing the sdap_handle
* so sdap_hanbdle must not be used after this function is called
*/
{
int msgid;
int msgtype;
int ret;
if (msgid == -1) {
return;
}
}
msgtype));
return;
}
/* shouldn't happen */
return;
}
switch (msgtype) {
case LDAP_RES_SEARCH_ENTRY:
/* go and process entry */
break;
/* more ops to come with this msgid */
/* just ignore */
return;
case LDAP_RES_BIND:
case LDAP_RES_SEARCH_RESULT:
case LDAP_RES_MODIFY:
case LDAP_RES_ADD:
case LDAP_RES_DELETE:
case LDAP_RES_MODDN:
case LDAP_RES_COMPARE:
case LDAP_RES_EXTENDED:
case LDAP_RES_INTERMEDIATE:
/* no more results expected with this msgid */
break;
default:
/* unkwon msg type ?? */
return;
}
if (!reply) {
} else {
}
}
/* list exist, queue it */
} else {
/* create list, then call callback */
/* must be the last operation as it may end up freeing all memory
* including all ops handlers */
}
}
{
struct tevent_timer *te;
struct sdap_msg *next_reply;
/* get rid of the previous reply, it has been processed already */
}
/* if there are still replies to parse, queue a new operation */
/* use a very small timeout, so that fd operations have a chance to be
* served while processing a long reply */
tv = tevent_timeval_current();
/* wait 5 microsecond */
if (!te) {
}
}
}
struct tevent_timer *te,
{
}
/* ==LDAP-Operations-Helpers============================================== */
static int sdap_op_destructor(void *mem)
{
/* we don't check the result here, if a message was really abandoned,
* hopefully the server will get an abandon.
* If the operation was already fully completed, this is going to be
* just a noop */
return 0;
}
{
/* should never happen, but just in case */
return;
}
/* signal the caller that we have a timeout */
}
{
/* check if we need to set a timeout */
if (timeout) {
struct tevent_req *req;
tv = tevent_timeval_current();
/* allocate on op, so when it get freed the timeout is removed */
if (!req) {
return ENOMEM;
}
}
return EOK;
}
/* ==Modify-Password====================================================== */
struct sdap_exop_modify_passwd_state {
struct sdap_handle *sh;
char *user_error_message;
};
struct tevent_context *ev,
struct sdap_handle *sh,
char *user_dn,
const char *password,
const char *new_password)
{
struct sdap_exop_modify_passwd_state *state;
int ret;
int msgid;
struct sdap_exop_modify_passwd_state);
return NULL;
}
if (ret == -1) {
return NULL;
}
if (ret == -1) {
return NULL;
}
"Password Policy control.\n"));
ret = ERR_INTERNAL;
goto fail;
}
ber_bvfree(bv);
goto fail;
}
/* FIXME: get timeouts from configuration, for now 5 secs. */
if (ret) {
ret = ERR_INTERNAL;
goto fail;
}
return req;
fail:
return req;
}
{
struct sdap_exop_modify_passwd_state);
int ret;
int c;
int result;
if (error) {
return;
}
&response_controls, 0);
if (ret != LDAP_SUCCESS) {
ret = ERR_INTERNAL;
goto done;
}
if (response_controls == NULL) {
} else {
for (c = 0; response_controls[c] != NULL; c++) {
response_controls[c]->ldctl_oid));
LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
&pp_error);
if (ret != LDAP_SUCCESS) {
goto done;
}
}
}
}
switch (result) {
case LDAP_SUCCESS:
break;
"Please make sure the password meets the complexity constraints.");
goto done;
}
break;
default:
if (errmsg) {
goto done;
}
}
break;
}
done:
} else {
}
}
char **user_error_message)
{
struct sdap_exop_modify_passwd_state);
return EOK;
}
/* ==Update-passwordLastChanged-attribute====================== */
struct update_last_changed_state {
struct tevent_context *ev;
struct sdap_handle *sh;
const char *dn;
};
struct tevent_req *
struct tevent_context *ev,
struct sdap_handle *sh,
const char *dn,
char *lastchanged_name)
{
struct tevent_req *req;
struct update_last_changed_state *state;
char **values;
int msgid;
return NULL;
}
goto done;
}
goto done;
}
goto done;
}
/* The attribute contains number of days since the epoch */
goto done;
}
if (ret) {
goto done;
}
if (ret) {
goto done;
}
done:
}
return req;
}
{
struct update_last_changed_state *state;
char *errmsg;
int result;
int lret;
if (error) {
return;
}
NULL, 0);
if (lret != LDAP_SUCCESS) {
goto done;
}
done:
} else {
}
}
{
return EOK;
}
/* ==Fetch-RootDSE============================================= */
struct sdap_get_rootdse_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
struct sysdb_attrs *rootdse;
};
struct tevent_context *ev,
struct sdap_options *opts,
struct sdap_handle *sh)
{
struct sdap_get_rootdse_state *state;
const char *attrs[] = {
"*",
"altServer",
"supportedControl",
"supportedExtension",
"supportedFeatures",
"supportedLDAPVersion",
"supportedSASLMechanisms",
};
"", LDAP_SCOPE_BASE,
false);
if (!subreq) {
return NULL;
}
return req;
}
/* This is not a real attribute, it's just there to avoid
* actually pulling real data down, to save bandwidth
*/
#define SDAP_MATCHING_RULE_TEST_ATTR "sssmatchingruletest"
{
struct tevent_req);
struct sdap_get_rootdse_state);
struct sysdb_attrs **results;
int ret;
const char *filter;
if (ret) {
return;
}
if (num_results == 0 || !results) {
"Please check that anonymous access to RootDSE is allowed\n"
));
return;
}
if (num_results > 1) {
return;
}
/* Auto-detect the ldap matching rule if requested */
/* This feature is disabled for both groups
* and initgroups. Skip the auto-detection
* lookup.
*/
("Skipping auto-detection of match rule\n"));
return;
}
("Auto-detecting support for match rule\n"));
/* Create a filter using the matching rule. It need not point
* at any valid data. We're only going to be looking for the
* error code.
*/
/* Perform a trivial query with the matching rule in play.
* If it returns success, we know it is available. If it
* returns EIO, we know it isn't.
*/
false);
if (!subreq) {
return;
}
}
{
struct tevent_req);
struct sdap_get_rootdse_state);
struct sysdb_attrs **results;
/* The search succeeded */
/* The search failed. Disable support for
* matching rule lookups.
*/
} else {
("Unexpected error while testing for matching rule support\n"));
return;
}
("LDAP server %s the matching rule extension\n",
? "supports"
: "does not support"));
}
struct sysdb_attrs **rootdse)
{
struct sdap_get_rootdse_state);
return EOK;
}
/* ==Helpers for parsing replies============================== */
struct sdap_reply {
struct sysdb_attrs **reply;
};
struct sdap_reply *sreply,
struct sysdb_attrs *msg)
{
struct sysdb_attrs *,
return ENOMEM;
}
}
return EOK;
}
struct sdap_deref_reply {
struct sdap_deref_attrs **reply;
};
int num_maps,
struct sdap_deref_reply *dreply,
struct sdap_deref_attrs **res)
{
int i;
for (i=0; i < num_maps; i++) {
struct sdap_deref_attrs *,
return ENOMEM;
}
}
}
return EOK;
}
/* ==Generic Search exposing all options======================= */
void *pvt);
struct sdap_get_generic_ext_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
const char *search_base;
int scope;
const char *filter;
const char **attrs;
int timeout;
int attrsonly;
int sizelimit;
int nserverctrls;
void *cb_data;
bool allow_paging;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_options *opts,
struct sdap_handle *sh,
const char *search_base,
int scope,
const char *filter,
const char **attrs,
int attrsonly,
int sizelimit,
int timeout,
bool allow_paging,
void *cb_data)
{
struct sdap_get_generic_ext_state *state;
struct tevent_req *req;
int i;
/* Be extra careful and never allow paging for BASE searches,
* even if requested.
*/
if (scope == LDAP_SCOPE_BASE) {
state->allow_paging = false;
} else {
}
* paging on for those requests
*/
/* X-DEREF */
NULL);
if (control) {
state->allow_paging = true;
}
/* ASQ */
NULL);
if (control) {
state->allow_paging = true;
}
for (state->nserverctrls=0;
state->nserverctrls++) ;
/* One extra space for NULL, one for page control */
if (!state->serverctrls) {
return req;
}
for (i=0; i < state->nserverctrls; i++) {
}
return req;
}
return req;
}
{
struct sdap_get_generic_ext_state *state =
char *errmsg;
int lret;
int optret;
int msgid;
bool disable_paging;
/* Make sure to free any previous operations so
* if we are handling a large number of pages we
* don't waste memory.
*/
("calling ldap_search_ext with [%s][%s].\n",
state->search_base));
if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
int i;
}
}
}
if (!disable_paging
&& state->allow_paging
NULL,
false,
&page_control);
if (lret != LDAP_SUCCESS) {
goto done;
}
}
if (lret != LDAP_SUCCESS) {
if (lret == LDAP_SERVER_DOWN) {
&errmsg);
if (optret == LDAP_SUCCESS) {
}
else {
}
}
else {
}
goto done;
}
goto done;
}
done:
return ret;
}
{
struct sdap_get_generic_ext_state);
int result;
int ret;
int lret;
if (error) {
return;
}
/* ignore references for now */
/* unlock the operation so that we can proceed with the next result */
break;
case LDAP_RES_SEARCH_ENTRY:
return;
}
break;
case LDAP_RES_SEARCH_RESULT:
&returned_controls, 0);
if (ret != LDAP_SUCCESS) {
return;
}
if (result == LDAP_SIZELIMIT_EXCEEDED) {
/* Try to return what we've got */
("LDAP sizelimit was exceeded, returning incomplete data\n"));
} else if (result == LDAP_INAPPROPRIATE_MATCHING) {
/* This error should only occur when we're testing for
* specialized functionality like the ldap matching rule
* filter for Active Directory. Warn at a higher log
* level and return EIO.
*/
("LDAP_INAPPROPRIATE_MATCHING: %s\n",
return;
} else if (result == LDAP_UNAVAILABLE_CRITICAL_EXTENSION) {
return;
("Unexpected result from ldap: %s(%d), %s\n",
return;
}
/* Determine if there are more pages to retrieve */
if (!page_control) {
/* No paging support. We are done */
return;
}
&total_count, &cookie);
if (lret != LDAP_SUCCESS) {
return;
}
/* Cookie contains data, which means there are more requests
* to be processed.
*/
return;
}
return;
}
return;
}
/* The cookie must be freed even if len == 0 */
/* This was the last page. We're done */
return;
default:
/* what is going on here !? */
return;
}
}
static int
{
return EOK;
}
/* ==Generic Search============================================ */
struct sdap_get_generic_state {
struct sdap_attr_map *map;
int map_num_attrs;
struct sdap_reply sreply;
struct sdap_options *opts;
};
void *pvt);
struct tevent_context *ev,
struct sdap_options *opts,
struct sdap_handle *sh,
const char *search_base,
int scope,
const char *filter,
const char **attrs,
struct sdap_attr_map *map,
int map_num_attrs,
int timeout,
bool allow_paging)
{
if (!subreq) {
return NULL;
}
return req;
}
void *pvt)
{
struct sysdb_attrs *attrs;
struct sdap_get_generic_state *state =
return ret;
}
return ret;
}
/* add_to_reply steals attrs, no need to free them here */
return EOK;
}
{
struct tevent_req);
int ret;
if (ret) {
return;
}
}
struct sysdb_attrs ***reply)
{
struct sdap_get_generic_state);
return EOK;
}
/* ==OpenLDAP deref search============================================== */
const char *deref_attr,
const char **attrs,
LDAPControl **ctrl);
static int sdap_x_deref_search_ctrls_destructor(void *ptr);
void *pvt);
struct sdap_x_deref_search_state {
struct sdap_handle *sh;
struct sdap_attr_map_info *maps;
LDAPControl **ctrls;
struct sdap_deref_reply dreply;
int num_maps;
};
static struct tevent_req *
const char *base_dn, const char *deref_attr,
{
struct sdap_x_deref_search_state *state;
int ret;
return NULL;
}
return NULL;
}
true, sdap_x_deref_parse_entry,
state);
if (!subreq) {
return NULL;
}
return req;
}
const char *deref_attr,
const char **attrs,
LDAPControl **ctrl)
{
int ret;
if (ret != LDAP_SUCCESS) {
ldap_err2string(ret)));
return ret;
}
return ret;
}
return EOK;
}
void *pvt)
{
struct sdap_deref_attrs **res;
struct sdap_x_deref_search_state);
&ctrls);
if (ret != LDAP_SUCCESS) {
goto done;
}
if (!ctrls) {
goto done;
}
if (!derefctrl) {
goto done;
}
&deref_res);
if (ret != LDAP_SUCCESS) {
("ldap_parse_derefresponse_control failed: %s\n",
ldap_err2string(ret)));
goto done;
}
if (ret) {
goto done;
}
goto done;
}
}
("All deref results from a single control parsed\n"));
done:
return ret;
}
{
struct tevent_req);
int ret;
if (ret) {
return;
}
}
static int sdap_x_deref_search_ctrls_destructor(void *ptr)
{
ldap_control_free(ctrls[0]);
}
return 0;
}
static int
struct sdap_deref_attrs ***reply)
{
struct sdap_x_deref_search_state);
return EOK;
}
/* ==Attribute scoped search============================================ */
struct sdap_asq_search_state {
struct sdap_attr_map_info *maps;
int num_maps;
LDAPControl **ctrls;
struct sdap_options *opts;
struct sdap_deref_reply dreply;
};
const char *attr,
LDAPControl **ctrl);
static int sdap_asq_search_ctrls_destructor(void *ptr);
void *pvt);
static struct tevent_req *
const char *base_dn, const char *deref_attr,
{
struct sdap_asq_search_state *state;
int ret;
return NULL;
}
return NULL;
}
true, sdap_asq_search_parse_entry,
state);
if (!subreq) {
return NULL;
}
return req;
}
const char *attr,
LDAPControl **ctrl)
{
int ret;
return ENOMEM;
}
if (ret == -1) {
return EIO;
}
if (ret == -1) {
return EIO;
}
return ret;
}
return EOK;
}
void *pvt)
{
struct sdap_asq_search_state *state =
int i, mi;
struct sdap_attr_map *map;
int num_attrs;
struct sdap_deref_attrs **res;
char *tmp;
char *dn;
bool disable_range_rtrvl;
if (!res) {
goto done;
}
goto done;
}
}
if (!tmp) {
goto done;
}
if (!dn) {
goto done;
}
/* Find all suitable maps in the list */
if (!vals) {
("Unknown entry type, no objectClass found for DN [%s]!\n", dn));
goto done;
}
for (i = 0; vals[i]; i++) {
/* the objectclass is always the first name in the map */
/* it's an entry of the right type */
("Matched objectclass [%s] on DN [%s], will use associated map\n",
break;
}
}
if (!map) {
("DN [%s] did not match the objectClass [%s]\n",
continue;
}
goto done;
}
}
goto done;
}
done:
return ret;
}
{
struct tevent_req);
int ret;
if (ret) {
return;
}
}
static int sdap_asq_search_ctrls_destructor(void *ptr)
{
ldap_control_free(ctrls[0]);
}
return 0;
}
struct sdap_deref_attrs ***reply)
{
struct sdap_asq_search_state);
return EOK;
}
/* ==Generic Deref Search============================================ */
enum sdap_deref_type {
};
struct sdap_deref_search_state {
struct sdap_handle *sh;
struct sdap_deref_attrs **reply;
enum sdap_deref_type deref_type;
};
struct tevent_req *
struct tevent_context *ev,
struct sdap_options *opts,
struct sdap_handle *sh,
const char *base_dn,
const char *deref_attr,
const char **attrs,
int num_maps,
struct sdap_attr_map_info *maps,
int timeout)
{
struct sdap_deref_search_state *state;
state->reply_count = 0;
timeout);
if (!subreq) {
goto fail;
}
timeout);
if (!subreq) {
goto fail;
}
} else {
goto fail;
}
return req;
fail:
return NULL;
}
{
struct tevent_req);
struct sdap_deref_search_state);
int ret;
switch (state->deref_type) {
case SDAP_DEREF_OPENLDAP:
break;
case SDAP_DEREF_ASQ:
break;
default:
return;
}
"LDAP server claims to support deref, but deref search failed. "
"Disabling deref for further requests. You can permanently "
"disable deref by setting ldap_deref_threshold to 0 in domain "
"configuration.");
} else {
}
return;
}
}
struct sdap_deref_attrs ***reply)
{
struct sdap_deref_search_state);
return EOK;
}
{
{ LDAP_CONTROL_X_DEREF, "OpenLDAP" },
};
int i;
int deref_threshold;
if (sh->disable_deref) {
return false;
}
if (deref_threshold == 0) {
return false;
}
for (i=0; deref_oids[i][0]; i++) {
deref_oids[i][1]));
return true;
}
}
return false;
}
const char *attr_name,
const char *attr_desc,
bool multivalued,
const char *name,
struct sysdb_attrs *attrs)
{
struct ldb_message_element *el;
unsigned int num_values, i;
if (ret) {
"list of the LDAP attributes [%d]: %s\n",
return ret;
}
if (el->num_values == 0) {
} else {
for (i = 0; i < num_values; i++) {
if (ret) {
return ret;
}
}
}
return EOK;
}
sdap_save_all_names(const char *name,
struct sysdb_attrs *ldap_attrs,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs)
{
const char *domname;
int i;
if (!tmp_ctx) {
goto done;
}
goto done;
}
for (i = 0; aliases[i]; i++) {
goto done;
}
if (ret) {
"attribute list\n", aliases[i]));
goto done;
}
}
done:
return ret;
}