diff options
author | Alexander Bokovoy <abokovoy@redhat.com> | 2013-07-23 21:08:56 +0300 |
---|---|---|
committer | Alexander Bokovoy <abokovoy@redhat.com> | 2013-07-26 16:10:05 +0300 |
commit | d862fc6fc65f1fb5ff0582e3561a7ab988af9432 (patch) | |
tree | a175494a22ae321ea89c410afb53d79d14d9f375 | |
parent | 633c4c92b73efeb3d660b70480108b0db41fa3bd (diff) | |
download | slapi-nis-freeipa-nss-compat.tar.gz slapi-nis-freeipa-nss-compat.tar.xz slapi-nis-freeipa-nss-compat.zip |
WIP compat plugin: solve deadlock when looking up SSSD usersfreeipa-nss-compat
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | slapi-nis.spec | 6 | ||||
-rw-r--r-- | src/back-sch-pam.c | 44 | ||||
-rw-r--r-- | src/back-sch-sssd.c | 273 | ||||
-rw-r--r-- | src/back-sch.c | 341 | ||||
-rw-r--r-- | src/back-sch.h | 25 | ||||
-rw-r--r-- | src/format.c | 12 | ||||
-rw-r--r-- | src/format.h | 2 | ||||
-rw-r--r-- | src/plug-sch.c | 3 | ||||
-rw-r--r-- | src/plugin.h | 2 |
10 files changed, 451 insertions, 267 deletions
diff --git a/configure.ac b/configure.ac index 085f51d..54464f8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(slapi-nis,0.47) +AC_INIT(slapi-nis,0.47.5) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE(foreign) LT_INIT([disable-static]) @@ -321,18 +321,18 @@ if pkg-config sss_nss_idmap 2> /dev/null ; then SSS_NSS_IDMAP_LIBS= fi else - if test $use_sss_idmap = yes ; then + if test $use_sss_nss_idmap = yes ; then PKG_CHECK_MODULES(SSS_NSS_IDMAP,sss_nss_idmap) else SSS_NSS_IDMAP_CFLAGS= SSS_NSS_IDMAP_LIBS= fi fi -AM_CONDITIONAL([SSS_NSS_IDMAP], [test x$SSS_NSS_IDMAP_LIBS != x]) +AM_CONDITIONAL([SSS_NSS_IDMAP], [test "x$SSS_NSS_IDMAP_LIBS" != x]) AC_SUBST(SSS_NSS_IDMAP_CFLAGS) AC_SUBST(SSS_NSS_IDMAP_LIBS) -if test x$SSS_NSS_IDMAP_LIBS != x ; then +if test "x$SSS_NSS_IDMAP_LIBS" != x ; then AC_CHECK_HEADERS(security/pam_appl.h) if test x$ac_cv_header_security_pam_appl_h = xyes ; then use_pam=yes @@ -444,7 +444,7 @@ AC_DEFINE_UNQUOTED(SCH_CONTAINER_CONFIGURATION_ATTR_ATTR,"$attrattr", [Define to name of the attribute which is used to specify attributes to be used when constructing entries.]) sssdattr=schema-compat-lookup-sssd AC_DEFINE_UNQUOTED(SCH_CONTAINER_CONFIGURATION_SSSD_ATTR,"$sssdattr", - [Define to name of the attribute which dictates whether or not SSSD on FreeIPA master is consulted about trusted domains' users.]) + [Define to name of the attribute to cause the tree to talk to SSSD on FreeIPA master to resolve trusted domains' users and groups. The value of the attribute is either 'user' or 'group'.]) sssdminidattr=schema-compat-sssd-min-id AC_DEFINE_UNQUOTED(SCH_CONTAINER_CONFIGURATION_SSSD_MIN_ID_ATTR,"$sssdminidattr", [Define to name of the attribute which is used to define lower bound of IDs (uid or gid) looked up through sssd. Everything below is not considered belonging to trusted domains.]) diff --git a/slapi-nis.spec b/slapi-nis.spec index 295458e..3bec56f 100644 --- a/slapi-nis.spec +++ b/slapi-nis.spec @@ -10,7 +10,7 @@ %endif Name: slapi-nis -Version: 0.47 +Version: 0.47.5 Release: 1%{?dist} Summary: NIS Server and Schema Compatibility plugins for Directory Server Group: System Environment/Daemons @@ -21,6 +21,7 @@ Source0: https://fedorahosted.org/releases/s/l/slapi-nis/slapi-nis-%{version}.ta BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: 389-ds-base-devel, %{ldap_impl}-devel BuildRequires: nspr-devel, nss-devel, /usr/bin/rpcgen +BuildRequires: libsss_nss_idmap-devel, pam-devel %if 0%{?fedora} > 6 || 0%{?rhel} > 5 BuildRequires: tcp_wrappers-devel %else @@ -76,6 +77,9 @@ rm -rf $RPM_BUILD_ROOT %{_sbindir}/nisserver-plugin-defs %changelog +* Wed Jul 24 2013 Alexander Bokovoy <abokovoy@redhat.com> - 0.47.5-1 +- Experimental build with SSSD support for trusted AD domains + * Fri May 24 2013 Nalin Dahyabhai <nalin@redhat.com> - 0.47-1 - fix request argument memory leaks in NIS server - add a %sort function diff --git a/src/back-sch-pam.c b/src/back-sch-pam.c index 3266261..c37758a 100644 --- a/src/back-sch-pam.c +++ b/src/back-sch-pam.c @@ -54,12 +54,7 @@ #include <security/pam_appl.h> - -/* - * PAM is not thread safe. We have to execute any PAM API calls in - * a critical section. This is the lock that protects that code. - */ -static Slapi_Mutex *PAMLock = NULL; +#include "format.h" /* Utility struct to wrap strings to avoid mallocs if possible - use stack allocated string space */ @@ -98,15 +93,6 @@ struct my_pam_conv_str { char *pam_identity; }; -/* returns a berval value as a null terminated string */ -static char *strdupbv(struct berval *bv) -{ - char *str = slapi_ch_malloc(bv->bv_len+1); - memcpy(str, bv->bv_val, bv->bv_len); - str[bv->bv_len] = 0; - return str; -} - static void free_pam_response(int nresp, struct pam_response *resp) { @@ -131,25 +117,24 @@ pam_conv_func(int num_msg, const struct pam_message **msg, struct pam_response * int ii; struct berval *creds; struct my_pam_conv_str *my_data = (struct my_pam_conv_str *)mydata; - struct pam_response *reply; + struct pam_response *reply; int ret = PAM_SUCCESS; - if (num_msg <= 0) { + if (num_msg <= 0) { return PAM_CONV_ERR; } /* empty reply structure */ - reply = (struct pam_response *)slapi_ch_calloc(num_msg, - sizeof(struct pam_response)); + reply = (struct pam_response *)slapi_ch_calloc(num_msg, sizeof(struct pam_response)); slapi_pblock_get( my_data->pb, SLAPI_BIND_CREDENTIALS, &creds ); /* the password */ for (ii = 0; ii < num_msg; ++ii) { /* hard to tell what prompt is for . . . */ /* assume prompts for password are either BINARY or ECHO_OFF */ if (msg[ii]->msg_style == PAM_PROMPT_ECHO_OFF) { - reply[ii].resp = strdupbv(creds); + reply[ii].resp = format_strdupbv(creds); #ifdef LINUX } else if (msg[ii]->msg_style == PAM_BINARY_PROMPT) { - reply[ii].resp = strdupbv(creds); + reply[ii].resp = format_strdupbv(creds); #endif } else if (msg[ii]->msg_style == PAM_PROMPT_ECHO_ON) { /* assume username */ reply[ii].resp = slapi_ch_strdup(my_data->pam_identity); @@ -190,7 +175,7 @@ do_pam_auth( Slapi_PBlock *pb, char *pam_service, /* name of service for pam_start() */ int pw_response_requested, /* do we need to send pwd policy resp control */ - Slapi_Entry *entry + const char *username ) { MyStrBuf pam_id; @@ -212,10 +197,7 @@ do_pam_auth( } binddn = slapi_sdn_get_dn(bindsdn); - char *val = slapi_entry_attr_get_charptr(entry, "uid"); - init_my_str_buf(&pam_id, val); - slapi_ch_free_string(&val); - + init_my_str_buf(&pam_id, username); if (!pam_id.str) { errmsg = PR_smprintf("Bind DN [%s] is invalid or not found", binddn); retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */ @@ -226,7 +208,6 @@ do_pam_auth( my_data.pb = pb; my_data.pam_identity = pam_id.str; my_pam_conv.appdata_ptr = &my_data; - slapi_lock_mutex(PAMLock); /* from this point on we are in the critical section */ rc = pam_start(pam_service, pam_id.str, &my_pam_conv, &pam_handle); @@ -304,7 +285,6 @@ do_pam_auth( } rc = pam_end(pam_handle, rc); - slapi_unlock_mutex(PAMLock); /* not in critical section any more */ done: @@ -334,17 +314,13 @@ done: * depending on what methods are set in the config. */ int -backend_sch_do_pam_auth(Slapi_PBlock *pb, Slapi_Entry *entry) +backend_sch_do_pam_auth(Slapi_PBlock *pb, const char *username) { int rc = LDAP_SUCCESS; MyStrBuf pam_service; /* avoid malloc if possible */ int pw_response_requested; LDAPControl **reqctrls = NULL; - if (!PAMLock && !(PAMLock = slapi_new_mutex())) { - return LDAP_LOCAL_ERROR; - } - init_my_str_buf(&pam_service, "system-auth"); slapi_pblock_get (pb, SLAPI_REQCONTROLS, &reqctrls); @@ -353,7 +329,7 @@ backend_sch_do_pam_auth(Slapi_PBlock *pb, Slapi_Entry *entry) /* figure out which method is the last one - we only return error codes, controls to the client and send a response on the last method */ - rc = do_pam_auth(pb, pam_service.str, pw_response_requested, entry); + rc = do_pam_auth(pb, pam_service.str, pw_response_requested, username); delete_my_str_buf(&pam_service); diff --git a/src/back-sch-sssd.c b/src/back-sch-sssd.c index 8168675..2a02f90 100644 --- a/src/back-sch-sssd.c +++ b/src/back-sch-sssd.c @@ -47,18 +47,21 @@ #include "plugin.h" #include "map.h" #include "back-sch.h" +#include "format.h" struct backend_search_filter_config { bool_t search_user; bool_t search_group; bool_t search_uid; bool_t search_gid; + bool_t search_members; bool_t name_set; + bool_t wrong_search; char *name; }; /* Check simple filter to see if it has - * (cn|uid|uidNumber|gidNumber=<value>) or (objectClass=posixGroup) + * (cn|uid|uidNumber|gidNumber|memberUid=<value>) or (objectClass=posixGroup|shadowAccount) * Called by slapi_filter_apply(). */ static int backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg) @@ -82,13 +85,19 @@ backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg) config->name_set = TRUE; } else if (0 == strcasecmp(filter_type, "cn")) { config->name_set = TRUE; + } else if (0 == strcasecmp(filter_type, "memberUid")) { + config->name_set = TRUE; + config->search_members = TRUE; } else if ((0 == strcasecmp(filter_type, "objectClass")) && (0 == strcasecmp(bval->bv_val, "posixGroup"))) { config->search_group = TRUE; + } else if ((0 == strcasecmp(filter_type, "objectClass")) && + (0 == strcasecmp(bval->bv_val, "shadowAccount"))) { + config->wrong_search = TRUE; } if ((NULL == config->name) && config->name_set) { - config->name = bval->bv_val; + config->name = format_strdupbv(bval); } } @@ -103,27 +112,27 @@ backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg) static Slapi_Entry * backend_retrieve_user_entry_from_sssd(char *user_name, bool_t is_uid, - struct backend_set_data *set_data, - struct backend_search_cbdata *cbdata) + char *container_sdn, + struct backend_search_cbdata *cbdata) { struct passwd pwd, *result; Slapi_Entry *entry; int rc; enum sss_id_type id_type; - char *sid_str; + char *sid_str, *dn; - if (set_data->sssd_buffer == NULL) { + if (cbdata->sssd_buffer == NULL) { return NULL; } if (is_uid) { rc = getpwuid_r(atoi(user_name), &pwd, - set_data->sssd_buffer, - set_data->sssd_buffer_len, &result); + cbdata->sssd_buffer, + cbdata->sssd_buffer_len, &result); } else { rc = getpwnam_r(user_name, &pwd, - set_data->sssd_buffer, - set_data->sssd_buffer_len, &result); + cbdata->sssd_buffer, + cbdata->sssd_buffer_len, &result); } if ((result == NULL) || (rc != 0)) { return NULL; @@ -138,6 +147,14 @@ backend_retrieve_user_entry_from_sssd(char *user_name, bool_t is_uid, return NULL; } + dn = slapi_ch_smprintf("uid=%s,%s", + slapi_escape_filter_value(pwd.pw_name, -1), + container_sdn); + if (dn == NULL) { + slapi_entry_free(entry); + return NULL; + } + slapi_entry_add_string(entry, "objectClass", "top"); slapi_entry_add_string(entry, @@ -145,7 +162,7 @@ backend_retrieve_user_entry_from_sssd(char *user_name, bool_t is_uid, slapi_entry_add_string(entry, "objectClass", "extensibleObject"); slapi_entry_add_string(entry, - "uid", user_name); + "uid", pwd.pw_name); slapi_entry_attr_set_int(entry, "uidNumber", pwd.pw_uid); slapi_entry_attr_set_int(entry, @@ -157,16 +174,15 @@ backend_retrieve_user_entry_from_sssd(char *user_name, bool_t is_uid, "cn", pwd.pw_gecos); } else { slapi_entry_add_string(entry, - "cn", user_name); + "cn", pwd.pw_name); } slapi_entry_add_string(entry, "homeDirectory", pwd.pw_dir); slapi_entry_add_string(entry, "loginShell", pwd.pw_shell); - slapi_entry_set_sdn(entry, set_data->container_sdn); - slapi_entry_set_dn(entry, slapi_ch_smprintf("uid=%s,%s", - user_name, slapi_sdn_get_dn(set_data->container_sdn))); + + slapi_entry_set_dn(entry, dn); rc = sss_nss_getsidbyid(pwd.pw_uid, &sid_str, &id_type); if ((rc == 0) && (sid_str != NULL)) { @@ -175,35 +191,33 @@ backend_retrieve_user_entry_from_sssd(char *user_name, bool_t is_uid, free(sid_str); } - return entry; } static Slapi_Entry * backend_retrieve_group_entry_from_sssd(char *group_name, bool_t is_gid, - struct backend_set_data *set_data, - struct backend_search_cbdata *cbdata) + char *container_sdn, + struct backend_search_cbdata *cbdata) { struct group grp, *result; - const char *sdn; Slapi_Entry *entry; int rc, i; enum sss_id_type id_type; - char *sid_str; + char *sid_str, *dn; - if (set_data->sssd_buffer == NULL) { + if (cbdata->sssd_buffer == NULL) { return NULL; } if (is_gid) { rc = getgrgid_r(atoi(group_name), &grp, - set_data->sssd_buffer, - set_data->sssd_buffer_len, &result); + cbdata->sssd_buffer, + cbdata->sssd_buffer_len, &result); } else { rc = getgrnam_r(group_name, &grp, - set_data->sssd_buffer, - set_data->sssd_buffer_len, &result); + cbdata->sssd_buffer, + cbdata->sssd_buffer_len, &result); } if ((result == NULL) || (rc != 0)) { return NULL; @@ -218,6 +232,14 @@ backend_retrieve_group_entry_from_sssd(char *group_name, bool_t is_gid, return NULL; } + dn = slapi_ch_smprintf("cn=%s,%s", + slapi_escape_filter_value(grp.gr_name, -1), + container_sdn); + if (dn == NULL) { + slapi_entry_free(entry); + return NULL; + } + slapi_entry_add_string(entry, "objectClass", "top"); slapi_entry_add_string(entry, @@ -225,27 +247,18 @@ backend_retrieve_group_entry_from_sssd(char *group_name, bool_t is_gid, slapi_entry_add_string(entry, "objectClass", "extensibleObject"); slapi_entry_add_string(entry, - "cn", group_name); + "cn", grp.gr_name); slapi_entry_attr_set_int(entry, "gidNumber", grp.gr_gid); - slapi_entry_set_sdn(entry, set_data->container_sdn); - slapi_entry_set_dn(entry, - slapi_ch_smprintf("cn=%s,%s", group_name, - slapi_sdn_get_dn(set_data->container_sdn))); - if (grp.gr_mem) { - if (set_data->sssd_relevant_set != NULL) { - sdn = slapi_sdn_get_dn(set_data->sssd_relevant_set->container_sdn); - } else { - sdn = slapi_sdn_get_dn(set_data->container_sdn); - } for (i=0; grp.gr_mem[i]; i++) { - slapi_entry_add_string(entry, "memberUid", - slapi_ch_smprintf("uid=%s,%s", grp.gr_mem[i], sdn)); + slapi_entry_add_string(entry, "memberUid", grp.gr_mem[i]); } } + slapi_entry_set_dn(entry, dn); + rc = sss_nss_getsidbyid(grp.gr_gid, &sid_str, &id_type); if ((rc == 0) && (sid_str != NULL)) { slapi_entry_add_string(entry, "objectClass", "ipaNTGroupAttrs"); @@ -256,42 +269,89 @@ backend_retrieve_group_entry_from_sssd(char *group_name, bool_t is_gid, return entry; } -static void -backend_search_sssd_send_entry(struct backend_set_data *set_data, - struct backend_search_cbdata *cbdata, - Slapi_Entry *entry) +static Slapi_Entry ** +backend_retrieve_group_list_from_sssd(char *user_name, char *container_sdn, + struct backend_search_cbdata *cbdata, + int *count) { - char *ndn; - if (entry) { - slapi_entry_add_string(entry, - "schema-compat-origin", "sssd"); - cbdata->matched = TRUE; - ndn = slapi_entry_get_ndn(entry); - backend_set_entry(cbdata->pb, entry, set_data); - slapi_send_ldap_search_entry(cbdata->pb, entry, - NULL, cbdata->attrs, - cbdata->attrsonly); - cbdata->n_entries++; - if (cbdata->closest_match) { - free(cbdata->closest_match); + struct passwd pwd, *pwd_result; + gid_t *grouplist; + Slapi_Entry **entries, *entry; + int rc, ngroups, i, idx; + /* gid_t is unsigned int */ +#define GROUP_LEN_MAX (sizeof("4294967295") + 1) + char group[GROUP_LEN_MAX]; + + if (cbdata->sssd_buffer == NULL) { + return NULL; + } + + rc = getpwnam_r(user_name, &pwd, + cbdata->sssd_buffer, + cbdata->sssd_buffer_len, &pwd_result); + + if ((pwd_result == NULL) || (rc != 0)) { + return NULL; + } + + if (pwd.pw_uid < cbdata->sssd_min_id) { + return NULL; + } + + ngroups = 32; + grouplist = malloc(sizeof(gid_t) * ngroups); + if (grouplist == NULL) { + return NULL; + } + + do { + rc = getgrouplist(user_name, pwd.pw_gid, grouplist, &ngroups); + if (rc < ngroups) { + grouplist = realloc(grouplist, ngroups * sizeof(gid_t)); } - cbdata->closest_match = strdup(ndn); - /* Entry is created, cache it via map. - * Next request will be served from the cache */ - //backend_set_entry(cbdata->pb, entry, set_data); - slapi_entry_free(entry); + } while (rc != ngroups); + + entries = malloc(sizeof(Slapi_Entry *) * ngroups); + if (entries == NULL) { + free(grouplist); + return NULL; + } + + idx = 0; + /* At this point we are not interested in the buffer used in pwd anymore + * so the next function can take it over for getgrid_r() */ + for (i = 0; i < ngroups; i++) { + rc = snprintf(group, GROUP_LEN_MAX, "%u", grouplist[i]); + if ((rc > 0) && (rc <= GROUP_LEN_MAX)) { + entry = backend_retrieve_group_entry_from_sssd(group, TRUE, container_sdn, cbdata); + if (entry) { + entries[idx] = entry; + idx++; + } + } + } + + if (idx != ngroups) { + entries = realloc(entries, idx * sizeof(Slapi_Entry *)); } + + *count = idx; + + free(grouplist); + + return entries; + } /* Check filter for a component like (uid=<value>) and if found, - * perform look up against SSSD and create entry based on that */ + * stage SSSD lookup. Lookup will be performed later, with call to backend_retrieve_from_sssd */ void backend_search_sssd(struct backend_set_data *set_data, struct backend_search_cbdata *cbdata) { int result, rc; - Slapi_Entry *entry; - struct backend_search_filter_config config = {FALSE, FALSE, FALSE, FALSE, FALSE, NULL}; + struct backend_search_filter_config config = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL}; + struct backend_staged_data *staged = NULL; /* There was no match but we asked to check SSSD */ /* First, we search the filter to see if it includes cn|uid=<value> check */ @@ -302,6 +362,10 @@ backend_search_sssd(struct backend_set_data *set_data, return; } + if (config.wrong_search) { + return; + } + /* Drop irrelevant requests. Each set only works with a single type */ if ((cbdata->check_sssd == SCH_SSSD_GROUP) && (config.search_uid || config.search_user)) { @@ -314,22 +378,83 @@ backend_search_sssd(struct backend_set_data *set_data, } if ((config.search_gid || config.search_uid) && - (atol(config.name) < set_data->sssd_min_id)) { + (atol(config.name) < cbdata->sssd_min_id)) { return; } - if ((config.search_group || config.search_gid) && - (NULL != config.name)) { - entry = backend_retrieve_group_entry_from_sssd(config.name, - config.search_gid, set_data, cbdata); - backend_search_sssd_send_entry(set_data, cbdata, entry); + + staged = malloc(sizeof(struct backend_staged_data)); + if (staged == NULL) { + free(config.name); return; } - if ((config.search_user || config.search_uid) && - (NULL != config.name)) { - entry = backend_retrieve_user_entry_from_sssd(config.name, - config.search_uid, set_data, cbdata); - backend_search_sssd_send_entry(set_data, cbdata, entry); + staged->map_group = slapi_ch_strdup(set_data->common.group); + staged->map_set = slapi_ch_strdup(set_data->common.set); + staged->set_data_fixup = NULL; + staged->count = 0; + staged->entries = NULL; + + staged->container_sdn = slapi_ch_strdup(slapi_sdn_get_dn(set_data->container_sdn)); + + staged->type = cbdata->check_sssd; + staged->name = config.name; + staged->is_id = config.search_gid || config.search_uid; + staged->search_members = config.search_members; + + staged->next = cbdata->staged; + cbdata->staged = staged; +} + + /* perform look up against SSSD and create entry based on that */ +bool_t +backend_retrieve_from_sssd(struct backend_staged_data *staged, + struct backend_search_cbdata *cbdata) +{ + Slapi_Entry *entry, **entries; + int i; + + if (((staged->type == SCH_SSSD_GROUP) && staged->search_members) && + (NULL != staged->name)) { + entries = backend_retrieve_group_list_from_sssd(staged->name, staged->container_sdn, + cbdata, &staged->count); + if (entries != NULL) { + staged->entries = entries; + for (i = 0; i < staged->count; i++) { + slapi_entry_add_string(entries[i], "schema-compat-origin", "sssd"); + } + return TRUE; + } + return FALSE; } + + if ((staged->type == SCH_SSSD_GROUP) && (NULL != staged->name)) { + entry = backend_retrieve_group_entry_from_sssd(staged->name, staged->is_id, + staged->container_sdn, + cbdata); + if (entry) { + slapi_entry_add_string(entry, "schema-compat-origin", "sssd"); + staged->count = 1; + staged->entries = malloc(sizeof(Slapi_Entry *)); + staged->entries[0] = entry; + return TRUE; + } + return FALSE; + } + + if ((staged->type == SCH_SSSD_USER) && (NULL != staged->name)) { + entry = backend_retrieve_user_entry_from_sssd(staged->name, staged->is_id, + staged->container_sdn, + cbdata); + if (entry) { + slapi_entry_add_string(entry, "schema-compat-origin", "sssd"); + staged->count = 1; + staged->entries = malloc(sizeof(Slapi_Entry *)); + staged->entries[0] = entry; + return TRUE; + } + return FALSE; + } + + return FALSE; } diff --git a/src/back-sch.c b/src/back-sch.c index a235998..c2b7d41 100644 --- a/src/back-sch.c +++ b/src/back-sch.c @@ -85,18 +85,9 @@ backend_set_config_free_config_contents(void *data) format_free_ref_attr_list(set_data->common.ref_attr_list); format_free_ref_attr_list(set_data->common.inref_attr_list); free(set_data->common.entry_filter); - if (set_data->check_sssd != SCH_SSSD_NONE) { - /* Remove association to another set (groups/users) */ - if ((set_data->sssd_relevant_set != NULL) && - (set_data->sssd_relevant_set->sssd_relevant_set == set_data)) { - set_data->sssd_relevant_set->sssd_relevant_set = NULL; - set_data->sssd_relevant_set = NULL; - } - } slapi_sdn_free(&set_data->container_sdn); free(set_data->rdn_format); backend_shr_free_strlist(set_data->attribute_format); - free(set_data->sssd_buffer); } } void @@ -144,9 +135,6 @@ backend_copy_set_config(const struct backend_set_data *data) ret->check_access = data->check_access; ret->check_sssd = data->check_sssd; ret->sssd_min_id = data->sssd_min_id; - ret->sssd_buffer = data->sssd_buffer; - ret->sssd_buffer_len = data->sssd_buffer_len; - ret->sssd_relevant_set = data->sssd_relevant_set; if ((ret->common.group == NULL) || (ret->common.set == NULL) || @@ -231,7 +219,6 @@ backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e, } else { ret.check_sssd = SCH_SSSD_NONE; } - ret.sssd_buffer = NULL; /* Make sure we don't return system users/groups * by limiting lower bound on searches */ @@ -251,19 +238,8 @@ backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e, backend_shr_add_strlist(&ret.attribute_format, "objectClass=extensibleObject"); backend_shr_add_strlist(&ret.attribute_format, "schema-compat-origin=%{schema-compat-origin}"); backend_shr_add_strlist(&ret.attribute_format, "ipaNTSecurityIdentifier=%{ipaNTSecurityIdentifier}"); - - /* Allocate buffer to be used for getpwnam_r/getgrnam_r requests */ - if (ret.check_sssd == SCH_SSSD_USER) { - ret.sssd_buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX); - } else { - ret.sssd_buffer_len = sysconf(_SC_GETGR_R_SIZE_MAX); - } - if ((long) ret.sssd_buffer_len == -1 ) - ret.sssd_buffer_len = 16384; - ret.sssd_buffer = malloc(ret.sssd_buffer_len); } - ret.sssd_relevant_set = NULL; *pret = backend_copy_set_config(&ret); free(ret.common.group); free(ret.common.set); @@ -1082,6 +1058,10 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, if (slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) == 0) { cbdata->matched = TRUE; + slapi_log_error(SLAPI_LOG_PLUGIN, + cbdata->state->plugin_desc->spd_id, + "search set actual match: %d, check_sssd=%d\n", + cbdata->matched, cbdata->check_sssd); } /* Walk the set of entries in this set if they're in scope. */ @@ -1090,16 +1070,15 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, cbdata->scope)) { map_data_foreach_entry_id(cbdata->state, group, set, NULL, backend_search_entry_cb, cbdata); - } - #ifdef HAVE_SSS_NSS_IDMAP - /* If we didn't find an exact match for the entry but asked to look up SSSD, - * then try to search SSSD and if successful, return that entry */ - if ((n_entries_sssd == cbdata->n_entries) && - (cbdata->check_sssd != SCH_SSSD_NONE)) { - backend_search_sssd(set_data, cbdata); - } + /* If we didn't find an exact match for the entry but asked to look up SSSD, + * then try to search SSSD and if successful, return that entry */ + if ((n_entries_sssd == cbdata->n_entries) && + (cbdata->check_sssd != SCH_SSSD_NONE)) { + backend_search_sssd(set_data, cbdata); + } #endif + } /* If we didn't find an exact match for the entry, then store this * container's DN as the closest match. */ @@ -1114,6 +1093,36 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag, } static bool_t +backend_search_find_set_data_in_group_cb(const char *group, const char *set, bool_t flag, + void *backend_data, void *cb_data) +{ + struct backend_search_cbdata *cbdata; + struct backend_set_data *set_data; + + cbdata = cb_data; + set_data = backend_data; + + if ((0 == strcmp(group, cbdata->cur_staged->map_group)) && + (0 == strcmp(set, cbdata->cur_staged->map_set))) { + cbdata->cur_staged->set_data_fixup = set_data; + } + + return TRUE; + +} + +static bool_t +backend_search_find_set_data_cb(const char *group, void *cb_data) +{ + struct backend_search_cbdata *cbdata; + + cbdata = cb_data; + map_data_foreach_map(cbdata->state, group, + backend_search_find_set_data_in_group_cb, cbdata); + return TRUE; +} + +static bool_t backend_search_group_cb(const char *group, void *cb_data) { struct backend_search_cbdata *cbdata; @@ -1216,6 +1225,9 @@ static int backend_search_cb(Slapi_PBlock *pb) { struct backend_search_cbdata cbdata; + struct backend_staged_data *staged, *next; + int i; + if (wrap_get_call_level() > 0) { return 0; } @@ -1240,6 +1252,8 @@ backend_search_cb(Slapi_PBlock *pb) cbdata.closest_match = NULL; cbdata.text = NULL; cbdata.n_entries = 0; + cbdata.staged = NULL; + cbdata.cur_staged = NULL; /* Okay, we can search. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, "searching from \"%s\" for \"%s\" with scope %d%s\n", @@ -1261,14 +1275,66 @@ backend_search_cb(Slapi_PBlock *pb) map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata); map_unlock(); wrap_dec_call_level(); +#ifdef HAVE_SSS_NSS_IDMAP + /* Search caused some data to be staged for addition */ + if (cbdata.staged != NULL) { + /* Allocate buffer to be used for getpwnam_r/getgrnam_r requests */ + cbdata.sssd_buffer_len = MAX(sysconf(_SC_GETPW_R_SIZE_MAX), sysconf(_SC_GETGR_R_SIZE_MAX)); + if ((long) cbdata.sssd_buffer_len == -1) + cbdata.sssd_buffer_len = 16384; + cbdata.sssd_buffer = malloc(cbdata.sssd_buffer_len); + staged = cbdata.staged; + while (staged) { + if (staged->entries == NULL) { + backend_retrieve_from_sssd(staged, &cbdata); + } + next = staged->next; + staged = next; + } + staged = cbdata.staged; + wrap_inc_call_level(); + map_wrlock(); + while (staged) { + if (staged->entries) { + cbdata.cur_staged = staged; + map_data_foreach_domain(cbdata.state, backend_search_find_set_data_cb, &cbdata); + if (cbdata.cur_staged->set_data_fixup != NULL) { + for (i = 0; i < staged->count ; i++) { + if ((staged->entries[i] != NULL) && + !map_data_check_entry(cbdata.state, + staged->map_group, staged->map_set, + slapi_sdn_get_ndn(staged->entries[i]))) { + backend_set_entry(cbdata.pb, staged->entries[i], staged->set_data_fixup); + slapi_entry_free(staged->entries[i]); + } + } + } + free(staged->entries); + staged->count = 0; + staged->entries = NULL; + } + free(staged->map_group); + free(staged->map_set); + free(staged->name); + free(staged->container_sdn); + next = staged->next; + free(staged); + staged = next; + } + cbdata.staged = NULL; + map_unlock(); + cbdata.sssd_buffer_len = 0; + free(cbdata.sssd_buffer); + /* Perform search again, this time to send out staged data */ + map_rdlock(); + map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata); + map_unlock(); + wrap_dec_call_level(); + } +#endif /* If we "own" the search target DN, then we need to send a response. */ if (cbdata.answer) { - if (cbdata.matched || (cbdata.n_entries > 0)) { - /* Just in case, free the closest-match that we've - * recorded. */ - free(cbdata.closest_match); - cbdata.closest_match = NULL; - } else { + if (!(cbdata.matched || (cbdata.n_entries > 0))) { /* Return a no-such-object error because the target DN * was not found. */ cbdata.result = LDAP_NO_SUCH_OBJECT; @@ -1294,6 +1360,12 @@ backend_search_cb(Slapi_PBlock *pb) send_ldap_result(cbdata.pb, cbdata.result, cbdata.closest_match, cbdata.text, cbdata.n_entries, NULL); + if (cbdata.matched || (cbdata.n_entries > 0)) { + /* Just in case, free the closest-match that we've + * recorded. */ + free(cbdata.closest_match); + cbdata.closest_match = NULL; + } } slapi_sdn_free(&cbdata.target_dn); free(cbdata.closest_match); @@ -1448,6 +1520,61 @@ backend_betxn_pre_write_cb(Slapi_PBlock *pb) return state->use_be_txns ? backend_write_cb(pb) : 0; } +#ifdef HAVE_SSS_NSS_IDMAP +static int +backend_bind_cb_pam(Slapi_PBlock *pb, const char *username, char *ndn) +{ + int ret = 0; + LDAPControl **reqctrls = NULL; + struct plugin_state *state; + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); + + /* PAM API is thread-unsafe, we need to lock exclusively */ + wrap_rwlock_wrlock(state->pam_lock); + ret = backend_sch_do_pam_auth(pb, username); + wrap_rwlock_unlock(state->pam_lock); + + if (ret != 0) { + slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, + NULL, NULL, 0, NULL); + ret = -1; + free(ndn); + } else { + /* + * If bind succeeded, change authentication information associated + * with this connection. + */ + if (ret == LDAP_SUCCESS) { + if ((slapi_pblock_set(pb, SLAPI_CONN_DN, (void*) ndn) != 0) || + (slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE) != 0)) { + ret = LDAP_OPERATIONS_ERROR; + } else { + slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls); + if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) { + slapi_add_auth_response_control(pb, ndn); + } + } + } + + if (ret == LDAP_SUCCESS) { + /* we are handling the result */ + slapi_send_ldap_result(pb, ret, NULL, NULL, 0, NULL); + /* tell bind code we handled the result */ + ret = 0; + } + } + return ret; +} +#else +static int +backend_bind_cb_pam(Slapi_PBlock *pb, const char *username, char *ndn) +{ + slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL); + free(ndn); + return -1; +} +#endif + static int backend_bind_cb(Slapi_PBlock *pb) { @@ -1457,6 +1584,7 @@ backend_bind_cb(Slapi_PBlock *pb) struct berval *urls[] = {&ref, NULL}; const char *ndn; char *is_sssd_origin = NULL; + char *username = NULL; if (wrap_get_call_level() > 0) { return 0; @@ -1466,61 +1594,41 @@ backend_bind_cb(Slapi_PBlock *pb) map_rdlock(); backend_locate(pb, &data); if (data != NULL) { -#ifdef HAVE_SSS_NSS_IDMAP is_sssd_origin = slapi_entry_attr_get_charptr(data->e, "schema-compat-origin"); + ndn = slapi_ch_strdup(slapi_sdn_get_ndn(data->original_entry_dn)); + username = slapi_entry_attr_get_charptr(data->e, "uid"); + map_unlock(); + wrap_dec_call_level(); + + /* If user comes from SSSD, it will get authentication handled by PAM. */ if ((is_sssd_origin != NULL) && (strcasecmp(is_sssd_origin, "sssd") == 0)) { - ret = backend_sch_do_pam_auth(pb, data->e); - if (ret != 0) { - slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, - NULL, NULL, 0, NULL); - ret = -1; - } else { - /* - * If bind succeeded, change authentication information associated - * with this connection. - */ - if (ret == LDAP_SUCCESS) { - ndn = slapi_ch_strdup(slapi_sdn_get_ndn(data->original_entry_dn)); - if ((slapi_pblock_set(pb, SLAPI_CONN_DN, (void*)ndn) != 0) || - (slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE) != 0)) { - ret = LDAP_OPERATIONS_ERROR; - } else { - LDAPControl **reqctrls = NULL; - slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls); - if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) { - slapi_add_auth_response_control(pb, ndn); - } - } - } - - if (ret == LDAP_SUCCESS) { - /* we are handling the result */ - slapi_send_ldap_result(pb, ret, NULL, NULL, 0, NULL); - /* tell bind code we handled the result */ - ret = 0; - } - } + /* backend_bind_cb_pam() takes over ndn */ + ret = backend_bind_cb_pam(pb, username, ndn); } else { -#endif - ndn = slapi_sdn_get_ndn(data->original_entry_dn); - ref.bv_len = strlen("ldap:///") + strlen(ndn); - ref.bv_val = malloc(ref.bv_len + 1); - if (ref.bv_val != NULL) { - sprintf(ref.bv_val, "ldap:///%s", ndn); - slapi_send_ldap_result(pb, LDAP_REFERRAL, - NULL, NULL, 0, urls); - free(ref.bv_val); + if (username != NULL) { + ret = backend_bind_cb_pam(pb, username, ndn); } else { - slapi_send_ldap_result(pb, LDAP_BUSY, - NULL, NULL, 0, NULL); + ref.bv_len = strlen("ldap:///") + strlen(ndn); + ref.bv_val = malloc(ref.bv_len + 1); + if (ref.bv_val != NULL) { + sprintf(ref.bv_val, "ldap:///%s", ndn); + slapi_send_ldap_result(pb, LDAP_REFERRAL, + NULL, NULL, 0, urls); + free(ref.bv_val); + } else { + slapi_send_ldap_result(pb, LDAP_BUSY, + NULL, NULL, 0, NULL); + } + ret = -1; + free(ndn); } -#ifdef HAVE_SSS_NSS_IDMAP } + free(username); free(is_sssd_origin); -#endif - ret = -1; } else { + map_unlock(); + wrap_dec_call_level(); if (backend_check_scope_pb(pb)) { slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL); @@ -1529,8 +1637,6 @@ backend_bind_cb(Slapi_PBlock *pb) ret = 0; } } - map_unlock(); - wrap_dec_call_level(); return ret; } @@ -1577,74 +1683,11 @@ backend_check_empty(struct plugin_state *state, } } -struct backend_start_fixup_cbdata { - Slapi_PBlock *pb; - struct plugin_state *state; - struct backend_set_data *groups; - struct backend_set_data *users; -}; - -static bool_t -backend_start_fixup_sssd_cb(const char *group, const char *set, bool_t flag, - void *backend_data, void *cb_data) -{ - struct backend_start_fixup_cbdata *cbdata; - struct backend_set_data *set_data; - - cbdata = cb_data; - set_data = backend_data; - - switch (set_data->check_sssd) { - case SCH_SSSD_NONE: - break; - case SCH_SSSD_USER: - cbdata->groups = set_data; - break; - case SCH_SSSD_GROUP: - cbdata->users = set_data; - break; - default: - break; - } - - return TRUE; -} - -static bool_t -backend_start_fixup_cb(const char *group, void *cb_data) -{ - struct backend_start_fixup_cbdata *cbdata; - cbdata = cb_data; - map_data_foreach_map(cbdata->state, group, - backend_start_fixup_sssd_cb, cbdata); - if ((cbdata->groups != NULL) && - (cbdata->users != NULL)) { - cbdata->groups->sssd_relevant_set = cbdata->users; - cbdata->users->sssd_relevant_set = cbdata->groups; - } - return TRUE; -} - /* Populate our data cache. */ void backend_startup(Slapi_PBlock *pb, struct plugin_state *state) { - struct backend_start_fixup_cbdata cbdata; backend_shr_startup(state, pb, SCH_CONTAINER_CONFIGURATION_FILTER); - /* Walk the list of groups and perform fixups for the cases where - * some sets depend on others. Right now following fixups are done: - * -- SSSD searches for the group tree should know user tree DN to - * produce proper membership - */ - cbdata.state = state; - cbdata.pb = pb; - cbdata.groups = NULL; - cbdata.users = NULL; - wrap_inc_call_level(); - map_rdlock(); - map_data_foreach_domain(state, backend_start_fixup_cb, &cbdata); - map_unlock(); - wrap_dec_call_level(); } int diff --git a/src/back-sch.h b/src/back-sch.h index 85ea86f..0741434 100644 --- a/src/back-sch.h +++ b/src/back-sch.h @@ -38,9 +38,6 @@ struct backend_set_data { bool_t check_access; enum sch_search_sssd_t check_sssd; long sssd_min_id; - char *sssd_buffer; - ssize_t sssd_buffer_len; - struct backend_set_data *sssd_relevant_set; }; struct backend_entry_data { @@ -48,6 +45,19 @@ struct backend_entry_data { Slapi_Entry *e; }; +struct backend_staged_data { + struct backend_staged_data *next; + struct backend_set_data *set_data_fixup; + char *map_group, *map_set; + enum sch_search_sssd_t type; + bool_t is_id; + bool_t search_members; + char *name; + char *container_sdn; + int count; + Slapi_Entry **entries; +}; + /* Intercept a search request, and if it belongs to one of our compatibility * trees, answer from our cache before letting the default database have a * crack at it. */ @@ -61,23 +71,30 @@ struct backend_search_cbdata { Slapi_DN *target_dn; Slapi_Filter *filter; long sssd_min_id; + char *sssd_buffer; + ssize_t sssd_buffer_len; bool_t answer; int result; bool_t matched; char *closest_match, *text; int n_entries; + struct backend_staged_data *staged; + struct backend_staged_data *cur_staged; }; void backend_search_sssd(struct backend_set_data *set_data, struct backend_search_cbdata *cbdata); +bool_t backend_retrieve_from_sssd(struct backend_staged_data *staged, + struct backend_search_cbdata *cbdata); + void backend_set_operational_attributes(Slapi_Entry *e, struct plugin_state *state, time_t timestamp, int n_subordinates, const char *usn); -int backend_sch_do_pam_auth(Slapi_PBlock *pb, Slapi_Entry *entry); +int backend_sch_do_pam_auth(Slapi_PBlock *pb, const char *username); #endif diff --git a/src/format.c b/src/format.c index 9ee2444..fbbfb7d 100644 --- a/src/format.c +++ b/src/format.c @@ -103,6 +103,18 @@ xmemdup(char *region, int size) return ret; } +/* returns a berval value as a null terminated string */ +char * +format_strdupbv(struct berval *bv) +{ + char *str = slapi_ch_malloc(bv->bv_len+1); + if (str != NULL) { + memcpy(str, bv->bv_val, bv->bv_len); + str[bv->bv_len] = 0; + } + return str; +} + /* Maintain a DN list, which is list of distinguished names, and a sorted copy * which we can check for inclusion much faster. */ static int diff --git a/src/format.h b/src/format.h index 0aa7d20..84fa837 100644 --- a/src/format.h +++ b/src/format.h @@ -79,4 +79,6 @@ char **format_get_data_set(struct plugin_state *state, unsigned int **data_lengths); char *format_escape_for_filter(const char *unescaped); +/* Copy struct berval * as NULL-terminated string*/ +char *format_strdupbv(struct berval *bv); #endif diff --git a/src/plug-sch.c b/src/plug-sch.c index 5440e00..5d74beb 100644 --- a/src/plug-sch.c +++ b/src/plug-sch.c @@ -108,6 +108,7 @@ plugin_startup(Slapi_PBlock *pb) state->plugin_base ? "\"" : ""); /* Populate the tree of fake entries. */ backend_startup(pb, state); + state->pam_lock = wrap_new_rwlock(); /* Note that the plugin is ready to go. */ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "plugin startup completed\n"); @@ -120,6 +121,8 @@ plugin_shutdown(Slapi_PBlock *pb) struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); map_done(state); + wrap_free_rwlock(state->pam_lock); + state->pam_lock = NULL; 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 b54e2ad..3967fb0 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -44,6 +44,8 @@ struct plugin_state { struct { int fd, port, pf, type; } listener[4]; + /* Schema compat-specific data. */ + struct wrapped_rwlock *pam_lock; }; #endif |