From c7181f7eab2fd36084f51f36e49f2d663953ce86 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 21 Jul 2015 21:58:02 -0600 Subject: these changes were sitting here, I don't know how long --- nslcd/Makefile.am | 2 +- nslcd/db_group.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 nslcd/db_group.c (limited to 'nslcd') diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am index a6fc7f8..86bdbda 100644 --- a/nslcd/Makefile.am +++ b/nslcd/Makefile.am @@ -31,7 +31,7 @@ nslcd_SOURCES = nslcd.c ../nslcd.h ../common/nslcd-prot.h \ dispatch.c dispatch.h \ cfg.c cfg.h \ nsswitch.c invalidator.c \ - db_config.c db_pam.c db_passwd.c db_shadow.c \ + db_config.c db_pam.c db_passwd.c db_shadow.c db_group.c \ hackers.c hackers_watch.c hackers_parse.c \ hackers.h hackers_watch.h hackers_parse.h nslcd_LDADD = ../common/libtio.a ../common/libdict.a \ diff --git a/nslcd/db_group.c b/nslcd/db_group.c new file mode 100644 index 0000000..6741b48 --- /dev/null +++ b/nslcd/db_group.c @@ -0,0 +1,612 @@ +/* + group.c - group entry lookup routines + Parts of this file were part of the nss_ldap library (as ldap-grp.c) + which has been forked into the nss-pam-ldapd library. + + Copyright (C) 1997-2006 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006-2014 Arthur de Jong + Copyright (C) 2013 Steve Hill + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "config.h" + +#include +#include +#include +/* for gid_t */ +#include + +#include "common/set.h" +#include "common.h" +#include "log.h" +#include "cfg.h" +#include "compat/strndup.h" + +/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL + * DESC 'Abstraction of a group of accounts' + * MUST ( cn $ gidNumber ) + * MAY ( userPassword $ memberUid $ description ) ) + * + * apart from the above a member attribute is also supported that + * may contains a DN of a user + * + * nested groups (groups that are member of a group) are currently + * not supported + */ + +/* the search base for searches */ +const char *group_bases[NSS_LDAP_CONFIG_MAX_BASES] = { NULL }; + +/* the search scope for searches */ +int group_scope = LDAP_SCOPE_DEFAULT; + +/* the basic search filter for searches */ +const char *group_filter = "(objectClass=posixGroup)"; + +/* the attributes to request with searches */ +const char *attmap_group_cn = "cn"; +const char *attmap_group_userPassword = "\"*\""; +const char *attmap_group_gidNumber = "gidNumber"; +const char *attmap_group_memberUid = "memberUid"; +const char *attmap_group_member = "member"; + +/* special property for objectSid-based searches + (these are already LDAP-escaped strings) */ +static char *gidSid = NULL; + +/* BUILTIN SID definitions */ +static char *builtinSid = NULL; +const gid_t min_builtin_rid = 544; +const gid_t max_builtin_rid = 552; + +/* default values for attributes */ +static const char *default_group_userPassword = "*"; /* unmatchable */ + +/* the attribute list to request with searches */ +static const char **group_attrs = NULL; + +/* the attribute list for bymember searches (without member attributes) */ +static const char **group_bymember_attrs = NULL; + +/* create a search filter for searching a group entry + by name, return -1 on errors */ +static int mkfilter_group_byname(const char *name, + char *buffer, size_t buflen) +{ + char safename[BUFLEN_SAFENAME]; + /* escape attribute */ + if (myldap_escape(name, safename, sizeof(safename))) + { + log_log(LOG_ERR, "mkfilter_group_byname(): safename buffer too small"); + return -1; + } + /* build filter */ + return mysnprintf(buffer, buflen, "(&%s(%s=%s))", + group_filter, attmap_group_cn, safename); +} + +/* create a search filter for searching a group entry + by gid, return -1 on errors */ +static int mkfilter_group_bygid(gid_t gid, char *buffer, size_t buflen) +{ + /* if searching for a Windows domain SID */ + if (gidSid != NULL) + { + /* the given gid is a BUILTIN gid, the SID prefix is not the domain SID */ + if ((gid >= min_builtin_rid) && (gid <= max_builtin_rid)) + return mysnprintf(buffer, buflen, "(&%s(%s=%s\\%02x\\%02x\\%02x\\%02x))", + group_filter, attmap_group_gidNumber, builtinSid, + (int)(gid & 0xff), (int)((gid >> 8) & 0xff), + (int)((gid >> 16) & 0xff), (int)((gid >> 24) & 0xff)); + return mysnprintf(buffer, buflen, "(&%s(%s=%s\\%02x\\%02x\\%02x\\%02x))", + group_filter, attmap_group_gidNumber, gidSid, + (int)(gid & 0xff), (int)((gid >> 8) & 0xff), + (int)((gid >> 16) & 0xff), (int)((gid >> 24) & 0xff)); + } + else + { + return mysnprintf(buffer, buflen, "(&%s(%s=%lu))", + group_filter, attmap_group_gidNumber, (unsigned long int)gid); + } +} + +/* create a search filter for searching a group entry + by member uid, return -1 on errors */ +static int mkfilter_group_bymember(MYLDAP_SESSION *session, + const char *uid, + char *buffer, size_t buflen) +{ + char dn[BUFLEN_DN]; + char safeuid[BUFLEN_SAFENAME]; + char safedn[BUFLEN_SAFEDN]; + /* escape attribute */ + if (myldap_escape(uid, safeuid, sizeof(safeuid))) + { + log_log(LOG_ERR, "mkfilter_group_bymember(): safeuid buffer too small"); + return -1; + } + /* try to translate uid to DN */ + if ((strcasecmp(attmap_group_member, "\"\"") == 0) || + (uid2dn(session, uid, dn, sizeof(dn)) == NULL)) + return mysnprintf(buffer, buflen, "(&%s(%s=%s))", + group_filter, attmap_group_memberUid, safeuid); + /* escape DN */ + if (myldap_escape(dn, safedn, sizeof(safedn))) + { + log_log(LOG_ERR, "mkfilter_group_bymember(): safedn buffer too small"); + return -1; + } + /* also lookup using user DN */ + return mysnprintf(buffer, buflen, "(&%s(|(%s=%s)(%s=%s)))", + group_filter, + attmap_group_memberUid, safeuid, + attmap_group_member, safedn); +} + +static int mkfilter_group_bymemberdn(const char *dn, + char *buffer, size_t buflen) +{ + char safedn[BUFLEN_SAFEDN]; + /* escape DN */ + if (myldap_escape(dn, safedn, sizeof(safedn))) + { + log_log(LOG_ERR, "mkfilter_group_bymemberdn(): safedn buffer too small"); + return -1; + } + return mysnprintf(buffer, buflen, + "(&%s(%s=%s))", + group_filter, + attmap_group_member, safedn); +} + +void group_init(void) +{ + int i; + SET *set; + /* set up search bases */ + if (group_bases[0] == NULL) + for (i = 0; i < NSS_LDAP_CONFIG_MAX_BASES; i++) + group_bases[i] = nslcd_cfg->bases[i]; + /* set up scope */ + if (group_scope == LDAP_SCOPE_DEFAULT) + group_scope = nslcd_cfg->scope; + /* special case when gidNumber references objectSid */ + if (strncasecmp(attmap_group_gidNumber, "objectSid:", 10) == 0) + { + gidSid = sid2search(attmap_group_gidNumber + 10); + builtinSid = sid2search("S-1-5-32"); + attmap_group_gidNumber = strndup(attmap_group_gidNumber, 9); + } + /* set up attribute list */ + set = set_new(); + attmap_add_attributes(set, attmap_group_cn); + attmap_add_attributes(set, attmap_group_userPassword); + attmap_add_attributes(set, attmap_group_memberUid); + attmap_add_attributes(set, attmap_group_gidNumber); + attmap_add_attributes(set, attmap_group_member); + group_attrs = set_tolist(set); + if (group_attrs == NULL) + { + log_log(LOG_CRIT, "malloc() failed to allocate memory"); + exit(EXIT_FAILURE); + } + set_free(set); + /* set up bymember attribute list */ + set = set_new(); + attmap_add_attributes(set, attmap_group_cn); + attmap_add_attributes(set, attmap_group_userPassword); + attmap_add_attributes(set, attmap_group_gidNumber); + group_bymember_attrs = set_tolist(set); + if (group_bymember_attrs == NULL) + { + log_log(LOG_CRIT, "malloc() failed to allocate memory"); + exit(EXIT_FAILURE); + } + set_free(set); +} + +static int do_write_group(TFILE *fp, MYLDAP_ENTRY *entry, + const char **names, gid_t gids[], int numgids, + const char *passwd, const char **members, + const char *reqname) +{ + int32_t tmpint32, tmp2int32, tmp3int32; + int i, j; + /* write entries for all names and gids */ + for (i = 0; names[i] != NULL; i++) + { + if (!isvalidname(names[i])) + { + log_log(LOG_WARNING, "%s: %s: denied by validnames option", + myldap_get_dn(entry), attmap_group_cn); + } + else if ((reqname == NULL) || (STR_CMP(reqname, names[i]) == 0)) + { + for (j = 0; j < numgids; j++) + { + WRITE_INT32(fp, NSLCD_RESULT_BEGIN); + WRITE_STRING(fp, names[i]); + WRITE_STRING(fp, passwd); + WRITE_INT32(fp, gids[j]); + WRITE_STRINGLIST(fp, members); + } + } + } + return 0; +} + +static void getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session, + SET *members, SET *seen, SET *subgroups) +{ + char buf[BUFLEN_NAME]; + int i; + const char **values; + const char ***derefs; + /* add the memberUid values */ + values = myldap_get_values(entry, attmap_group_memberUid); + if (values != NULL) + for (i = 0; values[i] != NULL; i++) + { + /* only add valid usernames */ + if (isvalidname(values[i])) + set_add(members, values[i]); + } + /* skip rest if attmap_group_member is blank */ + if (strcasecmp(attmap_group_member, "\"\"") == 0) + return; + /* add deref'd entries if we have them*/ + derefs = myldap_get_deref_values(entry, attmap_group_member, attmap_passwd_uid); + if (derefs != NULL) + { + /* add deref'd uid attributes */ + for (i = 0; derefs[0][i] != NULL; i++) + set_add(members, derefs[0][i]); + /* add non-deref'd attribute values as subgroups */ + for (i = 0; derefs[1][i] != NULL; i++) + { + if ((seen == NULL) || (!set_contains(seen, derefs[1][i]))) + { + if (seen != NULL) + set_add(seen, derefs[1][i]); + if (subgroups != NULL) + set_add(subgroups, derefs[1][i]); + } + } + return; /* no need to parse the member attribute ourselves */ + } + /* add the member values */ + values = myldap_get_values(entry, attmap_group_member); + if (values != NULL) + for (i = 0; values[i] != NULL; i++) + { + if ((seen == NULL) || (!set_contains(seen, values[i]))) + { + if (seen != NULL) + set_add(seen, values[i]); + /* transform the DN into a uid (dn2uid() already checks validity) */ + if (dn2uid(session, values[i], buf, sizeof(buf)) != NULL) + set_add(members, buf); + /* wasn't a UID - try handling it as a nested group */ + else if (subgroups != NULL) + set_add(subgroups, values[i]); + } + } +} + +/* the maximum number of gidNumber attributes per entry */ +#define MAXGIDS_PER_ENTRY 5 + +static int write_group(TFILE *fp, MYLDAP_ENTRY *entry, const char *reqname, + const gid_t *reqgid, int wantmembers, + MYLDAP_SESSION *session) +{ + const char **names, **gidvalues; + const char *passwd; + const char **members = NULL; + SET *set, *seen=NULL, *subgroups=NULL; + gid_t gids[MAXGIDS_PER_ENTRY]; + int numgids; + char *tmp; + char passbuffer[BUFLEN_PASSWORDHASH]; + MYLDAP_SEARCH *search; + MYLDAP_ENTRY *entry2; + int rc; + /* get group name (cn) */ + names = myldap_get_values(entry, attmap_group_cn); + if ((names == NULL) || (names[0] == NULL)) + { + log_log(LOG_WARNING, "%s: %s: missing", + myldap_get_dn(entry), attmap_group_cn); + return 0; + } + /* get the group id(s) */ + if (reqgid != NULL) + { + gids[0] = *reqgid; + numgids = 1; + } + else + { + gidvalues = myldap_get_values_len(entry, attmap_group_gidNumber); + if ((gidvalues == NULL) || (gidvalues[0] == NULL)) + { + log_log(LOG_WARNING, "%s: %s: missing", + myldap_get_dn(entry), attmap_group_gidNumber); + return 0; + } + for (numgids = 0; (numgids < MAXGIDS_PER_ENTRY) && (gidvalues[numgids] != NULL); numgids++) + { + if (gidSid != NULL) + gids[numgids] = (gid_t)binsid2id(gidvalues[numgids]); + else + { + errno = 0; + gids[numgids] = strtogid(gidvalues[numgids], &tmp, 10); + if ((*(gidvalues[numgids]) == '\0') || (*tmp != '\0')) + { + log_log(LOG_WARNING, "%s: %s: non-numeric", + myldap_get_dn(entry), attmap_group_gidNumber); + return 0; + } + else if ((errno != 0) || (strchr(gidvalues[numgids], '-') != NULL)) + { + log_log(LOG_WARNING, "%s: %s: out of range", + myldap_get_dn(entry), attmap_group_gidNumber); + return 0; + } + } + } + } + /* get group passwd (userPassword) (use only first entry) */ + passwd = get_userpassword(entry, attmap_group_userPassword, + passbuffer, sizeof(passbuffer)); + if (passwd == NULL) + passwd = default_group_userPassword; + /* get group members (memberUid&member) */ + if (wantmembers) + { + set = set_new(); + if (set != NULL) + { + if (nslcd_cfg->nss_nested_groups) + { + seen = set_new(); + subgroups = set_new(); + } + /* collect the members from this group */ + getmembers(entry, session, set, seen, subgroups); + /* add the members of any nested groups */ + if (subgroups != NULL) + { + while ((tmp = set_pop(subgroups)) != NULL) + { + search = myldap_search(session, tmp, LDAP_SCOPE_BASE, group_filter, group_attrs, NULL); + if (search != NULL) + while ((entry2 = myldap_get_entry(search, NULL)) != NULL) + getmembers(entry2, session, set, seen, subgroups); + free(tmp); + } + } + members = set_tolist(set); + set_free(set); + if (seen != NULL) + set_free(seen); + if (subgroups != NULL) + set_free(subgroups); + } + } + /* write entries (split to a separate function so we can ensure the call + to free() below in case a write fails) */ + rc = do_write_group(fp, entry, names, gids, numgids, passwd, members, + reqname); + /* free and return */ + if (members != NULL) + free(members); + return rc; +} + +NSLCD_HANDLE(GROUP, BYNAME + ,/* decls */ + char name[BUFLEN_NAME]; + char filter[BUFLEN_FILTER]; + ,/* read */ + READ_STRING(fp, name); + log_setrequest("group=\"%s\"", name); + ,/* check */ + if (!isvalidname(name)) + { + log_log(LOG_WARNING, "request denied by validnames option"); + return -1; + }, + ,/* search */ + mkfilter_group_byname(name, filter, sizeof(filter)); + ,/* write */ + write_group(fp, entry, name, NULL, 1, session); + ,/* cleanup */ +) + +NSLCD_HANDLE(GROUP, BYGID + ,/* decls */ + gid_t gid; + char filter[BUFLEN_FILTER]; + ,/* read */ + READ_INT32(fp, gid); + log_setrequest("group=%lu", (unsigned long int)gid); + ,/* check */ + ,/* search */ + mkfilter_group_bygid(gid, filter, sizeof(filter)); + ,/* write */ + write_group(fp, entry, NULL, &gid, 1, session); + ,/* cleanup */ +) + +int nslcd_group_bymember(TFILE *fp, MYLDAP_SESSION *session) +{ + /* define common variables */ + int32_t tmpint32; + MYLDAP_SEARCH *search; + MYLDAP_ENTRY *entry; + const char *dn; + const char *base; + int rc, i; + char name[BUFLEN_NAME]; + char filter[BUFLEN_FILTER]; + SET *seen=NULL, *tocheck=NULL; + /* read request parameters */ + READ_STRING(fp, name); + log_setrequest("group/member=\"%s\"", name); + /* validate request */ + if (!isvalidname(name)) + { + log_log(LOG_WARNING, "request denied by validnames option"); + return -1; + } + if ((nslcd_cfg->nss_initgroups_ignoreusers != NULL) && + set_contains(nslcd_cfg->nss_initgroups_ignoreusers, name)) + { + log_log(LOG_DEBUG, "ignored group member"); + /* just end the request, returning no results */ + WRITE_INT32(fp, NSLCD_VERSION); + WRITE_INT32(fp, NSLCD_ACTION_GROUP_BYMEMBER); + WRITE_INT32(fp, NSLCD_RESULT_END); + return 0; + } + /* write the response header */ + WRITE_INT32(fp, NSLCD_VERSION); + WRITE_INT32(fp, NSLCD_ACTION_GROUP_BYMEMBER); + /* prepare the search filter */ + if (mkfilter_group_bymember(session, name, filter, sizeof(filter))) + { + log_log(LOG_WARNING, "nslcd_group_bymember(): filter buffer too small"); + return -1; + } + if ((nslcd_cfg->nss_nested_groups) && (strcasecmp(attmap_group_member, "\"\"") != 0)) + { + seen = set_new(); + tocheck = set_new(); + if ((seen != NULL) && (tocheck == NULL)) + { + set_free(seen); + seen = NULL; + } + else if ((tocheck != NULL) && (seen == NULL)) + { + set_free(tocheck); + tocheck = NULL; + } + } + /* perform a search for each search base */ + for (i = 0; (base = group_bases[i]) != NULL; i++) + { + /* do the LDAP search */ + search = myldap_search(session, base, group_scope, filter, + group_bymember_attrs, NULL); + if (search == NULL) + { + if (seen != NULL) + { + set_free(seen); + set_free(tocheck); + } + return -1; + } + /* go over results */ + while ((entry = myldap_get_entry(search, &rc)) != NULL) + { + if ((seen == NULL) || (!set_contains(seen, dn = myldap_get_dn(entry)))) + { + if (seen != NULL) + { + set_add(seen, dn); + set_add(tocheck, dn); + } + if (write_group(fp, entry, NULL, NULL, 0, session)) + { + if (seen != NULL) + { + set_free(seen); + set_free(tocheck); + } + return -1; + } + } + } + } + /* write possible parent groups */ + if (tocheck != NULL) + { + while ((dn = set_pop(tocheck)) != NULL) + { + /* make filter for finding groups with our group as member */ + if (mkfilter_group_bymemberdn(dn, filter, sizeof(filter))) + { + log_log(LOG_WARNING, "nslcd_group_bymember(): filter buffer too small"); + free((void *)dn); + set_free(seen); + set_free(tocheck); + return -1; + } + free((void *)dn); + /* do the LDAP searches */ + for (i = 0; (base = group_bases[i]) != NULL; i++) + { + search = myldap_search(session, base, group_scope, filter, group_bymember_attrs, NULL); + if (search != NULL) + { + while ((entry = myldap_get_entry(search, NULL)) != NULL) + { + dn = myldap_get_dn(entry); + if (!set_contains(seen, dn)) + { + set_add(seen, dn); + set_add(tocheck, dn); + if (write_group(fp, entry, NULL, NULL, 0, session)) + { + set_free(seen); + set_free(tocheck); + return -1; + } + } + } + } + } + } + set_free(seen); + set_free(tocheck); + } + /* write the final result code */ + if (rc == LDAP_SUCCESS) + { + WRITE_INT32(fp, NSLCD_RESULT_END); + } + return 0; +} + +NSLCD_HANDLE(GROUP, ALL + ,/* decls */ + const char *filter; + ,/* read */ + log_setrequest("group(all)"); + ,/* check */ + ,/* search */ + (filter = group_filter, 0), + ,/* write */ + return write_group(fp, entry, NULL, NULL, 1, session); + ,/* cleanup */ +) -- cgit v1.2.2