/* * 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 "=*)(" SCH_CONTAINER_CONFIGURATION_ATTR_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. */ }; /* 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); } } 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); if ((ret->common.group == NULL) || (ret->common.set == NULL) || (ret->common.bases == NULL) || (ret->common.entry_filter == 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; 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 = 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); } /* 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; *pret = backend_copy_set_config(&ret); free(ret.common.group); free(ret.common.set); if (ret.common.bases != NULL) { for (i = 0; ret.common.bases[i] != NULL; i++) { free(ret.common.bases[i]); } free(ret.common.bases); } free(ret.common.entry_filter); } /* 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_one(Slapi_Entry *e, struct backend_set_data *data) { char *ldif, *ndn, *plugin_id, *ndns[2]; plugin_id = data->common.state->plugin_desc->spd_id; /* Pull out the NDN of this entry. */ ndn = slapi_entry_get_ndn(e); ndns[0] = ndn; ndns[1] = NULL; /* Generate the LDIF for the entry. */ ldif = format_get_data(data->common.state, e, data->common.group, data->common.set, "", NULL, &data->common.ref_attrs, &data->common.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->common.group, data->common.set, ndn, ndn, ldif); map_data_set_entry(data->common.state, data->common.group, data->common.set, 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->common.group, data->common.set, ndn); map_data_unset_entry_id(data->common.state, data->common.group, data->common.set, ndn); } format_free_data(ldif); } /* 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 = 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_shr_set_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; } /* 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) { backend_shr_free_strlist(bases); free(entry_filter); } static bool_t backend_get_set_config_entry_cb(Slapi_Entry *e, void *callback_data) { Slapi_ValueSet *values; Slapi_Value *value; struct backend_get_set_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_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. */ slapi_pblock_destroy(pb); } /* Given an entry, return true if it describes a compatibility container. */ bool_t backend_entry_is_a_set(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. The item should be a direct child of * our plugin entry. */ 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 in 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; } /* Re-read plugin-wide settings that may have changed. Nothing to do. */ void backend_update_params(struct plugin_state *state) { } static int backend_search_cb(Slapi_PBlock *pb) { return 0; } /* Populate our data. */ void backend_startup(struct plugin_state *state) { backend_shr_startup(state, SCH_CONTAINER_CONFIGURATION_FILTER); } static void backend_preop_init(Slapi_PBlock *pb, struct plugin_state *state) { 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"); } } static void backend_postop_init(Slapi_PBlock *pb, struct plugin_state *state) { backend_shr_postop_init(pb, state); } /* Set up our callbacks. */ void backend_init(Slapi_PBlock *pb, struct plugin_state *state) { backend_preop_init(pb, state); backend_postop_init(pb, state); }