/* * Copyright 2013 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 #include #include #include #include #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include #include #include #else #include #endif #include #ifdef HAVE_SSS_NSS_IDMAP #include #endif #include "backend.h" #include "back-shr.h" #include "plugin.h" #include "map.h" #include "back-sch.h" #include "format.h" struct backend_search_filter_config { bool_t search_user; bool_t search_group; bool_t search_uid; bool_t search_gid; bool_t search_members; bool_t name_set; bool_t wrong_search; char *name; }; /* Check simple filter to see if it has * (cn|uid|uidNumber|gidNumber|memberUid=) or (objectClass=posixGroup|shadowAccount) * Called by slapi_filter_apply(). */ static int backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg) { struct backend_search_filter_config *config = arg; struct berval *bval; char *filter_type; int f_choice, rc; f_choice = slapi_filter_get_choice(filter); rc = slapi_filter_get_ava(filter, &filter_type, &bval); if ((LDAP_FILTER_EQUALITY == f_choice) && (0 == rc)) { if (0 == strcasecmp(filter_type, "uidNumber")) { config->search_uid = TRUE; config->name_set = TRUE; } else if (0 == strcasecmp(filter_type, "gidNumber")) { config->search_gid = TRUE; config->name_set = TRUE; } else if (0 == strcasecmp(filter_type, "uid")) { config->search_user = TRUE; config->name_set = TRUE; } else if (0 == strcasecmp(filter_type, "cn")) { config->name_set = TRUE; } else if (0 == strcasecmp(filter_type, "memberUid")) { config->name_set = TRUE; config->search_members = TRUE; } else if ((0 == strcasecmp(filter_type, "objectClass")) && (0 == strncasecmp(bval->bv_val, "posixGroup", bval->bv_len))) { config->search_group = TRUE; } else if ((0 == strcasecmp(filter_type, "objectClass")) && (0 == strncasecmp(bval->bv_val, "shadowAccount", bval->bv_len))) { config->wrong_search = TRUE; } if ((NULL == config->name) && config->name_set) { config->name = slapi_ch_malloc(bval->bv_len + 1); if (config->name != NULL) { memcpy(config->name, bval->bv_val, bval->bv_len); config->name[bval->bv_len] = '\0'; } } } if ((config->search_uid || config->search_gid || config->search_user || config->search_group) && (config->name != NULL)) { return SLAPI_FILTER_SCAN_STOP; } return SLAPI_FILTER_SCAN_CONTINUE; } static Slapi_Entry * backend_retrieve_user_entry_from_nsswitch(char *user_name, bool_t is_uid, char *container_sdn, struct backend_search_cbdata *cbdata) { struct passwd pwd, *result; Slapi_Entry *entry; int rc; char *name; char *dn = NULL; char *buf = NULL; #ifdef HAVE_SSS_NSS_IDMAP enum sss_id_type id_type; char *sid_str; #endif repeat: if (cbdata->nsswitch_buffer == NULL) { return NULL; } if (is_uid) { rc = getpwuid_r((uid_t) atoll(user_name), &pwd, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &result); } else { rc = getpwnam_r(user_name, &pwd, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &result); } if ((result == NULL) || (rc != 0)) { if (rc == ERANGE) { buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2); if (buf != NULL) { cbdata->nsswitch_buffer = buf; goto repeat; } } return NULL; } if (pwd.pw_uid < cbdata->nsswitch_min_id) { return NULL; } entry = slapi_entry_alloc(); if (entry == NULL) { return NULL; } name = slapi_escape_filter_value(pwd.pw_name, -1); if (name != NULL) { dn = slapi_ch_smprintf("uid=%s,%s", name, container_sdn); slapi_ch_free_string(&name); } if (dn == NULL) { slapi_entry_free(entry); return NULL; } slapi_entry_add_string(entry, "objectClass", "top"); slapi_entry_add_string(entry, "objectClass", "posixAccount"); slapi_entry_add_string(entry, "objectClass", "extensibleObject"); slapi_entry_add_string(entry, "uid", pwd.pw_name); slapi_entry_attr_set_uint(entry, "uidNumber", pwd.pw_uid); slapi_entry_attr_set_uint(entry, "gidNumber", pwd.pw_gid); slapi_entry_add_string(entry, "gecos", pwd.pw_gecos); if (strlen(pwd.pw_gecos) > 0) { slapi_entry_add_string(entry, "cn", pwd.pw_gecos); } else { slapi_entry_add_string(entry, "cn", pwd.pw_name); } slapi_entry_add_string(entry, "homeDirectory", pwd.pw_dir); if ((pwd.pw_shell != NULL) && (strlen(pwd.pw_shell) > 0)) { slapi_entry_add_string(entry, "loginShell", pwd.pw_shell); } slapi_entry_set_dn(entry, dn); #ifdef HAVE_SSS_NSS_IDMAP rc = sss_nss_getsidbyid(pwd.pw_uid, &sid_str, &id_type); if ((rc == 0) && (sid_str != NULL)) { slapi_entry_add_string(entry, "ipaNTSecurityIdentifier", sid_str); free(sid_str); } #endif return entry; } static Slapi_Entry * backend_retrieve_group_entry_from_nsswitch_helper(struct group *grp, char *container_sdn) { Slapi_Entry *entry; int rc, i; char *name; char *dn = NULL; #ifdef HAVE_SSS_NSS_IDMAP enum sss_id_type id_type; char *sid_str; #endif entry = slapi_entry_alloc(); if (entry == NULL) { return NULL; } name = slapi_escape_filter_value(grp->gr_name, -1); if (name != NULL) { dn = slapi_ch_smprintf("cn=%s,%s", name, container_sdn); slapi_ch_free_string(&name); } if (dn == NULL) { slapi_entry_free(entry); return NULL; } slapi_entry_add_string(entry, "objectClass", "top"); slapi_entry_add_string(entry, "objectClass", "posixGroup"); slapi_entry_add_string(entry, "objectClass", "extensibleObject"); slapi_entry_add_string(entry, "cn", grp->gr_name); slapi_entry_attr_set_uint(entry, "gidNumber", grp->gr_gid); if (grp->gr_mem) { for (i=0; grp->gr_mem[i]; i++) { slapi_entry_add_string(entry, "memberUid", grp->gr_mem[i]); } } slapi_entry_set_dn(entry, dn); #ifdef HAVE_SSS_NSS_IDMAP rc = sss_nss_getsidbyid(grp->gr_gid, &sid_str, &id_type); if ((rc == 0) && (sid_str != NULL)) { slapi_entry_add_string(entry, "ipaNTSecurityIdentifier", sid_str); free(sid_str); } #endif return entry; } static Slapi_Entry * backend_retrieve_group_entry_from_nsswitch(char *group_name, bool_t is_gid, char *container_sdn, struct backend_search_cbdata *cbdata) { struct group grp, *result; Slapi_Entry *entry; int rc; char *buf = NULL; repeat: if (cbdata->nsswitch_buffer == NULL) { return NULL; } if (is_gid) { rc = getgrgid_r((gid_t) atoll(group_name), &grp, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &result); } else { rc = getgrnam_r(group_name, &grp, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &result); } if ((result == NULL) || (rc != 0)) { if (rc == ERANGE) { buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2); if (buf != NULL) { cbdata->nsswitch_buffer = buf; goto repeat; } } return NULL; } if (grp.gr_gid < cbdata->nsswitch_min_id) { return NULL; } entry = backend_retrieve_group_entry_from_nsswitch_helper(&grp, container_sdn); return entry; } static Slapi_Entry * backend_retrieve_group_entry_from_nsswitch_by_gid(gid_t gid, char *container_sdn, struct backend_search_cbdata *cbdata) { struct group grp, *result; Slapi_Entry *entry; int rc; char *buf = NULL; repeat: if (cbdata->nsswitch_buffer == NULL) { return NULL; } rc = getgrgid_r(gid, &grp, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &result); if ((result == NULL) || (rc != 0)) { if (rc == ERANGE) { buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2); if (buf != NULL) { cbdata->nsswitch_buffer = buf; goto repeat; } } return NULL; } if (grp.gr_gid < cbdata->nsswitch_min_id) { return NULL; } entry = backend_retrieve_group_entry_from_nsswitch_helper(&grp, container_sdn); return entry; } static Slapi_Entry ** backend_retrieve_group_list_from_nsswitch(char *user_name, char *container_sdn, struct backend_search_cbdata *cbdata, int *count) { struct passwd pwd, *pwd_result; gid_t *grouplist, *tmp_list; Slapi_Entry **entries, *entry, **tmp; char *buf = NULL; int rc, ngroups, i, idx; repeat: if (cbdata->nsswitch_buffer == NULL) { return NULL; } rc = getpwnam_r(user_name, &pwd, cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len, &pwd_result); if ((pwd_result == NULL) || (rc != 0)) { if (rc == ERANGE) { buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2); if (buf != NULL) { cbdata->nsswitch_buffer = buf; goto repeat; } } return NULL; } if (pwd.pw_uid < cbdata->nsswitch_min_id) { return NULL; } ngroups = 32; grouplist = malloc(sizeof(gid_t) * ngroups); if (grouplist == NULL) { return NULL; } do { rc = getgrouplist(user_name, pwd.pw_gid, grouplist, &ngroups); if (rc < ngroups) { tmp_list = realloc(grouplist, ngroups * sizeof(gid_t)); if (tmp_list == NULL) { free(grouplist); return NULL; } grouplist = tmp_list; } } while (rc != ngroups); entries = malloc(sizeof(Slapi_Entry *) * ngroups); if (entries == NULL) { free(grouplist); return NULL; } idx = 0; /* At this point we are not interested in the buffer used in pwd anymore * so the next function can take it over for getgrid_r() */ for (i = 0; i < ngroups; i++) { entry = backend_retrieve_group_entry_from_nsswitch_by_gid(grouplist[i], container_sdn, cbdata); if (entry != NULL) { entries[idx] = entry; idx++; } } if (idx != ngroups) { tmp = realloc(entries, idx * sizeof(Slapi_Entry *)); if (tmp != NULL) { entries = tmp; } } *count = 0; if (entries != NULL) { *count = idx; } free(grouplist); return entries; } /* Check filter for a component like (uid=) and if found, * stage NSSWITCH lookup. Lookup will be performed later, with call to backend_retrieve_from_nsswitch */ void backend_search_nsswitch(struct backend_set_data *set_data, struct backend_search_cbdata *cbdata) { int result, rc; struct backend_search_filter_config config = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL}; struct backend_staged_data *staged = NULL; char *idptr = NULL; unsigned long id; /* There was no match but we asked to check NSSWITCH */ /* First, we search the filter to see if it includes cn|uid= check */ result = slapi_filter_apply(cbdata->filter, backend_search_filter_has_cn_uid, &config, &rc); if ((result != SLAPI_FILTER_SCAN_STOP)) { return; } if (NULL == config.name) { return; } if (config.wrong_search) { goto fail; } /* Drop irrelevant requests. Each set only works with a single type */ if ((cbdata->check_nsswitch == SCH_NSSWITCH_GROUP) && (config.search_uid || config.search_user)) { goto fail; } if ((cbdata->check_nsswitch == SCH_NSSWITCH_USER) && (config.search_gid || config.search_group)) { goto fail; } if ((config.search_gid || config.search_uid)) { errno = 0; id = strtoul(config.name, &idptr, 10); if ((errno != 0) || ((idptr != NULL) && (*idptr != '\0'))) { goto fail; } if (id < cbdata->nsswitch_min_id) { goto fail; } } staged = malloc(sizeof(struct backend_staged_data)); if (staged == NULL) { goto fail; } staged->map_group = slapi_ch_strdup(set_data->common.group); staged->map_set = slapi_ch_strdup(set_data->common.set); staged->set_data_fixup = NULL; staged->count = 0; staged->entries = NULL; staged->container_sdn = slapi_ch_strdup(slapi_sdn_get_dn(set_data->container_sdn)); staged->type = cbdata->check_nsswitch; staged->name = config.name; staged->is_id = config.search_gid || config.search_uid; staged->search_members = config.search_members; staged->next = cbdata->staged; cbdata->staged = staged; return; fail: slapi_ch_free_string(&config.name); return; } /* perform look up against NSSWITCH and create entry based on that */ bool_t backend_retrieve_from_nsswitch(struct backend_staged_data *staged, struct backend_search_cbdata *cbdata) { Slapi_Entry *entry, **entries; int i; if (((staged->type == SCH_NSSWITCH_GROUP) && staged->search_members) && (NULL != staged->name)) { entries = backend_retrieve_group_list_from_nsswitch(staged->name, staged->container_sdn, cbdata, &staged->count); if (entries != NULL) { staged->entries = entries; for (i = 0; i < staged->count; i++) { slapi_entry_add_string(entries[i], "schema-compat-origin", "nsswitch"); } return TRUE; } return FALSE; } if ((staged->type == SCH_NSSWITCH_GROUP) && (NULL != staged->name)) { entry = backend_retrieve_group_entry_from_nsswitch(staged->name, staged->is_id, staged->container_sdn, cbdata); if (entry) { slapi_entry_add_string(entry, "schema-compat-origin", "nsswitch"); staged->entries = malloc(sizeof(Slapi_Entry *)); if (staged->entries != NULL) { staged->entries[0] = entry; staged->count = 1; return TRUE; } else { slapi_entry_free(entry); } } return FALSE; } if ((staged->type == SCH_NSSWITCH_USER) && (NULL != staged->name)) { entry = backend_retrieve_user_entry_from_nsswitch(staged->name, staged->is_id, staged->container_sdn, cbdata); if (entry) { slapi_entry_add_string(entry, "schema-compat-origin", "nsswitch"); staged->entries = malloc(sizeof(Slapi_Entry *)); if (staged->entries != NULL) { staged->entries[0] = entry; staged->count = 1; return TRUE; } else { slapi_entry_free(entry); } } return FALSE; } return FALSE; }