Skip to content

Commit

Permalink
7024 Add getgrouplist() to illumos
Browse files Browse the repository at this point in the history
Reviewed by: Robert Mustacchi <rm@fingolfin.org>
Approved by: Dan McDonald <danmcd@joyent.com>
  • Loading branch information
Jason King committed Dec 9, 2020
1 parent adc5288 commit f2c438c
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 14 deletions.
11 changes: 3 additions & 8 deletions usr/src/head/grp.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright 2020 Joyent, Inc.
*/

#ifndef _GRP_H
Expand Down Expand Up @@ -62,6 +64,7 @@ extern struct group *fgetgrent_r(FILE *, struct group *, char *, int);

extern struct group *fgetgrent(FILE *); /* MT-unsafe */
extern int initgroups(const char *, gid_t);
extern int getgrouplist(const char *, gid_t, gid_t *, int *);
#endif /* defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) */

#if defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) || defined(_XPG4_2)
Expand Down Expand Up @@ -125,13 +128,6 @@ extern int __posix_getgrgid_r(gid_t, struct group *, char *, size_t,
extern int __posix_getgrnam_r(const char *, struct group *, char *, size_t,
struct group **);

#ifdef __lint

#define getgrgid_r __posix_getgrgid_r
#define getgrnam_r __posix_getgrnam_r

#else /* !__lint */

static int
getgrgid_r(gid_t __gid, struct group *__grp, char *__buf, size_t __len,
struct group **__res)
Expand All @@ -145,7 +141,6 @@ getgrnam_r(const char *__cb, struct group *__grp, char *__buf, size_t __len,
return (__posix_getgrnam_r(__cb, __grp, __buf, __len, __res));
}

#endif /* !__lint */
#endif /* __PRAGMA_REDEFINE_EXTNAME */

#else /* (_POSIX_C_SOURCE - 0 >= 199506L) || ... */
Expand Down
129 changes: 126 additions & 3 deletions usr/src/lib/libc/port/gen/initgroups.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2020 Joyent, Inc.
*/

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

#pragma ident "%Z%%M% %I% %E% SMI"

#pragma weak _initgroups = initgroups

#include "lint.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
Expand Down Expand Up @@ -84,3 +85,125 @@ initgroups(const char *uname, gid_t agroup)
errno = errsave;
return (retsave);
}

int
getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *ngroups)
{
gid_t *grouplist = NULL;
gid_t *grpptr;
long ngroups_max;
int sz, ret;

/*
* We require sysconf(_SC_NGROUPS_MAX) either returns a sane value (>0)
* or fails. If it returns 0, something has gone horribly, horribly
* wrong.
*/
ngroups_max = sysconf(_SC_NGROUPS_MAX);
if (ngroups_max > INT_MAX)
ngroups_max = INT_MAX;
else if (ngroups_max < 0)
return (-1);
VERIFY3S(ngroups_max, >, 0);

/*
* The documented behavior of getgrouplist(3C) on other platforms
* (e.g. Linux and FreeBSD) do not list any failures other than
* 'groups is too small'. However, examination of some popular
* implementations of getgrouplist on those platforms (e.g. glibc and
* musl -- both appear to share the same man page for getgrouplist(3))
* show that they can in fact fail for other reasons (e.g. ENOMEM,
* EIO).
*
* As such, we don't attempt to catch and deal with any underlying
* errors here. Instead, any underlying errors cause getgrouplist(3C)
* to fail, and any errno value set is left unmodified for examination
* by the caller.
*
* One small complication is that the internal _getgroupsbymember()
* itself doesn't provide any way to report back if the buffer supplied
* to _getgroupsbymember() is too small. Instead, we always supply
* a buffer large enough to hold _SC_NGROUPS_MAX entries -- either
* by allocating one ourselves or using the user supplied buffer if
* sufficiently large.
*
* The system behavior is undefined for any user in more groups than
* _SC_NGROUPS_MAX -- initgroups(3C) for example just ignores any
* excess groups (and which _SC_NGROUPS_MAX sized subset of groups end
* up being set as the secondary groups is non-deterministic), so this
* seems reasonable. Modifying _getgroupsbymember() would require
* modification of the NSS code (due to the pervasive special handling
* of _getgroupsbymember() in the NSS code) as well as modification of
* all NSS backends that implement it. As there are at least a few
* known third party NSS backends, we've opted to avoid doing this
* for now.
*/

if ((ngroups == NULL) || (*ngroups <= 0) || (groups == NULL)) {
*ngroups = ngroups_max;
errno = EINVAL;
return (-1);
}

if (*ngroups < ngroups_max) {
/*
* The caller's buffer might be too small, try to use our own
* buffer instead.
*/
grouplist = calloc(ngroups_max, sizeof (gid_t));
if (grouplist == NULL)
return (-1);

grpptr = grouplist;
sz = ngroups_max;
} else {
/* The caller's buffer is large enough, so use it */
grpptr = groups;
sz = *ngroups;
}

/*
* Always add agroup as the first member -- it should always appear
* in the resulting list of groups, and this allows the backends to
* skip adding it.
*/
grpptr[0] = agroup;

ret = _getgroupsbymember(uname, grpptr, sz, 1);

/*
* We passed in 1 group entry. We should at minimum get 1 entry back
* from _getgroupsbymember(). If we don't, there is a bug in the NSS
* code or a backend. Since the return value is used to size a copy
* further below, we hard fail (abort) here if we get back an
* impossible value so we're not traipsing all over memory (which would
* just make debugging any such problem all the more difficult).
*/
VERIFY3S(ret, >, 0);

/*
* If we used the caller's buffer, it means its size was >= ngroups_max
* entries, and we're done.
*/
if (grpptr == groups) {
/* Set *ngroups to the number of entries in groups */
*ngroups = ret;
return (ret);
}

/* We verified earlier *ngroups > 0 */
if (ret < *ngroups) {
/* Copy as many gids that will fit */
(void) memcpy(groups, grpptr, *ngroups * sizeof (gid_t));

*ngroups = ret;
ret = -1;
errno = ERANGE;
} else {
(void) memcpy(groups, grpptr, ret * sizeof (gid_t));
*ngroups = ret;
}

free(grouplist);
return (ret);
}
7 changes: 6 additions & 1 deletion usr/src/lib/libc/port/mapfile-vers
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2018 Nexenta Systems, Inc.
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2018 Joyent, Inc.
# Copyright 2020 Joyent, Inc.
# Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright (c) 2013 Gary Mills
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
Expand Down Expand Up @@ -78,6 +78,11 @@ $if _x86 && _ELF64
$add amd64
$endif

SYMBOL_VERSION ILLUMOS_0.38 {
protected:
getgrouplist;
} ILLUMOS_0.37;

SYMBOL_VERSION ILLUMOS_0.37 {
global:
__stack_chk_guard;
Expand Down
3 changes: 2 additions & 1 deletion usr/src/man/man3c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# Copyright 2018 Nexenta Systems, Inc.
# Copyright 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Joyent, Inc.
# Copyright 2020 Joyent, Inc.
# Copyright 2018 Jason King
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
Expand Down Expand Up @@ -172,6 +172,7 @@ MANFILES= __fbufsize.3c \
getenv.3c \
getexecname.3c \
getgrnam.3c \
getgrouplist.3c \
gethostid.3c \
gethostname.3c \
gethrtime.3c \
Expand Down
Loading

0 comments on commit f2c438c

Please sign in to comment.