diff options
Diffstat (limited to 'winsup/cygwin/grp.cc')
-rw-r--r-- | winsup/cygwin/grp.cc | 530 |
1 files changed, 187 insertions, 343 deletions
diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index f7ce61b08..d4d1b653f 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -1,7 +1,7 @@ /* grp.cc Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2011, 2012, 2013, 2014 Red Hat, Inc. + 2008, 2009, 2011, 2012, 2013 Red Hat, Inc. Original stubs by Jason Molenda of Cygnus Support, crash@cygnus.com First implementation by Gunther Ebert, gunther.ebert@ixos-leipzig.de @@ -13,7 +13,6 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include <lm.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> @@ -24,166 +23,134 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "ntdll.h" -#include "miscfuncs.h" -#include "ldap.h" -#include "tls_pbuf.h" +#include "pwdgrp.h" +static group *group_buf; +static pwdgrp gr (group_buf); static char * NO_COPY_RO null_ptr; bool pwdgrp::parse_group () { - pg_grp &grp = group ()[curr_lines]; - grp.g.gr_name = next_str (':'); - if (!*grp.g.gr_name) + group &grp = (*group_buf)[curr_lines]; + grp.gr_name = next_str (':'); + if (!*grp.gr_name) return false; - grp.g.gr_passwd = next_str (':'); - /* Note that lptr points to the first byte of the gr_gid field. - We deliberately ignore the gr_gid and gr_mem entries when copying - the buffer content since they are not referenced anymore. */ - grp.len = lptr - grp.g.gr_name; - if (!next_num (grp.g.gr_gid)) + + grp.gr_passwd = next_str (':'); + + if (!next_num (grp.gr_gid)) return false; - /* Don't generate gr_mem entries. */ - grp.g.gr_mem = &null_ptr; - grp.sid.getfromgr (&grp.g); + + int n; + char *dp = raw_ptr (); + for (n = 0; *next_str (','); n++) + continue; + + grp.gr_mem = &null_ptr; + if (n) + { + char **namearray = (char **) calloc (n + 1, sizeof (char *)); + if (namearray) + { + for (int i = 0; i < n; i++, dp = strchr (dp, '\0') + 1) + namearray[i] = dp; + grp.gr_mem = namearray; + } + } + return true; } -muto NO_COPY pwdgrp::pglock; - +/* Cygwin internal */ +/* Read in /etc/group and save contents in the group cache */ +/* This sets group_in_memory_p to 1 so functions in this file can + tell that /etc/group has been read in */ void -pwdgrp::init_grp () +pwdgrp::read_group () { - pwdgrp_buf_elem_size = sizeof (pg_grp); - parse = &pwdgrp::parse_group; -} + for (int i = 0; i < gr.curr_lines; i++) + if ((*group_buf)[i].gr_mem != &null_ptr) + free ((*group_buf)[i].gr_mem); -struct group * -pwdgrp::find_group (cygpsid &sid) -{ - for (ULONG i = 0; i < curr_lines; i++) - if (sid == group ()[i].sid) - return &group ()[i].g; - return NULL; + load (L"\\etc\\group"); + + /* Complete /etc/group in memory if needed */ + if (!internal_getgrgid (myself->gid)) + { + static char linebuf [200]; + char group_name [UNLEN + 1] = "mkgroup"; + char strbuf[128] = ""; + struct group *gr; + + cygheap->user.groups.pgsid.string (strbuf); + if ((gr = internal_getgrsid (cygheap->user.groups.pgsid))) + snprintf (group_name, sizeof (group_name), + "passwd/group_GID_clash(%u/%u)", myself->gid, gr->gr_gid); + if (myself->uid == UNKNOWN_UID) + strcpy (group_name, "mkpasswd"); /* Feedback... */ + snprintf (linebuf, sizeof (linebuf), "%s:%s:%u:%s", + group_name, strbuf, myself->gid, cygheap->user.name ()); + debug_printf ("Completing /etc/group: %s", linebuf); + add_line (linebuf); + } + static char NO_COPY pretty_ls[] = "????????::-1:"; + add_line (pretty_ls); } -struct group * -pwdgrp::find_group (const char *name) +muto NO_COPY pwdgrp::pglock; + +pwdgrp::pwdgrp (passwd *&pbuf) : + pwdgrp_buf_elem_size (sizeof (*pbuf)), passwd_buf (&pbuf) { - for (ULONG i = 0; i < curr_lines; i++) - if (strcasematch (group ()[i].g.gr_name, name)) - return &group ()[i].g; - return NULL; + read = &pwdgrp::read_passwd; + parse = &pwdgrp::parse_passwd; + pglock.init ("pglock"); } -struct group * -pwdgrp::find_group (gid_t gid) +pwdgrp::pwdgrp (group *&gbuf) : + pwdgrp_buf_elem_size (sizeof (*gbuf)), group_buf (&gbuf) { - for (ULONG i = 0; i < curr_lines; i++) - if (gid == group ()[i].g.gr_gid) - return &group ()[i].g; - return NULL; + read = &pwdgrp::read_group; + parse = &pwdgrp::parse_group; + pglock.init ("pglock"); } struct group * -internal_getgrsid (cygpsid &sid, cyg_ldap *pldap) +internal_getgrsid (cygpsid &sid) { - struct group *ret; - - cygheap->pg.nss_init (); - /* Check caches first. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.find_group (sid))) - return ret; - if (cygheap->pg.nss_grp_files () - && (ret = cygheap->pg.grp_cache.file.find_group (sid))) - return ret; - if (cygheap->pg.nss_grp_db () - && (ret = cygheap->pg.grp_cache.win.find_group (sid))) - return ret; - /* Ask sources afterwards. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (sid))) - return ret; - if (cygheap->pg.nss_grp_files ()) - { - cygheap->pg.grp_cache.file.check_file (); - if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (sid))) - return ret; - } - if (cygheap->pg.nss_grp_db ()) - return cygheap->pg.grp_cache.win.add_group_from_windows (sid, pldap); + char sid_string[128]; + + gr.refresh (false); + + if (sid.string (sid_string)) + for (int i = 0; i < gr.curr_lines; i++) + if (!strcmp (sid_string, group_buf[i].gr_passwd)) + return group_buf + i; return NULL; } -/* This function gets only called from mkgroup via cygwin_internal. */ struct group * -internal_getgrsid_from_db (cygpsid &sid) +internal_getgrgid (gid_t gid, bool check) { - cygheap->pg.nss_init (); - return cygheap->pg.grp_cache.win.add_group_from_windows (sid); -} + gr.refresh (check); -struct group * -internal_getgrnam (const char *name, cyg_ldap *pldap) -{ - struct group *ret; - - cygheap->pg.nss_init (); - /* Check caches first. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.find_group (name))) - return ret; - if (cygheap->pg.nss_grp_files () - && (ret = cygheap->pg.grp_cache.file.find_group (name))) - return ret; - if (cygheap->pg.nss_grp_db () - && (ret = cygheap->pg.grp_cache.win.find_group (name))) - return ret; - /* Ask sources afterwards. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (name))) - return ret; - if (cygheap->pg.nss_grp_files ()) - { - cygheap->pg.grp_cache.file.check_file (); - if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (name))) - return ret; - } - if (cygheap->pg.nss_grp_db ()) - return cygheap->pg.grp_cache.win.add_group_from_windows (name, pldap); + for (int i = 0; i < gr.curr_lines; i++) + if (group_buf[i].gr_gid == gid) + return group_buf + i; return NULL; } struct group * -internal_getgrgid (gid_t gid, cyg_ldap *pldap) +internal_getgrnam (const char *name, bool check) { - struct group *ret; - - cygheap->pg.nss_init (); - /* Check caches first. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.find_group (gid))) - return ret; - if (cygheap->pg.nss_grp_files () - && (ret = cygheap->pg.grp_cache.file.find_group (gid))) - return ret; - if (cygheap->pg.nss_grp_db () - && (ret = cygheap->pg.grp_cache.win.find_group (gid))) - return ret; - /* Ask sources afterwards. */ - if (cygheap->pg.nss_cygserver_caching () - && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (gid))) - return ret; - if (cygheap->pg.nss_grp_files ()) - { - cygheap->pg.grp_cache.file.check_file (); - if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (gid))) - return ret; - } - if (cygheap->pg.nss_grp_db () || gid == ILLEGAL_GID) - return cygheap->pg.grp_cache.win.add_group_from_windows (gid, pldap); + gr.refresh (check); + + for (int i = 0; i < gr.curr_lines; i++) + if (strcasematch (group_buf[i].gr_name, name)) + return group_buf + i; + + /* Didn't find requested group */ return NULL; } @@ -214,65 +181,37 @@ getgrgid_r (gid_t gid, struct group *grp, char *buffer, size_t bufsize, if (!grp || !buffer) return ERANGE; - struct group *tempgr = internal_getgrgid (gid); + struct group *tempgr = internal_getgrgid (gid, true); pthread_testcancel (); if (!tempgr) return 0; - /* Check needed buffer size. Deliberately ignore gr_mem. */ + /* check needed buffer size. */ + int i; size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) + 2 + sizeof (char *); + for (i = 0; tempgr->gr_mem[i]; ++i) + needsize += strlen (tempgr->gr_mem[i]) + 1 + sizeof (char *); if (needsize > bufsize) return ERANGE; - /* Make a copy of tempgr. Deliberately ignore gr_mem. */ + /* make a copy of tempgr */ *result = grp; grp->gr_gid = tempgr->gr_gid; buffer = stpcpy (grp->gr_name = buffer, tempgr->gr_name); buffer = stpcpy (grp->gr_passwd = buffer + 1, tempgr->gr_passwd); grp->gr_mem = (char **) (buffer + 1); - grp->gr_mem[0] = NULL; + buffer = (char *) grp->gr_mem + (i + 1) * sizeof (char *); + for (i = 0; tempgr->gr_mem[i]; ++i) + buffer = stpcpy (grp->gr_mem[i] = buffer, tempgr->gr_mem[i]) + 1; + grp->gr_mem[i] = NULL; return 0; } -/* getgrgid/getgrnam are not reentrant. */ -static struct { - struct group g; - char *buf; - size_t bufsiz; -} app_gr; - -static struct group * -getgr_cp (struct group *tempgr) -{ - if (!tempgr) - return NULL; - pg_grp *gr = (pg_grp *) tempgr; - if (app_gr.bufsiz < gr->len) - { - char *newbuf = (char *) realloc (app_gr.buf, gr->len); - if (!newbuf) - { - set_errno (ENOMEM); - return NULL; - } - app_gr.buf = newbuf; - app_gr.bufsiz = gr->len; - } - memcpy (app_gr.buf, gr->g.gr_name, gr->len); - memcpy (&app_gr.g, &gr->g, sizeof gr->g); - ptrdiff_t diff = app_gr.buf - gr->g.gr_name; - app_gr.g.gr_name += diff; - app_gr.g.gr_passwd += diff; - return &app_gr.g; -} - extern "C" struct group * getgrgid32 (gid_t gid) { - struct group *tempgr = internal_getgrgid (gid); - pthread_testcancel (); - return getgr_cp (tempgr); + return internal_getgrgid (gid, true); } #ifdef __x86_64__ @@ -296,33 +235,37 @@ getgrnam_r (const char *nam, struct group *grp, char *buffer, if (!grp || !buffer) return ERANGE; - struct group *tempgr = internal_getgrnam (nam); + struct group *tempgr = internal_getgrnam (nam, true); pthread_testcancel (); if (!tempgr) return 0; - /* Check needed buffer size. Deliberately ignore gr_mem. */ + /* check needed buffer size. */ + int i; size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) + 2 + sizeof (char *); + for (i = 0; tempgr->gr_mem[i]; ++i) + needsize += strlen (tempgr->gr_mem[i]) + 1 + sizeof (char *); if (needsize > bufsize) return ERANGE; - /* Make a copy of tempgr. Deliberately ignore gr_mem. */ + /* make a copy of tempgr */ *result = grp; grp->gr_gid = tempgr->gr_gid; buffer = stpcpy (grp->gr_name = buffer, tempgr->gr_name); buffer = stpcpy (grp->gr_passwd = buffer + 1, tempgr->gr_passwd); grp->gr_mem = (char **) (buffer + 1); - grp->gr_mem[0] = NULL; + buffer = (char *) grp->gr_mem + (i + 1) * sizeof (char *); + for (i = 0; tempgr->gr_mem[i]; ++i) + buffer = stpcpy (grp->gr_mem[i] = buffer, tempgr->gr_mem[i]) + 1; + grp->gr_mem[i] = NULL; return 0; } extern "C" struct group * getgrnam32 (const char *name) { - struct group *tempgr = internal_getgrnam (name); - pthread_testcancel (); - return getgr_cp (tempgr); + return internal_getgrnam (name, true); } #ifdef __x86_64__ @@ -337,119 +280,21 @@ getgrnam (const char *name) } #endif -/* getgrent functions are not reentrant. */ -static gr_ent grent; - -void * -gr_ent::enumerate_caches () -{ - switch (max) - { - case 0: - if (cygheap->pg.nss_cygserver_caching ()) - { - pwdgrp &grc = cygheap->pg.grp_cache.cygserver; - if (cnt < grc.cached_groups ()) - return &grc.group ()[cnt++].g; - } - cnt = 0; - max = 1; - /*FALLTHRU*/ - case 1: - if (from_files) - { - pwdgrp &grf = cygheap->pg.grp_cache.file; - grf.check_file (); - if (cnt < grf.cached_groups ()) - return &grf.group ()[cnt++].g; - } - cnt = 0; - max = 2; - /*FALLTHRU*/ - case 2: - if (from_db) - { - pwdgrp &grw = cygheap->pg.grp_cache.win; - if (cnt < grw.cached_groups ()) - return &grw.group ()[cnt++].g; - } - break; - } - cnt = max = 0; - return NULL; -} - -void * -gr_ent::enumerate_local () -{ - while (true) - { - if (!cnt) - { - DWORD total; - NET_API_STATUS ret; - - if (buf) - { - NetApiBufferFree (buf); - buf = NULL; - } - if (resume == ULONG_MAX) - ret = ERROR_NO_MORE_ITEMS; - else - ret = NetLocalGroupEnum (NULL, 0, (PBYTE *) &buf, - MAX_PREFERRED_LENGTH, - &max, &total, &resume); - if (ret == NERR_Success) - resume = ULONG_MAX; - else if (ret != ERROR_MORE_DATA) - { - cnt = max = resume = 0; - return NULL; - } - } - while (cnt < max) - { - cygsid sid; - DWORD slen = SECURITY_MAX_SID_SIZE; - WCHAR dom[DNLEN + 1]; - DWORD dlen = DNLEN + 1; - SID_NAME_USE acc_type; - - LookupAccountNameW (NULL, - ((PLOCALGROUP_INFO_0) buf)[cnt++].lgrpi0_name, - sid, &slen, dom, &dlen, &acc_type); - fetch_user_arg_t arg; - arg.type = SID_arg; - arg.sid = &sid; - char *line = pg.fetch_account_from_windows (arg); - if (line) - return pg.add_account_post_fetch (line, false); - } - cnt = 0; - } -} - -struct group * -gr_ent::getgrent (void) -{ - if (state == rewound) - setent (true); - else - clear_cache (); - return (struct group *) getent (); -} - extern "C" void -setgrent () +endgrent () { - grent.setgrent (); + _my_tls.locals.grp_pos = 0; } extern "C" struct group * -getgrent32 (void) +getgrent32 () { - return grent.getgrent (); + if (_my_tls.locals.grp_pos == 0) + gr.refresh (true); + if (_my_tls.locals.grp_pos < gr.curr_lines) + return group_buf + _my_tls.locals.grp_pos++; + + return NULL; } #ifdef __x86_64__ @@ -465,85 +310,90 @@ getgrent () #endif extern "C" void -endgrent (void) -{ - grent.endgrent (); -} - -/* *_filtered functions are called from mkgroup */ -void * -setgrent_filtered (int enums, PCWSTR enum_tdoms) +setgrent () { - gr_ent *gr = new gr_ent; - if (gr) - gr->setgrent (enums, enum_tdoms); - return (void *) gr; + _my_tls.locals.grp_pos = 0; } -void * -getgrent_filtered (void *gr) +/* Internal function. ONLY USE THIS INTERNALLY, NEVER `getgrent'!!! */ +struct group * +internal_getgrent (int pos) { - return (void *) ((gr_ent *) gr)->getgrent (); -} + gr.refresh (false); -void -endgrent_filtered (void *gr) -{ - ((gr_ent *) gr)->endgrent (); + if (pos < gr.curr_lines) + return group_buf + pos; + return NULL; } int -internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap) +internal_getgroups (int gidsetsize, gid_t *grouplist, cygpsid * srchsid) { NTSTATUS status; - HANDLE tok; + HANDLE hToken = NULL; ULONG size; int cnt = 0; - struct group *grp; + struct group *gr; - if (cygheap->user.groups.issetgroups ()) + if (!srchsid && cygheap->user.groups.issetgroups ()) { - for (int pg = 0; pg < cygheap->user.groups.sgsids.count (); ++pg) - if ((grp = internal_getgrsid (cygheap->user.groups.sgsids.sids[pg], - pldap))) - { - if (cnt < gidsetsize) - grouplist[cnt] = grp->gr_gid; - ++cnt; - if (gidsetsize && cnt > gidsetsize) - goto error; - } + cygsid sid; + for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) + if (sid.getfromgr (gr)) + for (int pg = 0; pg < cygheap->user.groups.sgsids.count (); ++pg) + if (sid == cygheap->user.groups.sgsids.sids[pg] + && sid != well_known_world_sid) + { + if (cnt < gidsetsize) + grouplist[cnt] = gr->gr_gid; + ++cnt; + if (gidsetsize && cnt > gidsetsize) + goto error; + break; + } return cnt; } + /* If impersonated, use impersonation token. */ - tok = cygheap->user.issetuid () ? cygheap->user.primary_token () : hProcToken; + if (cygheap->user.issetuid ()) + hToken = cygheap->user.primary_token (); + else + hToken = hProcToken; - status = NtQueryInformationToken (tok, TokenGroups, NULL, 0, &size); + status = NtQueryInformationToken (hToken, TokenGroups, NULL, 0, &size); if (NT_SUCCESS (status) || status == STATUS_BUFFER_TOO_SMALL) { PTOKEN_GROUPS groups = (PTOKEN_GROUPS) alloca (size); - status = NtQueryInformationToken (tok, TokenGroups, groups, size, &size); + status = NtQueryInformationToken (hToken, TokenGroups, groups, + size, &size); if (NT_SUCCESS (status)) { - for (DWORD pg = 0; pg < groups->GroupCount; ++pg) + cygsid sid; + + if (srchsid) { - cygpsid sid = groups->Groups[pg].Sid; - if ((grp = internal_getgrsid (sid, pldap))) - { - if ((groups->Groups[pg].Attributes - & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) + for (DWORD pg = 0; pg < groups->GroupCount; ++pg) + if ((cnt = (*srchsid == groups->Groups[pg].Sid))) + break; + } + else + for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) + if (sid.getfromgr (gr)) + for (DWORD pg = 0; pg < groups->GroupCount; ++pg) + if (sid == groups->Groups[pg].Sid + && (groups->Groups[pg].Attributes + & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) && sid != well_known_world_sid) { if (cnt < gidsetsize) - grouplist[cnt] = grp->gr_gid; + grouplist[cnt] = gr->gr_gid; ++cnt; if (gidsetsize && cnt > gidsetsize) goto error; + break; } - } - } } } else @@ -558,9 +408,7 @@ error: extern "C" int getgroups32 (int gidsetsize, gid_t *grouplist) { - cyg_ldap cldap; - - return internal_getgroups (gidsetsize, grouplist, &cldap); + return internal_getgroups (gidsetsize, grouplist); } #ifdef __x86_64__ @@ -579,7 +427,7 @@ getgroups (int gidsetsize, __gid16_t *grouplist) if (gidsetsize > 0 && grouplist) grouplist32 = (gid_t *) alloca (gidsetsize * sizeof (gid_t)); - int ret = getgroups32 (gidsetsize, grouplist32); + int ret = internal_getgroups (gidsetsize, grouplist32); if (gidsetsize > 0 && grouplist) for (int i = 0; i < ret; ++ i) @@ -593,15 +441,13 @@ getgroups (int gidsetsize, __gid16_t *grouplist) static void get_groups (const char *user, gid_t gid, cygsidlist &gsids) { - cyg_ldap cldap; - cygheap->user.deimpersonate (); - struct passwd *pw = internal_getpwnam (user, &cldap); - struct group *grp = internal_getgrgid (gid, &cldap); + struct passwd *pw = internal_getpwnam (user); + struct group *gr = internal_getgrgid (gid); cygsid usersid, grpsid; if (usersid.getfrompw (pw)) get_server_groups (gsids, usersid, pw); - if (gid != ILLEGAL_GID && grpsid.getfromgr (grp)) + if (gid != ILLEGAL_GID && grpsid.getfromgr (gr)) gsids += grpsid; cygheap->user.reimpersonate (); } @@ -636,8 +482,7 @@ getgrouplist (const char *user, gid_t gid, gid_t *groups, int *ngroups) { int ret = 0; int cnt = 0; - struct group *grp; - cyg_ldap cldap; + struct group *gr; /* Note that it's not defined if groups or ngroups may be NULL! GLibc does not check the pointers on entry and just uses them. @@ -650,10 +495,10 @@ getgrouplist (const char *user, gid_t gid, gid_t *groups, int *ngroups) cygsidlist tmp_gsids (cygsidlist_auto, 12); get_groups (user, gid, tmp_gsids); for (int i = 0; i < tmp_gsids.count (); i++) - if ((grp = internal_getgrsid (tmp_gsids.sids[i], &cldap)) != NULL) + if ((gr = internal_getgrsid (tmp_gsids.sids[i])) != NULL) { if (groups && cnt < *ngroups) - groups[cnt] = grp->gr_gid; + groups[cnt] = gr->gr_gid; ++cnt; } if (cnt > *ngroups) @@ -679,16 +524,15 @@ setgroups32 (int ngroups, const gid_t *grouplist) } cygsidlist gsids (cygsidlist_alloc, ngroups); - struct group *grp; - cyg_ldap cldap; + struct group *gr; if (ngroups && !gsids.sids) return -1; for (int gidx = 0; gidx < ngroups; ++gidx) { - if ((grp = internal_getgrgid (grouplist[gidx], &cldap)) - && gsids.addfromgr (grp)) + if ((gr = internal_getgrgid (grouplist[gidx])) + && gsids.addfromgr (gr)) continue; debug_printf ("No sid found for gid %u", grouplist[gidx]); gsids.free_sids (); |