94cced
From 89b7a6f79cbcdbaea3b073983864a2a59ab6daaa Mon Sep 17 00:00:00 2001
94cced
From: Alexander Bokovoy <abokovoy@redhat.com>
94cced
Date: Wed, 12 Nov 2014 13:23:17 +0200
94cced
Subject: [PATCH] schema-compat: use libnss_sss.so.2 explicitly to resolve
94cced
 trusted domain users via NSS
94cced
94cced
When Schema Compatibility plugin is configured to enumerate users and groups
94cced
from Active Directory domains trusted by FreeIPA, use nss_sss module directly
94cced
instead of following nsswitch.conf configuration.
94cced
94cced
The issue with nsswitch.conf configuration is in the fact that for each request
94cced
all modules in NSS chain are processed while only one of them is responsible
94cced
for users from trusted Active Directory domains, namely, nss_sss.
94cced
---
94cced
 configure.ac        |   1 +
94cced
 doc/ipa/sch-ipa.txt |  14 ++-
94cced
 src/back-sch-nss.c  | 246 ++++++++++++++++++++++++++++++++++++++++++++--------
94cced
 src/back-sch.h      |   5 ++
94cced
 src/plug-sch.c      |   3 +
94cced
 src/plugin.h        |   1 +
94cced
 6 files changed, 231 insertions(+), 39 deletions(-)
94cced
94cced
diff --git a/configure.ac b/configure.ac
94cced
index 9174980..92647ea 100644
94cced
--- a/configure.ac
94cced
+++ b/configure.ac
94cced
@@ -343,6 +343,7 @@ fi
94cced
 AM_CONDITIONAL([USE_PAM], [test "x$use_pam" != xno])
94cced
 
94cced
 if test "x$use_nsswitch" != xno ; then
94cced
+	AC_CHECK_HEADERS([stdint.h nss.h dlfcn.h])
94cced
 	if pkg-config sss_nss_idmap 2> /dev/null ; then
94cced
 		if test x$use_sss_nss_idmap != xno ; then
94cced
 			AC_DEFINE(HAVE_SSS_NSS_IDMAP,1,[Define if you have libsss_nss_idmap.])
94cced
diff --git a/doc/ipa/sch-ipa.txt b/doc/ipa/sch-ipa.txt
94cced
index f560580..106e6cc 100644
94cced
--- a/doc/ipa/sch-ipa.txt
94cced
+++ b/doc/ipa/sch-ipa.txt
94cced
@@ -47,6 +47,11 @@ Plugin allows to expose users and groups from trusted domains. These users
94cced
 and groups are available on the compatibility trees and can be used for
94cced
 querying their attributes and authenticating against them.
94cced
 
94cced
+Schema Compatibility Plugin relies on SSSD to discover users from trusted
94cced
+domains. NSS module provided by SSSD (libnss_sss.so.2) is loaded explicitly by
94cced
+Schema Compatibility Plugin and all calls are directed to SSSD instead of using
94cced
+generic NSSWITCH API.
94cced
+
94cced
 Additionally, authentication against IPA users is also supported, provided
94cced
 that the Schema Compatibility Plugin is given an ordering preference in
94cced
 the Directory Server configuration. By default, all Directory server plugins
94cced
@@ -70,10 +75,11 @@ schema-compat-nsswitch-min-id: <value>
94cced
 specifies that the minimal numeric id of the user or group should be not less
94cced
 than the value. Defaults to 1000.
94cced
 
94cced
-When FreeIPA 3.3 is in use, ipa-adtrust-install utility will automatically configure
94cced
-the Schema Compatibility Plugin to allow serving users and groups from trusted domains.
94cced
-No additional configuration is needed. ipa-adtrust-install, however, will not set the
94cced
-minimal numeric id for user or group.
94cced
+When FreeIPA 3.3 or later is in use, ipa-adtrust-install utility will
94cced
+automatically configure the Schema Compatibility Plugin to allow serving users
94cced
+and groups from trusted domains. No additional configuration is needed.
94cced
+ipa-adtrust-install, however, will not set the minimal numeric id for user or
94cced
+group.
94cced
 
94cced
 == Authentication of the trusted domains' users ==
94cced
 
94cced
diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
94cced
index 12ae589..3a21ff6 100644
94cced
--- a/src/back-sch-nss.c
94cced
+++ b/src/back-sch-nss.c
94cced
@@ -28,9 +28,10 @@
94cced
 #include <string.h>
94cced
 #include <time.h>
94cced
 #include <unistd.h>
94cced
+#include <dlfcn.h>
94cced
+#include <errno.h>
94cced
 #include <pwd.h>
94cced
 #include <grp.h>
94cced
-#include <errno.h>
94cced
 
94cced
 #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
94cced
 #include <nspr.h>
94cced
@@ -307,6 +308,144 @@ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
94cced
 	return entry;
94cced
 }
94cced
 
94cced
+/* Possible results of lookup using a nss_* function.
94cced
+ * Note: don't include nss.h as its path gets overriden by NSS library */
94cced
+enum nss_status
94cced
+{
94cced
+  NSS_STATUS_TRYAGAIN = -2,
94cced
+  NSS_STATUS_UNAVAIL,
94cced
+  NSS_STATUS_NOTFOUND,
94cced
+  NSS_STATUS_SUCCESS,
94cced
+  NSS_STATUS_RETURN
94cced
+};
94cced
+
94cced
+struct nss_ops_ctx {
94cced
+	void *dl_handle;
94cced
+
94cced
+	enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*setpwent)(void);
94cced
+	enum nss_status (*getpwent_r)(struct passwd *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*endpwent)(void);
94cced
+
94cced
+	enum nss_status (*getgrnam_r)(const char *name, struct group *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*setgrent)(void);
94cced
+	enum nss_status (*getgrent_r)(struct group *result,
94cced
+			  char *buffer, size_t buflen, int *errnop);
94cced
+	enum nss_status (*endgrent)(void);
94cced
+
94cced
+	enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
94cced
+			      long int *start, long int *size,
94cced
+			      gid_t **groups, long int limit,
94cced
+			      int *errnop);
94cced
+};
94cced
+
94cced
+void backend_nss_init_context(struct nss_ops_ctx **nss_context)
94cced
+{
94cced
+	struct nss_ops_ctx *ctx = NULL;
94cced
+
94cced
+	if (nss_context == NULL) {
94cced
+		return;
94cced
+	}
94cced
+
94cced
+	ctx = calloc(1, sizeof(struct nss_ops_ctx));
94cced
+
94cced
+	*nss_context = ctx;
94cced
+	if (ctx == NULL) {
94cced
+		return;
94cced
+	}
94cced
+
94cced
+	ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
94cced
+	if (ctx->dl_handle == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
94cced
+	if (ctx->getpwnam_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
94cced
+	if (ctx->getpwuid_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->setpwent = dlsym(ctx->dl_handle, "_nss_sss_setpwent");
94cced
+	if (ctx->setpwent == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getpwent_r = dlsym(ctx->dl_handle, "_nss_sss_getpwent_r");
94cced
+	if (ctx->getpwent_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->endpwent = dlsym(ctx->dl_handle, "_nss_sss_endpwent");
94cced
+	if (ctx->endpwent == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
94cced
+	if (ctx->getgrnam_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
94cced
+	if (ctx->getgrgid_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->setgrent = dlsym(ctx->dl_handle, "_nss_sss_setgrent");
94cced
+	if (ctx->setgrent == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->getgrent_r = dlsym(ctx->dl_handle, "_nss_sss_getgrent_r");
94cced
+	if (ctx->getgrent_r == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->endgrent = dlsym(ctx->dl_handle, "_nss_sss_endgrent");
94cced
+	if (ctx->endgrent == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
94cced
+	if (ctx->initgroups_dyn == NULL) {
94cced
+		goto fail;
94cced
+	}
94cced
+
94cced
+	return;
94cced
+
94cced
+fail:
94cced
+	backend_nss_free_context(nss_context);
94cced
+
94cced
+	return;
94cced
+}
94cced
+
94cced
+void
94cced
+backend_nss_free_context(struct nss_ops_ctx **nss_context)
94cced
+{
94cced
+	if (nss_context == NULL) {
94cced
+		return;
94cced
+	}
94cced
+
94cced
+	if ((*nss_context)->dl_handle != NULL) {
94cced
+		dlclose((*nss_context)->dl_handle);
94cced
+	}
94cced
+
94cced
+	free((*nss_context));
94cced
+	*nss_context = NULL;
94cced
+}
94cced
+
94cced
+
94cced
+
94cced
 static Slapi_Entry **
94cced
 backend_retrieve_user_entry_from_nsswitch(char *user_name, bool_t is_uid,
94cced
 					  char *container_sdn,
94cced
@@ -315,25 +454,33 @@ backend_retrieve_user_entry_from_nsswitch(char *user_name, bool_t is_uid,
94cced
 {
94cced
 	struct passwd pwd, *result;
94cced
 	Slapi_Entry *entry, **entries;
94cced
-	int rc;
94cced
+	enum nss_status rc;
94cced
 	char *buf = NULL;
94cced
+	struct nss_ops_ctx *ctx = NULL;
94cced
+	int lerrno;
94cced
+
94cced
+	ctx = cbdata->state->nss_context;
94cced
 
94cced
+	if (ctx == NULL) {
94cced
+		return NULL;
94cced
+	}
94cced
 repeat:
94cced
 	if (cbdata->nsswitch_buffer == NULL) {
94cced
 		return NULL;
94cced
 	}
94cced
 
94cced
 	if (is_uid) {
94cced
-		rc = getpwuid_r((uid_t) atoll(user_name), &pwd,
94cced
-				cbdata->nsswitch_buffer,
94cced
-				cbdata->nsswitch_buffer_len, &result);
94cced
+		rc = ctx->getpwuid_r((uid_t) atoll(user_name), &pwd,
94cced
+				     cbdata->nsswitch_buffer,
94cced
+				     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 	} else {
94cced
-		rc = getpwnam_r(user_name, &pwd,
94cced
-				cbdata->nsswitch_buffer,
94cced
-				cbdata->nsswitch_buffer_len, &result);
94cced
+		rc = ctx->getpwnam_r(user_name, &pwd,
94cced
+				     cbdata->nsswitch_buffer,
94cced
+				     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 	}
94cced
-	if ((result == NULL) || (rc != 0)) {
94cced
-		if (rc == ERANGE) {
94cced
+
94cced
+	if ((rc != NSS_STATUS_SUCCESS)) {
94cced
+		if (lerrno == ERANGE) {
94cced
 			buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2);
94cced
 			if (buf != NULL) {
94cced
 				cbdata->nsswitch_buffer = buf;
94cced
@@ -437,25 +584,32 @@ backend_retrieve_group_entry_from_nsswitch(char *group_name, bool_t is_gid,
94cced
 {
94cced
 	struct group grp, *result;
94cced
 	Slapi_Entry *entry, **entries;
94cced
-	int rc;
94cced
+	enum nss_status rc;
94cced
 	char *buf = NULL;
94cced
+	struct nss_ops_ctx *ctx = NULL;
94cced
+	int lerrno = 0;
94cced
+
94cced
+	ctx = cbdata->state->nss_context;
94cced
 
94cced
+	if (ctx == NULL) {
94cced
+		return NULL;
94cced
+	}
94cced
 repeat:
94cced
 	if (cbdata->nsswitch_buffer == NULL) {
94cced
 		return NULL;
94cced
 	}
94cced
 
94cced
 	if (is_gid) {
94cced
-		rc = getgrgid_r((gid_t) atoll(group_name), &grp,
94cced
-				cbdata->nsswitch_buffer,
94cced
-				cbdata->nsswitch_buffer_len, &result);
94cced
+		rc = ctx->getgrgid_r((gid_t) atoll(group_name), &grp,
94cced
+				     cbdata->nsswitch_buffer,
94cced
+				     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 	} else {
94cced
-		rc = getgrnam_r(group_name, &grp,
94cced
-				cbdata->nsswitch_buffer,
94cced
-				cbdata->nsswitch_buffer_len, &result);
94cced
+		rc = ctx->getgrnam_r(group_name, &grp,
94cced
+				     cbdata->nsswitch_buffer,
94cced
+				     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 	}
94cced
-	if ((result == NULL) || (rc != 0)) {
94cced
-		if (rc == ERANGE) {
94cced
+	if ((rc != NSS_STATUS_SUCCESS)) {
94cced
+		if (lerrno == ERANGE) {
94cced
 			buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2);
94cced
 			if (buf != NULL) {
94cced
 				cbdata->nsswitch_buffer = buf;
94cced
@@ -490,20 +644,27 @@ backend_retrieve_group_entry_from_nsswitch_by_gid(gid_t gid,
94cced
 {
94cced
 	struct group grp, *result;
94cced
 	Slapi_Entry *entry;
94cced
-	int rc;
94cced
+	enum nss_status rc;
94cced
 	char *buf = NULL;
94cced
+	struct nss_ops_ctx *ctx = NULL;
94cced
+	int lerrno = 0;
94cced
 
94cced
+	ctx = cbdata->state->nss_context;
94cced
+
94cced
+	if (ctx == NULL) {
94cced
+		return NULL;
94cced
+	}
94cced
 repeat:
94cced
 	if (cbdata->nsswitch_buffer == NULL) {
94cced
 		return NULL;
94cced
 	}
94cced
 
94cced
-	rc = getgrgid_r(gid, &grp,
94cced
-			cbdata->nsswitch_buffer,
94cced
-			cbdata->nsswitch_buffer_len, &result);
94cced
+	rc = ctx->getgrgid_r(gid, &grp,
94cced
+			     cbdata->nsswitch_buffer,
94cced
+			     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 
94cced
-	if ((result == NULL) || (rc != 0)) {
94cced
-		if (rc == ERANGE) {
94cced
+	if ((rc != NSS_STATUS_SUCCESS)) {
94cced
+		if (lerrno == ERANGE) {
94cced
 			buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2);
94cced
 			if (buf != NULL) {
94cced
 				cbdata->nsswitch_buffer = buf;
94cced
@@ -532,19 +693,28 @@ backend_retrieve_group_list_from_nsswitch(char *user_name, char *container_sdn,
94cced
 	gid_t *grouplist, *tmp_list;
94cced
 	Slapi_Entry **entries, *entry, **tmp;
94cced
 	char *buf = NULL;
94cced
-	int rc, ngroups, i, idx;
94cced
-
94cced
+	int i, idx;
94cced
+	struct nss_ops_ctx *ctx = NULL;
94cced
+	int lerrno = 0;
94cced
+	long int ngroups = 0;
94cced
+	long int start = 0;
94cced
+	enum nss_status rc;
94cced
+
94cced
+	ctx = cbdata->state->nss_context;
94cced
+	if (ctx == NULL) {
94cced
+		return NULL;
94cced
+	}
94cced
 repeat:
94cced
 	if (cbdata->nsswitch_buffer == NULL) {
94cced
 		return NULL;
94cced
 	}
94cced
 
94cced
-	rc = getpwnam_r(user_name, &pwd,
94cced
-			cbdata->nsswitch_buffer,
94cced
-			cbdata->nsswitch_buffer_len, &pwd_result);
94cced
+	rc = ctx->getpwnam_r(user_name, &pwd,
94cced
+			     cbdata->nsswitch_buffer,
94cced
+			     cbdata->nsswitch_buffer_len, &lerrno);
94cced
 
94cced
-	if ((pwd_result == NULL) || (rc != 0)) {
94cced
-		if (rc == ERANGE) {
94cced
+	if ((rc != NSS_STATUS_SUCCESS)) {
94cced
+		if (lerrno == ERANGE) {
94cced
 			buf = realloc(cbdata->nsswitch_buffer, cbdata->nsswitch_buffer_len * 2);
94cced
 			if (buf != NULL) {
94cced
 				cbdata->nsswitch_buffer = buf;
94cced
@@ -559,14 +729,20 @@ repeat:
94cced
 	}
94cced
 
94cced
 	ngroups = 32;
94cced
+	start = 0;
94cced
 	grouplist = malloc(sizeof(gid_t) * ngroups);
94cced
 	if (grouplist == NULL) {
94cced
 		return NULL;
94cced
 	}
94cced
 
94cced
+	grouplist[0] = pwd.pw_gid;
94cced
+	start++;
94cced
+
94cced
 	do {
94cced
-		rc = getgrouplist(user_name, pwd.pw_gid, grouplist, &ngroups);
94cced
-		if (rc < ngroups) {
94cced
+		rc = ctx->initgroups_dyn(user_name, pwd.pw_gid,
94cced
+					 &start, &ngroups, &grouplist,
94cced
+					 -1, &lerrno);
94cced
+		if ((rc != NSS_STATUS_SUCCESS)) {
94cced
 			tmp_list = realloc(grouplist, ngroups * sizeof(gid_t));
94cced
 			if (tmp_list == NULL) {
94cced
 				free(grouplist);
94cced
@@ -574,7 +750,7 @@ repeat:
94cced
 			}
94cced
 			grouplist = tmp_list;
94cced
 		}
94cced
-	} while (rc != ngroups);
94cced
+	} while (rc != NSS_STATUS_SUCCESS);
94cced
 
94cced
 	entries = calloc(ngroups + 1, sizeof(entries[0]));
94cced
 	if (entries == NULL) {
94cced
diff --git a/src/back-sch.h b/src/back-sch.h
94cced
index 26e12d1..1aedf36 100644
94cced
--- a/src/back-sch.h
94cced
+++ b/src/back-sch.h
94cced
@@ -115,6 +115,11 @@ struct backend_search_filter_config {
94cced
 
94cced
 int backend_analyze_search_filter(Slapi_Filter *filter, struct backend_search_filter_config *config);
94cced
 
94cced
+/* Operations against nsswitch API */
94cced
+struct nss_ops_ctx;
94cced
+void backend_nss_init_context(struct nss_ops_ctx **nss_context);
94cced
+void backend_nss_free_context(struct nss_ops_ctx **nss_context);
94cced
+
94cced
 void backend_search_nsswitch(struct backend_set_data *set_data,
94cced
 			     struct backend_search_cbdata *cbdata);
94cced
 
94cced
diff --git a/src/plug-sch.c b/src/plug-sch.c
94cced
index 5d74beb..5a6e736 100644
94cced
--- a/src/plug-sch.c
94cced
+++ b/src/plug-sch.c
94cced
@@ -52,6 +52,7 @@
94cced
 
94cced
 #include "backend.h"
94cced
 #include "back-shr.h"
94cced
+#include "back-sch.h"
94cced
 #include "map.h"
94cced
 #include "plugin.h"
94cced
 #include "portmap.h"
94cced
@@ -109,6 +110,7 @@ plugin_startup(Slapi_PBlock *pb)
94cced
 	/* Populate the tree of fake entries. */
94cced
 	backend_startup(pb, state);
94cced
 	state->pam_lock = wrap_new_rwlock();
94cced
+	backend_nss_init_context((struct nss_ops_ctx**) &state->nss_context);
94cced
 	/* Note that the plugin is ready to go. */
94cced
 	slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id,
94cced
 			"plugin startup completed\n");
94cced
@@ -123,6 +125,7 @@ plugin_shutdown(Slapi_PBlock *pb)
94cced
 	map_done(state);
94cced
 	wrap_free_rwlock(state->pam_lock);
94cced
 	state->pam_lock = NULL;
94cced
+	backend_nss_free_context((struct nss_ops_ctx**) &state->nss_context);
94cced
 	state->plugin_base = NULL;
94cced
 	slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
94cced
 			"plugin shutdown completed\n");
94cced
diff --git a/src/plugin.h b/src/plugin.h
94cced
index 3967fb0..94ad747 100644
94cced
--- a/src/plugin.h
94cced
+++ b/src/plugin.h
94cced
@@ -46,6 +46,7 @@ struct plugin_state {
94cced
 	} listener[4];
94cced
 	/* Schema compat-specific data. */
94cced
 	struct wrapped_rwlock *pam_lock;
94cced
+	void *nss_context;
94cced
 };
94cced
 
94cced
 #endif
94cced
-- 
94cced
2.1.0
94cced