diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2013-03-24 22:52:44 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2013-03-24 22:52:44 +0100 |
commit | 3daa68d35cf18c0dc80c8c24c7aa23c6273d06c4 (patch) | |
tree | 3b1e8c1596f292dbe67fb1cc903237de0466be66 /nslcd | |
parent | edd119c3a0d532fc5f87ccf89585370cb2fa3fed (diff) | |
parent | 642064cc205cf484bd904d94141eba8740aa0a28 (diff) |
Implement support for nested groups
Diffstat (limited to 'nslcd')
-rw-r--r-- | nslcd/cfg.c | 7 | ||||
-rw-r--r-- | nslcd/cfg.h | 1 | ||||
-rw-r--r-- | nslcd/group.c | 207 |
3 files changed, 191 insertions, 24 deletions
diff --git a/nslcd/cfg.c b/nslcd/cfg.c index c2b9674..056b6e2 100644 --- a/nslcd/cfg.c +++ b/nslcd/cfg.c @@ -1089,6 +1089,7 @@ static void cfg_defaults(struct ldap_config *cfg) cfg->pagesize = 0; cfg->nss_initgroups_ignoreusers = NULL; cfg->nss_min_uid = 0; + cfg->nss_nested_groups = 0; cfg->validnames_str = NULL; handle_validnames(__FILE__, __LINE__, "", "/^[a-z0-9._@$()]([a-z0-9._@$() \\~-]*[a-z0-9._@$()~-])?$/i", @@ -1408,6 +1409,11 @@ static void cfg_read(const char *filename, struct ldap_config *cfg) cfg->nss_min_uid = get_int(filename, lnr, keyword, &line); get_eol(filename, lnr, keyword, &line); } + else if (strcasecmp(keyword, "nss_nested_groups") == 0) + { + cfg->nss_nested_groups = get_boolean(filename, lnr, keyword, &line); + get_eol(filename, lnr, keyword, &line); + } else if (strcasecmp(keyword, "validnames") == 0) { handle_validnames(filename, lnr, keyword, line, cfg); @@ -1671,6 +1677,7 @@ static void cfg_dump(void) log_log(LOG_DEBUG, "CFG: nss_initgroups_ignoreusers %s", buffer); } log_log(LOG_DEBUG, "CFG: nss_min_uid %d", nslcd_cfg->nss_min_uid); + log_log(LOG_DEBUG, "CFG: nss_nested_groups %s", print_boolean(nslcd_cfg->nss_nested_groups)); log_log(LOG_DEBUG, "CFG: validnames %s", nslcd_cfg->validnames_str); log_log(LOG_DEBUG, "CFG: ignorecase %s", print_boolean(nslcd_cfg->ignorecase)); for (i = 0; i < NSS_LDAP_CONFIG_MAX_AUTHZ_SEARCHES; i++) diff --git a/nslcd/cfg.h b/nslcd/cfg.h index 5acb1d0..7caaa02 100644 --- a/nslcd/cfg.h +++ b/nslcd/cfg.h @@ -119,6 +119,7 @@ struct ldap_config { int pagesize; /* set to a greater than 0 to enable handling of paged results with the specified size */ SET *nss_initgroups_ignoreusers; /* the users for which no initgroups() searches should be done */ uid_t nss_min_uid; /* minimum uid for users retrieved from LDAP */ + int nss_nested_groups; /* maximum group recursion depth */ regex_t validnames; /* the regular expression to determine valid names */ char *validnames_str; /* string version of validnames regexp */ int ignorecase; /* whether or not case should be ignored in lookups */ diff --git a/nslcd/group.c b/nslcd/group.c index 868110c..175fceb 100644 --- a/nslcd/group.c +++ b/nslcd/group.c @@ -6,6 +6,7 @@ Copyright (C) 1997-2006 Luke Howard Copyright (C) 2006 West Consulting Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 @@ -135,6 +136,20 @@ static int mkfilter_group_bymember(MYLDAP_SESSION *session, attmap_group_member, safedn); } +static int mkfilter_group_bymemberdn(MYLDAP_SESSION *session, + const char *dn, + char *buffer, size_t buflen) +{ + char safedn[300]; + /* escape DN */ + if (myldap_escape(dn, safedn, sizeof(safedn))) + return -1; + return mysnprintf(buffer, buflen, + "(&%s(%s=%s))", + group_filter, + attmap_group_member, safedn); +} + void group_init(void) { int i; @@ -198,16 +213,12 @@ static int do_write_group(TFILE *fp, MYLDAP_ENTRY *entry, return 0; } -/* return the list of members */ -static const char **getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session) +static void getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session, + SET *members, SET *seen, SET *subgroups) { char buf[256]; int i; const char **values; - SET *set; - set = set_new(); - if (set == NULL) - return NULL; /* add the memberUid values */ values = myldap_get_values(entry, attmap_group_memberUid); if (values != NULL) @@ -215,21 +226,25 @@ static const char **getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session) { /* only add valid usernames */ if (isvalidname(values[i])) - set_add(set, values[i]); + set_add(members, values[i]); } /* add the member values */ values = myldap_get_values(entry, attmap_group_member); if (values != NULL) for (i = 0; values[i] != NULL; i++) { - /* transform the DN into a uid (dn2uid() already checks validity) */ - if (dn2uid(session, values[i], buf, sizeof(buf)) != NULL) - set_add(set, buf); + 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]); + } } - /* return the members */ - values = set_tolist(set); - set_free(set); - return values; } /* the maximum number of gidNumber attributes per entry */ @@ -241,11 +256,14 @@ static int write_group(TFILE *fp, MYLDAP_ENTRY *entry, const char *reqname, { const char **names, **gidvalues; const char *passwd; - const char **members; + const char **members = NULL; + SET *set, *seen=NULL, *subgroups=NULL; gid_t gids[MAXGIDS_PER_ENTRY]; int numgids; char *tmp; char passbuffer[64]; + MYLDAP_SEARCH *search; + MYLDAP_ENTRY *entry2; int rc; /* get group name (cn) */ names = myldap_get_values(entry, attmap_group_cn); @@ -300,9 +318,36 @@ static int write_group(TFILE *fp, MYLDAP_ENTRY *entry, const char *reqname, passwd = default_group_userPassword; /* get group memebers (memberUid&member) */ if (wantmembers) - members = getmembers(entry, session); - else - members = NULL; + { + 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); + } + } + 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, @@ -338,12 +383,22 @@ NSLCD_HANDLE( write_group(fp, entry, NULL, &gid, 1, session) ) -NSLCD_HANDLE( - group, bymember, NSLCD_ACTION_GROUP_BYMEMBER, +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[256]; char filter[4096]; + 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"); @@ -358,10 +413,114 @@ NSLCD_HANDLE( WRITE_INT32(fp, NSLCD_ACTION_GROUP_BYMEMBER); WRITE_INT32(fp, NSLCD_RESULT_END); return 0; - }, - mkfilter_group_bymember(session, name, filter, sizeof(filter)), - write_group(fp, entry, NULL, NULL, 0, session) -) + } + /* 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) + { + 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_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(session, dn, filter, sizeof(filter))) + { + log_log(LOG_WARNING, "nslcd_group_bymember(): filter buffer too small"); + set_free(seen); + set_free(tocheck); + return -1; + } + /* do the LDAP searches */ + for (i = 0; (base = group_bases[i]) != NULL; i++) + { + search = myldap_search(session, base, group_scope, filter, group_attrs, NULL); + if (search != NULL) + { + while ((entry = myldap_get_entry(search, NULL)) != NULL) + { + if (!set_contains(seen, dn = myldap_get_dn(entry))) + { + 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, NSLCD_ACTION_GROUP_ALL, |