c58629
From 8a80363e07b5c9309d785bf3b41f506f32a750a5 Mon Sep 17 00:00:00 2001
c58629
From: Alexander Bokovoy <abokovoy@redhat.com>
c58629
Date: Tue, 14 Nov 2017 12:44:02 +0200
c58629
Subject: [PATCH] ipa-extdom-extop: refactor nsswitch operations
c58629
c58629
Refactor nsswitch operations in ipa-extdom-extop plugin to allow use
c58629
of timeout-enabled nsswitch calls provided by libsss_nss_idmap.
c58629
c58629
Standard POSIX nsswitch API has no way to cancel requests which may
c58629
cause ipa-extdom-extop requests to hang far too long and potentially
c58629
exhaust LDAP server workers. In addition, glibc nsswitch API iterates
c58629
through all nsswitch modules one by one and with multiple parallel
c58629
requests a lock up may happen in an unrelated nsswitch module like
c58629
nss_files.so.2.
c58629
c58629
A solution to the latter issue is to directly load nss_sss.so.2 plugin
c58629
and utilize it. This, however, does not solve a problem with lack of
c58629
cancellable API.
c58629
c58629
With SSSD 1.16.1, libsss_nss_idmap provides a timeout-enabled variant of
c58629
nsswitch API that is directly integrated with SSSD client side machinery
c58629
used by nss_sss.so.2. As result, this API can be used instead of loading
c58629
nss_sss.so.2 directly.
c58629
c58629
To support older SSSD version, both direct loading of nss_sss.so.2 and
c58629
new timeout-enabled API are supported by this changeset. An API to
c58629
abstract both is designed to be a mix between internal glibc nsswitch
c58629
API and external nsswitch API that libsss_nss_idmap mimics. API does not
c58629
expose per-call timeout. Instead, it allows to set a timeout per
c58629
nsswitch operation context to reduce requirements on information
c58629
a caller has to maintain.
c58629
c58629
A choice which API to use is made at configure time.
c58629
c58629
In order to test the API, a cmocka test is updated to explicitly load
c58629
nss_files.so.2 as a backend. Since use of nss_sss.so.2 would always
c58629
depend on availablility of SSSD, predictable testing would not be
c58629
possible without it otherwise. Also, cmocka test does not use
c58629
nss_wrapper anymore because nss_wrapper overrides higher level glibc
c58629
nsswitch API while we are loading an individual nsswitch module
c58629
directly.
c58629
c58629
As result, cmocka test overrides fopen() call used by nss_files.so.2 to
c58629
load /etc/passwd and /etc/group. An overridden version changes paths to
c58629
/etc/passwd and /etc/group to a local test_data/passwd and
c58629
test_data/group. This way we can continue testing a backend API for
c58629
ipa-extdom-extop with the same data as with nss_wrapper.
c58629
c58629
Fixes https://pagure.io/freeipa/issue/5464
c58629
c58629
Reviewed-By: Christian Heimes <cheimes@redhat.com>
c58629
Reviewed-By: Simo Sorce <ssorce@redhat.com>
c58629
Reviewed-By: Robbie Harwood <rharwood@redhat.com>
c58629
---
c58629
 configure.ac                                       |  25 +-
c58629
 .../ipa-slapi-plugins/ipa-extdom-extop/Makefile.am |  17 +-
c58629
 .../ipa-extdom-extop/back_extdom.h                 |  79 ++++++
c58629
 .../ipa-extdom-extop/back_extdom_nss_sss.c         | 276 +++++++++++++++++++++
c58629
 .../ipa-extdom-extop/back_extdom_sss_idmap.c       | 260 +++++++++++++++++++
c58629
 .../ipa-extdom-extop/ipa_extdom.h                  |  13 +-
c58629
 .../ipa-extdom-extop/ipa_extdom_cmocka_tests.c     | 241 +++++++++++++++---
c58629
 .../ipa-extdom-extop/ipa_extdom_common.c           | 242 +++++++++---------
c58629
 .../ipa-extdom-extop/ipa_extdom_extop.c            |  17 ++
c58629
 .../ipa-extdom-extop/test_data/test_setup.sh       |   3 -
c58629
 freeipa.spec.in                                    |   1 -
c58629
 server.m4                                          |  10 +
c58629
 12 files changed, 994 insertions(+), 190 deletions(-)
c58629
 create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h
c58629
 create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c
c58629
 create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c
c58629
 delete mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh
c58629
c58629
diff --git a/configure.ac b/configure.ac
c58629
index e7a8b11153209fdfb4903cd3876e21a871a92f03..8a99b028886790aca211ddf164772920221f3ec7 100644
c58629
--- a/configure.ac
c58629
+++ b/configure.ac
c58629
@@ -140,30 +140,6 @@ PKG_CHECK_EXISTS(cmocka,
c58629
 )
c58629
 AM_CONDITIONAL([HAVE_CMOCKA], [test x$have_cmocka = xyes])
c58629
 
c58629
-dnl A macro to check presence of a cwrap (http://cwrap.org) wrapper on the system
c58629
-dnl Usage:
c58629
-dnl     AM_CHECK_WRAPPER(name, conditional)
c58629
-dnl If the cwrap library is found, sets the HAVE_$name conditional
c58629
-AC_DEFUN([AM_CHECK_WRAPPER],
c58629
-[
c58629
-    FOUND_WRAPPER=0
c58629
-
c58629
-    AC_MSG_CHECKING([for $1])
c58629
-    PKG_CHECK_EXISTS([$1],
c58629
-                     [
c58629
-                        AC_MSG_RESULT([yes])
c58629
-                        FOUND_WRAPPER=1
c58629
-                     ],
c58629
-                     [
c58629
-                        AC_MSG_RESULT([no])
c58629
-                        AC_MSG_WARN([cwrap library $1 not found, some tests will not run])
c58629
-                     ])
c58629
-
c58629
-    AM_CONDITIONAL($2, [ test x$FOUND_WRAPPER = x1])
c58629
-])
c58629
-
c58629
-AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER)
c58629
-
c58629
 dnl ---------------------------------------------------------------------------
c58629
 dnl - Check for POPT
c58629
 dnl ---------------------------------------------------------------------------
c58629
@@ -235,6 +211,7 @@ dnl ---------------------------------------------------------------------------
c58629
 AM_COND_IF([ENABLE_SERVER], [
c58629
     m4_include(server.m4)
c58629
 ])
c58629
+AM_CONDITIONAL([USE_SSS_NSS_TIMEOUT], [test "x$ac_cv_have_decl_sss_nss_getpwnam_timeout" = xyes])
c58629
 
c58629
 dnl ---------------------------------------------------------------------------
c58629
 dnl - Check if IPA certauth plugin can be build
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am
c58629
index 1213965c96607bf14c6c92ce592585aed1a125db..cbdd570eabeb12b95fdc26213a64749f9ba9fdde 100644
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am
c58629
@@ -25,6 +25,7 @@ libipa_extdom_extop_la_SOURCES = 	\
c58629
 	ipa_extdom.h			\
c58629
 	ipa_extdom_extop.c		\
c58629
 	ipa_extdom_common.c		\
c58629
+	back_extdom.h			\
c58629
 	$(NULL)
c58629
 
c58629
 libipa_extdom_extop_la_LDFLAGS = -avoid-version
c58629
@@ -34,20 +35,29 @@ libipa_extdom_extop_la_LIBADD = 	\
c58629
 	$(SSSNSSIDMAP_LIBS)		\
c58629
 	$(NULL)
c58629
 
c58629
+# We have two backends for nss operations:
c58629
+# (1) directly loading nss_sss.so.2
c58629
+# (2) using timeout-enabled API from libsss_nss_idmap
c58629
+# We prefer (2) if available
c58629
+if USE_SSS_NSS_TIMEOUT
c58629
+libipa_extdom_extop_la_SOURCES += back_extdom_sss_idmap.c
c58629
+else
c58629
+libipa_extdom_extop_la_SOURCES += back_extdom_nss_sss.c
c58629
+endif
c58629
+
c58629
+
c58629
 TESTS =
c58629
 check_PROGRAMS =
c58629
 
c58629
 if HAVE_CMOCKA
c58629
-if HAVE_NSS_WRAPPER
c58629
-TESTS_ENVIRONMENT = . ./test_data/test_setup.sh;
c58629
 TESTS += extdom_cmocka_tests
c58629
 check_PROGRAMS += extdom_cmocka_tests
c58629
 endif
c58629
-endif
c58629
 
c58629
 extdom_cmocka_tests_SOURCES = 		\
c58629
 	ipa_extdom_cmocka_tests.c	\
c58629
 	ipa_extdom_common.c		\
c58629
+	back_extdom_nss_sss.c		\
c58629
 	$(NULL)
c58629
 extdom_cmocka_tests_CFLAGS = $(CMOCKA_CFLAGS)
c58629
 extdom_cmocka_tests_LDFLAGS = 	\
c58629
@@ -58,6 +68,7 @@ extdom_cmocka_tests_LDADD = 	\
c58629
 	$(LDAP_LIBS)		\
c58629
 	$(DIRSRV_LIBS)		\
c58629
 	$(SSSNSSIDMAP_LIBS)	\
c58629
+	-ldl			\
c58629
 	$(NULL)
c58629
 
c58629
 
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h
c58629
new file mode 100644
c58629
index 0000000000000000000000000000000000000000..d2937c8c8ecf8b960b5b31e9449c719bfda86de4
c58629
--- /dev/null
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h
c58629
@@ -0,0 +1,79 @@
c58629
+/*
c58629
+ * Copyright 2017 Red Hat, Inc.
c58629
+ *
c58629
+ * This Program is free software; you can redistribute it and/or modify
c58629
+ * it under the terms of the GNU General Public License as published by
c58629
+ * the Free Software Foundation; version 2 of the License.
c58629
+ *
c58629
+ * This Program is distributed in the hope that it will be useful, but
c58629
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
c58629
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c58629
+ * General Public License for more details.
c58629
+ *
c58629
+ * You should have received a copy of the GNU General Public License
c58629
+ * along with this Program; if not, write to the
c58629
+ *
c58629
+ *   Free Software Foundation, Inc.
c58629
+ *   59 Temple Place, Suite 330
c58629
+ *   Boston, MA 02111-1307 USA
c58629
+ *
c58629
+ */
c58629
+
c58629
+#ifndef BACK_EXTDOM_H
c58629
+#define BACK_EXTDOM_H
c58629
+#include <unistd.h>
c58629
+#include <pwd.h>
c58629
+#include <grp.h>
c58629
+
c58629
+/* Possible results of lookup using a nss_* function.
c58629
+ * Note: don't include nss.h as its path gets overriden by NSS library */
c58629
+enum nss_status {
c58629
+    NSS_STATUS_TRYAGAIN = -2,
c58629
+    NSS_STATUS_UNAVAIL,
c58629
+    NSS_STATUS_NOTFOUND,
c58629
+    NSS_STATUS_SUCCESS,
c58629
+    NSS_STATUS_RETURN
c58629
+};
c58629
+
c58629
+/* NSS backend operations implemented using either nss_sss.so.2 or libsss_nss_idmap API */
c58629
+struct nss_ops_ctx;
c58629
+
c58629
+int back_extdom_init_context(struct nss_ops_ctx **nss_context);
c58629
+void back_extdom_free_context(struct nss_ops_ctx **nss_context);
c58629
+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context,
c58629
+                             unsigned int timeout);
c58629
+void back_extdom_evict_user(struct nss_ops_ctx *nss_context,
c58629
+                            const char *name);
c58629
+void back_extdom_evict_group(struct nss_ops_ctx *nss_context,
c58629
+                             const char *name);
c58629
+
c58629
+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno);
c58629
+
c58629
+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context,
c58629
+                                     uid_t uid, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno);
c58629
+
c58629
+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno);
c58629
+
c58629
+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context,
c58629
+                                     gid_t gid, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno);
c58629
+
c58629
+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context,
c58629
+                                         const char *name, gid_t group,
c58629
+                                         gid_t *groups, int *ngroups,
c58629
+                                         int *lerrno);
c58629
+
c58629
+#endif /* BACK_EXTDOM_H */
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c
c58629
new file mode 100644
c58629
index 0000000000000000000000000000000000000000..346c7d4301a607c7bc07ca5a9c53fe84618ac8ad
c58629
--- /dev/null
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c
c58629
@@ -0,0 +1,276 @@
c58629
+/*
c58629
+ * Copyright 2013-2017 Red Hat, Inc.
c58629
+ *
c58629
+ * This Program is free software; you can redistribute it and/or modify
c58629
+ * it under the terms of the GNU General Public License as published by
c58629
+ * the Free Software Foundation; version 2 of the License.
c58629
+ *
c58629
+ * This Program is distributed in the hope that it will be useful, but
c58629
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
c58629
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c58629
+ * General Public License for more details.
c58629
+ *
c58629
+ * You should have received a copy of the GNU General Public License
c58629
+ * along with this Program; if not, write to the
c58629
+ *
c58629
+ *   Free Software Foundation, Inc.
c58629
+ *   59 Temple Place, Suite 330
c58629
+ *   Boston, MA 02111-1307 USA
c58629
+ *
c58629
+ */
c58629
+
c58629
+#ifdef HAVE_CONFIG_H
c58629
+#include "config.h"
c58629
+#endif
c58629
+
c58629
+#include <sys/types.h>
c58629
+#include <stdlib.h>
c58629
+#include <string.h>
c58629
+#include <time.h>
c58629
+#include <unistd.h>
c58629
+#include <dlfcn.h>
c58629
+#include <errno.h>
c58629
+#include <pwd.h>
c58629
+#include <grp.h>
c58629
+#include <sys/param.h>
c58629
+#include "back_extdom.h"
c58629
+
c58629
+struct nss_ops_ctx {
c58629
+    void *dl_handle;
c58629
+    long int initgroups_start;
c58629
+
c58629
+    enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getgrnam_r)(const char *name, struct group *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
c58629
+                                      long int *start, long int *size,
c58629
+                                      gid_t **groups, long int limit,
c58629
+                                      int *errnop);
c58629
+};
c58629
+
c58629
+void back_extdom_free_context(struct nss_ops_ctx **nss_context)
c58629
+{
c58629
+    if ((nss_context == NULL) || (*nss_context == NULL)) {
c58629
+        return;
c58629
+    }
c58629
+
c58629
+    if ((*nss_context)->dl_handle != NULL) {
c58629
+        dlclose((*nss_context)->dl_handle);
c58629
+    }
c58629
+
c58629
+    free((*nss_context));
c58629
+    *nss_context = NULL;
c58629
+}
c58629
+
c58629
+int back_extdom_init_context(struct nss_ops_ctx **nss_context)
c58629
+{
c58629
+    struct nss_ops_ctx *ctx = NULL;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return EINVAL;
c58629
+    }
c58629
+
c58629
+    ctx = calloc(1, sizeof(struct nss_ops_ctx));
c58629
+    if (ctx == NULL) {
c58629
+        return ENOMEM;
c58629
+    }
c58629
+    *nss_context = ctx;
c58629
+
c58629
+    ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
c58629
+    if (ctx->dl_handle == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
c58629
+    if (ctx->getpwnam_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
c58629
+    if (ctx->getpwuid_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
c58629
+    if (ctx->getgrnam_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
c58629
+    if (ctx->getgrgid_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
c58629
+    if (ctx->initgroups_dyn == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    return 0;
c58629
+
c58629
+fail:
c58629
+    back_extdom_free_context(nss_context);
c58629
+
c58629
+    return EINVAL;
c58629
+}
c58629
+
c58629
+
c58629
+/* Following three functions cannot be implemented with nss_sss.so.2
c58629
+ * As result, we simply do nothing here */
c58629
+
c58629
+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context,
c58629
+                             unsigned int timeout) {
c58629
+        /* no operation */
c58629
+}
c58629
+
c58629
+void back_extdom_evict_user(struct nss_ops_ctx *nss_context,
c58629
+                            const char *name) {
c58629
+        /* no operation */
c58629
+}
c58629
+
c58629
+void back_extdom_evict_group(struct nss_ops_ctx *nss_context,
c58629
+                             const char *name) {
c58629
+        /* no operation */
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno) {
c58629
+    enum nss_status ret;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = nss_context->getpwnam_r(name, pwd,
c58629
+                                  buffer, buflen,
c58629
+                                  lerrno);
c58629
+
c58629
+    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
c58629
+        *result = pwd;
c58629
+        *lerrno = 0;
c58629
+    }
c58629
+
c58629
+    return ret;
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context,
c58629
+                                     uid_t uid, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno) {
c58629
+    enum nss_status ret;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = nss_context->getpwuid_r(uid, pwd,
c58629
+                                  buffer, buflen,
c58629
+                                  lerrno);
c58629
+
c58629
+    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
c58629
+        *result = pwd;
c58629
+        *lerrno = 0;
c58629
+    }
c58629
+
c58629
+    return ret;
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno) {
c58629
+    enum nss_status ret;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = nss_context->getgrnam_r(name, grp,
c58629
+                                  buffer, buflen,
c58629
+                                  lerrno);
c58629
+
c58629
+    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
c58629
+        *result = grp;
c58629
+        *lerrno = 0;
c58629
+    }
c58629
+
c58629
+    return ret;
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context,
c58629
+                                     gid_t gid, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno) {
c58629
+
c58629
+    enum nss_status ret;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = nss_context->getgrgid_r(gid, grp,
c58629
+                                  buffer, buflen,
c58629
+                                  lerrno);
c58629
+
c58629
+    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
c58629
+        *result = grp;
c58629
+        *lerrno = 0;
c58629
+    }
c58629
+
c58629
+    return ret;
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context,
c58629
+                                         const char *name, gid_t group,
c58629
+                                         gid_t *groups, int *ngroups,
c58629
+                                         int *lerrno) {
c58629
+
c58629
+    enum nss_status ret = NSS_STATUS_UNAVAIL;
c58629
+    long int tsize = MAX (1, *ngroups);
c58629
+    gid_t *newgroups = NULL;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    newgroups = (gid_t *) calloc (tsize, sizeof (gid_t));
c58629
+    if (newgroups == NULL) {
c58629
+        *lerrno = ENOMEM;
c58629
+        return NSS_STATUS_TRYAGAIN;
c58629
+    }
c58629
+
c58629
+    newgroups[0] = group;
c58629
+    nss_context->initgroups_start = 1;
c58629
+
c58629
+    ret = nss_context->initgroups_dyn(name, group,
c58629
+                                      &nss_context->initgroups_start,
c58629
+                                      &tsize, &newgroups,
c58629
+                                      -1, lerrno);
c58629
+
c58629
+    (void) memcpy(groups, newgroups,
c58629
+                  MIN(*ngroups, nss_context->initgroups_start) * sizeof(gid_t));
c58629
+    free(newgroups);
c58629
+
c58629
+    if (*ngroups < nss_context->initgroups_start) {
c58629
+        ret = NSS_STATUS_TRYAGAIN;
c58629
+        *lerrno = ERANGE;
c58629
+    }
c58629
+
c58629
+    *ngroups = (int) nss_context->initgroups_start;
c58629
+
c58629
+    nss_context->initgroups_start = 0;
c58629
+
c58629
+    return ret;
c58629
+}
c58629
+
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c
c58629
new file mode 100644
c58629
index 0000000000000000000000000000000000000000..89c58ca2de333b26954d916836b57aed5d7e18fb
c58629
--- /dev/null
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c
c58629
@@ -0,0 +1,260 @@
c58629
+/*
c58629
+ * Copyright 2013-2017 Red Hat, Inc.
c58629
+ *
c58629
+ * This Program is free software; you can redistribute it and/or modify
c58629
+ * it under the terms of the GNU General Public License as published by
c58629
+ * the Free Software Foundation; version 2 of the License.
c58629
+ *
c58629
+ * This Program is distributed in the hope that it will be useful, but
c58629
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
c58629
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c58629
+ * General Public License for more details.
c58629
+ *
c58629
+ * You should have received a copy of the GNU General Public License
c58629
+ * along with this Program; if not, write to the
c58629
+ *
c58629
+ *   Free Software Foundation, Inc.
c58629
+ *   59 Temple Place, Suite 330
c58629
+ *   Boston, MA 02111-1307 USA
c58629
+ *
c58629
+ */
c58629
+
c58629
+#ifdef HAVE_CONFIG_H
c58629
+#include "config.h"
c58629
+#endif
c58629
+
c58629
+#include <sys/types.h>
c58629
+#include <stdlib.h>
c58629
+#include <string.h>
c58629
+#include <time.h>
c58629
+#include <unistd.h>
c58629
+#include <errno.h>
c58629
+#include <pwd.h>
c58629
+#include <grp.h>
c58629
+#include "back_extdom.h"
c58629
+
c58629
+/* SSSD only exposes *_timeout() variants if the following symbol is defined */
c58629
+#define IPA_389DS_PLUGIN_HELPER_CALLS
c58629
+#include <sss_nss_idmap.h>
c58629
+
c58629
+struct nss_ops_ctx {
c58629
+    unsigned int timeout;
c58629
+};
c58629
+
c58629
+static enum nss_status __convert_sss_nss2nss_status(int errcode) {
c58629
+    switch(errcode) {
c58629
+    case 0:
c58629
+        return NSS_STATUS_SUCCESS;
c58629
+    case ENOENT:
c58629
+        return NSS_STATUS_NOTFOUND;
c58629
+    case ETIME:
c58629
+        /* fall-through */
c58629
+    case ERANGE:
c58629
+        return NSS_STATUS_TRYAGAIN;
c58629
+    case ETIMEDOUT:
c58629
+        /* fall-through */
c58629
+    default:
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+    return NSS_STATUS_UNAVAIL;
c58629
+}
c58629
+
c58629
+int back_extdom_init_context(struct nss_ops_ctx **nss_context)
c58629
+{
c58629
+    struct nss_ops_ctx *ctx = NULL;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return EINVAL;
c58629
+    }
c58629
+
c58629
+    ctx = calloc(1, sizeof(struct nss_ops_ctx));
c58629
+
c58629
+    if (ctx == NULL) {
c58629
+        return ENOMEM;
c58629
+    }
c58629
+    *nss_context = ctx;
c58629
+    return 0;
c58629
+}
c58629
+
c58629
+void back_extdom_free_context(struct nss_ops_ctx **nss_context)
c58629
+{
c58629
+    if ((nss_context == NULL) || (*nss_context == NULL)) {
c58629
+        return;
c58629
+    }
c58629
+
c58629
+    free((*nss_context));
c58629
+    *nss_context = NULL;
c58629
+}
c58629
+
c58629
+
c58629
+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context,
c58629
+                             unsigned int timeout) {
c58629
+    if (nss_context == NULL) {
c58629
+        return;
c58629
+    }
c58629
+
c58629
+    nss_context->timeout = timeout;
c58629
+}
c58629
+
c58629
+void back_extdom_evict_user(struct nss_ops_ctx *nss_context,
c58629
+                            const char *name) {
c58629
+    if (nss_context == NULL) {
c58629
+        return;
c58629
+    }
c58629
+
c58629
+    (void) sss_nss_getpwnam_timeout(name, NULL,
c58629
+                                    NULL, 0,
c58629
+                                    NULL,
c58629
+                                    SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
c58629
+                                    nss_context->timeout);
c58629
+}
c58629
+
c58629
+void back_extdom_evict_group(struct nss_ops_ctx *nss_context,
c58629
+                             const char *name) {
c58629
+    if (nss_context == NULL) {
c58629
+            return;
c58629
+    }
c58629
+
c58629
+    (void) sss_nss_getgrnam_timeout(name, NULL,
c58629
+                                    NULL, 0,
c58629
+                                    NULL,
c58629
+                                    SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
c58629
+                                    nss_context->timeout);
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno) {
c58629
+    int ret = 0;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = sss_nss_getpwnam_timeout(name, pwd,
c58629
+                                   buffer, buflen,
c58629
+                                   result,
c58629
+                                   SSS_NSS_EX_FLAG_NO_FLAGS,
c58629
+                                   nss_context->timeout);
c58629
+
c58629
+    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
c58629
+     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
c58629
+     * but writes down errno into return code so we propagate it in case
c58629
+     * of error and translate the return code */
c58629
+    if (lerrno != NULL) {
c58629
+        *lerrno = ret;
c58629
+    }
c58629
+    return __convert_sss_nss2nss_status(ret);
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context,
c58629
+                                     uid_t uid, struct passwd *pwd,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct passwd **result,
c58629
+                                     int *lerrno) {
c58629
+
c58629
+    int ret = 0;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = sss_nss_getpwuid_timeout(uid, pwd,
c58629
+                                   buffer, buflen,
c58629
+                                   result,
c58629
+                                   SSS_NSS_EX_FLAG_NO_FLAGS,
c58629
+                                   nss_context->timeout);
c58629
+
c58629
+    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
c58629
+     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
c58629
+     * but writes down errno into return code so we propagate it in case
c58629
+     * of error and translate the return code */
c58629
+    if (lerrno != NULL) {
c58629
+        *lerrno = ret;
c58629
+    }
c58629
+    return __convert_sss_nss2nss_status(ret);
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context,
c58629
+                                     const char *name, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno) {
c58629
+
c58629
+    int ret = 0;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = sss_nss_getgrnam_timeout(name, grp,
c58629
+                                   buffer, buflen,
c58629
+                                   result,
c58629
+                                   SSS_NSS_EX_FLAG_NO_FLAGS,
c58629
+                                   nss_context->timeout);
c58629
+
c58629
+    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
c58629
+     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
c58629
+     * but writes down errno into return code so we propagate it in case
c58629
+     * of error and translate the return code */
c58629
+    if (lerrno != NULL) {
c58629
+        *lerrno = ret;
c58629
+    }
c58629
+    return __convert_sss_nss2nss_status(ret);
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context,
c58629
+                                     gid_t gid, struct group *grp,
c58629
+                                     char *buffer, size_t buflen,
c58629
+                                     struct group **result,
c58629
+                                     int *lerrno) {
c58629
+
c58629
+    int ret = 0;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = sss_nss_getgrgid_timeout(gid, grp,
c58629
+                                   buffer, buflen,
c58629
+                                   result,
c58629
+                                   SSS_NSS_EX_FLAG_NO_FLAGS,
c58629
+                                   nss_context->timeout);
c58629
+
c58629
+    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
c58629
+     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
c58629
+     * but writes down errno into return code so we propagate it in case
c58629
+     * of error and translate the return code */
c58629
+    if (lerrno != NULL) {
c58629
+        *lerrno = ret;
c58629
+    }
c58629
+    return __convert_sss_nss2nss_status(ret);
c58629
+}
c58629
+
c58629
+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context,
c58629
+                                         const char *name, gid_t group,
c58629
+                                         gid_t *groups, int *ngroups,
c58629
+                                         int *lerrno) {
c58629
+    int ret = 0;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return NSS_STATUS_UNAVAIL;
c58629
+    }
c58629
+
c58629
+    ret = sss_nss_getgrouplist_timeout(name, group,
c58629
+                                       groups, ngroups,
c58629
+                                       SSS_NSS_EX_FLAG_NO_FLAGS,
c58629
+                                       nss_context->timeout);
c58629
+
c58629
+    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
c58629
+     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
c58629
+     * but writes down errno into return code so we propagate it in case
c58629
+     * of error and translate the return code */
c58629
+    if (lerrno != NULL) {
c58629
+        *lerrno = ret;
c58629
+    }
c58629
+    return __convert_sss_nss2nss_status(ret);
c58629
+}
c58629
+
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h
c58629
index bc29f069816b0ce13578c9ae14c61edb832d44e4..bbc574747e8bbe045dfc9882198cb34b0bb8cab9 100644
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h
c58629
@@ -150,10 +150,13 @@ struct extdom_res {
c58629
     } data;
c58629
 };
c58629
 
c58629
+struct nss_ops_ctx;
c58629
+
c58629
 struct ipa_extdom_ctx {
c58629
     Slapi_ComponentId *plugin_id;
c58629
     char *base_dn;
c58629
     size_t max_nss_buf_size;
c58629
+    struct nss_ops_ctx *nss_ctx;
c58629
 };
c58629
 
c58629
 struct domain_info {
c58629
@@ -179,15 +182,15 @@ int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req,
c58629
                    struct berval **berval);
c58629
 int pack_response(struct extdom_res *res, struct berval **ret_val);
c58629
 int get_buffer(size_t *_buf_len, char **_buf);
c58629
-int getpwnam_r_wrapper(size_t buf_max, const char *name,
c58629
+int getpwnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name,
c58629
                        struct passwd *pwd, char **_buf, size_t *_buf_len);
c58629
-int getpwuid_r_wrapper(size_t buf_max, uid_t uid,
c58629
+int getpwuid_r_wrapper(struct ipa_extdom_ctx *ctx, uid_t uid,
c58629
                        struct passwd *pwd, char **_buf, size_t *_buf_len);
c58629
-int getgrnam_r_wrapper(size_t buf_max, const char *name,
c58629
+int getgrnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name,
c58629
                        struct group *grp, char **_buf, size_t *_buf_len);
c58629
-int getgrgid_r_wrapper(size_t buf_max, gid_t gid,
c58629
+int getgrgid_r_wrapper(struct ipa_extdom_ctx *ctx, gid_t gid,
c58629
                        struct group *grp, char **_buf, size_t *_buf_len);
c58629
-int get_user_grouplist(const char *name, gid_t gid,
c58629
+int get_user_grouplist(struct ipa_extdom_ctx *ctx, const char *name, gid_t gid,
c58629
                        size_t *_ngroups, gid_t **_groups);
c58629
 int pack_ber_sid(const char *sid, struct berval **berval);
c58629
 int pack_ber_name(const char *domain_name, const char *name,
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c
c58629
index 526f903d2416e62ee5781909c234bd5ee2d89183..29699cfa390f5469d7c009388b90e68616cbf984 100644
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c
c58629
@@ -19,6 +19,7 @@
c58629
     You should have received a copy of the GNU General Public License
c58629
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
c58629
 */
c58629
+#define _GNU_SOURCE
c58629
 
c58629
 #include <errno.h>
c58629
 #include <stdarg.h>
c58629
@@ -31,24 +32,166 @@
c58629
 
c58629
 
c58629
 #include "ipa_extdom.h"
c58629
+#include "back_extdom.h"
c58629
+#include <stdio.h>
c58629
+#include <dlfcn.h>
c58629
 
c58629
 #define MAX_BUF (1024*1024*1024)
c58629
+struct test_data {
c58629
+    struct extdom_req *req;
c58629
+    struct ipa_extdom_ctx *ctx;
c58629
+};
c58629
+
c58629
+/*
c58629
+ * redefine logging for mocks
c58629
+ */
c58629
+#ifdef __GNUC__
c58629
+    __attribute__((format(printf, 3, 4)))
c58629
+#endif
c58629
+int slapi_log_error(int loglevel, char *subsystem, char *fmt, ...)
c58629
+{
c58629
+    va_list ap;
c58629
+    va_start(ap, fmt);
c58629
+    vprint_error(fmt, ap);
c58629
+    va_end(ap);
c58629
+    return 0;
c58629
+}
c58629
+
c58629
+
c58629
+/*
c58629
+ * We cannot run cmocka tests against SSSD as that would require to set up SSSD
c58629
+ * and the rest of environment. Instead, we compile cmocka tests against
c58629
+ * back_extdom_nss_sss.c and re-define context initialization to use
c58629
+ * nsswrapper with our test data.
c58629
+ *
c58629
+ * This means we have to keep struct nss_ops_ctx definition in sync with tests!
c58629
+ */
c58629
+
c58629
+struct nss_ops_ctx {
c58629
+    void *dl_handle;
c58629
+    long int initgroups_start;
c58629
+
c58629
+    enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getgrnam_r)(const char *name, struct group *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
c58629
+                                  char *buffer, size_t buflen, int *errnop);
c58629
+    enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
c58629
+                                      long int *start, long int *size,
c58629
+                                      gid_t **groups, long int limit,
c58629
+                                      int *errnop);
c58629
+};
c58629
+
c58629
+int cmocka_extdom_init_context(struct nss_ops_ctx **nss_context)
c58629
+{
c58629
+    struct nss_ops_ctx *ctx = NULL;
c58629
+
c58629
+    if (nss_context == NULL) {
c58629
+        return -1;
c58629
+    }
c58629
+
c58629
+    ctx = calloc(1, sizeof(struct nss_ops_ctx));
c58629
+
c58629
+    if (ctx == NULL) {
c58629
+        return ENOMEM;
c58629
+    }
c58629
+    *nss_context = ctx;
c58629
+
c58629
+    ctx->dl_handle = dlopen("libnss_files.so.2", RTLD_NOW);
c58629
+    if (ctx->dl_handle == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_files_getpwnam_r");
c58629
+    if (ctx->getpwnam_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_files_getpwuid_r");
c58629
+    if (ctx->getpwuid_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_files_getgrnam_r");
c58629
+    if (ctx->getgrnam_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_files_getgrgid_r");
c58629
+    if (ctx->getgrgid_r == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_files_initgroups_dyn");
c58629
+    if (ctx->initgroups_dyn == NULL) {
c58629
+        goto fail;
c58629
+    }
c58629
+
c58629
+    return 0;
c58629
+
c58629
+fail:
c58629
+    back_extdom_free_context(nss_context);
c58629
+
c58629
+    return -1;
c58629
+}
c58629
+
c58629
+struct {
c58629
+    const char *o, *n;
c58629
+} path_table[] = {
c58629
+    { .o = "/etc/passwd", .n = "./test_data/passwd"},
c58629
+    { .o = "/etc/group",  .n = "./test_data/group"},
c58629
+    { .o = NULL, .n = NULL}};
c58629
+
c58629
+FILE *(*original_fopen)(const char*, const char*) = NULL;
c58629
+
c58629
+FILE *fopen(const char *path, const char *mode) {
c58629
+    const char *_path = NULL;
c58629
+
c58629
+    /* Do not handle before-main() cases */
c58629
+    if (original_fopen == NULL) {
c58629
+        return NULL;
c58629
+    }
c58629
+    for(int i=0; path_table[i].o != NULL; i++) {
c58629
+        if (strcmp(path, path_table[i].o) == 0) {
c58629
+                _path = path_table[i].n;
c58629
+                break;
c58629
+        }
c58629
+    }
c58629
+    return (*original_fopen)(_path ? _path : path, mode);
c58629
+}
c58629
+
c58629
+/* Attempt to initialize original_fopen before main()
c58629
+ * There is no explicit order when all initializers are called,
c58629
+ * so we might still be late here compared to a code in a shared
c58629
+ * library initializer, like libselinux */
c58629
+void redefined_fopen_ctor (void) __attribute__ ((constructor));
c58629
+void redefined_fopen_ctor(void) {
c58629
+    original_fopen = dlsym(RTLD_NEXT, "fopen");
c58629
+}
c58629
 
c58629
 void test_getpwnam_r_wrapper(void **state)
c58629
 {
c58629
     int ret;
c58629
     struct passwd pwd;
c58629
     char *buf;
c58629
-    size_t buf_len;
c58629
+    size_t buf_len, max_big_buf_len;
c58629
+    struct test_data *test_data;
c58629
+
c58629
+    test_data = (struct test_data *) *state;
c58629
 
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwnam_r_wrapper(MAX_BUF, "non_exisiting_user", &pwd, &buf,
c58629
-                             &buf_len);
c58629
+    ret = getpwnam_r_wrapper(test_data->ctx,
c58629
+                             "non_exisiting_user", &pwd,
c58629
+                             &buf, &buf_len);
c58629
     assert_int_equal(ret, ENOENT);
c58629
 
c58629
-    ret = getpwnam_r_wrapper(MAX_BUF, "user", &pwd, &buf, &buf_len);
c58629
+    ret = getpwnam_r_wrapper(test_data->ctx,
c58629
+                             "user", &pwd, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(pwd.pw_name, "user");
c58629
     assert_string_equal(pwd.pw_passwd, "x");
c58629
@@ -62,7 +205,8 @@ void test_getpwnam_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwnam_r_wrapper(MAX_BUF, "user_big", &pwd, &buf, &buf_len);
c58629
+    ret = getpwnam_r_wrapper(test_data->ctx,
c58629
+                             "user_big", &pwd, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(pwd.pw_name, "user_big");
c58629
     assert_string_equal(pwd.pw_passwd, "x");
c58629
@@ -76,7 +220,11 @@ void test_getpwnam_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwnam_r_wrapper(1024, "user_big", &pwd, &buf, &buf_len);
c58629
+    max_big_buf_len = test_data->ctx->max_nss_buf_size;
c58629
+    test_data->ctx->max_nss_buf_size = 1024;
c58629
+    ret = getpwnam_r_wrapper(test_data->ctx,
c58629
+                             "user_big", &pwd, &buf, &buf_len);
c58629
+    test_data->ctx->max_nss_buf_size = max_big_buf_len;
c58629
     assert_int_equal(ret, ERANGE);
c58629
     free(buf);
c58629
 }
c58629
@@ -86,15 +234,18 @@ void test_getpwuid_r_wrapper(void **state)
c58629
     int ret;
c58629
     struct passwd pwd;
c58629
     char *buf;
c58629
-    size_t buf_len;
c58629
+    size_t buf_len, max_big_buf_len;
c58629
+    struct test_data *test_data;
c58629
+
c58629
+    test_data = (struct test_data *) *state;
c58629
 
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwuid_r_wrapper(MAX_BUF, 99999, &pwd, &buf, &buf_len);
c58629
+    ret = getpwuid_r_wrapper(test_data->ctx, 99999, &pwd, &buf, &buf_len);
c58629
     assert_int_equal(ret, ENOENT);
c58629
 
c58629
-    ret = getpwuid_r_wrapper(MAX_BUF, 12345, &pwd, &buf, &buf_len);
c58629
+    ret = getpwuid_r_wrapper(test_data->ctx, 12345, &pwd, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(pwd.pw_name, "user");
c58629
     assert_string_equal(pwd.pw_passwd, "x");
c58629
@@ -108,7 +259,7 @@ void test_getpwuid_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwuid_r_wrapper(MAX_BUF, 12346, &pwd, &buf, &buf_len);
c58629
+    ret = getpwuid_r_wrapper(test_data->ctx, 12346, &pwd, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(pwd.pw_name, "user_big");
c58629
     assert_string_equal(pwd.pw_passwd, "x");
c58629
@@ -122,7 +273,10 @@ void test_getpwuid_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getpwuid_r_wrapper(1024, 12346, &pwd, &buf, &buf_len);
c58629
+    max_big_buf_len = test_data->ctx->max_nss_buf_size;
c58629
+    test_data->ctx->max_nss_buf_size = 1024;
c58629
+    ret = getpwuid_r_wrapper(test_data->ctx, 12346, &pwd, &buf, &buf_len);
c58629
+    test_data->ctx->max_nss_buf_size = max_big_buf_len;
c58629
     assert_int_equal(ret, ERANGE);
c58629
     free(buf);
c58629
 }
c58629
@@ -132,15 +286,19 @@ void test_getgrnam_r_wrapper(void **state)
c58629
     int ret;
c58629
     struct group grp;
c58629
     char *buf;
c58629
-    size_t buf_len;
c58629
+    size_t buf_len, max_big_buf_len;
c58629
+    struct test_data *test_data;
c58629
+
c58629
+    test_data = (struct test_data *) *state;
c58629
 
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrnam_r_wrapper(MAX_BUF, "non_exisiting_group", &grp, &buf, &buf_len);
c58629
+    ret = getgrnam_r_wrapper(test_data->ctx,
c58629
+                             "non_exisiting_group", &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, ENOENT);
c58629
 
c58629
-    ret = getgrnam_r_wrapper(MAX_BUF, "group", &grp, &buf, &buf_len);
c58629
+    ret = getgrnam_r_wrapper(test_data->ctx, "group", &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(grp.gr_name, "group");
c58629
     assert_string_equal(grp.gr_passwd, "x");
c58629
@@ -153,7 +311,7 @@ void test_getgrnam_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrnam_r_wrapper(MAX_BUF, "group_big", &grp, &buf, &buf_len);
c58629
+    ret = getgrnam_r_wrapper(test_data->ctx, "group_big", &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(grp.gr_name, "group_big");
c58629
     assert_string_equal(grp.gr_passwd, "x");
c58629
@@ -165,7 +323,10 @@ void test_getgrnam_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrnam_r_wrapper(1024, "group_big", &grp, &buf, &buf_len);
c58629
+    max_big_buf_len = test_data->ctx->max_nss_buf_size;
c58629
+    test_data->ctx->max_nss_buf_size = 1024;
c58629
+    ret = getgrnam_r_wrapper(test_data->ctx, "group_big", &grp, &buf, &buf_len);
c58629
+    test_data->ctx->max_nss_buf_size = max_big_buf_len;
c58629
     assert_int_equal(ret, ERANGE);
c58629
     free(buf);
c58629
 }
c58629
@@ -175,15 +336,18 @@ void test_getgrgid_r_wrapper(void **state)
c58629
     int ret;
c58629
     struct group grp;
c58629
     char *buf;
c58629
-    size_t buf_len;
c58629
+    size_t buf_len, max_big_buf_len;
c58629
+    struct test_data *test_data;
c58629
+
c58629
+    test_data = (struct test_data *) *state;
c58629
 
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrgid_r_wrapper(MAX_BUF, 99999, &grp, &buf, &buf_len);
c58629
+    ret = getgrgid_r_wrapper(test_data->ctx, 99999, &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, ENOENT);
c58629
 
c58629
-    ret = getgrgid_r_wrapper(MAX_BUF, 11111, &grp, &buf, &buf_len);
c58629
+    ret = getgrgid_r_wrapper(test_data->ctx, 11111, &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(grp.gr_name, "group");
c58629
     assert_string_equal(grp.gr_passwd, "x");
c58629
@@ -196,7 +360,7 @@ void test_getgrgid_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrgid_r_wrapper(MAX_BUF, 22222, &grp, &buf, &buf_len);
c58629
+    ret = getgrgid_r_wrapper(test_data->ctx, 22222, &grp, &buf, &buf_len);
c58629
     assert_int_equal(ret, 0);
c58629
     assert_string_equal(grp.gr_name, "group_big");
c58629
     assert_string_equal(grp.gr_passwd, "x");
c58629
@@ -208,7 +372,10 @@ void test_getgrgid_r_wrapper(void **state)
c58629
     ret = get_buffer(&buf_len, &buf;;
c58629
     assert_int_equal(ret, 0);
c58629
 
c58629
-    ret = getgrgid_r_wrapper(1024, 22222, &grp, &buf, &buf_len);
c58629
+    max_big_buf_len = test_data->ctx->max_nss_buf_size;
c58629
+    test_data->ctx->max_nss_buf_size = 1024;
c58629
+    ret = getgrgid_r_wrapper(test_data->ctx, 22222, &grp, &buf, &buf_len);
c58629
+    test_data->ctx->max_nss_buf_size = max_big_buf_len;
c58629
     assert_int_equal(ret, ERANGE);
c58629
     free(buf);
c58629
 }
c58629
@@ -219,16 +386,21 @@ void test_get_user_grouplist(void **state)
c58629
     size_t ngroups;
c58629
     gid_t *groups;
c58629
     size_t c;
c58629
+    struct test_data *test_data;
c58629
+
c58629
+    test_data = (struct test_data *) *state;
c58629
 
c58629
     /* This is a bit odd behaviour of getgrouplist() it does not check if the
c58629
      * user exists, only if memberships of the user can be found. */
c58629
-    ret = get_user_grouplist("non_exisiting_user", 23456, &ngroups, &groups);
c58629
+    ret = get_user_grouplist(test_data->ctx,
c58629
+                             "non_exisiting_user", 23456, &ngroups, &groups);
c58629
     assert_int_equal(ret, LDAP_SUCCESS);
c58629
     assert_int_equal(ngroups, 1);
c58629
     assert_int_equal(groups[0], 23456);
c58629
     free(groups);
c58629
 
c58629
-    ret = get_user_grouplist("member0001", 23456, &ngroups, &groups);
c58629
+    ret = get_user_grouplist(test_data->ctx,
c58629
+                             "member0001", 23456, &ngroups, &groups);
c58629
     assert_int_equal(ret, LDAP_SUCCESS);
c58629
     assert_int_equal(ngroups, 3);
c58629
     assert_int_equal(groups[0], 23456);
c58629
@@ -236,14 +408,16 @@ void test_get_user_grouplist(void **state)
c58629
     assert_int_equal(groups[2], 22222);
c58629
     free(groups);
c58629
 
c58629
-    ret = get_user_grouplist("member0003", 23456, &ngroups, &groups);
c58629
+    ret = get_user_grouplist(test_data->ctx,
c58629
+                             "member0003", 23456, &ngroups, &groups);
c58629
     assert_int_equal(ret, LDAP_SUCCESS);
c58629
     assert_int_equal(ngroups, 2);
c58629
     assert_int_equal(groups[0], 23456);
c58629
     assert_int_equal(groups[1], 22222);
c58629
     free(groups);
c58629
 
c58629
-    ret = get_user_grouplist("user_big", 23456, &ngroups, &groups);
c58629
+    ret = get_user_grouplist(test_data->ctx,
c58629
+                             "user_big", 23456, &ngroups, &groups);
c58629
     assert_int_equal(ret, LDAP_SUCCESS);
c58629
     assert_int_equal(ngroups, 1001);
c58629
     assert_int_equal(groups[0], 23456);
c58629
@@ -253,11 +427,6 @@ void test_get_user_grouplist(void **state)
c58629
     free(groups);
c58629
 }
c58629
 
c58629
-struct test_data {
c58629
-    struct extdom_req *req;
c58629
-    struct ipa_extdom_ctx *ctx;
c58629
-};
c58629
-
c58629
 static int  extdom_req_setup(void **state)
c58629
 {
c58629
     struct test_data *test_data;
c58629
@@ -269,8 +438,14 @@ static int  extdom_req_setup(void **state)
c58629
     assert_non_null(test_data->req);
c58629
 
c58629
     test_data->ctx = calloc(sizeof(struct ipa_extdom_ctx), 1);
c58629
-    assert_non_null(test_data->req);
c58629
+    assert_non_null(test_data->ctx);
c58629
+
c58629
+    test_data->ctx->max_nss_buf_size = MAX_BUF;
c58629
+
c58629
+    assert_int_equal(cmocka_extdom_init_context(&test_data->ctx->nss_ctx), 0);
c58629
+    assert_non_null(test_data->ctx->nss_ctx);
c58629
 
c58629
+    back_extdom_set_timeout(test_data->ctx->nss_ctx, 10000);
c58629
     *state = test_data;
c58629
 
c58629
     return 0;
c58629
@@ -283,6 +458,7 @@ static int  extdom_req_teardown(void **state)
c58629
     test_data = (struct test_data *) *state;
c58629
 
c58629
     free_req_data(test_data->req);
c58629
+    back_extdom_free_context(&test_data->ctx->nss_ctx);
c58629
     free(test_data->ctx);
c58629
     free(test_data);
c58629
 
c58629
@@ -450,5 +626,6 @@ int main(int argc, const char *argv[])
c58629
         cmocka_unit_test(test_decode),
c58629
     };
c58629
 
c58629
-    return cmocka_run_group_tests(tests, NULL, NULL);
c58629
+    assert_non_null(original_fopen);
c58629
+    return cmocka_run_group_tests(tests, extdom_req_setup, extdom_req_teardown);
c58629
 }
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c
c58629
index fe225fa86669a6728bec5014be41d80275f10717..86c6638ba73c2f59aff29191e3e68dc5c85d50fc 100644
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c
c58629
@@ -43,11 +43,12 @@
c58629
 
c58629
 #include <errno.h>
c58629
 #include <stdio.h>
c58629
+#include <sys/param.h>
c58629
 
c58629
 #include "ipa_extdom.h"
c58629
+#include "back_extdom.h"
c58629
 #include "util.h"
c58629
 
c58629
-#define MAX(a,b) (((a)>(b))?(a):(b))
c58629
 #define SSSD_DOMAIN_SEPARATOR '@'
c58629
 
c58629
 int get_buffer(size_t *_buf_len, char **_buf)
c58629
@@ -97,134 +98,137 @@ static int inc_buffer(size_t buf_max, size_t *_buf_len, char **_buf)
c58629
     return 0;
c58629
 }
c58629
 
c58629
-int getpwnam_r_wrapper(size_t buf_max, const char *name,
c58629
-                       struct passwd *pwd, char **_buf, size_t *_buf_len)
c58629
+int __nss_to_err(enum nss_status errcode)
c58629
 {
c58629
-    char *buf = NULL;
c58629
-    size_t buf_len = 0;
c58629
-    int ret;
c58629
-    struct passwd *result = NULL;
c58629
+    switch(errcode) {
c58629
+    case NSS_STATUS_SUCCESS:
c58629
+        return 0;
c58629
+    case NSS_STATUS_NOTFOUND:
c58629
+        return ENOENT;
c58629
+    case NSS_STATUS_TRYAGAIN:
c58629
+        return ERANGE;
c58629
+    case NSS_STATUS_UNAVAIL:
c58629
+        return ETIMEDOUT;
c58629
+    }
c58629
 
c58629
-    buf = *_buf;
c58629
-    buf_len = *_buf_len;
c58629
+    return -1;
c58629
+}
c58629
 
c58629
-    while (buf != NULL
c58629
-            && (ret = getpwnam_r(name, pwd, buf, buf_len, &result)) == ERANGE) {
c58629
-        ret = inc_buffer(buf_max, &buf_len, &buf;;
c58629
-        if (ret != 0) {
c58629
-            if (ret == ERANGE) {
c58629
-                LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
-            }
c58629
-            goto done;
c58629
+int getpwnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name,
c58629
+                       struct passwd *pwd, char **buf, size_t *buf_len)
c58629
+{
c58629
+    int ret, lerrno = 0;
c58629
+    struct passwd *result = NULL;
c58629
+    enum nss_status rc;
c58629
+
c58629
+    for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) {
c58629
+        rc = back_extdom_getpwnam(ctx->nss_ctx, name, pwd, *buf, *buf_len, &result, &lerrno);
c58629
+        ret = __nss_to_err(rc);
c58629
+        if (ret == ERANGE) {
c58629
+            ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf);
c58629
+            if (ret != 0) goto done;
c58629
         }
c58629
     }
c58629
 
c58629
-    if (ret == 0 && result == NULL) {
c58629
-        ret = ENOENT;
c58629
-    }
c58629
-
c58629
 done:
c58629
-    *_buf = buf;
c58629
-    *_buf_len = buf_len;
c58629
-
c58629
+    switch(ret) {
c58629
+    case 0:
c58629
+        if (result == NULL) ret = ENOENT;
c58629
+        break;
c58629
+    case ERANGE:
c58629
+        LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
+    default:
c58629
+        break;
c58629
+    }
c58629
     return ret;
c58629
 }
c58629
 
c58629
-int getpwuid_r_wrapper(size_t buf_max, uid_t uid,
c58629
-                       struct passwd *pwd, char **_buf, size_t *_buf_len)
c58629
+int getpwuid_r_wrapper(struct ipa_extdom_ctx *ctx, uid_t uid,
c58629
+                       struct passwd *pwd, char **buf, size_t *buf_len)
c58629
 {
c58629
-    char *buf = NULL;
c58629
-    size_t buf_len = 0;
c58629
-    int ret;
c58629
+    int ret, lerrno;
c58629
     struct passwd *result = NULL;
c58629
-
c58629
-    buf = *_buf;
c58629
-    buf_len = *_buf_len;
c58629
-
c58629
-    while (buf != NULL
c58629
-            && (ret = getpwuid_r(uid, pwd, buf, buf_len, &result)) == ERANGE) {
c58629
-        ret = inc_buffer(buf_max, &buf_len, &buf;;
c58629
-        if (ret != 0) {
c58629
-            if (ret == ERANGE) {
c58629
-                LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
-            }
c58629
-            goto done;
c58629
+    enum nss_status rc;
c58629
+
c58629
+    for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) {
c58629
+        rc = back_extdom_getpwuid(ctx->nss_ctx, uid, pwd, *buf, *buf_len, &result, &lerrno);
c58629
+        ret = __nss_to_err(rc);
c58629
+        if (ret == ERANGE) {
c58629
+            ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf);
c58629
+            if (ret != 0) goto done;
c58629
         }
c58629
     }
c58629
 
c58629
-    if (ret == 0 && result == NULL) {
c58629
-        ret = ENOENT;
c58629
-    }
c58629
-
c58629
 done:
c58629
-    *_buf = buf;
c58629
-    *_buf_len = buf_len;
c58629
+    switch(ret) {
c58629
+    case 0:
c58629
+        if (result == NULL) ret = ENOENT;
c58629
+        break;
c58629
+    case ERANGE:
c58629
+        LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
+    default:
c58629
+        break;
c58629
+    }
c58629
 
c58629
     return ret;
c58629
 }
c58629
 
c58629
-int getgrnam_r_wrapper(size_t buf_max, const char *name,
c58629
-                       struct group *grp, char **_buf, size_t *_buf_len)
c58629
+int getgrnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name,
c58629
+                       struct group *grp, char **buf, size_t *buf_len)
c58629
 {
c58629
-    char *buf = NULL;
c58629
-    size_t buf_len = 0;
c58629
-    int ret;
c58629
+    int ret, lerrno;
c58629
     struct group *result = NULL;
c58629
-
c58629
-    buf = *_buf;
c58629
-    buf_len = *_buf_len;
c58629
-
c58629
-    while (buf != NULL
c58629
-            && (ret = getgrnam_r(name, grp, buf, buf_len, &result)) == ERANGE) {
c58629
-        ret = inc_buffer(buf_max, &buf_len, &buf;;
c58629
-        if (ret != 0) {
c58629
-            if (ret == ERANGE) {
c58629
-                LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
-            }
c58629
-            goto done;
c58629
+    enum nss_status rc;
c58629
+
c58629
+    for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) {
c58629
+        rc = back_extdom_getgrnam(ctx->nss_ctx, name, grp, *buf, *buf_len, &result, &lerrno);
c58629
+        ret = __nss_to_err(rc);
c58629
+        if (ret == ERANGE) {
c58629
+            ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf);
c58629
+            if (ret != 0) goto done;
c58629
         }
c58629
     }
c58629
 
c58629
-    if (ret == 0 && result == NULL) {
c58629
-        ret = ENOENT;
c58629
-    }
c58629
-
c58629
 done:
c58629
-    *_buf = buf;
c58629
-    *_buf_len = buf_len;
c58629
+    switch(ret) {
c58629
+    case 0:
c58629
+        if (result == NULL) ret = ENOENT;
c58629
+        break;
c58629
+    case ERANGE:
c58629
+        LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
+    default:
c58629
+        break;
c58629
+    }
c58629
 
c58629
     return ret;
c58629
 }
c58629
 
c58629
-int getgrgid_r_wrapper(size_t buf_max, gid_t gid,
c58629
-                       struct group *grp, char **_buf, size_t *_buf_len)
c58629
+int getgrgid_r_wrapper(struct ipa_extdom_ctx *ctx, gid_t gid,
c58629
+                       struct group *grp, char **buf, size_t *buf_len)
c58629
 {
c58629
-    char *buf = NULL;
c58629
-    size_t buf_len = 0;
c58629
-    int ret;
c58629
+    int ret, lerrno;
c58629
     struct group *result = NULL;
c58629
-
c58629
-    buf = *_buf;
c58629
-    buf_len = *_buf_len;
c58629
-
c58629
-    while (buf != NULL
c58629
-            && (ret = getgrgid_r(gid, grp, buf, buf_len, &result)) == ERANGE) {
c58629
-        ret = inc_buffer(buf_max, &buf_len, &buf;;
c58629
-        if (ret != 0) {
c58629
-            if (ret == ERANGE) {
c58629
-                LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
-            }
c58629
-            goto done;
c58629
+    enum nss_status rc;
c58629
+
c58629
+    for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) {
c58629
+        rc = back_extdom_getgrgid(ctx->nss_ctx, gid, grp, *buf, *buf_len, &result, &lerrno);
c58629
+        ret = __nss_to_err(rc);
c58629
+        if (ret == ERANGE) {
c58629
+            ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf);
c58629
+            if (ret != 0) goto done;
c58629
         }
c58629
     }
c58629
 
c58629
-    if (ret == 0 && result == NULL) {
c58629
-        ret = ENOENT;
c58629
-    }
c58629
-
c58629
 done:
c58629
-    *_buf = buf;
c58629
-    *_buf_len = buf_len;
c58629
+    switch(ret) {
c58629
+    case 0:
c58629
+        if (result == NULL) ret = ENOENT;
c58629
+        break;
c58629
+    case ERANGE:
c58629
+        LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
c58629
+    default:
c58629
+        break;
c58629
+    }
c58629
 
c58629
     return ret;
c58629
 }
c58629
@@ -406,13 +410,14 @@ int check_request(struct extdom_req *req, enum extdom_version version)
c58629
     return LDAP_SUCCESS;
c58629
 }
c58629
 
c58629
-int get_user_grouplist(const char *name, gid_t gid,
c58629
+int get_user_grouplist(struct ipa_extdom_ctx *ctx, const char *name, gid_t gid,
c58629
                        size_t *_ngroups, gid_t **_groups)
c58629
 {
c58629
-    int ret;
c58629
+    int lerrno;
c58629
     int ngroups;
c58629
     gid_t *groups;
c58629
     gid_t *new_groups;
c58629
+    enum nss_status rc;
c58629
 
c58629
     ngroups = 128;
c58629
     groups = malloc(ngroups * sizeof(gid_t));
c58629
@@ -420,19 +425,18 @@ int get_user_grouplist(const char *name, gid_t gid,
c58629
         return LDAP_OPERATIONS_ERROR;
c58629
     }
c58629
 
c58629
-    ret = getgrouplist(name, gid, groups, &ngroups);
c58629
-    if (ret == -1) {
c58629
-        new_groups = realloc(groups, ngroups * sizeof(gid_t));
c58629
-        if (new_groups == NULL) {
c58629
-            free(groups);
c58629
-            return LDAP_OPERATIONS_ERROR;
c58629
-        }
c58629
-        groups = new_groups;
c58629
-
c58629
-        ret = getgrouplist(name, gid, groups, &ngroups);
c58629
-        if (ret == -1) {
c58629
-            free(groups);
c58629
-            return LDAP_OPERATIONS_ERROR;
c58629
+    for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) {
c58629
+        rc = back_extdom_getgrouplist(ctx->nss_ctx, name, gid, groups, &ngroups, &lerrno);
c58629
+        if (rc == NSS_STATUS_TRYAGAIN) {
c58629
+            new_groups = NULL;
c58629
+            if (lerrno == ERANGE) {
c58629
+                new_groups = realloc(groups, ngroups * sizeof(gid_t));
c58629
+            }
c58629
+            if ((new_groups == NULL) || (lerrno == ENOMEM)) {
c58629
+                free(groups);
c58629
+                return LDAP_OPERATIONS_ERROR;
c58629
+            }
c58629
+            groups = new_groups;
c58629
         }
c58629
     }
c58629
 
c58629
@@ -538,7 +542,7 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx,
c58629
     }
c58629
 
c58629
     if (response_type == RESP_USER_GROUPLIST) {
c58629
-        ret = get_user_grouplist(user_name, gid, &ngroups, &groups);
c58629
+        ret = get_user_grouplist(ctx, user_name, gid, &ngroups, &groups);
c58629
         if (ret != LDAP_SUCCESS) {
c58629
             goto done;
c58629
         }
c58629
@@ -561,7 +565,7 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx,
c58629
         }
c58629
 
c58629
         for (c = 0; c < ngroups; c++) {
c58629
-            ret = getgrgid_r_wrapper(ctx->max_nss_buf_size,
c58629
+            ret = getgrgid_r_wrapper(ctx,
c58629
                                      groups[c], &grp, &buf, &buf_len);
c58629
             if (ret != 0) {
c58629
                 if (ret == ENOMEM || ret == ERANGE) {
c58629
@@ -841,8 +845,7 @@ static int handle_uid_request(struct ipa_extdom_ctx *ctx,
c58629
 
c58629
         ret = pack_ber_sid(sid_str, berval);
c58629
     } else {
c58629
-        ret = getpwuid_r_wrapper(ctx->max_nss_buf_size, uid, &pwd, &buf,
c58629
-                                 &buf_len);
c58629
+        ret = getpwuid_r_wrapper(ctx, uid, &pwd, &buf, &buf_len);
c58629
         if (ret != 0) {
c58629
             if (ret == ENOMEM || ret == ERANGE) {
c58629
                 ret = LDAP_OPERATIONS_ERROR;
c58629
@@ -913,8 +916,7 @@ static int handle_gid_request(struct ipa_extdom_ctx *ctx,
c58629
 
c58629
         ret = pack_ber_sid(sid_str, berval);
c58629
     } else {
c58629
-        ret = getgrgid_r_wrapper(ctx->max_nss_buf_size, gid, &grp, &buf,
c58629
-                                 &buf_len);
c58629
+        ret = getgrgid_r_wrapper(ctx, gid, &grp, &buf, &buf_len);
c58629
         if (ret != 0) {
c58629
             if (ret == ENOMEM || ret == ERANGE) {
c58629
                 ret = LDAP_OPERATIONS_ERROR;
c58629
@@ -1053,8 +1055,7 @@ static int handle_sid_request(struct ipa_extdom_ctx *ctx,
c58629
     switch(id_type) {
c58629
     case SSS_ID_TYPE_UID:
c58629
     case SSS_ID_TYPE_BOTH:
c58629
-        ret = getpwnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &pwd, &buf,
c58629
-                                 &buf_len);
c58629
+        ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len);
c58629
         if (ret != 0) {
c58629
             if (ret == ENOMEM || ret == ERANGE) {
c58629
                 ret = LDAP_OPERATIONS_ERROR;
c58629
@@ -1086,8 +1087,7 @@ static int handle_sid_request(struct ipa_extdom_ctx *ctx,
c58629
                             pwd.pw_shell, kv_list, berval);
c58629
         break;
c58629
     case SSS_ID_TYPE_GID:
c58629
-        ret = getgrnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &grp, &buf,
c58629
-                                 &buf_len);
c58629
+        ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len);
c58629
         if (ret != 0) {
c58629
             if (ret == ENOMEM || ret == ERANGE) {
c58629
                 ret = LDAP_OPERATIONS_ERROR;
c58629
@@ -1181,8 +1181,7 @@ static int handle_name_request(struct ipa_extdom_ctx *ctx,
c58629
             goto done;
c58629
         }
c58629
 
c58629
-        ret = getpwnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &pwd, &buf,
c58629
-                                 &buf_len);
c58629
+        ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len);
c58629
         if (ret == 0) {
c58629
             if (request_type == REQ_FULL_WITH_GROUPS) {
c58629
                 ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type);
c58629
@@ -1211,8 +1210,7 @@ static int handle_name_request(struct ipa_extdom_ctx *ctx,
c58629
              * error codes which can indicate that the user was not found. To
c58629
              * be on the safe side we fail back to the group lookup on all
c58629
              * errors. */
c58629
-            ret = getgrnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &grp, &buf,
c58629
-                                     &buf_len);
c58629
+            ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len);
c58629
             if (ret != 0) {
c58629
                 if (ret == ENOMEM || ret == ERANGE) {
c58629
                     ret = LDAP_OPERATIONS_ERROR;
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
c58629
index 5bc8c2f571e311c5ae4cc56e2e1ae7d4e2f77ee6..83c30e7e6aad72af603c0b4ed1c49b80fa57560f 100644
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
c58629
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
c58629
@@ -38,9 +38,11 @@
c58629
  * END COPYRIGHT BLOCK **/
c58629
 
c58629
 #include "ipa_extdom.h"
c58629
+#include "back_extdom.h"
c58629
 #include "util.h"
c58629
 
c58629
 #define DEFAULT_MAX_NSS_BUFFER (128*1024*1024)
c58629
+#define DEFAULT_MAX_NSS_TIMEOUT (10*1000)
c58629
 
c58629
 Slapi_PluginDesc ipa_extdom_plugin_desc = {
c58629
     IPA_EXTDOM_FEATURE_DESC,
c58629
@@ -166,6 +168,7 @@ static int ipa_extdom_init_ctx(Slapi_PBlock *pb, struct ipa_extdom_ctx **_ctx)
c58629
     struct ipa_extdom_ctx *ctx;
c58629
     Slapi_Entry *e;
c58629
     int ret;
c58629
+    unsigned int timeout;
c58629
 
c58629
     ctx = calloc(1, sizeof(struct ipa_extdom_ctx));
c58629
     if (!ctx) {
c58629
@@ -202,6 +205,20 @@ static int ipa_extdom_init_ctx(Slapi_PBlock *pb, struct ipa_extdom_ctx **_ctx)
c58629
     }
c58629
     LOG("Maximal nss buffer size set to [%zu]!\n", ctx->max_nss_buf_size);
c58629
 
c58629
+
c58629
+    ret = back_extdom_init_context(&ctx->nss_ctx);
c58629
+    if (ret != 0) {
c58629
+        LOG("Unable to initialize nss interface: returned [%d]!\n", ret);
c58629
+        goto done;
c58629
+    }
c58629
+
c58629
+    timeout = slapi_entry_attr_get_uint(e, "ipaExtdomMaxNssTimeout");
c58629
+    if (timeout == 0) {
c58629
+        timeout = DEFAULT_MAX_NSS_TIMEOUT;
c58629
+    }
c58629
+    back_extdom_set_timeout(ctx->nss_ctx, timeout);
c58629
+    LOG("Maximal nss timeout (in ms) set to [%u]!\n", timeout);
c58629
+
c58629
     ret = 0;
c58629
 
c58629
 done:
c58629
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh b/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh
c58629
deleted file mode 100644
c58629
index ad839f340efe989a91cd6902f59c9a41483f68e0..0000000000000000000000000000000000000000
c58629
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh
c58629
+++ /dev/null
c58629
@@ -1,3 +0,0 @@
c58629
-export LD_PRELOAD=$(pkg-config --libs nss_wrapper)
c58629
-export NSS_WRAPPER_PASSWD=./test_data/passwd
c58629
-export NSS_WRAPPER_GROUP=./test_data/group
c58629
diff --git a/freeipa.spec.in b/freeipa.spec.in
c58629
index a8b5ce81fcf9bdb61cd3707e6b68b6f2196e0776..80ae98c5515f64a8df8d981ad5e91b05c84e31c1 100644
c58629
--- a/freeipa.spec.in
c58629
+++ b/freeipa.spec.in
c58629
@@ -246,7 +246,6 @@ BuildRequires:  python3-augeas
c58629
 #
c58629
 %if ! %{ONLY_CLIENT}
c58629
 BuildRequires:  libcmocka-devel
c58629
-BuildRequires:  nss_wrapper
c58629
 # Required by ipa_kdb_tests
c58629
 BuildRequires:  %{_libdir}/krb5/plugins/kdb/db2.so
c58629
 %endif # ONLY_CLIENT
c58629
diff --git a/server.m4 b/server.m4
c58629
index a9670c87372bb7b92d08dad634c0bda123a02597..f0a8bbcc778596dade89d9332abb2939b8a44143 100644
c58629
--- a/server.m4
c58629
+++ b/server.m4
c58629
@@ -35,6 +35,16 @@ AC_CHECK_LIB([sss_nss_idmap],
c58629
              [AC_MSG_ERROR([Required sss_nss_getlistbycert symbol in sss_nss_idmap not found])],
c58629
              [])
c58629
 
c58629
+dnl --- if sss_nss_idmap provides _timeout() API, use it
c58629
+bck_cflags="$CFLAGS"
c58629
+CFLAGS="$CFLAGS -DIPA_389DS_PLUGIN_HELPER_CALLS"
c58629
+AC_CHECK_DECLS([sss_nss_getpwnam_timeout], [], [], [[#include <sss_nss_idmap.h>]])
c58629
+CFLAGS="$bck_cflags"
c58629
+
c58629
+if test "x$ac_cv_have_decl_sss_nss_getpwnam_timeout" = xyes ; then
c58629
+    AC_DEFINE(USE_SSS_NSS_TIMEOUT,1,[Use extended NSS API provided by SSSD])
c58629
+fi
c58629
+
c58629
 dnl -- sss_certmap and certauth.h are needed by the IPA KDB certauth plugin --
c58629
 PKG_CHECK_EXISTS([sss_certmap],
c58629
                  [PKG_CHECK_MODULES([SSSCERTMAP], [sss_certmap])],
c58629
-- 
c58629
2.14.3
c58629