/*
- * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <string.h>
#include <ctype.h>
-#include <rpc/types.h>
-#include <rpc/xdr.h>
-#include <grp.h>
#include <pwd.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <pthread.h>
#include <errno.h>
#include <servers/bootstrap.h>
-
-#include "_lu_types.h"
-#include "lookup.h"
+#include <sys/syscall.h>
#include "lu_utils.h"
#include "lu_overrides.h"
+#define ENTRY_SIZE sizeof(struct group)
+#define ENTRY_KEY _li_data_key_group
#define GROUP_CACHE_SIZE 10
-#define DEFAULT_GROUP_CACHE_TTL 10
static pthread_mutex_t _group_cache_lock = PTHREAD_MUTEX_INITIALIZER;
-static void *_group_cache[GROUP_CACHE_SIZE] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
-static unsigned int _group_cache_best_before[GROUP_CACHE_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static void *_group_cache[GROUP_CACHE_SIZE] = { NULL };
static unsigned int _group_cache_index = 0;
-static unsigned int _group_cache_ttl = DEFAULT_GROUP_CACHE_TTL;
+static unsigned int _group_cache_init = 0;
static pthread_mutex_t _group_lock = PTHREAD_MUTEX_INITIALIZER;
+/*
+ * Note that we don't include grp.h and define struct group privately in here
+ * to avoid needing to produce a variant version of setgrent, which changed
+ * for UXIX03 conformance.
+ */
+struct group
+{
+ char *gr_name;
+ char *gr_passwd;
+ gid_t gr_gid;
+ char **gr_mem;
+};
+
+/* forward */
+int setgrent(void);
+struct group *getgrent(void);
+void endgrent(void);
+
/*
* Support for memberd calls
*/
#define MEMBERD_NAME "com.apple.memberd"
-static mach_port_t mbr_port = MACH_PORT_NULL;
typedef uint32_t GIDArray[16];
-extern kern_return_t _mbr_GetGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, GIDArray gids, security_token_t *token);
+extern kern_return_t memberdDSmig_GetGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, GIDArray gids, audit_token_t *token);
+extern kern_return_t memberdDSmig_GetAllGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, gid_t **gids, uint32_t *gidsCnt, audit_token_t *token);
+__private_extern__ uid_t audit_token_uid(audit_token_t a);
#define GR_GET_NAME 1
#define GR_GET_GID 2
#define GR_GET_ENT 3
-static void
-free_group_data(struct group *g)
+static struct group *
+copy_group(struct group *in)
{
- char **mem;
-
- if (g == NULL) return;
-
- if (g->gr_name != NULL) free(g->gr_name);
- if (g->gr_passwd != NULL) free(g->gr_passwd);
+ if (in == NULL) return NULL;
- mem = g->gr_mem;
- if (mem != NULL)
- {
- while (*mem != NULL) free(*mem++);
- free(g->gr_mem);
- }
+ return (struct group *)LI_ils_create("ss4*", in->gr_name, in->gr_passwd, in->gr_gid, in->gr_mem);
}
-static void
-free_group(struct group *g)
-{
- if (g == NULL) return;
- free_group_data(g);
- free(g);
- }
-
-static void
-free_lu_thread_info_group(void *x)
+/*
+ * Extract the next group entry from a kvarray.
+ */
+static void *
+extract_group(kvarray_t *in)
{
- struct lu_thread_info *tdata;
-
- if (x == NULL) return;
+ struct group tmp;
+ uint32_t d, k, kcount;
+ char *empty[1];
- tdata = (struct lu_thread_info *)x;
+ if (in == NULL) return NULL;
- if (tdata->lu_entry != NULL)
- {
- free_group((struct group *)tdata->lu_entry);
- tdata->lu_entry = NULL;
- }
+ d = in->curr;
+ in->curr++;
- _lu_data_free_vm_xdr(tdata);
+ if (d >= in->count) return NULL;
- free(tdata);
-}
+ empty[0] = NULL;
+ memset(&tmp, 0, ENTRY_SIZE);
-static struct group *
-extract_group(XDR *xdr)
-{
- int i, j, nkeys, nvals, status;
- char *key, **vals;
- struct group *g;
-
- if (xdr == NULL) return NULL;
+ tmp.gr_gid = -2;
- if (!xdr_int(xdr, &nkeys)) return NULL;
+ kcount = in->dict[d].kcount;
- g = (struct group *)calloc(1, sizeof(struct group));
- g->gr_gid = -2;
-
- for (i = 0; i < nkeys; i++)
+ for (k = 0; k < kcount; k++)
{
- key = NULL;
- vals = NULL;
- nvals = 0;
-
- status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
- if (status < 0)
+ if (!strcmp(in->dict[d].key[k], "gr_name"))
{
- free_group(g);
- return NULL;
- }
+ if (tmp.gr_name != NULL) continue;
+ if (in->dict[d].vcount[k] == 0) continue;
- if (nvals == 0)
- {
- free(key);
- continue;
+ tmp.gr_name = (char *)in->dict[d].val[k][0];
}
-
- j = 0;
-
- if ((g->gr_name == NULL) && (!strcmp("name", key)))
+ else if (!strcmp(in->dict[d].key[k], "gr_passwd"))
{
- g->gr_name = vals[0];
- j = 1;
- }
- else if ((g->gr_passwd == NULL) && (!strcmp("passwd", key)))
- {
- g->gr_passwd = vals[0];
- j = 1;
+ if (tmp.gr_passwd != NULL) continue;
+ if (in->dict[d].vcount[k] == 0) continue;
+
+ tmp.gr_passwd = (char *)in->dict[d].val[k][0];
}
- else if ((g->gr_gid == (gid_t)-2) && (!strcmp("gid", key)))
+ else if (!strcmp(in->dict[d].key[k], "gr_gid"))
{
- g->gr_gid = atoi(vals[0]);
- if ((g->gr_gid == 0) && (strcmp(vals[0], "0"))) g->gr_gid = -2;
+ if (in->dict[d].vcount[k] == 0) continue;
+ tmp.gr_gid = atoi(in->dict[d].val[k][0]);
}
- else if ((g->gr_mem == NULL) && (!strcmp("users", key)))
+ else if (!strcmp(in->dict[d].key[k], "gr_mem"))
{
- g->gr_mem = vals;
- j = nvals;
- vals = NULL;
- }
+ if (tmp.gr_mem != NULL) continue;
+ if (in->dict[d].vcount[k] == 0) continue;
- free(key);
- if (vals != NULL)
- {
- for (; j < nvals; j++) free(vals[j]);
- free(vals);
+ tmp.gr_mem = (char **)in->dict[d].val[k];
}
}
- if (g->gr_name == NULL) g->gr_name = strdup("");
- if (g->gr_passwd == NULL) g->gr_passwd = strdup("");
- if (g->gr_mem == NULL) g->gr_mem = (char **)calloc(1, sizeof(char *));
+ if (tmp.gr_name == NULL) tmp.gr_name = "";
+ if (tmp.gr_passwd == NULL) tmp.gr_passwd = "";
+ if (tmp.gr_mem == NULL) tmp.gr_mem = empty;
- return g;
-}
-
-static struct group *
-copy_group(struct group *in)
-{
- struct group *g;
- int i, len;
-
- if (in == NULL) return NULL;
-
- g = (struct group *)calloc(1, sizeof(struct group));
-
- g->gr_name = LU_COPY_STRING(in->gr_name);
- g->gr_passwd = LU_COPY_STRING(in->gr_passwd);
- g->gr_gid = in->gr_gid;
-
- len = 0;
- if (in->gr_mem != NULL)
- {
- for (len = 0; in->gr_mem[len] != NULL; len++);
- }
-
- g->gr_mem = (char **)calloc(len + 1, sizeof(char *));
- for (i = 0; i < len; i++)
- {
- g->gr_mem[i] = strdup(in->gr_mem[i]);
- }
-
- return g;
+ return copy_group(&tmp);
}
static int
}
static void
-recycle_group(struct lu_thread_info *tdata, struct group *in)
+cache_group(struct group *gr)
{
- struct group *g;
+ struct group *grcache;
- if (tdata == NULL) return;
- g = (struct group *)tdata->lu_entry;
+ if (gr == NULL) return;
- if (in == NULL)
- {
- free_group(g);
- tdata->lu_entry = NULL;
- }
+ pthread_mutex_lock(&_group_cache_lock);
- if (tdata->lu_entry == NULL)
- {
- tdata->lu_entry = in;
- return;
- }
+ grcache = copy_group(gr);
- free_group_data(g);
+ if (_group_cache[_group_cache_index] != NULL) LI_ils_free(_group_cache[_group_cache_index], ENTRY_SIZE);
- g->gr_name = in->gr_name;
- g->gr_passwd = in->gr_passwd;
- g->gr_gid = in->gr_gid;
- g->gr_mem = in->gr_mem;
+ _group_cache[_group_cache_index] = grcache;
+ _group_cache_index = (_group_cache_index + 1) % GROUP_CACHE_SIZE;
- free(in);
-}
+ _group_cache_init = 1;
-__private_extern__ unsigned int
-get_group_cache_ttl()
-{
- return _group_cache_ttl;
+ pthread_mutex_unlock(&_group_cache_lock);
}
-__private_extern__ void
-set_group_cache_ttl(unsigned int ttl)
+static int
+group_cache_check()
{
- int i;
-
- pthread_mutex_lock(&_group_cache_lock);
+ uint32_t i, status;
- _group_cache_ttl = ttl;
+ /* don't consult cache if it has not been initialized */
+ if (_group_cache_init == 0) return 1;
- if (ttl == 0)
- {
- for (i = 0; i < GROUP_CACHE_SIZE; i++)
- {
- if (_group_cache[i] == NULL) continue;
+ status = LI_L1_cache_check(ENTRY_KEY);
- free_group((struct group *)_group_cache[i]);
- _group_cache[i] = NULL;
- _group_cache_best_before[i] = 0;
- }
- }
+ /* don't consult cache if it is disabled or if we can't validate */
+ if ((status == LI_L1_CACHE_DISABLED) || (status == LI_L1_CACHE_FAILED)) return 1;
- pthread_mutex_unlock(&_group_cache_lock);
-}
-
-static void
-cache_group(struct group *gr)
-{
- struct timeval now;
- struct group *grcache;
-
- if (_group_cache_ttl == 0) return;
- if (gr == NULL) return;
+ /* return 0 if cache is OK */
+ if (status == LI_L1_CACHE_OK) return 0;
+ /* flush cache */
pthread_mutex_lock(&_group_cache_lock);
- grcache = copy_group(gr);
-
- gettimeofday(&now, NULL);
+ for (i = 0; i < GROUP_CACHE_SIZE; i++)
+ {
+ LI_ils_free(_group_cache[i], ENTRY_SIZE);
+ _group_cache[i] = NULL;
+ }
- if (_group_cache[_group_cache_index] != NULL)
- free_group((struct group *)_group_cache[_group_cache_index]);
-
- _group_cache[_group_cache_index] = grcache;
- _group_cache_best_before[_group_cache_index] = now.tv_sec + _group_cache_ttl;
- _group_cache_index = (_group_cache_index + 1) % GROUP_CACHE_SIZE;
+ _group_cache_index = 0;
pthread_mutex_unlock(&_group_cache_lock);
+
+ /* don't consult cache - it's now empty */
+ return 1;
}
+
static struct group *
cache_getgrnam(const char *name)
{
int i;
struct group *gr, *res;
- struct timeval now;
- if (_group_cache_ttl == 0) return NULL;
if (name == NULL) return NULL;
+ if (group_cache_check() != 0) return NULL;
pthread_mutex_lock(&_group_cache_lock);
- gettimeofday(&now, NULL);
-
for (i = 0; i < GROUP_CACHE_SIZE; i++)
{
- if (_group_cache_best_before[i] == 0) continue;
- if ((unsigned int)now.tv_sec > _group_cache_best_before[i]) continue;
-
gr = (struct group *)_group_cache[i];
+ if (gr == NULL) continue;
if (gr->gr_name == NULL) continue;
{
int i;
struct group *gr, *res;
- struct timeval now;
- if (_group_cache_ttl == 0) return NULL;
+ if (group_cache_check() != 0) return NULL;
pthread_mutex_lock(&_group_cache_lock);
- gettimeofday(&now, NULL);
-
for (i = 0; i < GROUP_CACHE_SIZE; i++)
{
- if (_group_cache_best_before[i] == 0) continue;
- if ((unsigned int)now.tv_sec > _group_cache_best_before[i]) continue;
-
gr = (struct group *)_group_cache[i];
+ if (gr == NULL) continue;
if ((gid_t)gid == gr->gr_gid)
{
}
static struct group *
-lu_getgrgid(int gid)
+ds_getgrgid(int gid)
{
- struct group *g;
- unsigned int datalen;
- XDR inxdr;
static int proc = -1;
- int count;
- char *lookup_buf;
-
- if (proc < 0)
- {
- if (_lookup_link(_lu_port, "getgrgid", &proc) != KERN_SUCCESS)
- {
- return NULL;
- }
- }
-
- gid = htonl(gid);
- datalen = 0;
- lookup_buf = NULL;
-
- if (_lookup_all(_lu_port, proc, (unit *)&gid, 1, &lookup_buf, &datalen) != KERN_SUCCESS)
- {
- return NULL;
- }
+ char val[16];
- datalen *= BYTES_PER_XDR_UNIT;
- if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
-
- xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
-
- count = 0;
- if (!xdr_int(&inxdr, &count))
- {
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- return NULL;
- }
-
- if (count == 0)
- {
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- return NULL;
- }
-
- g = extract_group(&inxdr);
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
-
- return g;
+ snprintf(val, sizeof(val), "%d", gid);
+ return (struct group *)LI_getone("getgrgid", &proc, extract_group, "gid", val);
}
static struct group *
-lu_getgrnam(const char *name)
+ds_getgrnam(const char *name)
{
- struct group *g;
- unsigned int datalen;
- char namebuf[_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT];
- XDR outxdr;
- XDR inxdr;
static int proc = -1;
- int count;
- char *lookup_buf;
-
- if (proc < 0)
- {
- if (_lookup_link(_lu_port, "getgrnam", &proc) != KERN_SUCCESS)
- {
- return NULL;
- }
- }
-
- xdrmem_create(&outxdr, namebuf, sizeof(namebuf), XDR_ENCODE);
-
- if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
- {
- xdr_destroy(&outxdr);
- return NULL;
- }
- datalen = 0;
- lookup_buf = NULL;
-
- if (_lookup_all(_lu_port, proc, (unit *)namebuf, xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen) != KERN_SUCCESS)
- {
- return NULL;
- }
-
- xdr_destroy(&outxdr);
-
- datalen *= BYTES_PER_XDR_UNIT;
- if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
-
- xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
-
- count = 0;
- if (!xdr_int(&inxdr, &count))
- {
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- return NULL;
- }
-
- if (count == 0)
- {
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- return NULL;
- }
-
- g = extract_group(&inxdr);
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
-
- return g;
+ return (struct group *)LI_getone("getgrnam", &proc, extract_group, "name", name);
}
/*
* returns -1 if adding the gid would overflow the list
*
*/
-static int
-_add_group(int gid, int *list, int *listcount, int max, int dupok, int laststatus)
+static void
+_add_group(gid_t g, gid_t **list, uint32_t *count, int dupok)
{
- int i, n, addit, status;
-
- if (laststatus != 0) return laststatus;
+ uint32_t i, n, addit;
- status = 0;
addit = 1;
- n = *listcount;
+
+ if (list == NULL) return;
+ if (*list == NULL) *count = 0;
+
+ n = *count;
if (dupok == 0)
{
for (i = 0; (i < n) && (addit == 1); i++)
{
- if (list[i] == gid) addit = 0;
+ if ((*list)[i] == g) addit = 0;
}
}
- if (addit == 0) return 0;
- if (n >= max) return -1;
+ if (addit == 0) return;
- list[n] = gid;
- *listcount = n + 1;
- return 0;
+ if (*list == NULL) *list = (gid_t *)calloc(1, sizeof(gid_t));
+ else *list = (gid_t *)realloc(*list, (n + 1) * sizeof(gid_t));
+
+ if (*list == NULL)
+ {
+ *count = 0;
+ return;
+ }
+
+ (*list)[n] = g;
+ *count = n + 1;
}
int
_old_getgrouplist(const char *uname, int basegid, int *groups, int *grpcnt)
{
struct group *grp;
- int i, status, maxgroups;
+ int i, status;
+ uint32_t maxgroups, gg_count;
+ gid_t *gg_list;
status = 0;
- maxgroups = *grpcnt;
+ maxgroups = (uint32_t)*grpcnt;
*grpcnt = 0;
+ gg_list = NULL;
+ gg_count = 0;
+
/*
* When installing primary group, duplicate it;
* the first element of groups is the effective gid
* and will be overwritten when a setgid file is executed.
*/
- status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
- status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
+ _add_group(basegid, &gg_list, &gg_count, 0);
+ _add_group(basegid, &gg_list, &gg_count, 1);
+
+ if (gg_list == NULL)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
/*
* Scan the group file to find additional groups.
{
if (!strcmp(grp->gr_mem[i], uname))
{
- status = _add_group(grp->gr_gid, groups, grpcnt, maxgroups, 0, status);
+ _add_group(grp->gr_gid, &gg_list, &gg_count, 0);
break;
}
}
}
endgrent();
- return status;
-}
-static int
-_mbr_running()
-{
- kern_return_t status;
+ if (gg_list == NULL)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
- status = bootstrap_look_up(bootstrap_port, MEMBERD_NAME, &mbr_port);
- if (status != KERN_SUCCESS) return 0;
- if (mbr_port == MACH_PORT_NULL) return 0;
- return 1;
+ /* return -1 if the user-supplied list is too short */
+ status = 0;
+ if (gg_count > maxgroups) status = -1;
+
+ /* copy at most maxgroups gids from gg_list to groups */
+ for (i = 0; (i < maxgroups) && (i < gg_count); i++) groups[i] = gg_list[i];
+
+ *grpcnt = gg_count;
+ free(gg_list);
+
+ return status;
}
/*
* This adds to 6533 bytes (until one of the constants changes)
*/
#define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098)
+
+/*
+ * This is the "old" client side routine from memberd.
+ * It now talks to DirectoryService, but it retains the old style where
+ * the caller provides an array for the output gids. It fetches the
+ * user's gids from DS, then copies as many as possible into the
+ * caller-supplied array.
+ */
static int
mbr_getgrouplist(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
{
struct passwd p, *res;
char buf[MAXPWBUF];
kern_return_t kstatus;
- uint32_t i, count;
+ uint32_t i, maxgroups, count, gidptrCnt, gg_count;
int pwstatus;
GIDArray gids;
- int status, maxgroups;
- security_token_t token;
+ gid_t *gidptr, *gg_list;
+ int status, do_dealloc;
+ audit_token_t token;
- status = 0;
-
- if (mbr_port == MACH_PORT_NULL) return status;
- if (name == NULL) return status;
- if (groups == NULL) return status;
- if (grpcnt == NULL) return status;
+ if (_ds_port == MACH_PORT_NULL) return 0;
+ if (name == NULL) return 0;
+ if (groups == NULL) return 0;
+ if (grpcnt == NULL) return 0;
- maxgroups = *grpcnt;
+ maxgroups = (uint32_t)(*grpcnt);
+ do_dealloc = 0;
*grpcnt = 0;
+ gidptr = NULL;
+ gidptrCnt = 0;
+ gg_list = NULL;
+ gg_count = 0;
+
+ _add_group(basegid, &gg_list, &gg_count, 0);
+ if (dupbase != 0) _add_group(basegid, &gg_list, &gg_count, 1);
- status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
- if (dupbase != 0) status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
-
- if (status != 0) return status;
+ if (gg_list == NULL)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
memset(&p, 0, sizeof(struct passwd));
memset(buf, 0, sizeof(buf));
res = NULL;
pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
- if (pwstatus != 0) return status;
- if (res == NULL) return status;
-
- token.val[0] = -1;
- token.val[1] = -1;
+ if (pwstatus != 0) return 0;
+ if (res == NULL) return 0;
count = 0;
- kstatus = _mbr_GetGroups(mbr_port, p.pw_uid, &count, gids, &token);
- if (kstatus != KERN_SUCCESS) return status;
- if (token.val[0] != 0) return KERN_FAILURE;
+ memset(&token, 0, sizeof(audit_token_t));
+
+ kstatus = 0;
+ if (maxgroups > 16)
+ {
+ kstatus = memberdDSmig_GetAllGroups(_ds_port, p.pw_uid, &count, &gidptr, &gidptrCnt, &token);
+ do_dealloc = 1;
+ }
+ else
+ {
+ kstatus = memberdDSmig_GetGroups(_ds_port, p.pw_uid, &count, gids, &token);
+ gidptr = (gid_t *)gids;
+ }
+
+ if (kstatus != KERN_SUCCESS) return 0;
+ if (audit_token_uid(token) != 0)
+ {
+ if (gg_list != NULL) free(gg_list);
+ return 0;
+ }
+
+ for (i = 0; i < count; i++) _add_group(gidptr[i], &gg_list, &gg_count, 0);
+
+ if ((do_dealloc == 1) && (gidptr != NULL)) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrCnt);
- for (i = 0; (i < count) && (status == 0); i++)
+ if (gg_list == NULL)
{
- status = _add_group(gids[i], groups, grpcnt, maxgroups, 0, status);
+ errno = ENOMEM;
+ return 0;
}
+ /* return -1 if the user-supplied list is too short */
+ status = 0;
+ if (gg_count > maxgroups) status = -1;
+
+ /* copy at most maxgroups gids from gg_list to groups */
+ for (i = 0; (i < maxgroups) && (i < gg_count); i++) groups[i] = gg_list[i];
+
+ *grpcnt = gg_count;
+ free(gg_list);
+
return status;
}
-static int
-lu_getgrouplist(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
+/*
+ * This is the "modern" routine for fetching the group list for a user.
+ * The grplist output parameter is allocated and filled with the gids
+ * of the specified user's groups. Returns the number of gids in the
+ * list or -1 on failure. Caller must free() the returns grplist.
+ */
+static int32_t
+ds_getgrouplist(const char *name, gid_t basegid, gid_t **grplist, int dupbase)
{
- unsigned int datalen;
- XDR outxdr;
- XDR inxdr;
- static int proc = -1;
- char *lookup_buf;
- char namebuf[_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT];
- int gid;
- int i, count;
- int status, maxgroups;
-
- status = 0;
+ struct passwd p, *res;
+ char buf[MAXPWBUF];
+ kern_return_t kstatus;
+ uint32_t i, count, gidptrCnt, out_count;
+ int pwstatus;
+ gid_t *gidptr, *out_list;
+ audit_token_t token;
- if (name == NULL) return status;
- if (groups == NULL) return status;
- if (grpcnt == NULL) return status;
+ if (_ds_port == MACH_PORT_NULL) return -1;
+ if (name == NULL) return -1;
+ if (grplist == NULL) return -1;
- maxgroups = *grpcnt;
- *grpcnt = 0;
+ gidptr = NULL;
+ gidptrCnt = 0;
+ out_list = NULL;
+ out_count = 0;
- status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
- if (dupbase != 0) status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
+ _add_group(basegid, &out_list, &out_count, 0);
+ if (dupbase != 0) _add_group(basegid, &out_list, &out_count, 1);
- if (status != 0) return status;
+ if (out_list == NULL) return -1;
- if (proc < 0)
+ memset(&p, 0, sizeof(struct passwd));
+ memset(buf, 0, sizeof(buf));
+ res = NULL;
+
+ pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
+ if (pwstatus != 0) return -1;
+ if (res == NULL) return -1;
+
+ count = 0;
+ memset(&token, 0, sizeof(audit_token_t));
+
+ kstatus = memberdDSmig_GetAllGroups(_ds_port, p.pw_uid, &count, &gidptr, &gidptrCnt, &token);
+ if (kstatus != KERN_SUCCESS)
{
- if (_lookup_link(_lu_port, "initgroups", &proc) != KERN_SUCCESS) return status;
+ if (out_list != NULL) free(out_list);
+ return -1;
}
- xdrmem_create(&outxdr, namebuf, sizeof(namebuf), XDR_ENCODE);
- if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
+ if (audit_token_uid(token) != 0)
{
- xdr_destroy(&outxdr);
- return status;
+ if (out_list != NULL) free(out_list);
+ if (gidptr != NULL) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrCnt);
+ return -1;
}
- datalen = 0;
- lookup_buf = NULL;
+ for (i = 0; i < count; i++) _add_group(gidptr[i], &out_list, &out_count, 0);
- if (_lookup_all(_lu_port, proc, (unit *)namebuf, xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen) != KERN_SUCCESS)
- {
- xdr_destroy(&outxdr);
- return status;
- }
+ if (gidptr != NULL) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrCnt);
- xdr_destroy(&outxdr);
-
- datalen *= BYTES_PER_XDR_UNIT;
- if ((lookup_buf == NULL) || (datalen == 0)) return 0;
+ *grplist = out_list;
+ return out_count;
+}
- xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
+static int
+getgrouplist_internal(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
+{
+ int status, in_grpcnt;
- if (!xdr_int(&inxdr, &count))
- {
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- return status;
- }
+ /*
+ * The man page says that the grpcnt parameter will be set to the actual number
+ * of groups that were found. Unfortunately, older impementations of this API
+ * have always set grpcnt to the number of groups that are being returned.
+ * To prevent regressions in callers of this API, we respect the old and
+ * incorrect implementation.
+ */
- for (i = 0; (i < count) && (status == 0); i++)
- {
- if (!xdr_int(&inxdr, &gid)) break;
- status = _add_group(gid, groups, grpcnt, maxgroups, 0, status);
- }
+ in_grpcnt = *grpcnt;
+ status = 0;
- xdr_destroy(&inxdr);
- vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+ if (_ds_running()) status = mbr_getgrouplist(name, basegid, groups, grpcnt, dupbase);
+ else status = _old_getgrouplist(name, basegid, groups, grpcnt);
+ if ((status < 0) && (*grpcnt > in_grpcnt)) *grpcnt = in_grpcnt;
return status;
}
-static int
-getgrouplist_internal(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
+static int32_t
+getgrouplist_internal_2(const char *name, gid_t basegid, gid_t **gid_list, int dupbase)
{
- if (_mbr_running())
- {
- return mbr_getgrouplist(name, basegid, groups, grpcnt, dupbase);
- }
+ int status;
+ uint32_t gid_count;
- if (_lu_running())
- {
- return lu_getgrouplist(name, basegid, groups, grpcnt, dupbase);
- }
+ if (name == NULL) return -1;
+ if (gid_list == NULL) return -1;
- return _old_getgrouplist(name, basegid, groups, grpcnt);
+ *gid_list = NULL;
+
+ if (_ds_running()) return ds_getgrouplist(name, basegid, gid_list, dupbase);
+
+ gid_count = NGROUPS + 1;
+ *gid_list = (gid_t *)calloc(gid_count, sizeof(gid_t));
+ if (*gid_list == NULL) return -1;
+
+ status = _old_getgrouplist(name, basegid, (int *)gid_list, (int *)&gid_count);
+ if (status < 0) return -1;
+ return gid_count;
}
int
getgrouplist(const char *uname, int agroup, int *groups, int *grpcnt)
{
- return getgrouplist_internal(uname, agroup, groups, grpcnt, 1);
+ return getgrouplist_internal(uname, agroup, groups, grpcnt, 0);
}
-static void
-lu_endgrent(void)
+int32_t
+getgrouplist_2(const char *uname, gid_t agroup, gid_t **groups)
{
- struct lu_thread_info *tdata;
+ return getgrouplist_internal_2(uname, agroup, groups, 0);
+}
- tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
- _lu_data_free_vm_xdr(tdata);
+static void
+ds_endgrent(void)
+{
+ LI_data_free_kvarray(LI_data_find_key(ENTRY_KEY));
}
-static int
-lu_setgrent(void)
+static void
+ds_setgrent(void)
{
- lu_endgrent();
- return 1;
+ ds_endgrent();
}
static struct group *
-lu_getgrent()
+ds_getgrent()
{
- struct group *g;
static int proc = -1;
- struct lu_thread_info *tdata;
-
- tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
- if (tdata == NULL)
- {
- tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
- _lu_data_set_key(_lu_data_key_group, tdata);
- }
-
- if (tdata->lu_vm == NULL)
- {
- if (proc < 0)
- {
- if (_lookup_link(_lu_port, "getgrent", &proc) != KERN_SUCCESS)
- {
- lu_endgrent();
- return NULL;
- }
- }
-
- if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
- {
- lu_endgrent();
- return NULL;
- }
- /* mig stubs measure size in words (4 bytes) */
- tdata->lu_vm_length *= 4;
-
- if (tdata->lu_xdr != NULL)
- {
- xdr_destroy(tdata->lu_xdr);
- free(tdata->lu_xdr);
- }
- tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
-
- xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
- if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
- {
- lu_endgrent();
- return NULL;
- }
- }
-
- if (tdata->lu_vm_cursor == 0)
- {
- lu_endgrent();
- return NULL;
- }
-
- g = extract_group(tdata->lu_xdr);
- if (g == NULL)
- {
- lu_endgrent();
- return NULL;
- }
-
- tdata->lu_vm_cursor--;
-
- return g;
+ return (struct group *)LI_getent("getgrent", &proc, extract_group, ENTRY_KEY, ENTRY_SIZE);
}
static struct group *
getgr_internal(const char *name, gid_t gid, int source)
{
struct group *res = NULL;
- int from_cache;
+ int add_to_cache;
- from_cache = 0;
+ add_to_cache = 0;
res = NULL;
switch (source)
if (res != NULL)
{
- from_cache = 1;
}
- else if (_lu_running())
+ else if (_ds_running())
{
switch (source)
{
case GR_GET_NAME:
- res = lu_getgrnam(name);
+ res = ds_getgrnam(name);
break;
case GR_GET_GID:
- res = lu_getgrgid(gid);
+ res = ds_getgrgid(gid);
break;
case GR_GET_ENT:
- res = lu_getgrent();
+ res = ds_getgrent();
break;
default: res = NULL;
}
+
+ if (res != NULL) add_to_cache = 1;
}
else
{
pthread_mutex_lock(&_group_lock);
+
switch (source)
{
case GR_GET_NAME:
break;
default: res = NULL;
}
+
pthread_mutex_unlock(&_group_lock);
}
- if (from_cache == 0) cache_group(res);
+ if (add_to_cache == 1) cache_group(res);
return res;
}
getgr(const char *name, gid_t gid, int source)
{
struct group *res = NULL;
- struct lu_thread_info *tdata;
+ struct li_thread_info *tdata;
- tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
- if (tdata == NULL)
- {
- tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
- _lu_data_set_key(_lu_data_key_group, tdata);
- }
+ tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
+ if (tdata == NULL) return NULL;
res = getgr_internal(name, gid, source);
- recycle_group(tdata, res);
- return (struct group *)tdata->lu_entry;
+ LI_data_recycle(tdata, res, ENTRY_SIZE);
+ return (struct group *)tdata->li_entry;
}
static int
int status;
*result = NULL;
- errno = 0;
res = getgr_internal(name, gid, source);
- if (res == NULL) return -1;
+ if (res == NULL) return 0;
status = copy_group_r(res, grp, buffer, bufsize);
- free_group(res);
- if (status != 0)
- {
- errno = ERANGE;
- return -1;
- }
+ LI_ils_free(res, ENTRY_SIZE);
+
+ if (status != 0) return ERANGE;
*result = grp;
return 0;
int
initgroups(const char *name, int basegid)
{
- int status, ngroups, groups[NGROUPS];
+ int status, pwstatus, ngroups, groups[NGROUPS];
+ struct passwd p, *res;
+ char buf[MAXPWBUF];
+
+ /* get the UID for this user */
+ memset(&p, 0, sizeof(struct passwd));
+ memset(buf, 0, sizeof(buf));
+ res = NULL;
+
+ pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
+ if (pwstatus != 0) return -1;
+ if (res == NULL) return -1;
ngroups = NGROUPS;
status = getgrouplist_internal(name, basegid, groups, &ngroups, 0);
if (status < 0) return status;
- return setgroups(ngroups, groups);
+ status = syscall(SYS_initgroups, ngroups, groups, p.pw_uid);
+ if (status < 0) return -1;
+
+ return 0;
}
struct group *
int
setgrent(void)
{
- if (_lu_running()) lu_setgrent();
+ if (_ds_running()) ds_setgrent();
else _old_setgrent();
return 1;
}
void
endgrent(void)
{
- if (_lu_running()) lu_endgrent();
+ if (_ds_running()) ds_endgrent();
else _old_endgrent();
}