summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2013-07-31 14:35:15 +0300
committerNalin Dahyabhai <nalin@dahyabhai.net>2013-08-07 11:34:12 -0400
commit2e38e59b2fca5ec46ebd5eceffb2fc0f79a9d25a (patch)
tree8df004db0f8bdba50bb7df44fdcc43cf0d9b4e54 /src
parente609af8d68a8d3f534dfd4ea000a23c718de8c75 (diff)
downloadslapi-nis-2e38e59b2fca5ec46ebd5eceffb2fc0f79a9d25a.tar.gz
slapi-nis-2e38e59b2fca5ec46ebd5eceffb2fc0f79a9d25a.tar.xz
slapi-nis-2e38e59b2fca5ec46ebd5eceffb2fc0f79a9d25a.zip
schema-compat: add support for querying users and groups through NSSWITCH
src/back-sch-nss.c implements interface to query users and groups on FreeIPA master server via getpwnam_r(), getgrnam_r(), and libsss_idmap.
Diffstat (limited to 'src')
-rw-r--r--src/back-sch-nss.c574
1 files changed, 574 insertions, 0 deletions
diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
new file mode 100644
index 0000000..affed01
--- /dev/null
+++ b/src/back-sch-nss.c
@@ -0,0 +1,574 @@
+/*
+ * 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 <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.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>
+#ifdef HAVE_SSS_NSS_IDMAP
+#include <sss_nss_idmap.h>
+#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=<value>) 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 = format_strdupbv(bval);
+ }
+ }
+
+ 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;
+ enum sss_id_type id_type;
+ char *sid_str, *name;
+ char *dn = NULL;
+ char *buf = NULL;
+
+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);
+
+ 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);
+ }
+
+ return entry;
+}
+
+static Slapi_Entry *
+backend_retrieve_group_entry_from_nsswitch_helper(struct group *grp,
+ char *container_sdn)
+{
+ Slapi_Entry *entry;
+ int rc, i;
+ enum sss_id_type id_type;
+ char *sid_str, *name;
+ char *dn = NULL;
+
+ 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=<value>) 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=<value> 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;
+}