diff options
Diffstat (limited to 'src/back-sch-idview.c')
-rw-r--r-- | src/back-sch-idview.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/back-sch-idview.c b/src/back-sch-idview.c new file mode 100644 index 0000000..9f4025a --- /dev/null +++ b/src/back-sch-idview.c @@ -0,0 +1,376 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * 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; version 2 of the License. + * + * 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, write to the + * + * Free Software Foundation, Inc. + * 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> + +#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H +#include <nspr.h> +#include <nss.h> +#include <dirsrv/slapi-plugin.h> +#else +#include <slapi-plugin.h> +#endif + +#include <rpc/xdr.h> +#include "../yp/yp.h" + +#ifdef HAVE_TCPD_H +#include <tcpd.h> +#endif + +#include "backend.h" +#include "back-shr.h" +#include "format.h" +#include "plugin.h" +#include "map.h" +#include "back-sch.h" + +void +idview_get_overrides(struct backend_search_cbdata *cbdata) +{ + char *dn = NULL; + int ret = 0; + const Slapi_DN *suffix = NULL; + Slapi_PBlock *pb; + + if (cbdata->idview == NULL) + return; + + pb = wrap_pblock_new(cbdata->pb); + if (pb == NULL) + return; + + wrap_inc_call_level(); + + suffix = slapi_get_suffix_by_dn(cbdata->target_dn); + dn = slapi_ch_smprintf("cn=%s,cn=views,cn=accounts,%s", cbdata->idview, slapi_sdn_get_dn(suffix)); + /* Fetch all attributes; there is a bug in 389-ds: it gives out all attributes for the entry anyway + * when search returns Slapi_Entry* objects. Instead, we'll do removal later */ + slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_SUBTREE, + "(objectclass=ipaOverrideAnchor)", NULL, 0, + NULL, NULL, cbdata->state->plugin_identity, 0); + ret = slapi_search_internal_pb(pb); + slapi_ch_free_string(&dn); + + if (ret == 0) { + /* Steal search result entries to avoid re-allocating them */ + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &(cbdata->overrides)); + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, NULL); + } + + wrap_dec_call_level(); + slapi_pblock_destroy(pb); +} + +void +idview_free_overrides(struct backend_search_cbdata *cbdata) +{ + int i = 0; + if (cbdata->overrides != NULL) { + for(i=0; cbdata->overrides[i] != NULL; i++) { + slapi_entry_free(cbdata->overrides[i]); + } + slapi_ch_free((void**)&(cbdata->overrides)); + } +} + +void +idview_process_overrides(struct backend_search_cbdata *cbdata, + const char *key, const char *map, const char *domain, + Slapi_Entry *entry) +{ +#define VIEW_TEMPLATE_KEY_MAP_DOMAIN 0 +#define VIEW_TEMPLATE_KEY_MAP_DOMAIN_NEWKEY 3 +#define VIEW_TEMPLATE_MAP_DOMAIN 1 +#define VIEW_TEMPLATE_DOMAIN 2 + /* After view was applied, entry's DN needs to reflect the view */ + const char *dn_template[] = {"%s,%s,cn=%s,cn=views,%s", /* an entry for user or group */ + "%s,cn=%s,cn=views,%s", /* an entry for a map (container for users or groups) */ + "cn=%s,cn=views,%s", /* an entry is a base of the compat tree */ + "%s=%s,%s,cn=%s,cn=views,%s", /* an entry for user or group which RDN was overridden with new value */ + }; + const char *filterout_attrs[] = {"objectclass", "creatorsname", "modifiersname", + "createtimestamp", "modifytimestamp", "parentid", + "entryusn", "ipaanchoruuid", NULL }; + char *new_dn = NULL, *new_key = NULL, *sep = NULL, *new_val = NULL; + Slapi_Entry *override_entry = NULL; + Slapi_Attr *anchor = NULL, *id_attr = NULL; + Slapi_Value *anchor_value = NULL, *id_value = NULL; + int i, result, dn_template_id; + + if (cbdata->overrides == NULL) { + /* Only retrieve overrides for the view first time when neccessary */ + idview_get_overrides(cbdata); + if (cbdata->overrides == NULL) + return; + } + + /* 1. See if the entry has ipaAnchorUUID and selected idview has an override for it */ + /* The code below intentionally uses Slapi_Value instead of comparing string values to + * avoid allocating additional memory */ + result = slapi_entry_attr_find(entry, IPA_IDVIEWS_ATTR_ANCHORUUID, &anchor); + if ((result == 0) && (anchor != NULL) && (cbdata->overrides != NULL)) { + result = slapi_attr_first_value(anchor, &anchor_value); + for(i = 0; cbdata->overrides[i] != NULL; i++) { + result = slapi_entry_attr_find(cbdata->overrides[i], IPA_IDVIEWS_ATTR_ANCHORUUID, &id_attr); + if ((result == 0) && (id_attr != NULL)) { + result = slapi_attr_first_value(id_attr, &id_value); + result = slapi_value_compare(id_attr, anchor_value, id_value); + if (result == 0) { + override_entry = cbdata->overrides[i]; + break; + } + } + } + } + + /* 2. If there is indeed an override, replace attribute values except for the ones that should be ignored */ + if (override_entry != NULL) { + Slapi_Attr *entry_attr = NULL, *override_attr = NULL; + result = slapi_entry_first_attr(override_entry, &override_attr); + while (result == 0) { + Slapi_ValueSet *override_valueset = NULL; + char *override_type = NULL; + + /* Filter out override attributes that we don't care about */ + result = slapi_attr_get_type(override_attr, &override_type); + for (i = 0; filterout_attrs[i] != NULL; i++) { + if (strcasecmp(override_type, filterout_attrs[i]) == 0) { + break; + } + } + + if (filterout_attrs[i] == NULL) { + /* Replace the attribute's value with the override */ + result = slapi_entry_attr_find(entry, override_type, &entry_attr); + if (result == 0) { + result = slapi_attr_get_valueset(override_attr, &override_valueset); + result = slapi_attr_set_valueset(entry_attr, override_valueset); + } + } + result = slapi_entry_next_attr(override_entry, override_attr, &override_attr); + } + } + + /* 3. If entry has memberUid, we need to replace memberUid values too, if they were overridden */ + result = slapi_entry_attr_find(entry, "memberUid", &anchor); + if ((result == 0) && (anchor != NULL) && (cbdata->overrides != NULL)) { + int value_idx = 0; + Slapi_ValueSet *new_valueset = slapi_valueset_new(); + + if (new_valueset != NULL) { + /* For each memberUid value, find an override with ipaOriginalUid attribute of the same value */ + value_idx = slapi_attr_first_value(anchor, &anchor_value); + while (value_idx != -1) { + bool_t value_found = FALSE; + for(i = 0; cbdata->overrides[i] != NULL; i++) { + result = slapi_entry_attr_find(cbdata->overrides[i], IPA_IDVIEWS_ATTR_ORIGINALUID, &id_attr); + if ((result == 0) && (id_attr != NULL)) { + result = slapi_attr_first_value(id_attr, &id_value); + result = slapi_value_compare(id_attr, anchor_value, id_value); + if (result == 0) { + /* If there is an override with ipaOriginalUid: <memberUid value>, use its 'uid' value to override */ + result = slapi_entry_attr_find(cbdata->overrides[i], "uid", &id_attr); + if ((result == 0) && (id_attr != NULL)) { + result = slapi_attr_first_value(id_attr, &id_value); + if (result == 0) { + /* finally: we have an override with ipaOriginalUid: <memberUid value> _and_ + * this override is changing the 'uid' attribute so we have something to replace */ + slapi_valueset_add_value(new_valueset, id_value); + value_found = TRUE; + break; + } + } + } + } + } + + if (value_found == FALSE) { + slapi_valueset_add_value(new_valueset, anchor_value); + } + value_idx = slapi_attr_next_value(anchor, value_idx, &anchor_value); + } + + slapi_attr_set_valueset(anchor, new_valueset); + slapi_valueset_free(new_valueset); + } + } + + /* 4. Even if there were no overrides, since we are serving throught the view, replace DN value */ + dn_template_id = (key == NULL ? 1 : 0) + (map == NULL ? 1 : 0); + switch (dn_template_id) { + case VIEW_TEMPLATE_KEY_MAP_DOMAIN: + /* update RDN with proper value from the entry after overrides were applied */ + sep = strchr(key, '='); + if (sep != NULL) { + sep[0] = '\0'; + new_val = slapi_entry_attr_get_charptr(entry, key); + new_dn = slapi_ch_smprintf(dn_template[VIEW_TEMPLATE_KEY_MAP_DOMAIN_NEWKEY], key, new_val, map, cbdata->idview, domain); + slapi_ch_free_string(&new_val); + sep[0] = '='; + } else { + new_dn = slapi_ch_smprintf(dn_template[dn_template_id], key, map, cbdata->idview, domain); + } + break; + case VIEW_TEMPLATE_MAP_DOMAIN: + new_dn = slapi_ch_smprintf(dn_template[dn_template_id], map, cbdata->idview, domain); + break; + case VIEW_TEMPLATE_DOMAIN: + new_dn = slapi_ch_smprintf(dn_template[dn_template_id], cbdata->idview, domain); + break; + }; + slapi_entry_set_dn(entry, new_dn); +} + +void +idview_replace_target_dn(char **target, char **idview) +{ + char *idview_p = NULL; + char *cnviews = NULL; + char *new_target = NULL; + + cnviews = strstr(*target, ",cn=views,"); + if (cnviews != NULL && cnviews != *target) { + cnviews[0] = '\0'; + idview_p = strrchr(*target, ','); + if (idview_p == NULL) { + idview_p = *target; + } else { + idview_p++; + } + if (strstr(idview_p, "cn=") != idview_p) { + cnviews[0] = ','; + return; + } + *idview = slapi_ch_strdup(&idview_p[3]); + if (idview_p != *target) { + idview_p[0] = '\0'; + new_target = slapi_ch_smprintf("%s%s", *target, cnviews+10); + idview_p--; + idview_p[0] = ','; + } else { + new_target = slapi_ch_smprintf("%s", cnviews+10); + } + cnviews[0] = ','; + *target = new_target; + } +} + +struct filter_cb_data { + struct backend_search_cbdata *cbdata; + Slapi_Filter *filter; +}; + +static void +idview_process_filter_cb(Slapi_Filter *filter, const char *filter_type, struct berval *bval, struct backend_search_filter_config *config) +{ + int res, i; + Slapi_Value *filter_val, *value, *anchor_val; + Slapi_Attr *anchor, *attr = NULL; + struct backend_search_cbdata *cbdata = (struct backend_search_cbdata *) config->callback_data; + + if (cbdata == NULL || cbdata->idview == NULL) { + return; + } + + if (filter_type == NULL || config->name == NULL) { + return; + } + + if (cbdata->overrides == NULL) { + /* Only retrieve overrides for the view first time when neccessary */ + idview_get_overrides(cbdata); + } + + if (cbdata->overrides == NULL) { + return; + } + + filter_val = slapi_value_new_berval(bval); + + /* If filter contains an attribute name which is overridden in the view and filter value + * corresponds to the override, replace the filter by (ipaAnchorUUID=...) from the override + * to point to the original because otherwise an entry will not be found in the slapi-nis map */ + for(i=0; cbdata->overrides[i] != NULL; i++) { + res = slapi_entry_attr_find(cbdata->overrides[i], filter_type, &attr); + if ((res == 0) && (attr != NULL)) { + res = slapi_attr_first_value(attr, &value); + res = slapi_value_compare(attr, value, filter_val); + if (res == 0) { + res = slapi_entry_attr_find(cbdata->overrides[i], "ipaAnchorUUID", &anchor); + if (res == 0) { + res = slapi_attr_first_value(anchor, &anchor_val); + slapi_filter_changetype(filter, "ipaanchoruuid"); + slapi_ber_bvdone(bval); + slapi_ber_bvcpy(bval, slapi_value_get_berval(anchor_val)); + config->override_found = TRUE; + break; + } + + } + } + } + + slapi_value_free(&filter_val); + +} + +/* Traverse through the filter and replace overridden attribute/value pairs with references to the original + * entries. This allows to properly handle overrides of uid and cn attributes where searches look like + * (&(objectclass=posixAccount)(uid=foobar)) -- if uid=foobar is part of an override for uid=admin, we need + * to point back to uid=admin to be able to find original entry in the slapi-nis cache. + * + * Note that in reality we don't use original value of the uid/cn attribue. Instead, we use ipaAnchorUUID + * to refer to the original entry. */ +void +idview_replace_filter(struct backend_search_cbdata *cbdata) +{ + struct filter_cb_data filter_data = {cbdata, NULL}; + struct backend_search_filter_config config = + {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL, NULL, NULL}; + int res = 0; + + + config.callback = idview_process_filter_cb; + config.callback_data = cbdata; + + res = backend_analyze_search_filter(cbdata->filter, &config); + + if (filter_data.filter != NULL) { + Slapi_Filter *f = cbdata->filter; + cbdata->filter = filter_data.filter; + slapi_filter_free(f, 1); + } + + if (config.name != NULL) { + slapi_ch_free_string(&config.name); + } + +} |