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