From 258d4b400f72e89f4428302d82c886f9c4c45c3e Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Thu, 7 Apr 2011 11:44:40 +0200 Subject: OpenLDAP dereference searches This dereference method is supported at least by OpenLDAP and 389DS/RHDS For more details, see: http://tools.ietf.org/html/draft-masarati-ldap-deref-00 --- src/providers/ldap/sdap.c | 159 ++++++++++++++++++++++++++++++ src/providers/ldap/sdap.h | 7 ++ src/providers/ldap/sdap_async.c | 210 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+) (limited to 'src') diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c index e82720929..61044bb58 100644 --- a/src/providers/ldap/sdap.c +++ b/src/providers/ldap/sdap.c @@ -277,6 +277,165 @@ int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts, SDAP_OPTS_GROUP, _attrs, _dn); } +/* Parses an LDAPDerefRes into sdap_deref_attrs structure */ +errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx, + struct sdap_attr_map_info *minfo, + size_t num_maps, + struct sdap_handle *sh, + LDAPDerefRes *dref, + struct sdap_deref_attrs ***_res) +{ + TALLOC_CTX *tmp_ctx; + LDAPDerefVal *dval; + const char *orig_dn; + const char **ocs; + struct sdap_attr_map *map; + int num_attrs; + struct ldb_val v; + int ret, i, a, mi; + const char *name; + size_t len; + struct sdap_deref_attrs **res; + + if (!dref || !minfo) return EINVAL; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + res = talloc_array(tmp_ctx, struct sdap_deref_attrs *, num_maps); + if (!res) { + ret = ENOMEM; + goto done; + } + + for (i=0; i < num_maps; i++) { + res[i] = talloc_zero(res, struct sdap_deref_attrs); + if (!res[i]) { + ret = ENOMEM; + goto done; + } + + res[i]->map = minfo[i].map; + } + + if (!dref->derefVal.bv_val) { + DEBUG(2, ("Entry has no DN?\n")); + ret = EINVAL; + goto done; + } + + if (!dref->attrVals) { + DEBUG(2, ("Dereferenced entry has no attributes\n")); + ret = EINVAL; + goto done; + } + + orig_dn = dref->derefVal.bv_val; + DEBUG(7, ("Dereferenced DN: %s\n", orig_dn)); + + ocs = NULL; + for (dval = dref->attrVals; dval != NULL; dval = dval->next) { + if (strcasecmp("objectClass", dval->type) == 0) { + if (dval->vals == NULL) { + DEBUG(4, ("No value for objectClass, skipping\n")); + continue; + } + + for(len=0; dval->vals[len].bv_val; len++); + + ocs = talloc_array(tmp_ctx, const char *, len+1); + if (!ocs) { + ret = ENOMEM; + goto done; + } + + for (i=0; ivals[i].bv_val)); + ocs[i] = talloc_strdup(ocs, dval->vals[i].bv_val); + if (!ocs[i]) { + ret = ENOMEM; + goto done; + } + } + ocs[i] = NULL; + break; + } + } + if (!ocs) { + DEBUG(1, ("Unknown entry type, no objectClasses found!\n")); + ret = EINVAL; + goto done; + } + + for (mi = 0; mi < num_maps; mi++) { + map = NULL; + + for (i=0; ocs[i]; i++) { + /* the objectclass is always the first name in the map */ + if (strcasecmp(minfo[mi].map[0].name, ocs[i]) == 0) { + DEBUG(9, ("Found map for objectclass '%s'\n", ocs[i])); + map = minfo[mi].map; + num_attrs = minfo[mi].num_attrs; + break; + } + } + if (!map) continue; + + res[mi]->attrs = sysdb_new_attrs(res[mi]); + if (!res[mi]->attrs) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_add_string(res[mi]->attrs, SYSDB_ORIG_DN, + orig_dn); + if (ret) { + goto done; + } + + for (dval = dref->attrVals; dval != NULL; dval = dval->next) { + DEBUG(8, ("Dereferenced attribute: %s\n", dval->type)); + + for (a = 1; a < num_attrs; a++) { + /* check if this attr is valid with the chosen schema */ + if (!map[a].name) continue; + /* check if it is an attr we are interested in */ + if (strcasecmp(dval->type, map[a].name) == 0) break; + } + + /* interesting attr */ + if (a < num_attrs) { + name = map[a].sys_name; + } else { + continue; + } + + if (dval->vals == NULL) { + DEBUG(4, ("No value for attribute %s, skipping\n", name)); + continue; + } + + for (i=0; dval->vals[i].bv_val; i++) { + DEBUG(9, ("Dereferenced attribute value: %s\n", + dval->vals[i].bv_val)); + v.data = (uint8_t *) dval->vals[i].bv_val; + v.length = dval->vals[i].bv_len; + + ret = sysdb_attrs_add_val(res[mi]->attrs, name, &v); + if (ret) goto done; + } + } + } + + + *_res = talloc_steal(mem_ctx, res); + ret = EOK; +done: + talloc_zfree(tmp_ctx); + return ret; +} + /* =Get-DN-from-message=================================================== */ int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh, diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index cef2d23ae..e98b27bce 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -335,6 +335,13 @@ int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts, struct sdap_handle *sh, struct sdap_msg *sm, struct sysdb_attrs **_attrs, char **_dn); +errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx, + struct sdap_attr_map_info *minfo, + size_t num_maps, + struct sdap_handle *sh, + LDAPDerefRes *dref, + struct sdap_deref_attrs ***_res); + int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh, struct sdap_msg *sm, char **_dn); diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index bcc18135f..9d319bf33 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -1285,6 +1285,216 @@ int sdap_get_generic_recv(struct tevent_req *req, return EOK; } +/* ==OpenLDAP deref search============================================== */ +static int sdap_x_deref_create_control(struct sdap_handle *sh, + const char *deref_attr, + const char **attrs, + LDAPControl **ctrl); + +static void sdap_x_deref_search_done(struct tevent_req *subreq); + +static errno_t sdap_x_deref_parse_entry(struct sdap_handle *sh, + struct sdap_msg *msg, + void *pvt); +struct sdap_x_deref_search_state { + struct sdap_handle *sh; + struct sdap_op *op; + struct sdap_attr_map_info *maps; + + struct sdap_deref_reply dreply; + int num_maps; +}; + +static struct tevent_req * +sdap_x_deref_search_send(TALLOC_CTX *memctx, struct tevent_context *ev, + struct sdap_options *opts, struct sdap_handle *sh, + const char *base_dn, const char *deref_attr, + const char **attrs, struct sdap_attr_map_info *maps, + int num_maps, int timeout) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct sdap_x_deref_search_state *state; + LDAPControl *ctrls[2] = { NULL, NULL }; + int ret; + + req = tevent_req_create(memctx, &state, struct sdap_x_deref_search_state); + if (!req) return NULL; + + state->sh = sh; + state->maps = maps; + state->op = NULL; + state->num_maps = num_maps; + + ret = sdap_x_deref_create_control(sh, deref_attr, attrs, &ctrls[0]); + if (ret != EOK) { + DEBUG(1, ("Could not create OpenLDAP deref control\n")); + talloc_zfree(req); + return NULL; + } + + DEBUG(6, ("Dereferencing entry [%s] using OpenLDAP deref\n", base_dn)); + subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn, + LDAP_SCOPE_BASE, NULL, attrs, + false, ctrls, NULL, 0, timeout, + sdap_x_deref_parse_entry, + state); + ldap_control_free(ctrls[0]); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sdap_x_deref_search_done, req); + + return req; +} + +static int sdap_x_deref_create_control(struct sdap_handle *sh, + const char *deref_attr, + const char **attrs, + LDAPControl **ctrl) +{ + struct berval derefval; + int ret; + static LDAPDerefSpec ds; + + ds.derefAttr = discard_const(deref_attr); + ds.attributes = discard_const(attrs); + + ret = ldap_create_deref_control_value(sh->ldap, &ds, &derefval); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("sss_ldap_control_create failed: %s\n", + ldap_err2string(ret))); + return ret; + } + + ret = sdap_control_create(sh, LDAP_CONTROL_X_DEREF, + 1, &derefval, 1, ctrl); + ldap_memfree(derefval.bv_val); + if (ret != EOK) { + DEBUG(1, ("sss_ldap_control_create failed\n")); + return ret; + } + + return EOK; +} + +static errno_t sdap_x_deref_parse_entry(struct sdap_handle *sh, + struct sdap_msg *msg, + void *pvt) +{ + errno_t ret; + LDAPControl **ctrls = NULL; + LDAPControl **next = NULL; + LDAPControl **start = NULL; + LDAPControl *derefctrl = NULL; + LDAPDerefRes *deref_res; + LDAPDerefRes *dref; + struct sdap_deref_attrs **res; + TALLOC_CTX *tmp_ctx; + + struct sdap_x_deref_search_state *state = talloc_get_type(pvt, + struct sdap_x_deref_search_state); + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + ret = ldap_get_entry_controls(state->sh->ldap, msg->msg, + &ctrls); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_result failed\n")); + goto done; + } + + if (!ctrls) { + DEBUG(4, ("No controls found for entry\n")); + ret = ENOENT; + goto done; + } + + start = ctrls; + res = NULL; + do { + derefctrl = ldap_control_find(LDAP_CONTROL_X_DEREF, start, &next); + if (!derefctrl) break; + + DEBUG(9, ("Got deref control\n")); + start = next; + + ret = ldap_parse_derefresponse_control(state->sh->ldap, + derefctrl, + &deref_res); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_derefresponse_control failed: %s\n", + ldap_err2string(ret))); + goto done; + } + + for (dref = deref_res; dref; dref=dref->next) { + ret = sdap_parse_deref(tmp_ctx, state->maps, state->num_maps, + state->sh, dref, &res); + if (ret) { + DEBUG(1, ("sdap_parse_deref failed [%d]: %s\n", + ret, strerror(ret))); + goto done; + } + + ret = add_to_deref_reply(state, state->num_maps, + &state->dreply, res); + if (ret != EOK) { + DEBUG(1, ("add_to_deref_reply failed.\n")); + goto done; + } + } + + DEBUG(9, ("All deref results from a single control parsed\n")); + ldap_derefresponse_free(deref_res); + deref_res = NULL; + } while (derefctrl); + + ret = EOK; +done: + talloc_zfree(tmp_ctx); + ldap_controls_free(ctrls); + ldap_derefresponse_free(deref_res); + return ret; +} + +static void sdap_x_deref_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sdap_get_generic_ext_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(4, ("sdap_get_generic_ext_recv failed [%d]: %s\n", + ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static int +sdap_x_deref_search_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *reply_count, + struct sdap_deref_attrs ***reply) +{ + struct sdap_x_deref_search_state *state = tevent_req_data(req, + struct sdap_x_deref_search_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *reply_count = state->dreply.reply_count; + *reply = talloc_steal(mem_ctx, state->dreply.reply); + + return EOK; +} + /* ==Attribute scoped search============================================ */ struct sdap_asq_search_state { struct sdap_attr_map_info *maps; -- cgit