diff options
author | Nalin Dahyabhai <nalin.dahyabhai@pobox.com> | 2008-06-30 16:14:28 -0400 |
---|---|---|
committer | Nalin Dahyabhai <nalin.dahyabhai@pobox.com> | 2008-06-30 16:14:28 -0400 |
commit | 89516c787c3fcd041a0867472382c37529339f71 (patch) | |
tree | d29d052791e7f8d5c4fca21568afad3238840613 /src/back-sch.c | |
parent | a7c6155406df7b44e0e557305460d053af76cada (diff) | |
download | slapi-nis-89516c787c3fcd041a0867472382c37529339f71.tar.gz slapi-nis-89516c787c3fcd041a0867472382c37529339f71.tar.xz slapi-nis-89516c787c3fcd041a0867472382c37529339f71.zip |
- start adding an sch backend
- start factoring out the backend logic where the sch and nis backends overlap
Diffstat (limited to 'src/back-sch.c')
-rw-r--r-- | src/back-sch.c | 1356 |
1 files changed, 1356 insertions, 0 deletions
diff --git a/src/back-sch.c b/src/back-sch.c new file mode 100644 index 0000000..704e653 --- /dev/null +++ b/src/back-sch.c @@ -0,0 +1,1356 @@ +/* + * 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 <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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 <rpcsvc/yp_prot.h> + +#ifdef HAVE_TCPD_H +#include <tcpd.h> +#endif + +#include "backend.h" +#include "back-sch.h" +#include "format.h" +#include "plugin.h" +#include "map.h" + +#define SCH_CONTAINER_CONFIGURATION_FILTER "(&(objectClass=*)(" SCH_CONTAINER_CONFIGURATION_BASE_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_FILTER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_ENTRY_FORMAT_ATTR "=*))" + +/* The data we ask the map cache to keep, for us, for each map. */ +struct backend_map_data { + struct plugin_state *state; + char *group, *container, **bases, *entry_filter, *entry_format; + char **ref_attrs; + struct format_inref_attr **inref_attrs; +}; + +/* 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 string lists. */ +static void +backend_free_strlist(char **strlist) +{ + if (strlist) { + free(strlist); + } +} +static char ** +backend_dup_strlist_n(char **strlist, int n) +{ + int i, l; + char **ret, *s; + /* Handle the NULL case. */ + if (strlist == NULL) { + return NULL; + } + /* No strings = no list. */ + if (n == 0) { + return NULL; + } + /* Count the amount of space needed for the strings. */ + for (i = 0, l = 0; i < n; i++) { + l += (strlen(strlist[i]) + 1); + } + /* Allocate space for the array of pointers (with NULL terminator) and + * then the string data. */ + ret = malloc(((n + 1) * sizeof(char *)) + l); + if (ret != NULL) { + /* Figure out where the string data will start. */ + s = (char *) ret; + s += ((n + 1) * sizeof(char *)); + for (i = 0; i < n; i++) { + /* Set the address of this string, copy the data + * around, and then prepare the address of the next + * string. */ + ret[i] = s; + strcpy(s, strlist[i]); + s += (strlen(strlist[i]) + 1); + } + /* NULL-terminate the array. */ + ret[i] = NULL; + } + return ret; +} +static char ** +backend_dup_strlist(char **strlist) +{ + int i; + for (i = 0; (strlist != NULL) && (strlist[i] != NULL); i++) { + continue; + } + return backend_dup_strlist_n(strlist, i); +} + +/* Manipulate a backend map configuration. */ +static void +backend_free_map_data_contents(void *data) +{ + struct backend_map_data *map_data = data; + if (map_data != NULL) { + free(map_data->group); + free(map_data->container); + free(map_data->bases); + format_free_attr_list(map_data->ref_attrs); + format_free_inref_attrs(map_data->inref_attrs); + free(map_data->entry_filter); + free(map_data->entry_format); + } +} +static void +backend_free_map_data(void *data) +{ + backend_free_map_data_contents(data); + free(data); +} +static struct backend_map_data * +backend_copy_map_data(const struct backend_map_data *data) +{ + struct backend_map_data *ret; + ret = malloc(sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->state = data->state; + ret->group = strdup(data->group); + ret->container = strdup(data->container); + ret->bases = backend_dup_strlist(data->bases); + ret->ref_attrs = data->ref_attrs ? + format_dup_attr_list(data->ref_attrs) : + NULL; + ret->inref_attrs = data->inref_attrs ? + format_dup_inref_attrs(data->inref_attrs) : + NULL; + ret->entry_filter = strdup(data->entry_filter); + ret->entry_format = strdup(data->entry_format); + if ((ret->group == NULL) || + (ret->container == NULL) || + (ret->bases == NULL) || + (ret->entry_filter == NULL) || + (ret->entry_format == NULL)) { + backend_free_map_data(ret); + return NULL; + } + return ret; +} + +/* 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. */ +static int +backend_map_config_entry_add_one_cb(Slapi_Entry *e, void *callback_data) +{ + struct backend_map_data *data; + char *ldif, *ndn, *plugin_id, *ndns[2]; + int i, j, k, n_key_sets; + data = callback_data; + plugin_id = data->state->plugin_desc->spd_id; + /* Pull out the NDN of this entry. */ + ndn = slapi_entry_get_ndn(e); + ndns[0] = ndn; + ndns[1] = NULL; + /* Pull out the keys and value for the entry. */ + ldif = format_get_data(data->state, e, data->group, data->container, + data->entry_format, NULL, + &data->ref_attrs, &data->inref_attrs); + /* If we actually generated a new entry for this entry, then set it, + * otherwise clear it in case there was one set before. */ + if (ldif == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, + "setting group/container/key/value " + "\"%s\"/\"%s\"/\"%s\"(\"%s\")=\"%s\"\n", + data->group, data->container, ndn, ndn, ldif); + map_data_set_entry(data->state, data->group, data->container, + ndn, NULL, ndns, -1, ldif, NULL, NULL); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, + "no value for %s, unsetting domain/map/id" + "\"%s\"/\"%s\"/(\"%s\")\n", + ndn, data->group, data->container, ndn); + map_data_unset_entry_id(data->state, + data->group, data->container, + ndn); + } + format_free_data(ldif); + return 0; +} + +static int +backend_map_config_entry_set_one_cb(Slapi_Entry *e, void *cbdata) +{ + backend_map_config_entry_add_one_cb(e, cbdata); + return 0; +} + +static void +backend_map_config_entry_set_one(Slapi_Entry *e, + struct backend_map_data *map_data) +{ + backend_map_config_entry_set_one_cb(e, map_data); +} + +static void +backend_map_config_entry_set_one_dn(struct plugin_state *state, const char *dn, + struct backend_map_data *map_data) +{ + Slapi_DN *sdn; + Slapi_Entry *entry; + sdn = slapi_sdn_new_dn_byval(dn); + if (sdn == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error parsing DN \"%s\"\n", dn); + return; + } else { + entry = NULL; + slapi_search_internal_get_entry(sdn, NULL, &entry, + state->plugin_identity); + if (entry == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "failure reading entry \"%s\"\n", dn); + } else { + backend_map_config_entry_set_one(entry, map_data); + slapi_entry_free(entry); + } + slapi_sdn_free(&sdn); + } +} + +/* Given an entry, read the map configuration for the given group and container + * name. */ +static void +backend_map_config_read_config(struct plugin_state *state, Slapi_Entry *e, + const char *group, const char *container, + struct backend_map_data *ret) +{ + char **bases, *entry_filter, *entry_format; + char **entrykey_formats, **keys_formats, *value_format, *actual_attr; + char *disallowed_chars; + char **use_bases, *use_entry_filter; + char **use_key_formats, **use_keys_formats; + char *use_value_format, *use_disallowed_chars; + const char *cvalue; + int i, j, disposition, buffer_flags, count; + Slapi_ValueSet *values; + Slapi_Value *value; + /* Read the values from the configuration entry. */ + bases = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + count = slapi_valueset_count(values); + bases = malloc(sizeof(char *) * (count + 1)); + if (bases != NULL) { + i = 0; + for (j = slapi_valueset_first_value(values, &value); + j != -1; + j = slapi_valueset_next_value(values, j, &value)) { + cvalue = slapi_value_get_string(value); + bases[i++] = strdup(cvalue); + } + bases[i] = NULL; + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + entry_filter = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + i = slapi_valueset_first_value(values, &value); + if (i != -1) { + cvalue = slapi_value_get_string(value); + if (strlen(cvalue) > 1) { + if ((cvalue[0] != '(') || + (cvalue[strlen(cvalue) - 1] != ')')) { + entry_filter = malloc(strlen(cvalue) + + 3); + if (entry_filter != NULL) { + sprintf(entry_filter, "(%s)", + cvalue); + } + } + } + if (entry_filter == NULL) { + entry_filter = strdup(cvalue); + } + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + entry_format = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_ENTRY_FORMAT_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + i = slapi_valueset_first_value(values, &value); + if (i != -1) { + cvalue = slapi_value_get_string(value); + entry_format = strdup(cvalue); + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + /* Populate the returned structure. */ + ret->state = state; + ret->group = strdup(group); + ret->container = strdup(container); + ret->bases = backend_dup_strlist(bases); + if (bases != NULL) { + for (i = 0; bases[i] != NULL; i++) { + free(bases[i]); + } + free(bases); + } + ret->entry_filter = entry_filter; + ret->entry_format = entry_format; + ret->ref_attrs = NULL; + ret->inref_attrs = NULL; +} + +/* Given a directory server entry which represents a container's configuration, + * set up and populate the container. */ +static int +backend_map_config_entry_add_one(struct plugin_state *state, Slapi_Entry *e, + const char *group, const char *container) +{ + Slapi_PBlock *pb; + int i; + struct backend_map_data cb_data, *map_cb_data; + + pb = slapi_pblock_new(); + backend_map_config_read_config(state, e, group, container, &cb_data); + map_cb_data = backend_copy_map_data(&cb_data); + backend_free_map_data_contents(&cb_data); + if (map_cb_data == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "incomplete container definition %s in %s " + "(2)\n", container, group); + slapi_pblock_destroy(pb); + return 0; + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "initializing container %s in %s (2)\n", + group, container); + map_data_set_map(state, group, container, FALSE, + map_cb_data, &backend_free_map_data); + map_data_clear_map(state, group, container); + /* Search under each base in turn, adding the matching directory + * entries to the containers. */ + for (i = 0; + (map_cb_data->bases != NULL) && (map_cb_data->bases[i] != NULL); + i++) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "searching '%s' for '%s'\n", + map_cb_data->bases[i], + map_cb_data->entry_filter); + slapi_search_internal_set_pb(pb, + map_cb_data->bases[i], + LDAP_SCOPE_SUB, + map_cb_data->entry_filter, + NULL, FALSE, + NULL, + NULL, + state->plugin_identity, + 0); + slapi_search_internal_callback_pb(pb, map_cb_data, + NULL, + backend_map_config_entry_add_one_cb, + NULL); + slapi_free_search_results_internal(pb); + } + /* Clean up. */ + slapi_pblock_destroy(pb); + return 0; +} + +/* Process a map 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. */ +static int +backend_map_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 = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_GROUP_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + count = slapi_valueset_count(values); + groups = malloc(sizeof(char *) * (count + 1)); + if (groups != NULL) { + for (i = slapi_valueset_first_value(values, &value); + i != -1; + i = slapi_valueset_next_value(values, i, &value)) { + cvalue = slapi_value_get_string(value); + groups[i] = strdup(cvalue); + } + groups[count] = NULL; + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + containers = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + count = slapi_valueset_count(values); + containers = malloc(sizeof(char *) * (count + 1)); + if (containers != NULL) { + for (i = slapi_valueset_first_value(values, &value); + i != -1; + i = slapi_valueset_next_value(values, i, &value)) { + cvalue = slapi_value_get_string(value); + containers[i] = strdup(cvalue); + } + containers[count] = NULL; + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { + for (j = 0; + (containers != NULL) && (containers[j] != NULL); + j++) { + ret = backend_map_config_entry_add_one(state, e, + groups[i], + containers[j]); + } + } + if (groups != NULL) { + for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { + free(groups[i]); + } + free(groups); + } + if (containers != NULL) { + for (i = 0; + (containers != NULL) && (containers[i] != NULL); + i++) { + free(containers[i]); + } + free(containers); + } + return 0; +} + +/* Scan for the list of configured groups and containers. */ +void +backend_startup(struct plugin_state *state) +{ + Slapi_PBlock *pb; + + pb = slapi_pblock_new(); + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "searching \"%s\" for containers\n", + state->plugin_base); + slapi_search_internal_set_pb(pb, + state->plugin_base, + LDAP_SCOPE_ONE, + SCH_CONTAINER_CONFIGURATION_FILTER, + NULL, FALSE, + NULL, + NULL, + state->plugin_identity, + 0); + map_wrlock(); + slapi_search_internal_callback_pb(pb, state, + NULL, + backend_map_config_entry_add_cb, + NULL); + map_unlock(); + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); +} + +/* Process a map configuration directory entry. Pull out the domain and map + * names which are specified in the entry and delete each in turn. */ +static int +backend_map_config_entry_delete_cb(Slapi_Entry *e, void *callback_data) +{ + char **groups, **containers; + int i, j; + struct plugin_state *state; + + state = callback_data; + groups = slapi_entry_attr_get_charray(e, + SCH_CONTAINER_CONFIGURATION_GROUP_ATTR); + containers = slapi_entry_attr_get_charray(e, + SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR); + for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { + for (j = 0; + (containers != NULL) && (containers[j] != NULL); + j++) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "removing container %s in %s\n", + containers[j], groups[i]); + map_data_unset_map(state, groups[i], containers[j]); + } + } + slapi_ch_array_free(containers); + slapi_ch_array_free(groups); + return 0; +} + +/* Functions for passing information about a container's configuration to a + * caller. */ +struct backend_get_map_config_cb { + struct plugin_state *state; + char **bases; + char *entry_filter; +}; + +void +backend_free_map_config(char **bases, char *entry_filter) +{ + backend_free_strlist(bases); + free(entry_filter); +} + +static bool_t +backend_get_map_config_entry_cb(Slapi_Entry *e, void *callback_data) +{ + Slapi_ValueSet *values; + Slapi_Value *value; + struct backend_get_map_config_cb *cbdata; + char *actual_attr; + const char *cvalue; + int disposition, buffer_flags, i, count; + + 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)); + + values = NULL; + value = NULL; + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + count = slapi_valueset_count(values); + cbdata->bases = malloc(sizeof(char *) * (count + 1)); + if (cbdata->bases != NULL) { + for (i = slapi_valueset_first_value(values, &value); + i != -1; + i = slapi_valueset_next_value(values, i, &value)) { + cvalue = slapi_value_get_string(value); + cbdata->bases[i] = strdup(cvalue); + } + cbdata->bases[count] = NULL; + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, + &values, + &disposition, &actual_attr, + 0, &buffer_flags) == 0) { + if (slapi_valueset_first_value(values, &value) != -1) { + cvalue = slapi_value_get_string(value); + if (cvalue != NULL) { + free(cbdata->entry_filter); + cbdata->entry_filter = strdup(cvalue); + } + } + slapi_vattr_values_free(&values, &actual_attr, buffer_flags); + } + + return TRUE; +} + +void +backend_get_map_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}; + const char *default_filter; + struct backend_get_map_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_map_config_entry_cb, + NULL); + + /* Return the results. */ + *bases = cbdata.bases; + *entry_filter = cbdata.entry_filter; + + /* Clean up. */ + slapi_pblock_destroy(pb); +} + +/* Our postoperation callbacks. */ + +/* Given a map configuration, return true if an entry corresponding to the + * entry is supposed to be in the container. */ +static bool_t +backend_entry_matches_map(struct backend_map_data *map_data, + Slapi_PBlock *pb, Slapi_Entry *e) +{ + Slapi_DN *base_sdn; + const Slapi_DN *entry_sdn; + Slapi_Filter *filter; + int i; + /* Decide if the directory server entry belongs in this map. That + * means that it must be contained by one of the bases of the map. */ + entry_sdn = slapi_sdn_new_ndn_byref(slapi_entry_get_ndn(e)); + if (entry_sdn == NULL) { + return FALSE; + } else { + /* Check each base in turn. */ + for (i = 0; + (map_data->bases != NULL) && (map_data->bases[i] != NULL); + i++) { + base_sdn = slapi_sdn_new_dn_byval(map_data->bases[i]); + if (base_sdn == NULL) { + return FALSE; + } else { + if (slapi_sdn_scope_test(entry_sdn, + base_sdn, + LDAP_SCOPE_SUB) == 0) { + /* The entry is not contained by the + * base -- go on to try the next one. */ + slapi_sdn_free(&base_sdn); + continue; + } + /* The entry is contained by the base. */ + slapi_sdn_free(&base_sdn); + break; + } + } + /* If we ran out of bases to check, it doesn't match. */ + if ((map_data->bases == NULL) || (map_data->bases[i] == NULL)) { + return FALSE; + } + } + /* If it's contained by a search base, compare it to the filter. */ + filter = slapi_str2filter(map_data->entry_filter); + if (filter == NULL) { + return FALSE; + } else { + if (slapi_vattr_filter_test(pb, e, filter, 0) != 0) { + /* Didn't match -- return. */ + slapi_filter_free(filter, 1); + return FALSE; + } + slapi_filter_free(filter, 1); + } + return TRUE; +} + +/* Given an entry, return true if it describes a compatibility container. */ +static bool_t +backend_entry_is_a_map(struct plugin_state *state, + Slapi_PBlock *pb, Slapi_Entry *e) +{ + Slapi_DN *entry_sdn, *plugin_sdn; + Slapi_Filter *filter; + bool_t ret; + char configuration_filter[] = SCH_CONTAINER_CONFIGURATION_FILTER; + + /* First, just do the scope test. */ + entry_sdn = slapi_sdn_new_ndn_byref(slapi_entry_get_ndn(e)); + if (entry_sdn == NULL) { + return FALSE; + } else { + plugin_sdn = slapi_sdn_new_dn_byval(state->plugin_base); + if (plugin_sdn == NULL) { + slapi_sdn_free(&entry_sdn); + return FALSE; + } + } + if (slapi_sdn_scope_test(entry_sdn, + plugin_sdn, + LDAP_SCOPE_ONE) == 0) { + /* Didn't match. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "entry \"%s\" is not a child of \"%s\"\n", + slapi_sdn_get_ndn(entry_sdn), + slapi_sdn_get_ndn(plugin_sdn)); + ret = FALSE; + } else { + ret = TRUE; + } + slapi_sdn_free(&plugin_sdn); + slapi_sdn_free(&entry_sdn); + /* If it's actually part of our configuration tree, check if it's a + * valid entry. */ + if (ret) { + filter = slapi_str2filter(configuration_filter); + if (filter != NULL) { + if (slapi_vattr_filter_test(pb, e, filter, 0) != 0) { + /* Didn't match. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "entry \"%s\" doesn't look " + "like a container " + "configuration " + "(didn't match filter " + "\"%s\")\n", + slapi_sdn_get_ndn(entry_sdn), + configuration_filter); + ret = FALSE; + } + slapi_filter_free(filter, 1); + } + } + return ret; +} + +/* Update any entries to which the passed-in entry in the passed-in map refers + * to, if the referred-to entry is in this map. Everybody got that? */ +struct backend_update_references_cbdata { + Slapi_PBlock *pb; + Slapi_Entry *e; +}; + +static bool_t +backend_update_references_cb(const char *domain, const char *map, bool_t secure, + void *backend_data, void *cbdata_ptr) +{ + struct plugin_state *state; + struct backend_map_data *map_data; + struct backend_update_references_cbdata *cbdata; + Slapi_DN *referred_to_sdn; + Slapi_ValueSet *values; + Slapi_Value *value; + char **ref_attrs, *actual_attr, *filter, *tndn; + struct format_inref_attr **inref_attrs; + const char *ndn, *dn; + int i, j, disposition, buffer_flags, filter_size, n_ref_attrs; + + map_data = backend_data; + cbdata = cbdata_ptr; + state = map_data->state; + + /* For every entry in this map which refers to this entry using + * a DN stored in an attribute, update that entry. */ + + /* Build a filter with all of these attributes and this entry's DN. */ + ref_attrs = map_data->ref_attrs; + for (i = 0; (ref_attrs != NULL) && (ref_attrs[i] != NULL); i++) { + continue; + } + n_ref_attrs = i; + if (n_ref_attrs > 0) { + filter_size = strlen("(&(|))") + + strlen(map_data->entry_filter) + + 1; + ndn = slapi_entry_get_ndn(cbdata->e); + tndn = format_escape_for_filter(ndn); + if (tndn == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error building filter for " + "updating entries\n"); + return TRUE; + } + for (i = 0; + (ref_attrs != NULL) && (ref_attrs[i] != NULL); + i++) { + filter_size += (strlen("(=)") + + strlen(ref_attrs[i]) + + strlen(ndn)); + } + filter = malloc(filter_size); + if (filter == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error building filter for " + "updating entries\n"); + free(tndn); + return TRUE; + } + sprintf(filter, "(&%s(|", map_data->entry_filter); + for (i = 0; + (ref_attrs != NULL) && (ref_attrs[i] != NULL); + i++) { + sprintf(filter + strlen(filter), + "(%s=%s)", ref_attrs[i], tndn); + } + strcat(filter, "))"); + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "searching for referrers using filter \"%s\"\n", + filter); + free(tndn); + /* Update any matching entry. */ + for (i = 0; + (map_data->bases != NULL) && (map_data->bases[i] != NULL); + i++) { + slapi_search_internal_set_pb(cbdata->pb, + map_data->bases[i], + LDAP_SCOPE_SUB, + filter, + NULL, FALSE, + NULL, + NULL, + state->plugin_identity, + 0); + slapi_search_internal_callback_pb(cbdata->pb, map_data, + NULL, + backend_map_config_entry_set_one_cb, + NULL); + } + free(filter); + } + + /* Allocate the DN we'll use to hold values for comparison. */ + referred_to_sdn = slapi_sdn_new(); + if (referred_to_sdn == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error updating entries referred to by %s\n", + slapi_entry_get_ndn(cbdata->e)); + return TRUE; + } + + /* For every directory entry to which this directory entry refers and + * which also has a corresponding entry in this map, update it. */ + inref_attrs = map_data->inref_attrs; + for (i = 0; (inref_attrs != NULL) && (inref_attrs[i] != NULL); i++) { + /* We're only processing inref attributes for this map. */ + if ((strcmp(inref_attrs[i]->domain, domain) != 0) || + (strcmp(inref_attrs[i]->map, map) != 0)) { + continue; + } + /* Extract the named attribute from the entry. */ + values = NULL; + if (slapi_vattr_values_get(cbdata->e, + inref_attrs[i]->attribute, + &values, &disposition, &actual_attr, + 0, &buffer_flags) != 0) { + continue; + } + /* For each value of this attributes.. */ + for (j = slapi_valueset_first_value(values, &value); + j != -1; + j = slapi_valueset_next_value(values, j, &value)) { + /* Pull out the value, which is a referred-to entry's + * DN. */ + dn = slapi_value_get_string(value); + if (dn == NULL) { + continue; + } + /* Normalize the DN. */ + slapi_sdn_set_dn_byref(referred_to_sdn, dn); + ndn = slapi_sdn_get_ndn(referred_to_sdn); + /* If the named entry corresponds to an entry that's + * already in this map. */ + if (map_data_check_entry(state, domain, map, ndn)) { + /* ...update it. */ + backend_map_config_entry_set_one_dn(state, ndn, + map_data); + } + } + slapi_vattr_values_free(&values, &actual_attr, + buffer_flags); + } + slapi_sdn_free(&referred_to_sdn); + return TRUE; +} + +static void +backend_update_references(struct plugin_state *state, Slapi_Entry *e) +{ + struct backend_update_references_cbdata cbdata; + cbdata.e = e; + cbdata.pb = slapi_pblock_new(); + if (!map_data_foreach_map(state, NULL, + backend_update_references_cb, &cbdata)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error updating references for \"%s\"\n", + slapi_entry_get_ndn(cbdata.e)); + } + slapi_pblock_destroy(cbdata.pb); +} + +/* Add any map entries which correspond to a directory server entry in this + * map. */ + +struct backend_add_entry_cbdata { + struct plugin_state *state; + Slapi_PBlock *pb; + Slapi_Entry *e; + char *ndn; +}; + +static bool_t +backend_add_entry_cb(const char *domain, const char *map, bool_t secure, + void *backend_data, void *cbdata_ptr) +{ + struct backend_map_data *map_data; + struct backend_add_entry_cbdata *cbdata; + + map_data = backend_data; + cbdata = cbdata_ptr; + + /* If the entry doesn't match the map, skip it. */ + if (!backend_entry_matches_map(map_data, cbdata->pb, cbdata->e)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata->state->plugin_desc->spd_id, + "entry \"%s\" does not belong in " + "\"%s\"/\"%s\"\n", + cbdata->ndn, domain, map); + return TRUE; + } + + /* Set the entry in the map which corresponds to this entry, or clear + * any that might if this entry doesn't have a key and value. */ + backend_map_config_entry_set_one(cbdata->e, map_data); + + return TRUE; +} + +static int +backend_add_cb(Slapi_PBlock *pb) +{ + struct backend_add_entry_cbdata cbdata; + + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); + slapi_pblock_get(pb, SLAPI_ADD_TARGET, &cbdata.ndn); + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &cbdata.e); + cbdata.pb = pb; + slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, + "added \"%s\"\n", cbdata.ndn); + + /* Check for NULL entries, indicative of a failure elsewhere (?). */ + if (cbdata.e == NULL) { + slapi_pblock_get(pb, SLAPI_ADD_EXISTING_DN_ENTRY, &cbdata.e); + if (cbdata.e == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "added entry is NULL\n"); + return 0; + } + } + + /* Add map entries which corresponded to this directory server + * entry. */ + map_wrlock(); + if (!map_data_foreach_map(cbdata.state, NULL, + backend_add_entry_cb, &cbdata)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "error adding map entries corresponding to " + "\"%s\"\n", cbdata.ndn); + } + + /* If it's a map configuration entry, add and populate the maps it + * describes. */ + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "new entry \"%s\" is a map\n", cbdata.ndn); + backend_map_config_entry_add_cb(cbdata.e, cbdata.state); + } + + /* Update entries which need to be updated in case this new entry + * refers to them. */ + backend_update_references(cbdata.state, cbdata.e); + + map_unlock(); + return 0; +} + +struct backend_modify_entry_cbdata { + struct plugin_state *state; + Slapi_PBlock *pb; + LDAPMod **mods; + Slapi_Entry *e_pre, *e_post; + char *ndn; +}; + +static bool_t +backend_modify_entry_cb(const char *domain, const char *map, bool_t secure, + void *backend_data, void *cbdata_ptr) +{ + struct backend_map_data *map_data; + struct backend_modify_entry_cbdata *cbdata; + + map_data = backend_data; + cbdata = cbdata_ptr; + + /* If the entry used to match the map, remove it. */ + if (backend_entry_matches_map(map_data, cbdata->pb, cbdata->e_pre)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata->state->plugin_desc->spd_id, + "clearing group/container/id " + "\"%s\"/\"%s\"/(\"%s\")\n", + map_data->group, map_data->container, + cbdata->ndn); + map_data_unset_entry_id(cbdata->state, + map_data->group, map_data->container, + cbdata->ndn); + } + /* If the entry now matches the map, add it (or re-add it). */ + if (backend_entry_matches_map(map_data, cbdata->pb, cbdata->e_post)) { + /* Set the entry in the map which corresponds to this entry, or + * clear any that might if this entry doesn't have a key and + * value. */ + backend_map_config_entry_set_one(cbdata->e_post, map_data); + } + return TRUE; +} + +static int +backend_modify_cb(Slapi_PBlock *pb) +{ + Slapi_DN *sdn; + struct backend_modify_entry_cbdata cbdata; + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); + slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &cbdata.ndn); + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &cbdata.mods); + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &cbdata.e_pre); + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &cbdata.e_post); + cbdata.pb = pb; + slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, + "modified \"%s\"\n", cbdata.ndn); + /* Check for NULL entries, indicative of a failure elsewhere (?). */ + if (cbdata.e_pre == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "pre-modify entry is NULL\n"); + return 0; + } + if (cbdata.e_post == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "post-modify entry is NULL\n"); + return 0; + } + /* Modify map entries which corresponded to this directory server + * entry. */ + map_wrlock(); + if (!map_data_foreach_map(cbdata.state, NULL, + backend_modify_entry_cb, &cbdata)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "error modifying map entries corresponding to " + "\"%s\"\n", cbdata.ndn); + } + /* Update entries which need to be updated in case this entry + * no longer refers to them. */ + backend_update_references(cbdata.state, cbdata.e_pre); + /* Update entries which need to be updated in case this entry + * now refers to them. */ + backend_update_references(cbdata.state, cbdata.e_post); + /* If it's a map configuration entry, reconfigure, clear, and + * repopulate the map. */ + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e_pre)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "modified entry \"%s\" was a map\n", + cbdata.ndn); + backend_map_config_entry_delete_cb(cbdata.e_pre, cbdata.state); + } + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e_post)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "modified entry \"%s\" is now a map\n", + cbdata.ndn); + backend_map_config_entry_add_cb(cbdata.e_post, cbdata.state); + } + map_unlock(); + return 0; +} + +struct backend_modrdn_entry_cbdata { + struct plugin_state *state; + Slapi_PBlock *pb; + Slapi_Entry *e_pre, *e_post; + char *ndn_pre, *ndn_post; +}; + +static bool_t +backend_modrdn_entry_cb(const char *domain, const char *map, bool_t secure, + void *backend_data, void *cbdata_ptr) +{ + struct backend_map_data *map_data; + struct backend_modrdn_entry_cbdata *cbdata; + bool_t matched_pre, matched_post; + + map_data = backend_data; + cbdata = cbdata_ptr; + + matched_pre = backend_entry_matches_map(map_data, + cbdata->pb, cbdata->e_pre); + matched_post = backend_entry_matches_map(map_data, + cbdata->pb, cbdata->e_post); + + /* Now decide what to set, or unset, in this map. */ + if (matched_pre) { + /* If it was a match for the map, clear the entry. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata->state->plugin_desc->spd_id, + "clearing group/container/id " + "\"%s\"/\"%s\"/(\"%s\")\n", + map_data->group, map_data->container, + cbdata->ndn_pre); + map_data_unset_entry_id(cbdata->state, + map_data->group, map_data->container, + cbdata->ndn_pre); + } + /* Set the entry in the map which corresponds to this entry, or clear + * any that might if this entry doesn't have a key and value. */ + if (matched_post) { + backend_map_config_entry_set_one(cbdata->e_post, map_data); + } + return TRUE; +} + +static int +backend_modrdn_cb(Slapi_PBlock *pb) +{ + struct backend_modrdn_entry_cbdata cbdata; + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &cbdata.e_pre); + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &cbdata.e_post); + cbdata.ndn_pre = slapi_entry_get_ndn(cbdata.e_pre); + cbdata.ndn_post = slapi_entry_get_ndn(cbdata.e_post); + cbdata.pb = pb; + slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, + "renamed \"%s\" to \"%s\"\n", + cbdata.ndn_pre, cbdata.ndn_post); + /* Check for NULL entries, indicative of a failure elsewhere (?). */ + if (cbdata.e_pre == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "pre-modrdn entry is NULL\n"); + return 0; + } + if (cbdata.e_post == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "post-modrdn entry is NULL\n"); + return 0; + } + /* Modify map entries which corresponded to this directory server + * entry. */ + map_wrlock(); + if (!map_data_foreach_map(cbdata.state, NULL, + backend_modrdn_entry_cb, &cbdata)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "error renaming map entries corresponding to " + "\"%s\"\n", cbdata.ndn_post); + } + /* If it's a map configuration entry, reconfigure, clear, and + * repopulate the map. */ + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e_pre)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "renamed entry \"%s\" was a map\n", + cbdata.e_pre); + backend_map_config_entry_delete_cb(cbdata.e_pre, cbdata.state); + } + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e_post)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "renamed entry \"%s\" is now a map\n", + cbdata.e_post); + backend_map_config_entry_add_cb(cbdata.e_post, cbdata.state); + } + map_unlock(); + return 0; +} + +/* Delete any map entries which correspond to a directory server entry in this + * map. */ + +struct backend_delete_entry_cbdata { + struct plugin_state *state; + Slapi_PBlock *pb; + Slapi_Entry *e; + char *ndn; +}; + +static bool_t +backend_delete_entry_cb(const char *group, const char *container, bool_t secure, + void *backend_data, void *cbdata_ptr) +{ + struct backend_map_data *map_data; + struct backend_delete_entry_cbdata *cbdata; + map_data = backend_data; + cbdata = cbdata_ptr; + /* If it was in the map, remove it. */ + if (backend_entry_matches_map(map_data, cbdata->pb, cbdata->e)) { + /* Remove this entry from the map. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata->state->plugin_desc->spd_id, + "unsetting group/container/id" + "\"%s\"/\"%s\"=\"%s\"/\"%s\"/(\"%s\")\n", + group, container, + map_data->group, map_data->container, + cbdata->ndn); + map_data_unset_entry_id(cbdata->state, group, container, + cbdata->ndn); + } + return TRUE; +} + +/* Called by the server when a directory server entry is deleted. */ +static int +backend_delete_cb(Slapi_PBlock *pb) +{ + struct backend_delete_entry_cbdata cbdata; + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); + slapi_pblock_get(pb, SLAPI_DELETE_TARGET, &cbdata.ndn); + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &cbdata.e); + cbdata.pb = pb; + slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, + "deleted \"%s\"\n", cbdata.ndn); + /* Check for NULL entries, indicative of a failure elsewhere (?). */ + if (cbdata.e == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "deleted entry is NULL\n"); + return 0; + } + /* Remove map entries which corresponded to this directory server + * entry. */ + map_wrlock(); + if (!map_data_foreach_map(cbdata.state, NULL, + backend_delete_entry_cb, &cbdata)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "error removing map entries corresponding to " + "\"%s\"\n", cbdata.ndn); + } + /* If it's a map configuration entry, remove the map. */ + if (backend_entry_is_a_map(cbdata.state, pb, cbdata.e)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata.state->plugin_desc->spd_id, + "deleted entry \"%s\" is a map\n", cbdata.ndn); + backend_map_config_entry_delete_cb(cbdata.e, cbdata.state); + } + /* Update entries which need to be updated in case this entry no longer + * refers to them. */ + backend_update_references(cbdata.state, cbdata.e); + map_unlock(); + return 0; +} + +/* Set our post-op callbacks. */ +void +backend_init(Slapi_PBlock *pb, struct plugin_state *state) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "hooking up postoperation callbacks\n"); + if (slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, + backend_add_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_POST_MODIFY_FN, + backend_modify_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_POST_MODRDN_FN, + backend_modrdn_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_POST_DELETE_FN, + backend_delete_cb) != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "error hooking up delete callback\n"); + } +} |