From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- src/providers/ldap/sdap_async.c | 1018 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1018 insertions(+) create mode 100644 src/providers/ldap/sdap_async.c (limited to 'src/providers/ldap/sdap_async.c') diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c new file mode 100644 index 000000000..959c08a65 --- /dev/null +++ b/src/providers/ldap/sdap_async.c @@ -0,0 +1,1018 @@ +/* + SSSD + + Async LDAP Helper routines + + Copyright (C) Simo Sorce - 2009 + + This program is free software; you can redistribute it and/or modify + 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 . +*/ + +#include +#include "util/util.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; + + c = strchr(upn, REALM_SEPARATOR); + if (c == NULL) { + DEBUG(9, ("No realm delimiter found in upn [%s].\n", upn)); + return; + } + + while(*(++c) != '\0') { + c[0] = toupper(*c); + } + + return; +} + +/* ==LDAP-Memory-Handling================================================= */ + +static int lmsg_destructor(void *mem) +{ + ldap_msgfree((LDAPMessage *)mem); + return 0; +} + +static int sdap_msg_attach(TALLOC_CTX *memctx, LDAPMessage *msg) +{ + void *h; + + if (!msg) return EINVAL; + + h = sss_mem_attach(memctx, msg, lmsg_destructor); + if (!h) return ENOMEM; + + return EOK; +} + +/* ==sdap-hanlde-utility-functions======================================== */ + +static inline void sdap_handle_release(struct sdap_handle *sh); +static int sdap_handle_destructor(void *mem); + +struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx) +{ + struct sdap_handle *sh; + + sh = talloc_zero(memctx, struct sdap_handle); + if (!sh) return NULL; + + talloc_set_destructor((TALLOC_CTX *)sh, sdap_handle_destructor); + + return sh; +} + +static int sdap_handle_destructor(void *mem) +{ + struct sdap_handle *sh = talloc_get_type(mem, struct sdap_handle); + + sdap_handle_release(sh); + + return 0; +} + +static void sdap_handle_release(struct sdap_handle *sh) +{ + DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], ldap[%p]\n", + sh, (int)sh->connected, sh->ops, sh->ldap)); + + if (sh->connected) { + struct sdap_op *op; + +#ifdef HAVE_LDAP_CONNCB + /* remove all related fd events from the event loop */ + talloc_zfree(sh->conncb->lc_arg); +#else + talloc_zfree(sh->fde); +#endif + + while (sh->ops) { + op = sh->ops; + op->callback(op, NULL, EIO, op->data); + /* calling the callback may result in freeing the op */ + /* check if it is still the same or avoid freeing */ + if (op == sh->ops) talloc_free(op); + } + + if (sh->ldap) { + ldap_unbind_ext(sh->ldap, NULL, NULL); + } +#ifdef HAVE_LDAP_CONNCB + talloc_zfree(sh->conncb); +#endif + sh->connected = false; + sh->ldap = NULL; + sh->ops = NULL; + } +} + +/* ==Parse-Results-And-Handle-Disconnections============================== */ +static void sdap_process_message(struct tevent_context *ev, + struct sdap_handle *sh, LDAPMessage *msg); +static void sdap_process_result(struct tevent_context *ev, void *pvt); +static void sdap_process_next_reply(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +static void sdap_ldap_result(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + sdap_process_result(ev, pvt); +} + +static void sdap_ldap_next_result(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + sdap_process_result(ev, pvt); +} + +static void sdap_process_result(struct tevent_context *ev, void *pvt) +{ + struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle); + struct timeval no_timeout = {0, 0}; + struct tevent_timer *te; + LDAPMessage *msg; + int ret; + + DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], ldap[%p]\n", + sh, (int)sh->connected, sh->ops, sh->ldap)); + + if (!sh->connected || !sh->ldap) { + DEBUG(2, ("ERROR: LDAP connection is not connected!\n")); + return; + } + + ret = ldap_result(sh->ldap, LDAP_RES_ANY, 0, &no_timeout, &msg); + if (ret == 0) { + /* this almost always means we have reached the end of + * the list of received messages */ + DEBUG(8, ("Trace: ldap_result found nothing!\n")); + return; + } + + if (ret == -1) { + DEBUG(4, ("ldap_result gave -1, something bad happend!\n")); + sdap_handle_release(sh); + 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 */ + memset(&no_timeout, 0, sizeof(struct timeval)); + + te = tevent_add_timer(ev, sh, no_timeout, sdap_ldap_next_result, sh); + if (!te) { + DEBUG(1, ("Failed to add critical timer to fetch next result!\n")); + } + + /* now process this message */ + sdap_process_message(ev, sh, msg); +} + +/* 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 + */ +static void sdap_process_message(struct tevent_context *ev, + struct sdap_handle *sh, LDAPMessage *msg) +{ + struct sdap_msg *reply; + struct sdap_op *op; + int msgid; + int msgtype; + int ret; + + msgid = ldap_msgid(msg); + if (msgid == -1) { + DEBUG(2, ("can't fire callback, message id invalid!\n")); + ldap_msgfree(msg); + return; + } + + msgtype = ldap_msgtype(msg); + + for (op = sh->ops; op; op = op->next) { + if (op->msgid == msgid) break; + } + + if (op == NULL) { + DEBUG(2, ("Unmatched msgid, discarding message (type: %0x)\n", + msgtype)); + ldap_msgfree(msg); + return; + } + + /* shouldn't happen */ + if (op->done) { + DEBUG(2, ("Operation [%p] already handled (type: %0x)\n", op, msgtype)); + ldap_msgfree(msg); + return; + } + + switch (msgtype) { + case LDAP_RES_SEARCH_ENTRY: + /* go and process entry */ + break; + + case LDAP_RES_SEARCH_REFERENCE: + /* more ops to come with this msgid */ + /* just ignore */ + ldap_msgfree(msg); + 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 */ + op->done = true; + break; + + default: + /* unkwon msg type ?? */ + DEBUG(1, ("Couldn't figure out the msg type! [%0x]\n", msgtype)); + ldap_msgfree(msg); + return; + } + + reply = talloc_zero(op, struct sdap_msg); + if (!reply) { + ldap_msgfree(msg); + ret = ENOMEM; + } else { + reply->msg = msg; + ret = sdap_msg_attach(reply, msg); + if (ret != EOK) { + ldap_msgfree(msg); + talloc_zfree(reply); + } + } + + if (op->list) { + /* list exist, queue it */ + + op->last->next = reply; + op->last = reply; + + } else { + /* create list, then call callback */ + op->list = op->last = reply; + + /* must be the last operation as it may end up freeing all memory + * including all ops handlers */ + op->callback(op, reply, ret, op->data); + } +} + +static void sdap_unlock_next_reply(struct sdap_op *op) +{ + struct timeval tv; + struct tevent_timer *te; + struct sdap_msg *next_reply; + + if (op->list) { + next_reply = op->list->next; + /* get rid of the previous reply, it has been processed already */ + talloc_zfree(op->list); + op->list = next_reply; + } + + /* if there are still replies to parse, queue a new operation */ + if (op->list) { + /* 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 */ + tv.tv_usec += 5; + tv.tv_sec += tv.tv_usec / 1000000; + tv.tv_usec = tv.tv_usec % 1000000; + + te = tevent_add_timer(op->ev, op, tv, + sdap_process_next_reply, op); + if (!te) { + DEBUG(1, ("Failed to add critical timer for next reply!\n")); + op->callback(op, NULL, EFAULT, op->data); + } + } +} + +static void sdap_process_next_reply(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct sdap_op *op = talloc_get_type(pvt, struct sdap_op); + + op->callback(op, op->list, EOK, op->data); +} + +#ifdef HAVE_LDAP_CONNCB +int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, + struct sockaddr *addr, struct ldap_conncb *ctx) +{ + int ret; + ber_socket_t ber_fd; + struct fd_event_item *fd_event_item; + struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg, + struct ldap_cb_data); + + ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd); + if (ret == -1) { + DEBUG(1, ("ber_sockbuf_ctrl failed.\n")); + return EINVAL; + } + DEBUG(9, ("New LDAP connection to [%s] with fd [%d].\n", + ldap_url_desc2str(srv), ber_fd)); + + fd_event_item = talloc_zero(cb_data, struct fd_event_item); + if (fd_event_item == NULL) { + DEBUG(1, ("talloc failed.\n")); + return ENOMEM; + } + + fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd, + TEVENT_FD_READ, sdap_ldap_result, + cb_data->sh); + if (fd_event_item->fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + talloc_free(fd_event_item); + return ENOMEM; + } + fd_event_item->fd = ber_fd; + + DLIST_ADD(cb_data->fd_list, fd_event_item); + + return LDAP_SUCCESS; +} + +void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb, + struct ldap_conncb *ctx) +{ + int ret; + ber_socket_t ber_fd; + struct fd_event_item *fd_event_item; + struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg, + struct ldap_cb_data); + + if (sb == NULL || cb_data == NULL) { + return; + } + + ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd); + if (ret == -1) { + DEBUG(1, ("ber_sockbuf_ctrl failed.\n")); + return; + } + DEBUG(9, ("Closing LDAP connection with fd [%d].\n", ber_fd)); + + DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) { + if (fd_event_item->fd == ber_fd) { + break; + } + } + if (fd_event_item == NULL) { + DEBUG(1, ("No event for fd [%d] found.\n", ber_fd)); + return; + } + + DLIST_REMOVE(cb_data->fd_list, fd_event_item); + talloc_zfree(fd_event_item); + + return; +} + +#else + +static int get_fd_from_ldap(LDAP *ldap, int *fd) +{ + int ret; + + ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to get fd from ldap!!\n")); + *fd = -1; + return EIO; + } + + return EOK; +} + +int sdap_install_ldap_callbacks(struct sdap_handle *sh, + struct tevent_context *ev) +{ + int fd; + int ret; + + ret = get_fd_from_ldap(sh->ldap, &fd); + if (ret) return ret; + + sh->fde = tevent_add_fd(ev, sh, fd, TEVENT_FD_READ, sdap_ldap_result, sh); + if (!sh->fde) return ENOMEM; + + DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], fde[%p], ldap[%p]\n", + sh, (int)sh->connected, sh->ops, sh->fde, sh->ldap)); + + return EOK; +} + +#endif + + +/* ==LDAP-Operations-Helpers============================================== */ + +static int sdap_op_destructor(void *mem) +{ + struct sdap_op *op = (struct sdap_op *)mem; + + DLIST_REMOVE(op->sh->ops, op); + + if (op->done) return 0; + + /* 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 */ + ldap_abandon_ext(op->sh->ldap, op->msgid, NULL, NULL); + + return 0; +} + +static void sdap_op_timeout(struct tevent_req *req) +{ + struct sdap_op *op = tevent_req_callback_data(req, struct sdap_op); + + /* should never happen, but just in case */ + if (op->done) { + DEBUG(2, ("Timeout happened after op was finished !?\n")); + return; + } + + /* signal the caller that we have a timeout */ + op->callback(op, NULL, ETIMEDOUT, op->data); +} + +int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev, + struct sdap_handle *sh, int msgid, + sdap_op_callback_t *callback, void *data, + int timeout, struct sdap_op **_op) +{ + struct sdap_op *op; + + op = talloc_zero(memctx, struct sdap_op); + if (!op) return ENOMEM; + + op->sh = sh; + op->msgid = msgid; + op->callback = callback; + op->data = data; + op->ev = ev; + + /* check if we need to set a timeout */ + if (timeout) { + struct tevent_req *req; + struct timeval tv; + + tv = tevent_timeval_current(); + tv = tevent_timeval_add(&tv, timeout, 0); + + /* allocate on op, so when it get freed the timeout is removed */ + req = tevent_wakeup_send(op, ev, tv); + if (!req) { + talloc_zfree(op); + return ENOMEM; + } + tevent_req_set_callback(req, sdap_op_timeout, op); + } + + DLIST_ADD(sh->ops, op); + + talloc_set_destructor((TALLOC_CTX *)op, sdap_op_destructor); + + *_op = op; + return EOK; +} + +/* ==Modify-Password====================================================== */ + +struct sdap_exop_modify_passwd_state { + struct sdap_handle *sh; + + struct sdap_op *op; + + int result; + char *user_error_message; +}; + +static void sdap_exop_modify_passwd_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + char *user_dn, + char *password, + char *new_password) +{ + struct tevent_req *req = NULL; + struct sdap_exop_modify_passwd_state *state; + int ret; + BerElement *ber = NULL; + struct berval *bv = NULL; + int msgid; + LDAPControl *request_controls[2]; + + req = tevent_req_create(memctx, &state, + struct sdap_exop_modify_passwd_state); + if (!req) return NULL; + + state->sh = sh; + state->user_error_message = NULL; + + ber = ber_alloc_t( LBER_USE_DER ); + if (ber == NULL) { + DEBUG(7, ("ber_alloc_t failed.\n")); + talloc_zfree(req); + return NULL; + } + + ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, + user_dn, + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, password, + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, new_password); + if (ret == -1) { + DEBUG(1, ("ber_printf failed.\n")); + ber_free(ber, 1); + talloc_zfree(req); + return NULL; + } + + ret = ber_flatten(ber, &bv); + ber_free(ber, 1); + if (ret == -1) { + DEBUG(1, ("ber_flatten failed.\n")); + talloc_zfree(req); + return NULL; + } + + ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST, + 0, NULL, 0, &request_controls[0]); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("sss_ldap_control_create failed.\n")); + goto fail; + } + request_controls[1] = NULL; + + DEBUG(4, ("Executing extended operation\n")); + + ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD, + bv, request_controls, NULL, &msgid); + ber_bvfree(bv); + ldap_control_free(request_controls[0]); + if (ret == -1 || msgid == -1) { + DEBUG(1, ("ldap_extended_operation failed\n")); + goto fail; + } + DEBUG(8, ("ldap_extended_operation sent, msgid = %d\n", msgid)); + + /* FIXME: get timeouts from configuration, for now 5 secs. */ + ret = sdap_op_add(state, ev, state->sh, msgid, + sdap_exop_modify_passwd_done, req, 5, &state->op); + if (ret) { + DEBUG(1, ("Failed to set up operation!\n")); + goto fail; + } + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_exop_modify_passwd_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_exop_modify_passwd_state *state = tevent_req_data(req, + struct sdap_exop_modify_passwd_state); + char *errmsg = NULL; + int ret; + LDAPControl **response_controls = NULL; + int c; + ber_int_t pp_grace; + ber_int_t pp_expire; + LDAPPasswordPolicyError pp_error; + + if (error) { + tevent_req_error(req, error); + return; + } + + ret = ldap_parse_result(state->sh->ldap, reply->msg, + &state->result, NULL, &errmsg, NULL, + &response_controls, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); + ret = EIO; + goto done; + } + + if (response_controls == NULL) { + DEBUG(5, ("Server returned no controls.\n")); + } else { + for (c = 0; response_controls[c] != NULL; c++) { + DEBUG(9, ("Server returned control [%s].\n", + response_controls[c]->ldctl_oid)); + if (strcmp(response_controls[c]->ldctl_oid, + LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) { + ret = ldap_parse_passwordpolicy_control(state->sh->ldap, + response_controls[c], + &pp_expire, &pp_grace, + &pp_error); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n")); + ret = EIO; + goto done; + } + + DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] " + "error [%s].\n", pp_expire, pp_grace, + ldap_passwordpolicy_err2txt(pp_error))); + } + } + } + + if (state->result != LDAP_SUCCESS) { + state->user_error_message = talloc_strdup(state, errmsg); + if (state->user_error_message == NULL) { + DEBUG(1, ("talloc_strdup failed.\n")); + } + } + + DEBUG(3, ("ldap_extended_operation result: %s(%d), %s\n", + ldap_err2string(state->result), state->result, errmsg)); + + ret = LDAP_SUCCESS; +done: + ldap_controls_free(response_controls); + ldap_memfree(errmsg); + + if (ret == LDAP_SUCCESS) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +int sdap_exop_modify_passwd_recv(struct tevent_req *req, + TALLOC_CTX * mem_ctx, + enum sdap_result *result, + char **user_error_message) +{ + struct sdap_exop_modify_passwd_state *state = tevent_req_data(req, + struct sdap_exop_modify_passwd_state); + + *result = SDAP_ERROR; + *user_error_message = talloc_steal(mem_ctx, state->user_error_message); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->result == LDAP_SUCCESS) { + *result = SDAP_SUCCESS; + } + + 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; +}; + +static void sdap_get_rootdse_done(struct tevent_req *subreq); + +struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct sdap_handle *sh) +{ + struct tevent_req *req, *subreq; + struct sdap_get_rootdse_state *state; + + DEBUG(9, ("Getting rootdse\n")); + + req = tevent_req_create(memctx, &state, struct sdap_get_rootdse_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->sh = sh; + state->rootdse = NULL; + + subreq = sdap_get_generic_send(state, ev, opts, sh, + "", LDAP_SCOPE_BASE, + "(objectclass=*)", NULL, NULL, 0); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sdap_get_rootdse_done, req); + + return req; +} + +static void sdap_get_rootdse_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_rootdse_state *state = tevent_req_data(req, + struct sdap_get_rootdse_state); + struct sysdb_attrs **results; + size_t num_results; + int ret; + + ret = sdap_get_generic_recv(subreq, state, &num_results, &results); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (num_results == 0 || !results) { + DEBUG(2, ("No RootDSE for server ?!\n")); + tevent_req_error(req, ENOENT); + return; + } + + if (num_results > 1) { + DEBUG(2, ("Multiple replies when searching for RootDSE ??\n")); + tevent_req_error(req, EIO); + return; + } + + state->rootdse = talloc_steal(state, results[0]); + talloc_zfree(results); + + DEBUG(9, ("Got rootdse\n")); + + tevent_req_done(req); +} + +int sdap_get_rootdse_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + struct sysdb_attrs **rootdse) +{ + struct sdap_get_rootdse_state *state = tevent_req_data(req, + struct sdap_get_rootdse_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *rootdse = talloc_steal(memctx, state->rootdse); + + return EOK; +} + +/* ==Generic Search============================================ */ + +struct sdap_get_generic_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; + struct sdap_attr_map *map; + int map_num_attrs; + + struct sdap_op *op; + + size_t reply_max; + size_t reply_count; + struct sysdb_attrs **reply; +}; + +static errno_t add_to_reply(struct sdap_get_generic_state *state, + struct sysdb_attrs *msg); + +static void sdap_get_generic_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, + 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) +{ + struct tevent_req *req = NULL; + struct sdap_get_generic_state *state = NULL; + int lret; + int ret; + int msgid; + + req = tevent_req_create(memctx, &state, struct sdap_get_generic_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->sh = sh; + state->search_base = search_base; + state->scope = scope; + state->filter = filter; + state->attrs = attrs; + state->map = map; + state->map_num_attrs = map_num_attrs; + state->op = NULL; + state->reply_max = 0; + state->reply_count = 0; + state->reply = NULL; + + DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", state->filter, + state->search_base)); + if (debug_level >= 7) { + int i; + + if (state->attrs) { + for (i = 0; state->attrs[i]; i++) { + DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i])); + } + } + } + + lret = ldap_search_ext(state->sh->ldap, state->search_base, + state->scope, state->filter, + discard_const(state->attrs), + false, NULL, NULL, NULL, 0, &msgid); + if (lret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret))); + ret = EIO; + goto fail; + } + DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid)); + + ret = sdap_op_add(state, state->ev, state->sh, msgid, + sdap_get_generic_done, req, + dp_opt_get_int(state->opts->basic, + SDAP_SEARCH_TIMEOUT), + &state->op); + if (ret != EOK) { + DEBUG(1, ("Failed to set up operation!\n")); + goto fail; + } + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + + +static void sdap_get_generic_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_generic_state *state = tevent_req_data(req, + struct sdap_get_generic_state); + struct sysdb_attrs *attrs; + char *errmsg; + int result; + int ret; + + if (error) { + tevent_req_error(req, error); + return; + } + + switch (ldap_msgtype(reply->msg)) { + case LDAP_RES_SEARCH_REFERENCE: + /* ignore references for now */ + talloc_free(reply); + + /* unlock the operation so that we can proceed with the next result */ + sdap_unlock_next_reply(state->op); + break; + + case LDAP_RES_SEARCH_ENTRY: + ret = sdap_parse_entry(state, state->sh, reply, + state->map, state->map_num_attrs, + &attrs, NULL); + if (ret != EOK) { + DEBUG(1, ("sdap_parse_generic_entry failed.\n")); + tevent_req_error(req, ENOMEM); + return; + } + + ret = add_to_reply(state, attrs); + if (ret != EOK) { + DEBUG(1, ("add_to_reply failed.\n")); + tevent_req_error(req, ret); + return; + } + + sdap_unlock_next_reply(state->op); + break; + + case LDAP_RES_SEARCH_RESULT: + ret = ldap_parse_result(state->sh->ldap, reply->msg, + &result, NULL, &errmsg, NULL, NULL, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(6, ("Search result: %s(%d), %s\n", + ldap_err2string(result), result, errmsg)); + + tevent_req_done(req); + return; + + default: + /* what is going on here !? */ + tevent_req_error(req, EIO); + return; + } +} + +static errno_t add_to_reply(struct sdap_get_generic_state *state, + struct sysdb_attrs *msg) +{ + if (state->reply == NULL || state->reply_max == state->reply_count) { + state->reply_max += REPLY_REALLOC_INCREMENT; + state->reply = talloc_realloc(state, state->reply, + struct sysdb_attrs *, + state->reply_max); + if (state->reply == NULL) { + DEBUG(1, ("talloc_realloc failed.\n")); + return ENOMEM; + } + } + + state->reply[state->reply_count++] = talloc_steal(state->reply, msg); + + return EOK; +} + +int sdap_get_generic_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *reply_count, + struct sysdb_attrs ***reply) +{ + struct sdap_get_generic_state *state = tevent_req_data(req, + struct sdap_get_generic_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *reply_count = state->reply_count; + *reply = talloc_steal(mem_ctx, state->reply); + + return EOK; +} + -- cgit