| /* grp.cc |
| |
| Original stubs by Jason Molenda of Cygnus Support, crash@cygnus.com |
| First implementation by Gunther Ebert, gunther.ebert@ixos-leipzig.de |
| |
| This file is part of Cygwin. |
| |
| This software is a copyrighted work licensed under the terms of the |
| Cygwin license. Please consult the file "CYGWIN_LICENSE" for |
| details. */ |
| |
| #include "winsup.h" |
| #include <lm.h> |
| #include <ntsecapi.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "cygerrno.h" |
| #include "pinfo.h" |
| #include "path.h" |
| #include "fhandler.h" |
| #include "dtable.h" |
| #include "cygheap.h" |
| #include "ntdll.h" |
| #include "miscfuncs.h" |
| #include "ldap.h" |
| #include "tls_pbuf.h" |
| |
| 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) |
| 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)) |
| return false; |
| /* Don't generate gr_mem entries. */ |
| grp.g.gr_mem = &null_ptr; |
| cygsid csid; |
| if (csid.getfromgr_passwd (&grp.g)) |
| RtlCopySid (SECURITY_MAX_SID_SIZE, grp.sid, csid); |
| return true; |
| } |
| |
| muto NO_COPY pwdgrp::pglock; |
| |
| void |
| pwdgrp::init_grp () |
| { |
| pwdgrp_buf_elem_size = sizeof (pg_grp); |
| parse = &pwdgrp::parse_group; |
| } |
| |
| 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; |
| } |
| |
| struct group * |
| pwdgrp::find_group (const char *name) |
| { |
| for (ULONG i = 0; i < curr_lines; i++) |
| if (strcasematch (group ()[i].g.gr_name, name)) |
| return &group ()[i].g; |
| return NULL; |
| } |
| |
| struct group * |
| pwdgrp::find_group (gid_t gid) |
| { |
| for (ULONG i = 0; i < curr_lines; i++) |
| if (gid == group ()[i].g.gr_gid) |
| return &group ()[i].g; |
| return NULL; |
| } |
| |
| struct group * |
| internal_getgrsid (cygpsid &sid, 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 (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); |
| return NULL; |
| } |
| |
| /* Like internal_getgrsid but return only already cached data, |
| NULL otherwise. */ |
| static struct group * |
| internal_getgrsid_cachedonly (cygpsid &sid) |
| { |
| struct group *ret; |
| |
| /* Check caches only. */ |
| 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; |
| return NULL; |
| } |
| |
| /* Called from internal_getgroups. The full information required to create |
| a group account entry is already available from the LookupAccountSids |
| call. internal_getgrfull passes all available info into |
| pwdgrp::fetch_account_from_line, thus avoiding a LookupAccountSid call |
| for each group. This is quite a bit faster, especially in slower |
| environments. */ |
| static struct group * __attribute__((used)) |
| internal_getgrfull (fetch_acc_t &full_acc, cyg_ldap *pldap) |
| { |
| struct group *ret; |
| |
| cygheap->pg.nss_init (); |
| /* Skip local caches, internal_getgroups already called |
| internal_getgrsid_cachedonly. */ |
| if (cygheap->pg.nss_cygserver_caching () |
| && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver |
| (full_acc.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 |
| (full_acc.sid))) |
| return ret; |
| } |
| if (cygheap->pg.nss_grp_db ()) |
| return cygheap->pg.grp_cache.win.add_group_from_windows (full_acc, pldap); |
| return NULL; |
| } |
| |
| /* This function gets only called from mkgroup via cygwin_internal. */ |
| struct group * |
| internal_getgrsid_from_db (cygpsid &sid) |
| { |
| cygheap->pg.nss_init (); |
| return cygheap->pg.grp_cache.win.add_group_from_windows (sid); |
| } |
| |
| 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); |
| return NULL; |
| } |
| |
| struct group * |
| internal_getgrgid (gid_t gid, 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 (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); |
| return NULL; |
| } |
| |
| #ifdef __i386__ |
| static struct __group16 * |
| grp32togrp16 (struct __group16 *gp16, struct group *gp32) |
| { |
| if (!gp16 || !gp32) |
| return NULL; |
| |
| /* Copying the pointers is actually unnecessary. Just having the correct |
| return type is important. */ |
| gp16->gr_name = gp32->gr_name; |
| gp16->gr_passwd = gp32->gr_passwd; |
| gp16->gr_gid = (__gid16_t) gp32->gr_gid; /* Not loss-free */ |
| gp16->gr_mem = gp32->gr_mem; |
| |
| return gp16; |
| } |
| #endif |
| |
| extern "C" int |
| getgrgid_r (gid_t gid, struct group *grp, char *buffer, size_t bufsize, |
| struct group **result) |
| { |
| *result = NULL; |
| |
| if (!grp || !buffer) |
| return ERANGE; |
| |
| struct group *tempgr = internal_getgrgid (gid); |
| pthread_testcancel (); |
| if (!tempgr) |
| return 0; |
| |
| /* Check needed buffer size. Deliberately ignore gr_mem. */ |
| size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) |
| + 2 + sizeof (char *); |
| if (needsize > bufsize) |
| return ERANGE; |
| |
| /* Make a copy of tempgr. Deliberately ignore gr_mem. */ |
| *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; |
| 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); |
| } |
| |
| #ifdef __x86_64__ |
| EXPORT_ALIAS (getgrgid32, getgrgid) |
| #else |
| extern "C" struct __group16 * |
| getgrgid (__gid16_t gid) |
| { |
| static struct __group16 g16; /* FIXME: thread-safe? */ |
| |
| return grp32togrp16 (&g16, getgrgid32 (gid16togid32 (gid))); |
| } |
| #endif |
| |
| extern "C" int |
| getgrnam_r (const char *nam, struct group *grp, char *buffer, |
| size_t bufsize, struct group **result) |
| { |
| *result = NULL; |
| |
| if (!grp || !buffer) |
| return ERANGE; |
| |
| struct group *tempgr = internal_getgrnam (nam); |
| pthread_testcancel (); |
| if (!tempgr) |
| return 0; |
| |
| /* Check needed buffer size. Deliberately ignore gr_mem. */ |
| size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) |
| + 2 + sizeof (char *); |
| if (needsize > bufsize) |
| return ERANGE; |
| |
| /* Make a copy of tempgr. Deliberately ignore gr_mem. */ |
| *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; |
| return 0; |
| } |
| |
| extern "C" struct group * |
| getgrnam32 (const char *name) |
| { |
| struct group *tempgr = internal_getgrnam (name); |
| pthread_testcancel (); |
| return getgr_cp (tempgr); |
| } |
| |
| #ifdef __x86_64__ |
| EXPORT_ALIAS (getgrnam32, getgrnam) |
| #else |
| extern "C" struct __group16 * |
| getgrnam (const char *name) |
| { |
| static struct __group16 g16; /* FIXME: thread-safe? */ |
| |
| return grp32togrp16 (&g16, getgrnam32 (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 () |
| { |
| grent.setgrent (); |
| } |
| |
| extern "C" struct group * |
| getgrent32 (void) |
| { |
| return grent.getgrent (); |
| } |
| |
| #ifdef __x86_64__ |
| EXPORT_ALIAS (getgrent32, getgrent) |
| #else |
| extern "C" struct __group16 * |
| getgrent () |
| { |
| static struct __group16 g16; /* FIXME: thread-safe? */ |
| |
| return grp32togrp16 (&g16, getgrent32 ()); |
| } |
| #endif |
| |
| extern "C" void |
| endgrent (void) |
| { |
| grent.endgrent (); |
| } |
| |
| /* *_filtered functions are called from mkgroup */ |
| void * |
| setgrent_filtered (int enums, PCWSTR enum_tdoms) |
| { |
| gr_ent *gr = new gr_ent; |
| if (gr) |
| gr->setgrent (enums, enum_tdoms); |
| return (void *) gr; |
| } |
| |
| void * |
| getgrent_filtered (void *gr) |
| { |
| return (void *) ((gr_ent *) gr)->getgrent (); |
| } |
| |
| void |
| endgrent_filtered (void *gr) |
| { |
| ((gr_ent *) gr)->endgrent (); |
| } |
| |
| int |
| internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap) |
| { |
| NTSTATUS status; |
| HANDLE tok; |
| ULONG size; |
| PTOKEN_GROUPS groups; |
| PSID *sidp_buf; |
| ULONG scnt; |
| PLSA_REFERENCED_DOMAIN_LIST dlst = NULL; |
| PLSA_TRANSLATED_NAME nlst = NULL; |
| |
| tmp_pathbuf tp; |
| struct group *grp; |
| int cnt = 0; |
| |
| if (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) |
| { |
| cnt = -1; |
| break; |
| } |
| } |
| goto out; |
| } |
| |
| /* If impersonated, use impersonation token. */ |
| tok = cygheap->user.issetuid () ? cygheap->user.primary_token () |
| : hProcToken; |
| |
| /* Fetch groups from user token. */ |
| groups = (PTOKEN_GROUPS) tp.w_get (); |
| status = NtQueryInformationToken (tok, TokenGroups, groups, 2 * NT_MAX_PATH, |
| &size); |
| if (!NT_SUCCESS (status)) |
| { |
| debug_printf ("NtQueryInformationToken(TokenGroups) %y", status); |
| goto out; |
| } |
| /* Iterate over the group list and check which of them are already cached. |
| Those are simply copied to grouplist. The non-cached ones are collected |
| in sidp_buf for a later call to LsaLookupSids. */ |
| sidp_buf = (PSID *) tp.w_get (); |
| scnt = 0; |
| for (DWORD pg = 0; pg < groups->GroupCount; ++pg) |
| { |
| cygpsid sid = groups->Groups[pg].Sid; |
| if ((groups->Groups[pg].Attributes |
| & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) == 0 |
| || sid == well_known_world_sid) |
| continue; |
| if ((grp = internal_getgrsid_cachedonly (sid))) |
| { |
| if (cnt < gidsetsize) |
| grouplist[cnt] = grp->gr_gid; |
| ++cnt; |
| if (gidsetsize && cnt > gidsetsize) |
| { |
| cnt = -1; |
| goto out; |
| } |
| } |
| else |
| sidp_buf[scnt++] = sid; |
| } |
| /* If there are non-cached groups left, try to fetch them. */ |
| if (scnt > 0) |
| { |
| /* Don't call LsaLookupSids if we're not utilizing the Windows account |
| DBs. If we don't have access to the AD, which is one good reason to |
| disable passwd/group: db in nsswitch.conf, then the subsequent call |
| to LsaLookupSids will take 5 - 10 seconds in some environments. */ |
| if (!cygheap->pg.nss_grp_db ()) |
| { |
| for (DWORD pg = 0; pg < scnt; ++pg) |
| { |
| cygpsid sid = sidp_buf[pg]; |
| if ((grp = internal_getgrsid (sid, NULL))) |
| { |
| if (cnt < gidsetsize) |
| grouplist[cnt] = grp->gr_gid; |
| ++cnt; |
| if (gidsetsize && cnt > gidsetsize) |
| { |
| cnt = -1; |
| break; |
| } |
| } |
| } |
| goto out; |
| } |
| /* Otherwise call LsaLookupSids and call internal_getgrfull on the |
| returned groups. This performs a lot better than calling |
| internal_getgrsid on each group. */ |
| status = STATUS_ACCESS_DENIED; |
| HANDLE lsa = lsa_open_policy (NULL, POLICY_LOOKUP_NAMES); |
| if (!lsa) |
| { |
| debug_printf ("POLICY_LOOKUP_NAMES right not given?"); |
| goto out; |
| } |
| status = LsaLookupSids (lsa, scnt, sidp_buf, &dlst, &nlst); |
| lsa_close_policy (lsa); |
| if (NT_SUCCESS (status)) |
| { |
| for (ULONG ncnt = 0; ncnt < scnt; ++ncnt) |
| { |
| static UNICODE_STRING empty = { 0, 0, (PWSTR) L"" }; |
| fetch_acc_t full_acc = |
| { |
| .sid = sidp_buf[ncnt], |
| .name = &nlst[ncnt].Name, |
| .dom = &empty, |
| .acc_type = nlst[ncnt].Use |
| }; |
| |
| if (nlst[ncnt].DomainIndex >= 0) |
| full_acc.dom = &dlst->Domains[nlst[ncnt].DomainIndex].Name; |
| if ((grp = internal_getgrfull (full_acc, pldap))) |
| { |
| if (cnt < gidsetsize) |
| grouplist[cnt] = grp->gr_gid; |
| ++cnt; |
| if (gidsetsize && cnt > gidsetsize) |
| { |
| cnt = -1; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| out: |
| if (dlst) |
| LsaFreeMemory (dlst); |
| if (nlst) |
| LsaFreeMemory (nlst); |
| if (cnt == -1) |
| set_errno (EINVAL); |
| return cnt; |
| } |
| |
| extern "C" int |
| getgroups32 (int gidsetsize, gid_t *grouplist) |
| { |
| cyg_ldap cldap; |
| |
| return internal_getgroups (gidsetsize, grouplist, &cldap); |
| } |
| |
| #ifdef __x86_64__ |
| EXPORT_ALIAS (getgroups32, getgroups) |
| #else |
| extern "C" int |
| getgroups (int gidsetsize, __gid16_t *grouplist) |
| { |
| gid_t *grouplist32 = NULL; |
| |
| if (gidsetsize < 0) |
| { |
| set_errno (EINVAL); |
| return -1; |
| } |
| if (gidsetsize > 0 && grouplist) |
| grouplist32 = (gid_t *) alloca (gidsetsize * sizeof (gid_t)); |
| |
| int ret = getgroups32 (gidsetsize, grouplist32); |
| |
| if (gidsetsize > 0 && grouplist) |
| for (int i = 0; i < ret; ++ i) |
| grouplist[i] = grouplist32[i]; |
| |
| return ret; |
| } |
| #endif |
| |
| /* Core functionality of initgroups and getgrouplist. */ |
| 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); |
| cygsid usersid, grpsid; |
| if (usersid.getfrompw (pw)) |
| get_server_groups (gsids, usersid); |
| if (gid != ILLEGAL_GID && grpsid.getfromgr (grp)) |
| gsids += grpsid; |
| cygheap->user.reimpersonate (); |
| } |
| |
| extern "C" int |
| initgroups32 (const char *user, gid_t gid) |
| { |
| assert (user != NULL); |
| cygsidlist tmp_gsids (cygsidlist_auto, 12); |
| get_groups (user, gid, tmp_gsids); |
| cygsidlist new_gsids (cygsidlist_alloc, tmp_gsids.count ()); |
| for (int i = 0; i < tmp_gsids.count (); i++) |
| new_gsids.sids[i] = tmp_gsids.sids[i]; |
| new_gsids.count (tmp_gsids.count ()); |
| cygheap->user.groups.update_supp (new_gsids); |
| syscall_printf ( "0 = initgroups(%s, %u)", user, gid); |
| return 0; |
| } |
| |
| #ifdef __x86_64__ |
| EXPORT_ALIAS (initgroups32, initgroups) |
| #else |
| extern "C" int |
| initgroups (const char *user, __gid16_t gid) |
| { |
| return initgroups32 (user, gid16togid32(gid)); |
| } |
| #endif |
| |
| extern "C" int |
| getgrouplist (const char *user, gid_t gid, gid_t *groups, int *ngroups) |
| { |
| int ret = 0; |
| int cnt = 0; |
| struct group *grp; |
| cyg_ldap cldap; |
| |
| /* 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. |
| FreeBSD calls assert for ngroups and allows a NULL groups if |
| *ngroups is 0. We follow FreeBSD's lead here, but always allow |
| a NULL groups pointer. */ |
| assert (user != NULL); |
| assert (ngroups != NULL); |
| |
| 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 (groups && cnt < *ngroups) |
| groups[cnt] = grp->gr_gid; |
| ++cnt; |
| } |
| if (cnt > *ngroups) |
| ret = -1; |
| else |
| ret = cnt; |
| *ngroups = cnt; |
| |
| syscall_printf ( "%d = getgrouplist(%s, %u, %p, %d)", |
| ret, user, gid, groups, *ngroups); |
| return ret; |
| } |
| |
| /* setgroups32: standards? */ |
| extern "C" int |
| setgroups32 (int ngroups, const gid_t *grouplist) |
| { |
| syscall_printf ("setgroups32 (%d)", ngroups); |
| if (ngroups < 0 || (ngroups > 0 && !grouplist)) |
| { |
| set_errno (EINVAL); |
| return -1; |
| } |
| |
| cygsidlist gsids (cygsidlist_alloc, ngroups); |
| struct group *grp; |
| cyg_ldap cldap; |
| |
| if (ngroups && !gsids.sids) |
| return -1; |
| |
| for (int gidx = 0; gidx < ngroups; ++gidx) |
| { |
| if ((grp = internal_getgrgid (grouplist[gidx], &cldap)) |
| && gsids.addfromgr (grp)) |
| continue; |
| debug_printf ("No sid found for gid %u", grouplist[gidx]); |
| gsids.free_sids (); |
| set_errno (EINVAL); |
| return -1; |
| } |
| cygheap->user.groups.update_supp (gsids); |
| return 0; |
| } |
| |
| #ifdef __i386__ |
| extern "C" int |
| setgroups (int ngroups, const __gid16_t *grouplist) |
| { |
| gid_t *grouplist32 = NULL; |
| |
| if (ngroups > 0 && grouplist) |
| { |
| grouplist32 = (gid_t *) alloca (ngroups * sizeof (gid_t)); |
| if (grouplist32 == NULL) |
| return -1; |
| for (int i = 0; i < ngroups; i++) |
| grouplist32[i] = grouplist[i]; |
| } |
| return setgroups32 (ngroups, grouplist32); |
| } |
| #else |
| EXPORT_ALIAS (setgroups32, setgroups) |
| #endif |