diff options
author | Simo Sorce <ssorce@redhat.com> | 2009-06-13 12:17:55 -0400 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2009-07-08 19:29:53 -0400 |
commit | f7cfc12d431f2e435d7655d080db015ab9a7d554 (patch) | |
tree | 784bcae55f76799c0ca115bf2aa48481abe7b576 /server/providers/ldap | |
parent | f255b943157f76b53eb55c279e29e40a6c41a97a (diff) | |
download | sssd-f7cfc12d431f2e435d7655d080db015ab9a7d554.tar.gz sssd-f7cfc12d431f2e435d7655d080db015ab9a7d554.tar.bz2 sssd-f7cfc12d431f2e435d7655d080db015ab9a7d554.zip |
Implement the ldap identity module.
This uses and exapands the async helpers.
Diffstat (limited to 'server/providers/ldap')
-rw-r--r-- | server/providers/ldap/ldap_id.c | 1325 | ||||
-rw-r--r-- | server/providers/ldap/sdap.c | 69 | ||||
-rw-r--r-- | server/providers/ldap/sdap.h | 7 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.c | 467 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.h | 14 |
5 files changed, 1173 insertions, 709 deletions
diff --git a/server/providers/ldap/ldap_id.c b/server/providers/ldap/ldap_id.c index 744f29ca..12180270 100644 --- a/server/providers/ldap/ldap_id.c +++ b/server/providers/ldap/ldap_id.c @@ -23,756 +23,791 @@ */ #include <errno.h> -#include <ldap.h> +#include <time.h> #include <sys/time.h> -#include <security/pam_modules.h> - #include "util/util.h" -#include "providers/dp_backend.h" #include "db/sysdb.h" -#include "../sss_client/sss_cli.h" - -struct sdap_conf_ctx { - char *base_dn; - - char *usr_base_dn; - int usr_search_scope; - char *usr_search_filter; - - char *usr_objectclass; - char *usr_attr_name; - char *usr_attr_pwd; - char *usr_attr_uidnum; - char *usr_attr_gidnum; - char *usr_attr_gecos; - char *usr_attr_homedir; - char *usr_attr_shell; - char **usr_attr_others; - - char *grp_base_dn; - int grp_search_scope; - char *grp_search_filter; - - char *grp_objectclass; - char *grp_attr_name; - char *grp_attr_pwd; - char *grp_attr_gidnum; - char *grp_attr_member; - char **grp_attr_others; - - enum sdap_schema { - SDAP_SCHEMA_RFC2307, - SDAP_SCHEMA_RFC2307BIS - } schema; +#include "providers/dp_backend.h" +#include "providers/ldap/sdap_async.h" + +struct sdap_id_ctx { + struct sdap_options *opts; + + /* global sdap handler */ + struct sdap_handle *gsh; + + time_t went_offline; + bool offline; }; -struct sdap_ctx { - char *ldap_uri; +static void sdap_req_done(struct be_req *req, int ret, const char *err) +{ + return req->fn(req, ret, err); +} - char *bind_dn; - char *auth_method; - char *secret; - int secret_size; +static bool is_offline(struct sdap_id_ctx *ctx) +{ + time_t now = time(NULL); - struct sdap_conf_ctx *conf; + if (ctx->went_offline + ctx->opts->offline_timeout < now) { + return false; + } + return ctx->offline; +} - LDAP *ldap; - int network_timeout; - int opt_timeout; -}; +static void sdap_check_online(struct be_req *req) +{ + struct be_online_req *oreq; + struct sdap_id_ctx *ctx; -#if 0 -struct sdap_id_req { - struct be_req *req; - struct sdap_srv_ctx *srvctx; + ctx = talloc_get_type(req->be_ctx->pvt_id_data, struct sdap_id_ctx); + oreq = talloc_get_type(req->req_data, struct be_online_req); - + if (is_offline(ctx)) { + oreq->online = MOD_OFFLINE; + } else { + oreq->online = MOD_ONLINE; + } + + sdap_req_done(req, EOK, NULL); +} + +static int build_attrs_from_map(TALLOC_CTX *memctx, + struct sdap_id_map *map, + size_t size, + const char ***_attrs) +{ + char **attrs; + int i; + + attrs = talloc_array(memctx, char *, size + 1); + if (!attrs) return ENOMEM; + + /* first attribute is the objectclass value not the attribute name */ + attrs[0] = talloc_strdup(memctx, "objectClass"); + if (!attrs[0]) return ENOMEM; + + /* add the others */ + for (i = 1; i < size; i++) { + attrs[i] = map[i].name; + } + attrs[i] = NULL; + + *_attrs = (const char **)attrs; + + return EOK; +} + + +/* =Connection-handling-functions========================================= */ + +static bool connected(struct sdap_id_ctx *ctx) +{ + if (ctx->gsh) { + return ctx->gsh->connected; + } + + return false; +} + +struct sdap_id_connect_state { + struct tevent_context *ev; + struct sdap_options *opts; + bool use_start_tls; + + struct sdap_handle *sh; }; -static int schedule_next_task(struct sdap_id_req *lr, struct timeval tv, - tevent_timer_handler_t task) +static void sdap_id_connect_done(struct tevent_req *subreq); +static void sdap_id_anon_bind_done(struct tevent_req *subreq); + +struct tevent_req *sdap_id_connect_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_options *opts, + bool use_start_tls) { - int ret; - struct tevent_timer *te; - struct timeval timeout; + struct tevent_req *req, *subreq; + struct sdap_id_connect_state *state; - ret = gettimeofday(&timeout, NULL); - if (ret == -1) { - DEBUG(1, ("gettimeofday failed [%d][%s].\n", errno, strerror(errno))); - return ret; + req = tevent_req_create(memctx, &state, struct sdap_id_connect_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->use_start_tls = use_start_tls; + + subreq = sdap_connect_send(state, ev, opts, use_start_tls); + if (!subreq) { + talloc_zfree(req); + return NULL; } - timeout.tv_sec += tv.tv_sec; - timeout.tv_usec += tv.tv_usec; + tevent_req_set_callback(subreq, sdap_id_connect_done, req); + return req; +} - te = tevent_add_timer(lr->req->be_ctx->ev, lr, timeout, task, lr); - if (te == NULL) { - return EIO; +static void sdap_id_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_id_connect_state *state = tevent_req_data(req, + struct sdap_id_connect_state); + int ret; + + ret = sdap_connect_recv(subreq, state, &state->sh); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - return EOK; + /* TODO: use authentication (SASL/GSSAPI) when necessary */ + subreq = sdap_auth_send(state, state->ev, state->sh, NULL, NULL); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, sdap_id_anon_bind_done, req); } -static int wait_for_fd(struct sdap_req *lr) +static void sdap_id_anon_bind_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + enum sdap_result result; int ret; - int fd; - struct tevent_fd *fde; - ret = ldap_get_option(lr->ldap, LDAP_OPT_DESC, &fd); - if (ret != LDAP_OPT_SUCCESS) { - DEBUG(1, ("ldap_get_option failed.\n")); - return ret; + ret = sdap_auth_recv(subreq, &result); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + if (result != SDAP_AUTH_SUCCESS) { + tevent_req_error(req, EACCES); + return; } - fde = tevent_add_fd(lr->req->be_ctx->ev, lr, fd, TEVENT_FD_READ, lr->next_task, lr); - if (fde == NULL) { + tevent_req_done(req); +} + +static int sdap_id_connect_recv(struct tevent_req *req, + TALLOC_CTX *memctx, struct sdap_handle **sh) +{ + struct sdap_id_connect_state *state = tevent_req_data(req, + struct sdap_id_connect_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; return EIO; } + *sh = talloc_steal(memctx, state->sh); + if (!*sh) { + return ENOMEM; + } return EOK; } -static int sdap_pam_chauthtok(struct sdap_req *lr) + +/* =Users-Related-Functions-(by-name,by-uid)============================== */ + +struct users_get_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + + struct sss_domain_info *dom; + struct sysdb_ctx *sysdb; + + char *filter; + const char **attrs; +}; + +static void users_get_connect_done(struct tevent_req *subreq); +static void users_get_op_done(struct tevent_req *subreq); + +static struct tevent_req *users_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + struct be_ctx *bectx, + const char *wildcard, + int filter_type, + int attrs_type) { - BerElement *ber=NULL; + struct tevent_req *req, *subreq; + struct users_get_state *state; + const char *attr_name; int ret; - int pam_status=PAM_SUCCESS; - struct berval *bv; - int msgid; - LDAPMessage *result=NULL; - int ldap_ret; - - ber = ber_alloc_t( LBER_USE_DER ); - if (ber == NULL) { - DEBUG(1, ("ber_alloc_t failed.\n")); - return PAM_SYSTEM_ERR; - } - - ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, - lr->user_dn, - LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, lr->pd->authtok, - LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, lr->pd->newauthtok); - if (ret == -1) { - DEBUG(1, ("ber_printf failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto cleanup; - } - - ret = ber_flatten(ber, &bv); - if (ret == -1) { - DEBUG(1, ("ber_flatten failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto cleanup; - } - - ret = ldap_extended_operation(lr->ldap, LDAP_EXOP_MODIFY_PASSWD, bv, - NULL, NULL, &msgid); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_extended_operation failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto cleanup; - } - - ret = ldap_result(lr->ldap, msgid, FALSE, NULL, &result); - if (ret == -1) { - DEBUG(1, ("ldap_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto cleanup; - } - ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, - NULL, 0); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_parse_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto cleanup; - } - DEBUG(3, ("LDAP_EXOP_MODIFY_PASSWD result: [%d][%s]\n", ldap_ret, - ldap_err2string(ldap_ret))); - - ldap_msgfree(result); - - if (ldap_ret != LDAP_SUCCESS) pam_status = PAM_SYSTEM_ERR; - -cleanup: - ber_bvfree(bv); - ber_free(ber, 1); - return pam_status; + + req = tevent_req_create(memctx, &state, struct users_get_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + state->dom = bectx->domain; + state->sysdb = bectx->sysdb; + + switch(filter_type) { + case BE_FILTER_NAME: + attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name; + break; + case BE_FILTER_IDNUM: + attr_name = ctx->opts->user_map[SDAP_AT_USER_UID].name; + break; + default: + ret = EINVAL; + goto fail; + } + + state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + attr_name, wildcard, + ctx->opts->user_map[SDAP_OC_USER].name); + if (!state->filter) { + DEBUG(2, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; + } + + /* TODO: handle attrs_type */ + ret = build_attrs_from_map(state, ctx->opts->user_map, + SDAP_OPTS_USER, &state->attrs); + if (ret != EOK) goto fail; + + if (!connected(ctx)) { + + if (ctx->gsh) talloc_zfree(ctx->gsh); + + /* FIXME: add option to decide if tls should be used + * or SASL/GSSAPI, etc ... */ + subreq = sdap_id_connect_send(state, ev, ctx->opts, false); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, users_get_connect_done, req); + + return req; + } + + subreq = sdap_get_users_send(state, state->ev, + state->dom, bectx->sysdb, + state->ctx->opts, state->ctx->gsh, + state->attrs, state->filter); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, users_get_op_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -static int sdap_init(struct sdap_req *lr) +static void users_get_connect_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct users_get_state *state = tevent_req_data(req, + struct users_get_state); int ret; - int status=EOK; - int ldap_vers = LDAP_VERSION3; - int msgid; - struct timeval network_timeout; - struct timeval opt_timeout; - - ret = ldap_initialize(&(lr->ldap), lr->sdap_ctx->ldap_uri); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_initialize failed: %s\n", strerror(errno))); - return EIO; + + ret = sdap_id_connect_recv(subreq, state->ctx, &state->ctx->gsh); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - /* LDAPv3 is needed for TLS */ - ret = ldap_set_option(lr->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_vers); - if (ret != LDAP_OPT_SUCCESS) { - DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); - status = EIO; - goto cleanup; - } - - network_timeout.tv_sec = lr->sdap_ctx->network_timeout; - network_timeout.tv_usec = 0; - opt_timeout.tv_sec = lr->sdap_ctx->opt_timeout; - opt_timeout.tv_usec = 0; - ret = ldap_set_option(lr->ldap, LDAP_OPT_NETWORK_TIMEOUT, &network_timeout); - if (ret != LDAP_OPT_SUCCESS) { - DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); - status = EIO; - goto cleanup; - } - ret = ldap_set_option(lr->ldap, LDAP_OPT_TIMEOUT, &opt_timeout); - if (ret != LDAP_OPT_SUCCESS) { - DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); - status = EIO; - goto cleanup; - } - - /* For now TLS is forced. Maybe it would be necessary to make this - * configurable to allow people to expose their passwords over the - * network. */ - ret = ldap_start_tls(lr->ldap, NULL, NULL, &msgid); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_start_tls failed: [%d][%s]\n", ret, - ldap_err2string(ret))); - if (ret == LDAP_SERVER_DOWN) { - status = EAGAIN; - } else { - status = EIO; - } - goto cleanup; + subreq = sdap_get_users_send(state, state->ev, + state->dom, state->sysdb, + state->ctx->opts, state->ctx->gsh, + state->attrs, state->filter); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, users_get_op_done, req); +} - lr->msgid = msgid; +static void users_get_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; - return EOK; + ret = sdap_get_users_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void users_get_done(struct tevent_req *req) +{ + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + enum tevent_req_state tstate; + uint64_t err; + const char *error = NULL; + int ret = EOK; + + if (tevent_req_is_error(req, &tstate, &err)) { + ret = err; + } -cleanup: - ldap_unbind_ext(lr->ldap, NULL, NULL); - lr->ldap = NULL; - return status; + if (ret) error = "Enum Users Failed"; + + return sdap_req_done(breq, ret, error); } -static int sdap_bind(struct sdap_req *lr) +/* =Groups-Related-Functions-(by-name,by-uid)============================= */ + +struct groups_get_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + + struct sss_domain_info *dom; + struct sysdb_ctx *sysdb; + + char *filter; + const char **attrs; +}; + +static void groups_get_connect_done(struct tevent_req *subreq); +static void groups_get_op_done(struct tevent_req *subreq); + +static struct tevent_req *groups_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + struct be_ctx *bectx, + const char *wildcard, + int filter_type, + int attrs_type) { + struct tevent_req *req, *subreq; + struct groups_get_state *state; + const char *attr_name; int ret; - int msgid; - char *dn=NULL; - struct berval pw; - pw.bv_len = 0; - pw.bv_val = NULL; + req = tevent_req_create(memctx, &state, struct groups_get_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + state->dom = bectx->domain; + state->sysdb = bectx->sysdb; + + switch(filter_type) { + case BE_FILTER_NAME: + attr_name = ctx->opts->group_map[SDAP_AT_GROUP_NAME].name; + break; + case BE_FILTER_IDNUM: + attr_name = ctx->opts->group_map[SDAP_AT_GROUP_GID].name; + break; + default: + ret = EINVAL; + goto fail; + } - if (lr->user_dn != NULL) { - dn = lr->user_dn; - pw.bv_len = lr->pd->authtok_size; - pw.bv_val = (char *) lr->pd->authtok; + state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + attr_name, wildcard, + ctx->opts->group_map[SDAP_OC_GROUP].name); + if (!state->filter) { + DEBUG(2, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; } - if (lr->user_dn == NULL && lr->sdap_ctx->default_bind_dn != NULL) { - dn = lr->sdap_ctx->default_bind_dn; - pw.bv_len = lr->sdap_ctx->default_authtok_size; - pw.bv_val = lr->sdap_ctx->default_authtok; + + /* TODO: handle attrs_type */ + ret = build_attrs_from_map(state, ctx->opts->group_map, + SDAP_OPTS_GROUP, &state->attrs); + if (ret != EOK) goto fail; + + if (!connected(ctx)) { + + if (ctx->gsh) talloc_zfree(ctx->gsh); + + /* FIXME: add option to decide if tls should be used + * or SASL/GSSAPI, etc ... */ + subreq = sdap_id_connect_send(state, ev, ctx->opts, false); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, groups_get_connect_done, req); + + return req; } - DEBUG(3, ("Trying to bind as [%s][%*s]\n", dn, pw.bv_len, pw.bv_val)); - ret = ldap_sasl_bind(lr->ldap, dn, LDAP_SASL_SIMPLE, &pw, NULL, NULL, - &msgid); - if (ret == -1 || msgid == -1) { - DEBUG(1, ("ldap_bind failed\n")); - return LDAP_OTHER; + subreq = sdap_get_groups_send(state, state->ev, + state->dom, bectx->sysdb, + state->ctx->opts, state->ctx->gsh, + state->attrs, state->filter); + if (!subreq) { + ret = ENOMEM; + goto fail; } - lr->msgid = msgid; - return LDAP_SUCCESS; + tevent_req_set_callback(subreq, groups_get_op_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -static void sdap_pam_loop(struct tevent_context *ev, struct tevent_fd *te, - uint16_t fd, void *pvt) +static void groups_get_connect_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct users_get_state *state = tevent_req_data(req, + struct users_get_state); int ret; - int pam_status=PAM_SUCCESS; - int ldap_ret; - struct sdap_req *lr; - struct pam_data *pd; - struct be_req *req; - LDAPMessage *result=NULL; - LDAPMessage *msg=NULL; - struct timeval no_timeout={0, 0}; - char *errmsgp = NULL; -/* FIXME: user timeout form config */ - char *filter=NULL; - char *attrs[] = { LDAP_NO_ATTRS, NULL }; - - lr = talloc_get_type(pvt, struct sdap_req); - - switch (lr->next_step) { - case SDAP_OP_INIT: - ret = sdap_init(lr); - if (ret != EOK) { - DEBUG(1, ("sdap_init failed.\n")); - lr->ldap = NULL; - if (ret == EAGAIN) { - pam_status = PAM_AUTHINFO_UNAVAIL; - } else { - pam_status = PAM_SYSTEM_ERR; - } - goto done; - } - case SDAP_CHECK_INIT_RESULT: - ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); - if (ret == -1) { - DEBUG(1, ("ldap_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - if (ret == 0) { - DEBUG(1, ("ldap_result not ready yet, waiting.\n")); - lr->next_task = sdap_pam_loop; - lr->next_step = SDAP_CHECK_INIT_RESULT; - ret = wait_for_fd(lr); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - return; - } - - ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, NULL, 0); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_parse_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - DEBUG(3, ("ldap_start_tls result: [%d][%s]\n", ldap_ret, ldap_err2string(ldap_ret))); - - if (ldap_ret != LDAP_SUCCESS) { - DEBUG(1, ("setting up TLS failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - -/* FIXME: take care that ldap_install_tls might block */ - ret = ldap_install_tls(lr->ldap); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_install_tls failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - ret = sdap_bind(lr); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("sdap_bind failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - case SDAP_CHECK_STD_BIND: - ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); - if (ret == -1) { - DEBUG(1, ("ldap_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - if (ret == 0) { - DEBUG(1, ("ldap_result not ready yet, waiting.\n")); - lr->next_task = sdap_pam_loop; - lr->next_step = SDAP_CHECK_STD_BIND; - ret = wait_for_fd(lr); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - return; - } - - ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, - NULL, NULL, 0); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_parse_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, - ldap_err2string(ldap_ret), errmsgp)); - if (ldap_ret != LDAP_SUCCESS) { - DEBUG(1, ("bind failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - filter = talloc_asprintf(lr->sdap_ctx, - "(&(%s=%s)(objectclass=%s))", - lr->sdap_ctx->user_name_attribute, - lr->pd->user, - lr->sdap_ctx->user_object_class); - - DEBUG(4, ("calling ldap_search_ext with [%s].\n", filter)); - ret = ldap_search_ext(lr->ldap, - lr->sdap_ctx->user_search_base, - LDAP_SCOPE_SUBTREE, - filter, - attrs, - TRUE, - NULL, - NULL, - NULL, - 0, - &(lr->msgid)); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_search_ext failed [%d][%s].\n", ret, ldap_err2string(ret))); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - case SDAP_CHECK_SEARCH_DN_RESULT: - ret = ldap_result(lr->ldap, lr->msgid, TRUE, &no_timeout, &result); - if (ret == -1) { - DEBUG(1, ("ldap_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - if (ret == 0) { - DEBUG(1, ("ldap_result not ready yet, waiting.\n")); - lr->next_task = sdap_pam_loop; - lr->next_step = SDAP_CHECK_SEARCH_DN_RESULT; - ret = wait_for_fd(lr); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - return; - } - - msg = ldap_first_message(lr->ldap, result); - if (msg == NULL) { - DEBUG(1, ("ldap_first_message failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - do { - switch ( ldap_msgtype(msg) ) { - case LDAP_RES_SEARCH_ENTRY: - if (lr->user_dn != NULL) { - DEBUG(1, ("Found more than one object with filter [%s].\n", - filter)); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - lr->user_dn = ldap_get_dn(lr->ldap, msg); - if (lr->user_dn == NULL) { - DEBUG(1, ("ldap_get_dn failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - if ( *(lr->user_dn) == '\0' ) { - DEBUG(1, ("No user found.\n")); - pam_status = PAM_USER_UNKNOWN; - goto done; - } - DEBUG(3, ("Found dn: %s\n",lr->user_dn)); - - ldap_msgfree(result); - result = NULL; - break; - default: - DEBUG(3, ("ignoring message with type %d.\n", ldap_msgtype(msg))); - } - } while( (msg=ldap_next_message(lr->ldap, msg)) != NULL ); - - switch (lr->pd->cmd) { - case SSS_PAM_AUTHENTICATE: - case SSS_PAM_CHAUTHTOK: - break; - case SSS_PAM_ACCT_MGMT: - case SSS_PAM_SETCRED: - case SSS_PAM_OPEN_SESSION: - case SSS_PAM_CLOSE_SESSION: - pam_status = PAM_SUCCESS; - goto done; - break; - default: - DEBUG(1, ("Unknown pam command %d.\n", lr->pd->cmd)); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - ret = sdap_bind(lr); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("sdap_bind failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - case SDAP_CHECK_USER_BIND: - ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); - if (ret == -1) { - DEBUG(1, ("ldap_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - if (ret == 0) { - DEBUG(1, ("ldap_result not ready yet, waiting.\n")); - lr->next_task = sdap_pam_loop; - lr->next_step = SDAP_CHECK_USER_BIND; - ret = wait_for_fd(lr); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - return; - } - - ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, - NULL, NULL, 0); - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_parse_result failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; - } - DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, - ldap_err2string(ldap_ret), errmsgp)); - switch (ldap_ret) { - case LDAP_SUCCESS: - pam_status = PAM_SUCCESS; - break; - case LDAP_INVALID_CREDENTIALS: - pam_status = PAM_CRED_INSUFFICIENT; - goto done; - break; - default: - pam_status = PAM_SYSTEM_ERR; - goto done; - } - - switch (lr->pd->cmd) { - case SSS_PAM_AUTHENTICATE: - pam_status = PAM_SUCCESS; - break; - case SSS_PAM_CHAUTHTOK: - pam_status = sdap_pam_chauthtok(lr); - break; - case SSS_PAM_ACCT_MGMT: - case SSS_PAM_SETCRED: - case SSS_PAM_OPEN_SESSION: - case SSS_PAM_CLOSE_SESSION: - pam_status = PAM_SUCCESS; - break; - default: - DEBUG(1, ("Unknown pam command %d.\n", lr->pd->cmd)); - pam_status = PAM_SYSTEM_ERR; - } - break; - default: - DEBUG(1, ("Unknown ldap backend operation %d.\n", lr->next_step)); - pam_status = PAM_SYSTEM_ERR; + + ret = sdap_id_connect_recv(subreq, state->ctx, &state->ctx->gsh); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } -done: - ldap_memfree(errmsgp); - ldap_msgfree(result); - talloc_free(filter); - if (lr->ldap != NULL) ldap_unbind_ext(lr->ldap, NULL, NULL); - req = lr->req; - pd = talloc_get_type(lr->req->req_data, struct pam_data); - pd->pam_status = pam_status; + subreq = sdap_get_groups_send(state, state->ev, + state->dom, state->sysdb, + state->ctx->opts, state->ctx->gsh, + state->attrs, state->filter); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, groups_get_op_done, req); +} + +static void groups_get_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; - talloc_free(lr); + ret = sdap_get_groups_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } - req->fn(req, pam_status, NULL); + tevent_req_done(req); } -static void sdap_start(struct tevent_context *ev, struct tevent_timer *te, - struct timeval tv, void *pvt) +static void groups_get_done(struct tevent_req *req) { + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + enum tevent_req_state tstate; + uint64_t err; + const char *error = NULL; + int ret = EOK; + + if (tevent_req_is_error(req, &tstate, &err)) { + ret = err; + } + + if (ret) error = "Enum Groups Failed"; + + return sdap_req_done(breq, ret, error); +} + +/* =Get-Groups-for-User================================================== */ + +struct groups_by_user_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + struct sss_domain_info *dom; + struct sysdb_ctx *sysdb; + const char *name; + const char **attrs; +}; + +static void groups_by_user_connect_done(struct tevent_req *subreq); +static void groups_by_user_op_done(struct tevent_req *subreq); + +static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + const char *name) +{ + struct tevent_req *req, *subreq; + struct groups_by_user_state *state; int ret; - int pam_status; - struct sdap_req *lr; - struct be_req *req; - struct pam_data *pd; - lr = talloc_get_type(pvt, struct sdap_req); + req = tevent_req_create(memctx, &state, struct groups_by_user_state); + if (!req) return NULL; - ret = sdap_init(lr); - if (ret != EOK) { - DEBUG(1, ("sdap_init failed.\n")); - lr->ldap = NULL; - if (ret == EAGAIN) { - pam_status = PAM_AUTHINFO_UNAVAIL; - } else { - pam_status = PAM_SYSTEM_ERR; + state->ev = ev; + state->ctx = ctx; + state->dom = dom; + state->sysdb = sysdb; + state->name = name; + + ret = build_attrs_from_map(state, ctx->opts->group_map, + SDAP_OPTS_GROUP, &state->attrs); + if (ret != EOK) goto fail; + + if (!connected(ctx)) { + + if (ctx->gsh) talloc_zfree(ctx->gsh); + + /* FIXME: add option to decide if tls should be used + * or SASL/GSSAPI, etc ... */ + subreq = sdap_id_connect_send(state, ev, ctx->opts, false); + if (!subreq) { + ret = ENOMEM; + goto fail; } - goto done; + + tevent_req_set_callback(subreq, groups_by_user_connect_done, req); + + return req; } - lr->next_task = sdap_pam_loop; - lr->next_step = SDAP_CHECK_INIT_RESULT; - ret = wait_for_fd(lr); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; + subreq = sdap_get_initgr_send(state, state->ev, + state->dom, state->sysdb, + state->ctx->opts, state->ctx->gsh, + state->name, state->attrs); + if (!subreq) { + ret = ENOMEM; + goto fail; } - return; + tevent_req_set_callback(subreq, groups_by_user_op_done, req); -done: - if (lr->ldap != NULL ) ldap_unbind_ext(lr->ldap, NULL, NULL); - req = lr->req; - pd = talloc_get_type(lr->req->req_data, struct pam_data); - pd->pam_status = pam_status; + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void groups_by_user_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct groups_by_user_state *state = tevent_req_data(req, + struct groups_by_user_state); + int ret; - talloc_free(lr); + ret = sdap_id_connect_recv(subreq, state->ctx, &state->ctx->gsh); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } - req->fn(req, pam_status, NULL); + subreq = sdap_get_initgr_send(state, state->ev, + state->dom, state->sysdb, + state->ctx->opts, state->ctx->gsh, + state->name, state->attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, groups_by_user_op_done, req); } -static void sdap_pam_handler(struct be_req *req) +static void groups_by_user_op_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); int ret; - int pam_status=PAM_SUCCESS; - struct sdap_req *lr; - struct sdap_ctx *sdap_ctx; - struct pam_data *pd; - struct timeval timeout; - pd = talloc_get_type(req->req_data, struct pam_data); + ret = sdap_get_initgr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } - sdap_ctx = talloc_get_type(req->be_ctx->pvt_auth_data, struct sdap_ctx); + tevent_req_done(req); +} - lr = talloc(req, struct sdap_req); +static void groups_by_user_done(struct tevent_req *req) +{ + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + enum tevent_req_state tstate; + uint64_t err; + const char *error = NULL; + int ret = EOK; + + if (tevent_req_is_error(req, &tstate, &err)) { + ret = err; + } - lr->ldap = NULL; - lr->req = req; - lr->pd = pd; - lr->sdap_ctx = sdap_ctx; - lr->user_dn = NULL; - lr->next_task = NULL; - lr->next_step = SDAP_NOOP; + if (ret) error = "Init Groups Failed"; - timeout.tv_sec=0; - timeout.tv_usec=0; - ret = schedule_next_task(lr, timeout, sdap_start); - if (ret != EOK) { - DEBUG(1, ("schedule_next_task failed.\n")); - pam_status = PAM_SYSTEM_ERR; - goto done; + return sdap_req_done(breq, ret, error); +} + + + +/* =Get-Account-Info-Call================================================= */ + +/* FIXME: embed this function in sssd_be and only call out + * specific functions from modules */ +static void sdap_get_account_info(struct be_req *breq) +{ + struct sdap_id_ctx *ctx; + struct be_acct_req *ar; + struct tevent_req *req; + const char *err = "Unknown Error"; + int ret = EOK; + + ctx = talloc_get_type(breq->be_ctx->pvt_id_data, struct sdap_id_ctx); + + if (is_offline(ctx)) { + return sdap_req_done(breq, EAGAIN, "Offline"); } - return; + ar = talloc_get_type(breq->req_data, struct be_acct_req); -done: - talloc_free(lr); + switch (ar->entry_type) { + case BE_REQ_USER: /* user */ + + req = users_get_send(breq, breq->be_ctx->ev, + ctx, breq->be_ctx, + ar->filter_value, + ar->filter_type, + ar->attr_type); + if (!req) { + return sdap_req_done(breq, ENOMEM, "Out of memory"); + } + + tevent_req_set_callback(req, users_get_done, breq); + + break; + + case BE_REQ_GROUP: /* group */ + + req = groups_get_send(breq, breq->be_ctx->ev, + ctx, breq->be_ctx, + ar->filter_value, + ar->filter_type, + ar->attr_type); + if (!req) { + return sdap_req_done(breq, ENOMEM, "Out of memory"); + } + + tevent_req_set_callback(req, groups_get_done, breq); + + break; + + case BE_REQ_INITGROUPS: /* init groups for user */ + if (ar->filter_type != BE_FILTER_NAME) { + ret = EINVAL; + err = "Invalid filter type"; + break; + } + if (ar->attr_type != BE_ATTR_CORE) { + ret = EINVAL; + err = "Invalid attr type"; + break; + } + if (strchr(ar->filter_value, '*')) { + ret = EINVAL; + err = "Invalid filter value"; + break; + } + req = groups_by_user_send(breq, breq->be_ctx->ev, ctx, + breq->be_ctx->domain, breq->be_ctx->sysdb, + ar->filter_value); + if (!req) ret = ENOMEM; + /* tevent_req_set_callback(req, groups_by_user_done, breq); */ + + tevent_req_set_callback(req, groups_by_user_done, breq); + + break; - pd->pam_status = pam_status; - req->fn(req, pam_status, NULL); + default: /*fail*/ + ret = EINVAL; + err = "Invalid request type"; + } + + if (ret != EOK) return sdap_req_done(breq, ret, err); } static void sdap_shutdown(struct be_req *req) { /* TODO: Clean up any internal data */ - req->fn(req, EOK, NULL); + sdap_req_done(req, EOK, NULL); } -struct be_auth_ops sdap_auth_ops = { - .pam_handler = sdap_pam_handler, +struct be_id_ops sdap_id_ops = { + .check_online = sdap_check_online, + .get_account_info = sdap_get_account_info, .finalize = sdap_shutdown }; - int sssm_ldap_init(struct be_ctx *bectx, - struct be_id_ops **ops, void **pvt_data) + struct be_id_ops **ops, + void **pvt_data) { - struct sdap_ctx *ctx; -} + int ldap_opt_x_tls_require_cert; + struct sdap_id_ctx *ctx; + char *tls_reqcert; + int ret; + ctx = talloc_zero(bectx, struct sdap_id_ctx); + if (!ctx) return ENOMEM; -{ - char *ldap_uri; - char *default_bind_dn; - char *default_authtok_type; - char *default_authtok; - char *user_search_base; - char *user_name_attribute; - char *user_object_class; - int network_timeout; - int opt_timeout; - int ret; + ret = sdap_get_options(ctx, bectx->cdb, bectx->conf_path, + &ctx->opts); - ctx = talloc(bectx, struct sdap_ctx); - if (!ctx) { - return ENOMEM; + tls_reqcert = ctx->opts->basic[SDAP_TLS_REQCERT].value; + if (tls_reqcert) { + if (strcasecmp(tls_reqcert, "never") == 0) { + ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_NEVER; + } + else if (strcasecmp(tls_reqcert, "allow") == 0) { + ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_ALLOW; + } + else if (strcasecmp(tls_reqcert, "try") == 0) { + ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_TRY; + } + else if (strcasecmp(tls_reqcert, "demand") == 0) { + ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_DEMAND; + } + else if (strcasecmp(tls_reqcert, "hard") == 0) { + ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_HARD; + } + else { + DEBUG(1, ("Unknown value for tls_reqcert.\n")); + ret = EINVAL; + goto done; + } + /* LDAP_OPT_X_TLS_REQUIRE_CERT has to be set as a global option, + * because the SSL/TLS context is initialized from this value. */ + ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, + &ldap_opt_x_tls_require_cert); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); + ret = EIO; + goto done; + } } -/* TODO: add validation checks for ldapUri, user_search_base, - * user_name_attribute, etc */ - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "ldapUri", "ldap://localhost", &ldap_uri); - if (ret != EOK) goto done; - ctx->ldap_uri = ldap_uri; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "defaultBindDn", NULL, &default_bind_dn); - if (ret != EOK) goto done; - ctx->default_bind_dn = default_bind_dn; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "defaultAuthtokType", NULL, &default_authtok_type); - if (ret != EOK) goto done; - ctx->default_authtok_type = default_authtok_type; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "userSearchBase", NULL, &user_search_base); - if (ret != EOK) goto done; - if (user_search_base == NULL) { - DEBUG(1, ("missing userSearchBase.\n")); - ret = EINVAL; - goto done; - } - ctx->user_search_base = user_search_base; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "userNameAttribute", "uid", &user_name_attribute); - if (ret != EOK) goto done; - ctx->user_name_attribute = user_name_attribute; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "userObjectClass", "posixAccount", - &user_object_class); - if (ret != EOK) goto done; - ctx->user_object_class = user_object_class; - -/* TODO: better to have a blob object than a string here */ - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - "defaultAuthtok", NULL, &default_authtok); - if (ret != EOK) goto done; - ctx->default_authtok = default_authtok; - ctx->default_authtok_size = (default_authtok==NULL?0:strlen(default_authtok)); - - ret = confdb_get_int(bectx->cdb, ctx, bectx->conf_path, - "network_timeout", 5, &network_timeout); - if (ret != EOK) goto done; - ctx->network_timeout = network_timeout; - - ret = confdb_get_int(bectx->cdb, ctx, bectx->conf_path, - "opt_timeout", 5, &opt_timeout); - if (ret != EOK) goto done; - ctx->network_timeout = opt_timeout; - - *ops = &sdap_auth_ops; + *ops = &sdap_id_ops; *pvt_data = ctx; ret = EOK; @@ -782,5 +817,3 @@ done: } return ret; } - -#endif diff --git a/server/providers/ldap/sdap.c b/server/providers/ldap/sdap.c index 9abcf566..ddba0ba5 100644 --- a/server/providers/ldap/sdap.c +++ b/server/providers/ldap/sdap.c @@ -110,6 +110,48 @@ int sdap_get_options(TALLOC_CTX *memctx, opts->basic[i].opt_name, opts->basic[i].value)); } + /* re-read special options that are easier to be consumed after they are + * transformed */ + +/* TODO: better to have a blob object than a string here */ + ret = confdb_get_string(cdb, opts, conf_path, + "defaultAuthtok", NULL, + &opts->default_authtok); + if (ret != EOK) goto done; + if (opts->default_authtok) { + opts->default_authtok_size = strlen(opts->default_authtok); + } + + ret = confdb_get_int(cdb, opts, conf_path, + "network_timeout", 5, + &opts->network_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "opt_timeout", 5, + &opts->opt_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "offline_timeout", 60, + &opts->offline_timeout); + if (ret != EOK) goto done; + + /* schema type */ + if (strcasecmp(opts->basic[SDAP_SCHEMA].value, "rfc2307") == 0) { + opts->schema_type = SDAP_SCHEMA_RFC2307; + } else + if (strcasecmp(opts->basic[SDAP_SCHEMA].value, "rfc2307bis") == 0) { + opts->schema_type = SDAP_SCHEMA_RFC2307BIS; + } else { + DEBUG(0, ("Unrecognized schema type: %s\n", + opts->basic[SDAP_SCHEMA].value)); + ret = EINVAL; + goto done; + } + + +/* FIXME: make defaults per schema type memberUid vs member, etc... */ for (i = 0; i < SDAP_OPTS_USER; i++) { opts->user_map[i].opt_name = default_user_map[i].opt_name; @@ -154,33 +196,6 @@ int sdap_get_options(TALLOC_CTX *memctx, opts->group_map[i].opt_name, opts->group_map[i].name)); } - /* re-read special options that are easier to be consumed after they are - * transformed */ - -/* TODO: better to have a blob object than a string here */ - ret = confdb_get_string(cdb, opts, conf_path, - "defaultAuthtok", NULL, - &opts->default_authtok); - if (ret != EOK) goto done; - if (opts->default_authtok) { - opts->default_authtok_size = strlen(opts->default_authtok); - } - - ret = confdb_get_int(cdb, opts, conf_path, - "network_timeout", 5, - &opts->network_timeout); - if (ret != EOK) goto done; - - ret = confdb_get_int(cdb, opts, conf_path, - "opt_timeout", 5, - &opts->opt_timeout); - if (ret != EOK) goto done; - - ret = confdb_get_int(cdb, opts, conf_path, - "offline_timeout", 60, - &opts->offline_timeout); - if (ret != EOK) goto done; - ret = EOK; *_opts = opts; diff --git a/server/providers/ldap/sdap.h b/server/providers/ldap/sdap.h index b3435c8b..85b17515 100644 --- a/server/providers/ldap/sdap.h +++ b/server/providers/ldap/sdap.h @@ -113,6 +113,13 @@ struct sdap_options { int network_timeout; int opt_timeout; int offline_timeout; + + /* supported schema types */ + enum schema_type { + SDAP_SCHEMA_RFC2307 = 1, /* memberUid = uid */ + SDAP_SCHEMA_RFC2307BIS = 2, /* member = dn */ + SDAP_SCHEMA_IPA_V1 = 3 /* member/memberof with unrolling */ + } schema_type; }; int sdap_get_options(TALLOC_CTX *memctx, diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c index 2aba33c1..b795e83d 100644 --- a/server/providers/ldap/sdap_async.c +++ b/server/providers/ldap/sdap_async.c @@ -85,6 +85,13 @@ static int get_fd_from_ldap(LDAP *ldap, int *fd) return EOK; } +static bool valid_handle(struct sdap_handle *sh) +{ + if (!sh) return false; + if (sh->ldap) return true; + return false; +} + /* ==Parse-Results-And-Handle-Disconnections============================== */ static enum sdap_result sdap_check_result(struct sdap_handle *sh, @@ -117,6 +124,45 @@ static enum sdap_result sdap_check_result(struct sdap_handle *sh, return SDAP_SUCCESS; } +/* ==LDAP-Operations-Helpers============================================== */ + +struct ldap_operation_destructor_state { + struct sdap_handle *sh; + int msgid; +}; + +static int ldap_operation_destructor(void *mem) +{ + struct ldap_operation_destructor_state *state = + (struct ldap_operation_destructor_state *)mem; + + if (valid_handle(state->sh)) { + /* 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(state->sh->ldap, state->msgid, NULL, NULL); + } + + return 0; +} + +static int set_ldap_operation_destructor(TALLOC_CTX *parent, + struct sdap_handle *sh, int msgid) +{ + struct ldap_operation_destructor_state *state; + + state = talloc(parent, struct ldap_operation_destructor_state); + if (!state) return ENOMEM; + +/* TODO: should we talloc_reference sdap_handle here ? */ + state->sh = sh; + state->msgid = msgid; + + talloc_set_destructor((TALLOC_CTX *)state, ldap_operation_destructor); + + return EOK; +} /* ==Connect-to-LDAP-Server=============================================== */ @@ -862,6 +908,9 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, } DEBUG(8, ("ldap_search_ext called, msgid = %d\n", state->msgid)); + ret = set_ldap_operation_destructor(state, sh, state->msgid); + if (ret) goto fail; + subreq = sysdb_transaction_send(state, state->ev, sysdb); if (!subreq) { ret = ENOMEM; @@ -1055,24 +1104,12 @@ static void sdap_fake_users_done(struct tevent_context *ev, int sdap_get_users_recv(struct tevent_req *req) { - struct sdap_get_users_state *state = tevent_req_data(req, - struct sdap_get_users_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { - - /* FIXME: send abandon ? - * read all to flush the read queue ? - * close the connection ? */ - - /* closing for now */ - ldap_unbind_ext(state->sh->ldap, NULL, NULL); - state->sh->connected = false; - state->sh->ldap = NULL; - state->sh->fd = -1; - - return err; + if (err) return err; + return EIO; } return EOK; @@ -1131,6 +1168,9 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, } DEBUG(8, ("ldap_search_ext called, msgid = %d\n", state->msgid)); + ret = set_ldap_operation_destructor(state, sh, state->msgid); + if (ret) goto fail; + subreq = sysdb_transaction_send(state, state->ev, sysdb); if (!subreq) { ret = ENOMEM; @@ -1276,9 +1316,9 @@ static void sdap_get_groups_done(struct tevent_context *ev, } } -static void sdap_fake_groups_done(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval tv, void *pvt); +static void sdap_get_groups_fake_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); static void sdap_get_groups_save_done(struct tevent_req *subreq) { @@ -1303,16 +1343,16 @@ static void sdap_get_groups_save_done(struct tevent_req *subreq) * get a SDAP_RETRY it is fine. */ te = tevent_add_timer(state->ev, state, tv, - sdap_fake_groups_done, req); + sdap_get_groups_fake_done, req); if (!te) { tevent_req_error(req, ENOMEM); return; } } -static void sdap_fake_groups_done(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval tv, void *pvt) +static void sdap_get_groups_fake_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) { struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); struct sdap_get_groups_state *state = tevent_req_data(req, @@ -1324,24 +1364,387 @@ static void sdap_fake_groups_done(struct tevent_context *ev, int sdap_get_groups_recv(struct tevent_req *req) { - struct sdap_get_groups_state *state = tevent_req_data(req, - struct sdap_get_groups_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; + return EIO; + } - /* FIXME: send abandon ? - * read all to flush the read queue ? - * close the connection ? */ + return EOK; +} - /* closing for now */ - ldap_unbind_ext(state->sh->ldap, NULL, NULL); - state->sh->connected = false; - state->sh->ldap = NULL; - state->sh->fd = -1; +/* ==Initgr-call-(groups-a-user-is-member-of)============================= */ - return err; +struct sdap_get_initgr_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sdap_options *opts; + struct sss_domain_info *dom; + struct sdap_handle *sh; + const char *name; + const char **grp_attrs; + + struct sysdb_handle *handle; + struct tevent_fd *fde; + int msgid; +}; + +static void sdap_get_initgr_process(struct tevent_req *subreq); +static void sdap_get_initgr_transaction(struct tevent_req *subreq); +static void sdap_get_initgr_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); +static void sdap_get_initgr_save_done(struct tevent_req *subreq); + +struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_handle *sh, + const char *name, + const char **grp_attrs) +{ + struct tevent_req *req, *subreq; + struct sdap_get_initgr_state *state; + struct timeval tv = {0, 0}; + const char **attrs; + int ret; + + req = tevent_req_create(memctx, &state, struct sdap_get_initgr_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->sysdb = sysdb; + state->dom = dom; + state->sh = sh; + state->name = name; + state->grp_attrs = grp_attrs; + + switch (opts->schema_type) { + case SDAP_SCHEMA_RFC2307: + + subreq = tevent_wakeup_send(state, ev, tv); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sdap_get_initgr_process, req); + break; + + case SDAP_SCHEMA_RFC2307BIS: + + attrs = talloc_array(state, const char *, 2); + if (!attrs) { + ret = ENOMEM; + goto fail; + } + attrs[0] = SYSDB_ORIG_DN; + attrs[1] = NULL; + + subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, + dom, name, attrs); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sdap_get_initgr_process, req); + break; + + default: + ret = EINVAL; + goto fail; + } + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_get_initgr_process(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_initgr_state *state = tevent_req_data(req, + struct sdap_get_initgr_state); + struct ldb_message *msg; + const char *user_dn; + char *filter; + int ret; + + switch (state->opts->schema_type) { + case SDAP_SCHEMA_RFC2307: + + if (!tevent_wakeup_recv(subreq)) { + tevent_req_error(req, EFAULT); + return; + } + talloc_zfree(subreq); + + filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + state->opts->group_map[SDAP_AT_GROUP_MEMBER].name, + state->name, + state->opts->group_map[SDAP_OC_GROUP].name); + if (!filter) { + tevent_req_error(req, ENOENT); + return; + } + + break; + + case SDAP_SCHEMA_RFC2307BIS: + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + user_dn = ldb_msg_find_attr_as_string(msg, SYSDB_ORIG_DN, NULL); + if (!user_dn) { + tevent_req_error(req, ENOENT); + return; + } + + filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + state->opts->user_map[SDAP_AT_GROUP_MEMBER].name, + user_dn, + state->opts->user_map[SDAP_OC_GROUP].name); + if (!filter) { + tevent_req_error(req, ENOMEM); + return; + } + + talloc_free(msg); + + break; + + default: + tevent_req_error(req, EINVAL); + return; + } + + DEBUG(5, ("calling ldap_search_ext with filter:[%s].\n", filter)); + + + ret = ldap_search_ext(state->sh->ldap, + state->opts->basic[SDAP_GROUP_SEARCH_BASE].value, + LDAP_SCOPE_SUBTREE, filter, + discard_const(state->grp_attrs), + false, NULL, NULL, NULL, 0, &state->msgid); + if (ret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(ret))); + tevent_req_error(req, EIO); + return; + } + + DEBUG(8, ("ldap_search_ext called, msgid = %d\n", state->msgid)); + + ret = set_ldap_operation_destructor(state, state->sh, state->msgid); + if (ret) { + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_transaction_send(state, state->ev, state->sysdb); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_get_initgr_transaction, req); +} + +static void sdap_get_initgr_transaction(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_initgr_state *state = tevent_req_data(req, + struct sdap_get_initgr_state); + int ret; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + state->fde = tevent_add_fd(state->ev, state, + state->sh->fd, TEVENT_FD_READ, + sdap_get_initgr_done, req); + if (!state->fde) { + DEBUG(1, ("Failed to set up fd event!\n")); + tevent_req_error(req, ENOMEM); + } +} + +static void sdap_get_initgr_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_initgr_state *state = tevent_req_data(req, + struct sdap_get_initgr_state); + struct tevent_req *subreq; + LDAPMessage *msg = NULL; + struct sdap_msg *reply; + enum sdap_result res; + char *errmsg; + int restype; + int result; + int ret; + + res = sdap_check_result(state->sh, state->msgid, false, + &msg, &restype); + if (res != SDAP_SUCCESS) { + if (res != SDAP_RETRY) { + tevent_req_error(req, EIO); + return; + } + + /* make sure fd is readable so we can fetch the next result */ + TEVENT_FD_READABLE(state->fde); + return; + } + + if (!msg) { + tevent_req_error(req, EIO); + return; + } + + reply = talloc_zero(state, struct sdap_msg); + if (!reply) { + ldap_msgfree(msg); + tevent_req_error(req, ENOMEM); + return; + } + + reply->msg = msg; + ret = sdap_msg_attach(reply, msg); + if (ret) { + DEBUG(1, ("Error appending memory: %s(%d)\n", strerror(ret), ret)); + tevent_req_error(req, EFAULT); + return; + } + + switch (restype) { + case LDAP_RES_SEARCH_REFERENCE: + /* ignore references for now */ + ldap_msgfree(msg); + break; + + case LDAP_RES_SEARCH_ENTRY: + /* FIXME: should we set a timeout tevent timed function ? */ + + /* stop reading until operation is done */ + TEVENT_FD_NOT_READABLE(state->fde); + + subreq = sdap_save_group_send(state, state->ev, state->handle, + state->opts, state->dom, + state->sh, reply); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + /* attach reply to subreq, + * will not be needed anymore once subreq is done */ + talloc_steal(subreq, reply); + + tevent_req_set_callback(subreq, sdap_get_initgr_save_done, req); + break; + + case LDAP_RES_SEARCH_RESULT: + /* End of the story */ + + 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->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("Search result: %s(%d), %s\n", + ldap_err2string(result), result, errmsg)); + + subreq = sysdb_transaction_commit_send(state, state->ev, + state->handle); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + /* sysdb_transaction_complete will call tevent_req_done(req) */ + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + break; + + default: + /* what is going on here !? */ + tevent_req_error(req, EIO); + return; + } +} + +static void sdap_get_initgr_fake_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +static void sdap_get_initgr_save_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_initgr_state *state = tevent_req_data(req, + struct sdap_get_initgr_state); + struct timeval tv = { 0, 0 }; + struct tevent_timer *te; + int ret; + + ret = sdap_save_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + /* unfortunately LDAP libraries consume everything sitting on the wire but + * do not give us a way to know if there is anything waiting to be read or + * or not. So schedule a fake fde event and wake up ourselves again. If we + * get a SDAP_RETRY it is fine. */ + + te = tevent_add_timer(state->ev, state, tv, + sdap_get_initgr_fake_done, req); + if (!te) { + tevent_req_error(req, ENOMEM); + return; + } +} + +static void sdap_get_initgr_fake_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_initgr_state *state = tevent_req_data(req, + struct sdap_get_initgr_state); + + sdap_get_initgr_done(state->ev, state->fde, 0, pvt); +} + + +int sdap_get_initgr_recv(struct tevent_req *req) +{ + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; + return EIO; } return EOK; diff --git a/server/providers/ldap/sdap_async.h b/server/providers/ldap/sdap_async.h index 37c307a0..6ed95320 100644 --- a/server/providers/ldap/sdap_async.h +++ b/server/providers/ldap/sdap_async.h @@ -45,7 +45,6 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_options *opts, bool use_start_tls); - int sdap_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx, struct sdap_handle **sh); @@ -58,7 +57,6 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, struct sdap_handle *sh, const char **attrs, const char *wildcard); - int sdap_get_users_recv(struct tevent_req *req); struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, @@ -69,7 +67,6 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, struct sdap_handle *sh, const char **attrs, const char *wildcard); - int sdap_get_groups_recv(struct tevent_req *req); struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, @@ -77,5 +74,14 @@ struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, struct sdap_handle *sh, const char *user_dn, const char *password); - int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result); + +struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_handle *sh, + const char *name, + const char **grp_attrs); +int sdap_get_initgr_recv(struct tevent_req *req); |