From eb2613bd20ede10a567b263c3dfa843ee1578e35 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Wed, 31 Jul 2013 14:38:37 +0300 Subject: back-sch.c: search users and groups through NSSWITCH Schema-compat plugin can be configured to serve users and groups through the plugin configuration entry in directory server: schema-compat-lookup-nsswitch: schema-compat-nsswitch-min-id: Separate trees should be configured to look up users and groups. If minimal id value is missing, it will default to 1000. --- src/back-sch.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 124 insertions(+), 6 deletions(-) diff --git a/src/back-sch.c b/src/back-sch.c index 8911568..2bdf631 100644 --- a/src/back-sch.c +++ b/src/back-sch.c @@ -1004,11 +1004,17 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, struct backend_set_data *set_data; Slapi_Entry *set_entry; int result, n_entries; + int n_entries_nsswitch; const char *ndn; cbdata = cb_data; set_data = backend_data; cbdata->check_access = set_data->check_access; + cbdata->check_nsswitch = set_data->check_nsswitch; + cbdata->nsswitch_min_id = set_data->nsswitch_min_id; + /* If any entries were actually returned by the descending callback, + * avoid to look up in nsswitch even if this set is marked to look up */ + n_entries_nsswitch = cbdata->n_entries; /* Check the set itself, unless it's also the group, in which case we * already evaluated it for this search. */ @@ -1063,6 +1069,19 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, cbdata->scope)) { map_data_foreach_entry_id(cbdata->state, group, set, NULL, backend_search_entry_cb, cbdata); +#ifdef USE_NSSWITCH + /* If we didn't find an exact match for the entry but asked to look up NSSWITCH, + * then try to search NSSWITCH. If search filters would match, the search will be + * staged for retrieval. The retrieval process is run after lookup is completed + * for all maps as we need to ensure there is no contention for the global + * map cache lock. The contention might occur if NSSWITCH would need to re-kinit + * its kerberos credentials -- this would cause changes in the original LDAP tree + * which, in turn, will trigger modification of the map cache entries. */ + if ((n_entries_nsswitch == cbdata->n_entries) && + (cbdata->check_nsswitch != SCH_NSSWITCH_NONE)) { + backend_search_nsswitch(set_data, cbdata); + } +#endif } /* If we didn't find an exact match for the entry, then store this @@ -1077,6 +1096,36 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, return TRUE; } +static bool_t +backend_search_find_set_data_in_group_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; + + cbdata = cb_data; + set_data = backend_data; + + if ((0 == strcmp(group, cbdata->cur_staged->map_group)) && + (0 == strcmp(set, cbdata->cur_staged->map_set))) { + cbdata->cur_staged->set_data_fixup = set_data; + } + + return TRUE; + +} + +static bool_t +backend_search_find_set_data_cb(const char *group, void *cb_data) +{ + struct backend_search_cbdata *cbdata; + + cbdata = cb_data; + map_data_foreach_map(cbdata->state, group, + backend_search_find_set_data_in_group_cb, cbdata); + return TRUE; +} + static bool_t backend_search_group_cb(const char *group, void *cb_data) { @@ -1180,6 +1229,9 @@ static int backend_search_cb(Slapi_PBlock *pb) { struct backend_search_cbdata cbdata; + struct backend_staged_data *staged, *next; + int i; + if (wrap_get_call_level() > 0) { return 0; } @@ -1204,6 +1256,8 @@ backend_search_cb(Slapi_PBlock *pb) cbdata.closest_match = NULL; cbdata.text = NULL; cbdata.n_entries = 0; + cbdata.staged = NULL; + cbdata.cur_staged = NULL; /* Okay, we can search. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, "searching from \"%s\" for \"%s\" with scope %d%s\n", @@ -1225,14 +1279,72 @@ backend_search_cb(Slapi_PBlock *pb) map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata); map_unlock(); wrap_dec_call_level(); +#ifdef USE_NSSWITCH + /* If during search of some sets we staged additional lookups, perform them. */ + if (cbdata.staged != NULL) { + /* Allocate buffer to be used for getpwnam_r/getgrnam_r requests */ + cbdata.nsswitch_buffer_len = MAX(sysconf(_SC_GETPW_R_SIZE_MAX), sysconf(_SC_GETGR_R_SIZE_MAX)); + if (cbdata.nsswitch_buffer_len == -1) + cbdata.nsswitch_buffer_len = 16384; + cbdata.nsswitch_buffer = malloc(cbdata.nsswitch_buffer_len); + /* Go over the list of staged requests and retrieve entries. + * It is important to perform the retrieval *without* holding any locks to the map cache */ + staged = cbdata.staged; + while (staged) { + if (staged->entries == NULL) { + backend_retrieve_from_nsswitch(staged, &cbdata); + } + next = staged->next; + staged = next; + } + staged = cbdata.staged; + /* Add the entries to the map cache */ + wrap_inc_call_level(); + map_wrlock(); + while (staged) { + if (staged->entries) { + cbdata.cur_staged = staged; + /* We actually need to find the original set first */ + map_data_foreach_domain(cbdata.state, backend_search_find_set_data_cb, &cbdata); + if (cbdata.cur_staged->set_data_fixup != NULL) { + for (i = 0; i < staged->count ; i++) { + if (staged->entries[i] != NULL) { + if (!map_data_check_entry(cbdata.state, + staged->map_group, staged->map_set, + slapi_sdn_get_ndn(staged->entries[i]))) { + backend_set_entry(cbdata.pb, staged->entries[i], staged->set_data_fixup); + } + slapi_entry_free(staged->entries[i]); + staged->entries[i] = NULL; + } + } + } + free(staged->entries); + staged->count = 0; + staged->entries = NULL; + } + slapi_ch_free_string(&staged->map_group); + slapi_ch_free_string(&staged->map_set); + slapi_ch_free_string(&staged->name); + slapi_ch_free_string(&staged->container_sdn); + next = staged->next; + free(staged); + staged = next; + } + cbdata.staged = NULL; + map_unlock(); + cbdata.nsswitch_buffer_len = 0; + free(cbdata.nsswitch_buffer); + /* Perform search again, this time to collect the data added by the NSSWITCH search */ + map_rdlock(); + map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata); + map_unlock(); + wrap_dec_call_level(); + } +#endif /* If we "own" the search target DN, then we need to send a response. */ if (cbdata.answer) { - if (cbdata.matched || (cbdata.n_entries > 0)) { - /* Just in case, free the closest-match that we've - * recorded. */ - free(cbdata.closest_match); - cbdata.closest_match = NULL; - } else { + if (!(cbdata.matched || (cbdata.n_entries > 0))) { /* Return a no-such-object error because the target DN * was not found. */ cbdata.result = LDAP_NO_SUCH_OBJECT; @@ -1245,6 +1357,12 @@ backend_search_cb(Slapi_PBlock *pb) cbdata.closest_match ? cbdata.closest_match : "(null)", cbdata.closest_match ? "\"" : ""); + if (cbdata.matched || (cbdata.n_entries > 0)) { + /* Just in case, free the closest-match that we've + * recorded. */ + free(cbdata.closest_match); + cbdata.closest_match = NULL; + } slapi_pblock_set(cbdata.pb, SLAPI_PLUGIN_OPRETURN, &cbdata.result); /* XXX - THIS IS NOT A PUBLIC FUNCTION, but -- cgit