/* * Copyright 2008 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 #include #include #include #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include #include #include #else #include #endif #include #include #ifdef HAVE_TCPD_H #include #endif #include "backend.h" #include "backend.h" #include "back-shr.h" #include "format.h" #include "plugin.h" #include "map.h" #define SCH_CONTAINER_CONFIGURATION_FILTER "(&(objectClass=*)(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_BASE_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_FILTER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_RDN_ATTR "=*))" /* The data we ask the map cache to keep, for us, for each set. */ struct backend_set_data { struct backend_shr_set_data common; /* Schema compatibility-specific data. */ Slapi_DN *container_sdn; char *rdn_format; char **attribute_format; bool_t check_access; }; struct backend_entry_data { Slapi_DN *original_entry_dn; Slapi_Entry *e; }; /* Read the name of the NIS master. A dummy function for the schema * compatibility plugin. */ void backend_free_master_name(struct plugin_state *state, char *master) { } int backend_read_master_name(struct plugin_state *state, char **master) { *master = "localhost"; return -1; } /* Manipulate a backend map configuration. */ static void backend_set_config_free_config_contents(void *data) { struct backend_set_data *set_data = data; if (set_data != NULL) { free(set_data->common.group); free(set_data->common.set); free(set_data->common.bases); format_free_attr_list(set_data->common.ref_attrs); format_free_inref_attrs(set_data->common.inref_attrs); free(set_data->common.entry_filter); slapi_sdn_free(&set_data->container_sdn); free(set_data->rdn_format); backend_shr_free_strlist(set_data->attribute_format); } } void backend_set_config_free_config(struct backend_shr_set_data *data) { backend_set_config_free_config_contents(data->self); free(data); } static struct backend_shr_set_data * backend_copy_set_config(const struct backend_set_data *data) { struct backend_set_data *ret; ret = malloc(sizeof(*ret)); if (ret == NULL) { return NULL; } ret->common.self = ret; ret->common.state = data->common.state; ret->common.group = strdup(data->common.group); ret->common.set = strdup(data->common.set); ret->common.bases = backend_shr_dup_strlist(data->common.bases); ret->common.ref_attrs = data->common.ref_attrs ? format_dup_attr_list(data->common.ref_attrs) : NULL; ret->common.inref_attrs = data->common.inref_attrs ? format_dup_inref_attrs(data->common.inref_attrs) : NULL; ret->common.entry_filter = strdup(data->common.entry_filter); ret->container_sdn = slapi_sdn_dup(data->container_sdn); ret->rdn_format = strdup(data->rdn_format); ret->attribute_format = backend_shr_dup_strlist(data->attribute_format); ret->check_access = data->check_access; if ((ret->common.group == NULL) || (ret->common.set == NULL) || (ret->common.bases == NULL) || (ret->common.entry_filter == NULL) || (ret->container_sdn == NULL)) { backend_set_config_free_config(&ret->common); return NULL; } return &ret->common; } /* Given a configuration entry, read the map configuration for the given group * and container name from the entry. */ void backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e, const char *group, const char *container, bool_t *flag, struct backend_shr_set_data **pret) { char **bases, *entry_filter, **attributes, *rdn_format, *dn; bool_t check_access; char *actual_attr; const char *cvalue; int i, j, disposition, buffer_flags, count; struct backend_set_data ret; Slapi_ValueSet *values; Slapi_Value *value; /* Read the values from the configuration entry. */ bases = backend_shr_get_vattr_strlist(state, e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR); entry_filter = backend_shr_get_vattr_filter(state, e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR); rdn_format = backend_shr_get_vattr_str(state, e, SCH_CONTAINER_CONFIGURATION_RDN_ATTR); check_access = backend_shr_get_vattr_boolean(state, e, SCH_CONTAINER_CONFIGURATION_ACCESS_ATTR, TRUE); attributes = backend_shr_get_vattr_strlist(state, e, SCH_CONTAINER_CONFIGURATION_ATTR_ATTR); /* Populate the returned structure. */ ret.common.state = state; ret.common.group = strdup(group); ret.common.set = strdup(container); ret.common.bases = bases; ret.common.entry_filter = entry_filter; ret.common.ref_attrs = NULL; ret.common.inref_attrs = NULL; dn = slapi_dn_plus_rdn(ret.common.group, ret.common.set); ret.container_sdn = slapi_sdn_new_dn_passin(dn); ret.rdn_format = rdn_format; ret.attribute_format = attributes; ret.check_access = check_access; *pret = backend_copy_set_config(&ret); free(ret.common.group); free(ret.common.set); backend_shr_free_strlist(ret.common.bases); free(ret.common.entry_filter); slapi_sdn_free(&ret.container_sdn); backend_shr_free_strlist(ret.attribute_format); free(ret.rdn_format); } /* Create and destroy entry-specific data. */ static struct backend_entry_data * backend_entry_make_entry_data(Slapi_DN *original_entry_dn, Slapi_Entry *e) { struct backend_entry_data *ret; ret = malloc(sizeof(*ret)); if (ret != NULL) { ret->original_entry_dn = slapi_sdn_dup(original_entry_dn); ret->e = e; } else { slapi_entry_free(e); } return ret; } static void backend_entry_free_entry_data(void *p) { struct backend_entry_data *data; data = p; slapi_entry_free(data->e); slapi_sdn_free(&data->original_entry_dn); free(data); } /* Given a map-entry directory entry, determine a key, a value, and extra data * to be stored in the map cache, and add them to the map cache. */ void backend_set_entry(Slapi_Entry *e, struct backend_set_data *data) { char *dn, *rdn, *ndn, *ldif, *plugin_id, *keys[2], **ava, *val; unsigned int rdn_len, *ava_lens; const char *rdnstr; int len, i, j; Slapi_Entry *entry; Slapi_DN *e_dn, *sdn; Slapi_RDN *srdn; Slapi_Value *value; plugin_id = data->common.state->plugin_desc->spd_id; e_dn = slapi_entry_get_sdn(e); ndn = slapi_entry_get_ndn(e); /* Generate the RDN for the entry. */ rdn = format_get_data(data->common.state, e, data->common.group, data->common.set, data->rdn_format, NULL, &data->common.ref_attrs, &data->common.inref_attrs, &rdn_len); /* Now build the entry itself, and set the DN first. */ entry = slapi_entry_alloc(); dn = slapi_dn_plus_rdn(slapi_sdn_get_ndn(data->container_sdn), rdn); sdn = slapi_sdn_new_dn_byval(dn); slapi_entry_set_sdn(entry, sdn); slapi_sdn_free(&sdn); slapi_ch_free((void **) &dn); /* Iterate through the set of attributes. */ if (data->attribute_format != NULL) { value = slapi_value_new(); for (i = 0; data->attribute_format[i] != NULL; i++) { /* Expand the format specifier into a list. */ ava_lens = NULL; ava = format_get_data_set(data->common.state, e, data->common.group, data->common.set, data->attribute_format[i], NULL, &data->common.ref_attrs, &data->common.inref_attrs, &ava_lens); if ((ava != NULL) && (ava_lens != NULL) && (value != NULL)) { for (j = 0; ava[j] != NULL; j++) { /* Assume attribute=value. */ val = memchr(ava[j], '=', ava_lens[j]); /* Skip over anything that didn't have * a '=' or that produced an empty * value. */ if ((val != NULL) && (ava_lens[j] > val + 1 - ava[j])) { *val = '\0'; slapi_value_set(value, val + 1, ava_lens[j] - (val + 1 - ava[j])); slapi_entry_add_value(entry, ava[j], value); *val = '='; } } } format_free_data_set(ava, ava_lens); } slapi_value_free(&value); } /* Try to make the entry look "right". */ if (!slapi_entry_rdn_values_present(entry)) { slapi_entry_add_rdn_values(entry); } if (slapi_entry_schema_check(NULL, entry) != 0) { slapi_entry_add_string(entry, "objectClass", "extensibleObject"); } /* Clean up some LDIF. */ ldif = slapi_entry2str(entry, &len); /* Recreate the entry. */ slapi_entry_free(entry); entry = slapi_str2entry(ldif, SLAPI_STR2ENTRY_REMOVEDUPVALS | SLAPI_STR2ENTRY_ADDRDNVALS | SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES | SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF); slapi_ch_free((void **) &ldif); /* Normalize the RDN, so that we can use it as a key. */ srdn = slapi_rdn_new_sdn(slapi_entry_get_sdn(entry)); if (srdn != NULL) { rdnstr = slapi_rdn_get_rdn(srdn); } else { rdnstr = NULL; } /* If we actually generated a useful new entry for this entry, then set * it, otherwise clear it in case there was one set before. */ if ((rdnstr != NULL) && (slapi_entry_get_ndn(entry) != NULL)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "setting group/container/key/value " "\"%s\"/\"%s\"/\"%s\"(\"%s\")=\"%s\"\n", data->common.group, data->common.set, rdn, ndn, slapi_entry_get_ndn(entry)); keys[0] = (char *) rdnstr; keys[1] = NULL; map_data_set_entry(data->common.state, data->common.group, data->common.set, ndn, &rdn_len, keys, -1, slapi_entry_get_ndn(entry), backend_entry_make_entry_data(e_dn, entry), backend_entry_free_entry_data); } else { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "no value for %s, unsetting domain/map/id" "\"%s\"/\"%s\"/(\"%s\")\n", ndn, data->common.group, data->common.set, ndn); map_data_unset_entry(data->common.state, data->common.group, data->common.set, ndn); slapi_entry_free(entry); } slapi_rdn_free(&srdn); format_free_data(rdn); } /* Process a set configuration directory entry. Pull out the group and * container names which are valid for this configuration and configure such a * container for each in turn. */ int backend_set_config_entry_add_cb(Slapi_Entry *e, void *callback_data) { char **groups, **containers, *actual_attr; const char *cvalue; Slapi_ValueSet *values; Slapi_Value *value; int i, j, ret, disposition, buffer_flags, count; struct plugin_state *state; state = callback_data; groups = backend_shr_get_vattr_strlist(state, e, SCH_CONTAINER_CONFIGURATION_GROUP_ATTR); containers = backend_shr_get_vattr_strlist(state, e, SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR); for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { for (j = 0; (containers != NULL) && (containers[j] != NULL); j++) { ret = backend_shr_set_config_entry_add(state, e, groups[i], containers[j]); } } backend_shr_free_strlist(containers); backend_shr_free_strlist(groups); return 0; } /* Process a set configuration directory entry. Pull out the domain and map * names which are specified in the entry and delete each in turn. */ int backend_set_config_entry_delete_cb(Slapi_Entry *e, void *callback_data) { struct plugin_state *state; state = callback_data; return backend_shr_set_config_entry_delete(state, e, SCH_CONTAINER_CONFIGURATION_GROUP_ATTR, SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR); } /* Functions for passing information about a container's configuration to the * formatting functions. */ struct backend_get_set_config_cb { struct plugin_state *state; char **bases; char *entry_filter; }; void backend_free_set_config(char **bases, char *entry_filter) { int i; backend_shr_free_strlist(bases); free(entry_filter); } static bool_t backend_get_set_config_entry_cb(Slapi_Entry *e, void *callback_data) { struct backend_get_set_config_cb *cbdata; cbdata = callback_data; slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "reading container configuration from \"%s\"\n", slapi_entry_get_ndn(e)); cbdata->bases = backend_shr_get_vattr_strlist(cbdata->state, e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR); cbdata->entry_filter = backend_shr_get_vattr_filter(cbdata->state, e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR); return TRUE; } void backend_get_set_config(struct plugin_state *state, const char *group, const char *container, char ***bases, char **entry_filter) { Slapi_PBlock *pb; char *filter; char *attrs[] = {SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, NULL}; struct backend_get_set_config_cb cbdata; /* Build the search filter. */ filter = malloc(strlen("(&(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=)" ")") + strlen(group) + strlen(container) + strlen(SCH_CONTAINER_CONFIGURATION_FILTER) + 1); if (filter == NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "out of memory reading configuration for " "\"%s\"/\"%s\"!\n", group, container); return; } sprintf(filter, "(&(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=%s)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=%s)%s)", group, container, SCH_CONTAINER_CONFIGURATION_FILTER); /* Perform the search. */ pb = slapi_pblock_new(); slapi_search_internal_set_pb(pb, state->plugin_base, LDAP_SCOPE_SUB, filter, attrs, FALSE, NULL, NULL, state->plugin_identity, 0); cbdata.bases = NULL; cbdata.state = state; cbdata.entry_filter = NULL; slapi_search_internal_callback_pb(pb, &cbdata, NULL, backend_get_set_config_entry_cb, NULL); /* Return the results. */ *bases = cbdata.bases; *entry_filter = cbdata.entry_filter; /* Clean up. */ free(filter); slapi_pblock_destroy(pb); } /* Given an entry, return the filter which will match a container entry beneath * the plugin's configuration entry. */ const char * backend_entry_get_set_config_entry_filter(void) { return SCH_CONTAINER_CONFIGURATION_FILTER; } /* Re-read plugin-wide settings that may have changed. Nothing to do. */ void backend_update_params(struct plugin_state *state) { } /* Intercept a search request, and if it belongs to one of our compatibility * trees, answer from our cache before letting the default database have a * crack at it. */ struct backend_search_cbdata { Slapi_PBlock *pb; struct plugin_state *state; char *target, *strfilter, **attrs; int scope, sizelimit, timelimit, attrsonly; bool_t check_access; Slapi_DN *target_dn; Slapi_Filter *filter; bool_t answer; int result; const char *matched, *text; int n_entries; }; static bool_t backend_search_entry_cb(const char *domain, const char *map, bool_t secure, const char *key, unsigned int key_len, const char *value, unsigned int value_len, const char *id, int key_index, void *backend_data, void *cb_data) { Slapi_DN *sdn; struct backend_search_cbdata *cbdata; struct backend_entry_data *entry_data; int result; cbdata = cb_data; entry_data = backend_data; sdn = slapi_entry_get_sdn(entry_data->e); /* If the search was for scope==base, then the client only wants to * examine one entry, so we need to check that this is the right one. * */ switch (cbdata->scope) { case LDAP_SCOPE_BASE: if (slapi_sdn_compare(sdn, cbdata->target_dn) != 0) { /* The names don't match -- return now. */ return TRUE; } break; default: break; } /* Now check the entry against the filter. */ result = slapi_filter_test(cbdata->pb, entry_data->e, cbdata->filter, cbdata->check_access); switch (result) { case -1: /* Not a match. */ break; case 0: /* Match. Return the entry. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search matched %s\n", slapi_sdn_get_ndn(sdn)); slapi_send_ldap_search_entry(cbdata->pb, entry_data->e, NULL, cbdata->attrs, cbdata->attrsonly); cbdata->n_entries++; break; default: /* Return an LDAP error. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search got error %d checking %s\n", result, slapi_sdn_get_ndn(sdn)); cbdata->answer = TRUE; if (cbdata->result == 0) { cbdata->result = result; } break; } return TRUE; } static bool_t backend_search_set_cb(const char *group, const char *set, bool_t flag, void *backend_data, void *cb_data) { struct backend_search_cbdata *cbdata; struct backend_set_data *set_data; Slapi_Entry *set_entry; int result; const char *ndn; cbdata = cb_data; set_data = backend_data; cbdata->check_access = set_data->check_access; /* Check the set itself. */ if (slapi_sdn_scope_test(set_data->container_sdn, cbdata->target_dn, cbdata->scope)) { set_entry = slapi_entry_alloc(); slapi_entry_add_string(set_entry, "objectClass", "extensibleObject"); slapi_entry_set_sdn(set_entry, set_data->container_sdn); if (!slapi_entry_rdn_values_present(set_entry)) { slapi_entry_add_rdn_values(set_entry); } ndn = slapi_sdn_get_ndn(set_data->container_sdn); result = slapi_filter_test(cbdata->pb, set_entry, cbdata->filter, cbdata->check_access); switch (result) { case -1: /* Not a match. */ break; case 0: /* Match. Return the entry. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search matched %s\n", ndn); slapi_send_ldap_search_entry(cbdata->pb, set_entry, NULL, cbdata->attrs, cbdata->attrsonly); cbdata->n_entries++; break; default: /* Return an LDAP error. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search got error %d checking %s\n", result, ndn); cbdata->answer = TRUE; if (cbdata->result == 0) { cbdata->result = result; } break; } slapi_entry_free(set_entry); } /* Now scope-check the set's contents so that we only do it once. */ switch (cbdata->scope) { case LDAP_SCOPE_BASE: /* If the container is not the parent of the target, then we're * done with this set. */ if (slapi_sdn_isparent(set_data->container_sdn, cbdata->target_dn) != 1) { return TRUE; } break; case LDAP_SCOPE_ONE: /* If it's not a scope=one search of this set, then we're done * with this set. */ if (slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) != 0) { return TRUE; } break; case LDAP_SCOPE_SUB: /* If the search suffix is not a suffix of the set and it's not * the set itself, then we're done with this set. */ if ((slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) != 0) && (slapi_sdn_issuffix(set_data->container_sdn, cbdata->target_dn) != 1)) { return TRUE; } break; } /* Walk the set of entries in this set. */ map_data_foreach_entry_id(cbdata->state, group, set, NULL, backend_search_entry_cb, cbdata); return TRUE; } static bool_t backend_search_group_cb(const char *group, void *cb_data) { struct backend_search_cbdata *cbdata; Slapi_DN *group_dn; Slapi_Entry *group_entry; int result; cbdata = cb_data; /* Check the group itself. */ group_dn = slapi_sdn_new_dn_byref(group); if (slapi_sdn_scope_test(group_dn, cbdata->target_dn, cbdata->scope)) { group_entry = slapi_entry_alloc(); slapi_entry_add_string(group_entry, "objectClass", "extensibleObject"); slapi_entry_set_sdn(group_entry, group_dn); if (!slapi_entry_rdn_values_present(group_entry)) { slapi_entry_add_rdn_values(group_entry); } result = slapi_filter_test(cbdata->pb, group_entry, cbdata->filter, cbdata->check_access); switch (result) { case -1: /* Not a match. */ break; case 0: /* Match. Return the entry. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search matched %s\n", group); slapi_send_ldap_search_entry(cbdata->pb, group_entry, NULL, cbdata->attrs, cbdata->attrsonly); cbdata->n_entries++; break; default: /* Return an LDAP error. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search got error %d checking %s\n", result, group); cbdata->answer = TRUE; if (cbdata->result == 0) { cbdata->result = result; } break; } slapi_entry_free(group_entry); } /* Now check the group's sets and their contents. */ map_data_foreach_map(cbdata->state, group, backend_search_set_cb, cbdata); /* If the search is confined to this group, we need to send the result * ourselves. */ if (slapi_sdn_scope_test(cbdata->target_dn, group_dn, LDAP_SCOPE_SUB)) { cbdata->answer = TRUE; } slapi_sdn_free(&group_dn); return TRUE; } static int backend_search_cb(Slapi_PBlock *pb) { struct backend_search_cbdata cbdata; cbdata.pb = pb; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &cbdata.target); cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target); slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &cbdata.scope); slapi_pblock_get(pb, SLAPI_SEARCH_SIZELIMIT, &cbdata.sizelimit); slapi_pblock_get(pb, SLAPI_SEARCH_TIMELIMIT, &cbdata.timelimit); slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &cbdata.filter); slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &cbdata.strfilter); slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &cbdata.attrs); slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &cbdata.attrsonly); slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, "searching from \"%s\" for \"%s\" with scope %d\n", cbdata.target, cbdata.strfilter, cbdata.scope); cbdata.answer = FALSE; cbdata.result = 0; cbdata.matched = ""; cbdata.text = ""; cbdata.n_entries = 0; map_rdlock(); map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata); map_unlock(); slapi_sdn_free(&cbdata.target_dn); if (cbdata.answer) { slapi_send_ldap_result(cbdata.pb, cbdata.result, (char *) cbdata.matched, (char *) cbdata.text, cbdata.n_entries, NULL); } return cbdata.answer ? -1 : 0; } /* Locate the entry for a given DN. */ struct backend_locate_cbdata { struct plugin_state *state; char *target; Slapi_DN *target_dn; struct backend_entry_data *entry_data; }; /* Check if the target DN is an entry in this container's set of entries. If * it is, pull the entry's data out and save it. */ static bool_t backend_locate_cb(const char *group, const char *set, bool_t flag, void *backend_set_data, void *cb_data) { struct backend_locate_cbdata *cbdata; struct backend_set_data *set_data; struct backend_entry_data *entry_data; Slapi_RDN *rdn; const char *rdnstr, *ndn, *original_dn; unsigned int ndnlen; cbdata = cb_data; set_data = backend_set_data; /* Check if the target DN looks like it would be part of this set. */ if (slapi_sdn_scope_test(cbdata->target_dn, set_data->container_sdn, LDAP_SCOPE_ONE)) { /* Pull out the RDN and check for an entry which is using the * RDN as a key. */ rdn = slapi_rdn_new_sdn(cbdata->target_dn); if (rdn != NULL) { rdnstr = slapi_rdn_get_rdn(rdn); if (map_match(cbdata->state, group, set, &flag, strlen(rdnstr), rdnstr, &ndnlen, &ndn, &original_dn, (void **) &entry_data)) { if (entry_data != NULL) { cbdata->entry_data = entry_data; } } slapi_rdn_free(&rdn); } } return TRUE; } static void backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data) { struct backend_locate_cbdata cbdata; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); slapi_pblock_get(pb, SLAPI_TARGET_DN, &cbdata.target); cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target); cbdata.entry_data = NULL; map_data_foreach_map(cbdata.state, NULL, backend_locate_cb, &cbdata); *data = cbdata.entry_data; slapi_sdn_free(&cbdata.target_dn); } /* Check if the target DN is part of this group's tree. If it is, return an * insufficient-access error. */ struct backend_group_check_scope_cbdata { struct plugin_state *state; const char *target; Slapi_DN *target_dn; bool_t ours; }; static bool_t backend_group_check_scope_cb(const char *group, void *cb_data) { struct backend_group_check_scope_cbdata *cbdata; Slapi_DN *group_dn; cbdata = cb_data; group_dn = slapi_sdn_new_dn_byref(group); if (slapi_sdn_scope_test(cbdata->target_dn, group_dn, LDAP_SCOPE_SUB)) { cbdata->ours = TRUE; } slapi_sdn_free(&group_dn); return TRUE; } static bool_t backend_check_scope(struct plugin_state *state, const char *target) { struct backend_group_check_scope_cbdata cbdata; cbdata.state = state; cbdata.target = target; cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target); cbdata.ours = FALSE; map_rdlock(); map_data_foreach_domain(cbdata.state, backend_group_check_scope_cb, &cbdata); map_unlock(); slapi_sdn_free(&cbdata.target_dn); return cbdata.ours; } static bool_t backend_check_scope_pb(Slapi_PBlock *pb) { struct plugin_state *state; char *target; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); slapi_pblock_get(pb, SLAPI_TARGET_DN, &target); return backend_check_scope(state, target); } static int backend_write_cb(Slapi_PBlock *pb) { int ret; map_rdlock(); if (backend_check_scope_pb(pb)) { slapi_send_ldap_result(pb, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, 0, NULL); ret = -1; } else { ret = 0; } map_unlock(); return ret; } static int backend_bind_cb(Slapi_PBlock *pb) { struct backend_entry_data *data; int ret; struct berval ref; struct berval *urls[] = {&ref, NULL}; const char *ndn; map_rdlock(); backend_locate(pb, &data); if (data != NULL) { ndn = slapi_sdn_get_ndn(data->original_entry_dn); ref.bv_len = strlen("ldap:///") + strlen(ndn); ref.bv_val = malloc(ref.bv_len + 1); if (ref.bv_val != NULL) { sprintf(ref.bv_val, "ldap:///%s", ndn); slapi_send_ldap_result(pb, LDAP_REFERRAL, NULL, NULL, 0, urls); free(ref.bv_val); } else { slapi_send_ldap_result(pb, LDAP_BUSY, NULL, NULL, 0, NULL); } ret = -1; } else { if (backend_check_scope_pb(pb)) { slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL); ret = -1; } else { ret = 0; } } map_unlock(); return ret; } static int backend_compare_cb(Slapi_PBlock *pb) { int ret; map_rdlock(); if (backend_check_scope_pb(pb)) { slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL); ret = -1; } else { ret = 0; } map_unlock(); return ret; } /* Populate our data cache. */ void backend_startup(struct plugin_state *state) { backend_shr_startup(state, SCH_CONTAINER_CONFIGURATION_FILTER); } void backend_init_preop(Slapi_PBlock *pb, struct plugin_state *state) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "hooking up preoperation callbacks\n"); /* Intercept bind requests and return a referral or failure for entries * that we're managing. */ if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, backend_bind_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up bind callback\n"); } /* Intercept compare requests and return the right data. */ if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_COMPARE_FN, backend_compare_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up bind callback\n"); } /* Intercept search requests and return the right data. */ if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, backend_search_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up search callback\n"); } /* Intercept write requests and return an insufficient-access error for * attempts to write to anything we're managing. */ if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, backend_write_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up add callback\n"); } if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, backend_write_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up modify callback\n"); } if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN, backend_write_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up modrdn callback\n"); } if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_DELETE_FN, backend_write_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up delete callback\n"); } /* We don't hook abandonment requests. */ /* We don't hook unbind requests. */ } void backend_init_postop(Slapi_PBlock *pb, struct plugin_state *state) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "hooking up postoperation callbacks\n"); backend_shr_postop_init(pb, state); }