/* * 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 "back-shr.h" #include "defs-nis.h" #include "disp-nis.h" #include "format.h" #include "plugin.h" #include "map.h" /* The filter which we use to identify map configurations. It lists the * required attributes. */ #define NIS_MAP_CONFIGURATION_FILTER "(&(objectClass=*)(" NIS_MAP_CONFIGURATION_BASE_ATTR "=*)(" NIS_MAP_CONFIGURATION_DOMAIN_ATTR "=*)(" NIS_MAP_CONFIGURATION_MAP_ATTR "=*))" /* The data we ask the map cache to keep, for us, for each map. */ struct backend_set_data { struct backend_shr_set_data common; /* NIS-specific data. */ char **key_formats, **keys_formats, *value_format; int n_key_formats, n_keys_formats; char *disallowed_chars; }; /* Read the name of the NIS master. Used by the map module on behalf of the * NIS service logic. */ void backend_free_master_name(struct plugin_state *state, char *master) { backend_shr_free_server_name(state, master); } int backend_read_master_name(struct plugin_state *state, char **master) { return backend_shr_read_server_name(state, master); } /* Manipulate map configuration data. */ static void backend_free_set_data_contents(void *data) { struct backend_set_data *set_data = data; if (set_data != NULL) { free(set_data->common.group); free(set_data->common.set); backend_shr_free_strlist(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); free(set_data->disallowed_chars); backend_shr_free_strlist(set_data->key_formats); backend_shr_free_strlist(set_data->keys_formats); free(set_data->value_format); } } void backend_set_config_free_config(struct backend_shr_set_data *data) { backend_free_set_data_contents(data->self); free(data); } static struct backend_shr_set_data * backend_copy_set_data(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.entry_filter = strdup(data->common.entry_filter); 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->disallowed_chars = data->disallowed_chars ? strdup(data->disallowed_chars) : NULL; ret->key_formats = backend_shr_dup_strlist(data->key_formats); ret->keys_formats = backend_shr_dup_strlist(data->keys_formats); ret->n_key_formats = data->n_key_formats; ret->n_keys_formats = data->n_keys_formats; ret->value_format = strdup(data->value_format); if ((ret->common.group == NULL) || (ret->common.set == NULL) || (ret->common.bases == NULL) || (ret->common.entry_filter == NULL) || ((ret->key_formats == NULL) && (ret->keys_formats == NULL)) || (ret->value_format == NULL)) { backend_set_config_free_config(&ret->common); return NULL; } return &ret->common; } /* Given a map-entry directory entry, determine which keys it should have, * determine which value should be associated with those keys, and add them to * the map cache. */ void backend_set_entry(Slapi_Entry *e, struct backend_set_data *data) { char **keys, ***key_sets, **all_keys, *value, *ndn, *plugin_id; int i, j, k, n_key_sets; unsigned int *key_lengths, **key_length_sets, *all_key_lengths; unsigned int value_len; plugin_id = data->common.state->plugin_desc->spd_id; /* Pull out the NDN of this entry. */ ndn = slapi_entry_get_ndn(e); /* Pull out the keys and value for the entry. */ if (data->n_key_formats > 0) { keys = malloc((data->n_key_formats + 1) * sizeof(char *)); key_lengths = malloc((data->n_key_formats + 1) * sizeof(unsigned int)); } else { keys = NULL; key_lengths = NULL; } if ((keys != NULL) && (key_lengths != NULL)) { for (i = 0, j = 0; i < data->n_key_formats; i++) { keys[j] = format_get_data(data->common.state, e, data->common.group, data->common.set, data->key_formats[i], data->disallowed_chars, &data->common.ref_attrs, &data->common.inref_attrs, &key_lengths[j]); if ((keys[j] != NULL) && (key_lengths[j] > 0)) { j++; } else { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "no key for %s, " "unsetting domain/map/id" "\"%s\"/\"%s\"/(\"%s\")\n", data->key_formats[i], data->common.group, data->common.set, ndn); map_data_unset_entry(data->common.state, data->common.group, data->common.set, ndn); for (k = 0; k < j; k++) { format_free_data(keys[k]); } free(keys); return; } } keys[j] = NULL; } if (data->n_keys_formats > 0) { key_sets = malloc((data->n_keys_formats + 1) * sizeof(char **)); key_length_sets = malloc((data->n_keys_formats + 1) * sizeof(unsigned int *)); } else { key_sets = NULL; key_length_sets = NULL; } n_key_sets = 0; if (key_sets != NULL) { for (i = 0, j = 0; i < data->n_keys_formats; i++) { key_sets[j] = format_get_data_set(data->common.state, e, data->common.group, data->common.set, data->keys_formats[i], data->disallowed_chars, &data->common.ref_attrs, &data->common.inref_attrs, &key_length_sets[j]); if ((key_sets[j] != NULL) && (key_length_sets[j] != NULL)) { j++; } } key_sets[j] = NULL; key_length_sets[j] = NULL; n_key_sets = j; } value = format_get_data(data->common.state, e, data->common.group, data->common.set, data->value_format, data->disallowed_chars, &data->common.ref_attrs, &data->common.inref_attrs, &value_len); /* Build one big list of all of the keys and key sets. */ k = 0; if (keys != NULL) { for (i = 0; keys[i] != NULL; i++) { continue; } k += i; } if (key_sets != NULL) { for (i = 0; key_sets[i] != NULL; i++) { for (j = 0; key_sets[i][j] != NULL; j++) { continue; } k += j; } } if (k > 0) { all_keys = malloc(sizeof(char *) * (k + 1)); all_key_lengths = malloc(sizeof(unsigned int) * (k + 1)); if ((all_keys != NULL) && (all_key_lengths != NULL)) { k = 0; if (keys != NULL) { for (i = 0; keys[i] != NULL; i++) { all_keys[k] = keys[i]; all_key_lengths[k] = key_lengths[i]; k++; } } if (key_sets != NULL) { for (i = 0; key_sets[i] != NULL; i++) { for (j = 0; key_sets[i][j] != NULL; j++) { all_keys[k] = key_sets[i][j]; all_key_lengths[k] = key_length_sets[i][j]; k++; } } } all_keys[k] = NULL; all_key_lengths[k] = 0; } } else { all_keys = NULL; all_key_lengths = NULL; } /* If we actually generated a value, then set it, otherwise clear it in * case there was one set before. */ if ((all_keys != NULL) && (value != NULL)) { for (i = 0; all_keys[i] != NULL; i++) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "setting domain/map/key/value " "\"%s\"/\"%s\"/\"%s\"(\"%s\")=" "\"%.*s\"\n", data->common.group, data->common.set, all_keys[i], ndn, value_len, value); } map_data_set_entry(data->common.state, data->common.group, data->common.set, ndn, all_key_lengths, all_keys, value_len, value, 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(data->common.state, data->common.group, data->common.set, ndn); } format_free_data(value); free(all_key_lengths); free(all_keys); if (key_sets != NULL) { for (i = 0; key_sets[i] != NULL; i++) { format_free_data_set(key_sets[i], key_length_sets[i]); } free(key_sets); free(key_length_sets); } if (keys != NULL) { for (i = 0; i < data->n_key_formats; i++) { format_free_data(keys[i]); } free(keys); free(key_lengths); } } /* * Generate a copy of the filter string, with specific sequences replaced: * %d -> name of the domain * %m -> name of the map * %% -> literal '%' */ static char * backend_map_config_filter(const char *format, const char *domain, const char *map) { char *ret; int i, j, l; l = 0; for (i = 0; format[i] != '\0'; i++) { if (format[i] == '%') { switch (format[i + 1]) { case 'd': l += strlen(domain); i++; break; case 'm': l += strlen(map); i++; break; case '%': l++; i++; break; default: l++; break; } } else { l++; } } ret = malloc(l + 1); for (i = j = 0; format[i] != '\0'; i++) { if (format[i] == '%') { switch (format[i + 1]) { case 'd': strcpy(ret + j, domain); i++; j += strlen(domain); break; case 'm': strcpy(ret + j, map); i++; j += strlen(map); break; case '%': i++; ret[j++] = format[i]; break; default: ret[j++] = format[i]; break; } } else { ret[j++] = format[i]; } } ret[j] = '\0'; return ret; } /* Given a map configuration entry and domain and map names, read the rest of * the map configuration settings. */ void backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e, const char *domain, const char *map, bool_t *secure, struct backend_shr_set_data **pret) { struct backend_set_data ret; const char *default_filter, *default_key_format, *default_keys_format; const char *default_value_format, *default_disallowed_chars; char **bases, *entry_filter; char **key_formats, **keys_formats, *value_format; char *disallowed_chars; char **use_bases, *use_entry_filter; char **use_key_formats, **use_keys_formats; char *use_value_format, *use_disallowed_chars; int i; /* Read the hard-coded defaults for a map with this name. */ defaults_get_map_config(map, secure, &default_filter, &default_key_format, &default_keys_format, &default_value_format, &default_disallowed_chars); /* Read the values from the configuration entry. */ bases = backend_shr_get_vattr_strlist(state, e, NIS_MAP_CONFIGURATION_BASE_ATTR); entry_filter = backend_shr_get_vattr_filter(state, e, NIS_MAP_CONFIGURATION_FILTER_ATTR); key_formats = backend_shr_get_vattr_strlist(state, e, NIS_MAP_CONFIGURATION_KEY_ATTR); keys_formats = backend_shr_get_vattr_strlist(state, e, NIS_MAP_CONFIGURATION_KEYS_ATTR); value_format = backend_shr_get_vattr_str(state, e, NIS_MAP_CONFIGURATION_VALUE_ATTR); disallowed_chars = backend_shr_get_vattr_str(state, e, NIS_MAP_CONFIGURATION_DISALLOWED_CHARS_ATTR); *secure = backend_shr_get_vattr_boolean(state, e, NIS_MAP_CONFIGURATION_SECURE_ATTR, FALSE); /* Build a filter, using either the configured value or the default as * a template, we need to do this because RFC2307bis sometimes stores * the map name in each entry, so it's useful to be able to filter on * it. */ use_entry_filter = backend_map_config_filter(entry_filter ? entry_filter : default_filter, domain, map); use_key_formats = key_formats ? backend_shr_dup_strlist_unless_empty(key_formats) : default_key_format ? backend_shr_dup_strlist_n((char **) &default_key_format, 1) : NULL; use_keys_formats = keys_formats ? backend_shr_dup_strlist_unless_empty(keys_formats) : default_keys_format ? backend_shr_dup_strlist_n((char **) &default_keys_format, 1) : NULL; use_value_format = value_format ? strdup(value_format) : strdup(default_value_format); use_bases = backend_shr_dup_strlist(bases); use_disallowed_chars = disallowed_chars ? strdup(disallowed_chars) : (default_disallowed_chars ? strdup(default_disallowed_chars) : NULL); /* Free the values we read from the entry. */ free(disallowed_chars); free(value_format); backend_shr_free_strlist(key_formats); backend_shr_free_strlist(keys_formats); free(entry_filter); backend_shr_free_strlist(bases); /* Populate the returned structure. */ ret.common.state = state; ret.common.group = strdup(domain); ret.common.set = strdup(map); ret.common.bases = use_bases; ret.common.entry_filter = use_entry_filter; ret.common.ref_attrs = NULL; ret.common.inref_attrs = NULL; ret.disallowed_chars = use_disallowed_chars; ret.key_formats = use_key_formats; ret.n_key_formats = 0; ret.keys_formats = use_keys_formats; ret.n_keys_formats = 0; ret.value_format = use_value_format; for (i = 0; (use_key_formats != NULL) && (use_key_formats[i] != NULL); i++) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "initializing map %s in %s (3): " "filter \"%s\", " "key \"%s\", " "value \"%s\"\n", map, domain, use_entry_filter, use_key_formats[i], use_value_format); ret.n_key_formats++; } for (i = 0; (use_keys_formats != NULL) && (use_keys_formats[i] != NULL); i++) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "initializing map %s in %s (3): " "filter \"%s\", " "keys \"%s\", " "value \"%s\"\n", map, domain, use_entry_filter, use_keys_formats[i], use_value_format); ret.n_keys_formats++; } *pret = backend_copy_set_data(&ret); free(ret.common.group); free(ret.common.set); backend_shr_free_strlist(ret.common.bases); free(ret.disallowed_chars); free(ret.common.entry_filter); backend_shr_free_strlist(ret.key_formats); backend_shr_free_strlist(ret.keys_formats); free(ret.value_format); } /* Process a map configuration directory entry. Pull out the domain and map * names which are valid for this configuration and configure such a map for * each in turn. */ int backend_set_config_entry_add_cb(Slapi_Entry *e, void *callback_data) { char **domains, **maps; int i, j, ret; struct plugin_state *state; state = callback_data; domains = backend_shr_get_vattr_strlist(state, e, NIS_MAP_CONFIGURATION_DOMAIN_ATTR); maps = backend_shr_get_vattr_strlist(state, e, NIS_MAP_CONFIGURATION_MAP_ATTR); for (i = 0; (domains != NULL) && (domains[i] != NULL); i++) { for (j = 0; (maps != NULL) && (maps[j] != NULL); j++) { ret = backend_shr_set_config_entry_add(state, e, domains[i], maps[j]); } } backend_shr_free_strlist(maps); backend_shr_free_strlist(domains); return 0; } /* Update/initialize parameters stored in the plugin's configuration entry. */ void backend_update_params(struct plugin_state *state) { Slapi_DN *our_dn; Slapi_Entry *our_entry; char *tmp, **tmpv; int i; our_dn = slapi_sdn_new_dn_byval(state->plugin_base); if (our_dn == NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "backend_update_params: " "error parsing %s%s%s\n", state->plugin_base ? "\"" : "", state->plugin_base ? state->plugin_base : "NULL", state->plugin_base ? "\"" : ""); return; } slapi_search_internal_get_entry(our_dn, NULL, &our_entry, state->plugin_identity); slapi_sdn_free(&our_dn); our_dn = NULL; if (our_entry == NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "backend_update_params: failure reading entry " "\"%s\"\n", state->plugin_base); return; } /* Pull out the attribute values. */ state->max_value_size = backend_shr_get_vattr_uint(state, our_entry, NIS_PLUGIN_CONFIGURATION_MAXVALUE_ATTR, DEFAULT_MAX_VALUE_SIZE); state->max_dgram_size = backend_shr_get_vattr_uint(state, our_entry, NIS_PLUGIN_CONFIGURATION_MAXDGRAM_ATTR, DEFAULT_MAX_DGRAM_SIZE); tmpv = backend_shr_get_vattr_strlist(state, our_entry, NIS_PLUGIN_CONFIGURATION_SECURENET_ATTR); dispatch_securenets_clear(state); if (tmpv != NULL) { for (i = 0; tmpv[i] != NULL; i++) { dispatch_securenets_add(state, tmpv[i]); } backend_shr_free_strlist(tmpv); } #ifdef HAVE_TCPD_H tmp = backend_shr_get_vattr_str(state, our_entry, NIS_PLUGIN_CONFIGURATION_TCPWRAPNAME_ATTR); if (tmp != NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "setting tcp_wrappers context at %p's " "name to \"%s\"\n", state->request_info, tmp); request_set(state->request_info, RQ_DAEMON, tmp); free(tmp); } else { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "(re)setting tcp_wrappers context at %p's " "name to \"%s\"\n", state->request_info, DEFAULT_TCPWRAP_NAME); request_set(state->request_info, RQ_DAEMON, DEFAULT_TCPWRAP_NAME); } #endif slapi_entry_free(our_entry); } /* Process a map 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, NIS_MAP_CONFIGURATION_DOMAIN_ATTR, NIS_MAP_CONFIGURATION_MAP_ATTR); } /* Read enough of the map configuration for the formatting code to be able to * resolver references correctly. */ 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) { struct backend_get_set_config_cb *cbdata; cbdata = callback_data; slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "reading map configuration from \"%s\"\n", slapi_entry_get_ndn(e)); cbdata->bases = backend_shr_get_vattr_strlist(cbdata->state, e, NIS_MAP_CONFIGURATION_BASE_ATTR); cbdata->entry_filter = backend_shr_get_vattr_filter(cbdata->state, e, NIS_MAP_CONFIGURATION_FILTER_ATTR); return TRUE; } void backend_get_set_config(struct plugin_state *state, const char *domain, const char *map, char ***bases, char **entry_filter) { Slapi_PBlock *pb; char *filter; char *attrs[] = {NIS_MAP_CONFIGURATION_FILTER_ATTR, NIS_MAP_CONFIGURATION_BASE_ATTR, NULL}; const char *default_filter; bool_t map_secure; struct backend_get_set_config_cb cbdata; /* Build the search filter. */ filter = malloc(strlen("(&(" NIS_MAP_CONFIGURATION_DOMAIN_ATTR "=)(" NIS_MAP_CONFIGURATION_MAP_ATTR "=)(" NIS_MAP_CONFIGURATION_BASE_ATTR "=*))") + strlen(domain) + strlen(map) + strlen(NIS_MAP_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", domain, map); return; } sprintf(filter, "(&(" NIS_MAP_CONFIGURATION_DOMAIN_ATTR "=%s)(" NIS_MAP_CONFIGURATION_MAP_ATTR "=%s)(" NIS_MAP_CONFIGURATION_BASE_ATTR "=*)%s)", domain, map, NIS_MAP_CONFIGURATION_FILTER); /* Perform the search. */ pb = slapi_pblock_new(); slapi_search_internal_set_pb(pb, state->plugin_base, LDAP_SCOPE_SUBTREE, filter, attrs, FALSE, NULL, NULL, state->plugin_identity, 0); cbdata.bases = NULL; cbdata.entry_filter = NULL; cbdata.state = state; map_secure = FALSE; slapi_search_internal_callback_pb(pb, &cbdata, NULL, backend_get_set_config_entry_cb, NULL); defaults_get_map_config(map, &map_secure, &default_filter, NULL, NULL, NULL, NULL); if (cbdata.entry_filter == NULL) { cbdata.entry_filter = strdup(default_filter); } /* Return the results. */ *bases = cbdata.bases; *entry_filter = backend_map_config_filter(cbdata.entry_filter, domain, map); free(cbdata.entry_filter); /* Clean up. */ slapi_pblock_destroy(pb); free(filter); } /* 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 NIS_MAP_CONFIGURATION_FILTER; } /* Scan for the list of configured domains and maps. */ void backend_startup(struct plugin_state *state) { backend_shr_startup(state, NIS_MAP_CONFIGURATION_FILTER); } /* Set up 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"); backend_shr_postop_init(pb, state); }