mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-25 16:47:20 +08:00
5f586adc63
Since commit 15e82eef3a40b ("Cygwin: getgrent: fix local SAM enumeration on domain member machines") we skip enumerating local BUILTIN accounts if we also enumerate AD. However, there are two local accounts which are only available in local SAM, not in AD. Don't skip enumerating those. Fixes: 15e82eef3a40b ("Cygwin: getgrent: fix local SAM enumeration on domain member machines") Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
760 lines
19 KiB
C++
760 lines
19 KiB
C++
/* 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;
|
|
}
|
|
|
|
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 *
|
|
getgrgid (gid_t gid)
|
|
{
|
|
struct group *tempgr = internal_getgrgid (gid);
|
|
pthread_testcancel ();
|
|
return getgr_cp (tempgr);
|
|
}
|
|
|
|
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 *
|
|
getgrnam (const char *name)
|
|
{
|
|
struct group *tempgr = internal_getgrnam (name);
|
|
pthread_testcancel ();
|
|
return getgr_cp (tempgr);
|
|
}
|
|
|
|
/* 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;
|
|
fallthrough;
|
|
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;
|
|
fallthrough;
|
|
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;
|
|
|
|
if (!LookupAccountNameW (NULL,
|
|
((PLOCALGROUP_INFO_0) buf)[cnt++].lgrpi0_name,
|
|
sid, &slen, dom, &dlen, &acc_type))
|
|
continue;
|
|
/* Skip builtin groups if we're enumerating AD as well to avoid
|
|
duplication. Don't skip "Power Users" and "Device Owners"
|
|
accounts, they don't show up in AD enumeration. */
|
|
if (cygheap->dom.member_machine ()
|
|
&& nss_db_enum_primary ()
|
|
&& sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */
|
|
&& sid_sub_auth (sid, 0) == SECURITY_BUILTIN_DOMAIN_RID
|
|
&& sid_sub_auth (sid, 1) != DOMAIN_ALIAS_RID_POWER_USERS
|
|
&& sid_sub_auth (sid, 1) != DOMAIN_ALIAS_RID_DEVICE_OWNERS)
|
|
continue;
|
|
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 *
|
|
getgrent (void)
|
|
{
|
|
return grent.getgrent ();
|
|
}
|
|
|
|
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
|
|
getgroups (int gidsetsize, gid_t *grouplist)
|
|
{
|
|
cyg_ldap cldap;
|
|
|
|
return internal_getgroups (gidsetsize, grouplist, &cldap);
|
|
}
|
|
|
|
/* 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, NO_CHK_DISABLED);
|
|
if (gid != ILLEGAL_GID && grpsid.getfromgr (grp))
|
|
gsids += grpsid;
|
|
cygheap->user.reimpersonate ();
|
|
}
|
|
|
|
extern "C" int
|
|
initgroups (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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* setgroups: standards? */
|
|
extern "C" int
|
|
setgroups (int ngroups, const gid_t *grouplist)
|
|
{
|
|
syscall_printf ("setgroups (%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;
|
|
}
|