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