859 lines
21 KiB
C++
859 lines
21 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;
|
||
|
}
|
||
|
|
||
|
#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;
|
||
|
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;
|
||
|
|
||
|
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, NO_CHK_DISABLED);
|
||
|
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
|