diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/head/grp.h | 11 | ||||
-rw-r--r-- | usr/src/lib/libc/port/gen/initgroups.c | 129 | ||||
-rw-r--r-- | usr/src/lib/libc/port/mapfile-vers | 7 | ||||
-rw-r--r-- | usr/src/man/man3c/Makefile | 3 | ||||
-rw-r--r-- | usr/src/man/man3c/getgrouplist.3c | 207 | ||||
-rw-r--r-- | usr/src/pkg/manifests/system-library.man3c.inc | 3 | ||||
-rw-r--r-- | usr/src/uts/common/io/aggr/aggr_grp.c | 91 |
7 files changed, 421 insertions, 30 deletions
diff --git a/usr/src/head/grp.h b/usr/src/head/grp.h index b5cea984f2..1a7e8ffb98 100644 --- a/usr/src/head/grp.h +++ b/usr/src/head/grp.h @@ -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 @@ -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) @@ -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) @@ -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) || ... */ diff --git a/usr/src/lib/libc/port/gen/initgroups.c b/usr/src/lib/libc/port/gen/initgroups.c index 29e63e6e53..f03f833d13 100644 --- a/usr/src/lib/libc/port/gen/initgroups.c +++ b/usr/src/lib/libc/port/gen/initgroups.c @@ -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> @@ -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); +} diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index 0dec599cf6..b1309a7071 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -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> @@ -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; diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile index ea7b4d4834..e9f8f2cafd 100644 --- a/usr/src/man/man3c/Makefile +++ b/usr/src/man/man3c/Makefile @@ -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. # @@ -172,6 +172,7 @@ MANFILES= __fbufsize.3c \ getenv.3c \ getexecname.3c \ getgrnam.3c \ + getgrouplist.3c \ gethostid.3c \ gethostname.3c \ gethrtime.3c \ diff --git a/usr/src/man/man3c/getgrouplist.3c b/usr/src/man/man3c/getgrouplist.3c new file mode 100644 index 0000000000..f1e10695e3 --- /dev/null +++ b/usr/src/man/man3c/getgrouplist.3c @@ -0,0 +1,207 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" +.\" Copyright 2020 Joyent, Inc. +.\" +.Dd November 22, 2020 +.Dt GETGROUPLIST 3C +.Os +.Sh NAME +.Nm getgrouplist +.Nd calculate group access list +.Sh SYNOPSIS +.In grp.h +.Ft int +.Fo getgrouplist +.Fa "const char *user" +.Fa "gid_t agroup" +.Fa "gid_t *groups" +.Fa "int *ngroups" +.Fc +.Sh DESCRIPTION +The +.Fn getgrouplist +function queries the group database to obtain the list of groups that +.Fa user +belongs to. +The +.Fa agroup +group is always added to the resulting group list. +This value is typically the primary gid of the user from the +.Sy passwd +database. +.Pp +When calling +.Fn getgrouplist , +the caller should set the maximum number of groups that +.Fa groups +can hold in +.Fa *ngroups . +The value of +.Dv NGROUPS_MAX +can be used to size +.Fa groups +to ensure it can hold any number of groups supported by the system. +.Pp +Upon return, +.Fn getgrouplist +stores the list of groups that +.Fa user +belongs to in +.Fa groups +and stores the number of groups +.Fa user +belongs to in +.Fa *ngroups +.Po +this may be a smaller than the value passed in when +calling +.Fn getgrouplist +.Pc . +If +.Fa groups +is too small to hold all of the groups +.Fa user +belongs to, +.Fn getgrouplist +fails and sets +.Fa *ngroups +to a value large enough to hold the full result. +.Sh RETURN VALUES +On success, +.Fn getgrouplist +returns the number of groups +.Fa user +belongs to, fills in +.Fa groups +with the gids of the groups +.Fa user +belongs to, and also sets +.Fa *ngroups +to the number of groups +.Fa user +belongs to. +.Pp +On failure, +.Fn getgrouplist +returns -1 and +.Va errno +is set. +.Pp +The behavior of +.Fn getgrouplist +is undefined if the total number of groups a user belongs to exceeds +.Dv NGROUPS_MAX . +.Pp +Note that on +.Fx , +.Fn getgrouplist +always returns -1 on failure or 0 on success. +A caller must rely on the value set in +.Fa *ngroups +upon return to determine the number of entries in +.Fa groups . +.Pp +On Linux, both glibc and musl return the number of groups +.Fa user +belongs to on success and returns -1 on failure. +.Pp +None of these other implementations document any +.Va errno +values on failure, however their implementations show that +.Va errno +may be set on failure. +Software using +.Fn getgrouplist +should be aware of these differences when attemping to write portable +software. +.Sh EXAMPLES +.Sy Example 1 +Print all the groups for a user. +.Bd -literal +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <err.h> + +void +printgroups(const char *user) +{ + struct passwd *pw; + gid_t *groups; + int ngroups, ret; + + if ((groups = calloc(NGROUPS_MAX, sizeof (gid_t))) == NULL) + err(EXIT_FAILURE, "calloc"); + + if ((pw = getpwnam(user)) == NULL) + err(EXIT_FAILURE, "getpwname"); + + ngroups = NGROUPS_MAX; + ret = getgrouplist(user, pw->pw_gid, groups, &ngroups); + if (ret < 0) + err(EXIT_FAILURE, "getgrouplist"); + + for (int i = 0; i < ret; i++) { + struct group *gr = getgrgid(groups[i]); + + (void) printf("%s ", gr->gr_name); + } + (void) fputc('\\n', stdout); + + free(groups); +} +.Ed +.Sh ERRORS +On failure, +.Fn getgrouplist +returns -1, and will set errno to one one of the following values: +.Bl -tag -width Dv +.It Er ENOMEM +Not enough memory to complete the request. +.It Er EINVAL +One of the parameters is invalid +.Po +for example, +.Fa ngroups +is +.Dv NULL +.Pc . +.It Dv ERANGE +The supplied value of +.Fa *ngroups +is too small to hold the results. +.Fa *ngroups +is set +.Po +upon return +.Pc +to a value large enough to hold the results, and a partial set of +results is written to +.Fa groups . +The value written to +.Fa *ngroups +may be larger than the value returned by a successful call to +.Fn getgrouplist . +.El +.Sh INTERFACE STABILITY +.Sy Uncommitted +.Sh MT-LEVEL +.Sy MT-Safe +.Sh SEE ALSO +.Xr groups 1 , +.Xr getuid 2 , +.Xr getgrnam 3C , +.Xr getgroups 3C , +.Xr initgroups 3C , +.Xr limits.h 3HEAD diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc index f892d1551e..70c9189c0f 100644 --- a/usr/src/pkg/manifests/system-library.man3c.inc +++ b/usr/src/pkg/manifests/system-library.man3c.inc @@ -15,7 +15,7 @@ # Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> # Copyright 2018 Jason King -# Copyright 2018, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # @@ -168,6 +168,7 @@ file path=usr/share/man/man3c/getentropy.3c file path=usr/share/man/man3c/getenv.3c file path=usr/share/man/man3c/getexecname.3c file path=usr/share/man/man3c/getgrnam.3c +file path=usr/share/man/man3c/getgrouplist.3c file path=usr/share/man/man3c/gethostid.3c file path=usr/share/man/man3c/gethostname.3c file path=usr/share/man/man3c/gethrtime.3c diff --git a/usr/src/uts/common/io/aggr/aggr_grp.c b/usr/src/uts/common/io/aggr/aggr_grp.c index 199625c377..9a4a936450 100644 --- a/usr/src/uts/common/io/aggr/aggr_grp.c +++ b/usr/src/uts/common/io/aggr/aggr_grp.c @@ -1374,11 +1374,14 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, { aggr_grp_t *grp = NULL; aggr_port_t *port; + aggr_port_t *last_attached = NULL; mac_register_t *mac; boolean_t link_state_changed; - mac_perim_handle_t mph; + mac_perim_handle_t mph, pmph; + datalink_id_t tempid; + boolean_t mac_registered = B_FALSE; int err; - int i; + int i, j; kt_did_t tid = 0; /* need at least one port */ @@ -1525,6 +1528,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, goto bail; } + mac_registered = B_TRUE; + mac_perim_enter_by_mh(grp->lg_mh, &mph); /* @@ -1554,12 +1559,33 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, * underlying port. Note that this is done after the * aggr registers its MAC. */ - VERIFY3S(aggr_add_pseudo_tx_group(port, &grp->lg_tx_group), - ==, 0); + err = aggr_add_pseudo_tx_group(port, &grp->lg_tx_group); + + if (err != 0) { + mac_perim_exit(mph); + goto bail; + } for (i = 0; i < grp->lg_rx_group_count; i++) { - VERIFY3S(aggr_add_pseudo_rx_group(port, - &grp->lg_rx_groups[i]), ==, 0); + err = aggr_add_pseudo_rx_group(port, + &grp->lg_rx_groups[i]); + + if (err != 0) { + /* + * Undo what we have added for the current + * port. + */ + aggr_rem_pseudo_tx_group(port, + &grp->lg_tx_group); + + for (j = 0; j < i; j++) { + aggr_rem_pseudo_rx_group(port, + &grp->lg_rx_groups[j]); + } + + mac_perim_exit(mph); + goto bail; + } } if (aggr_port_notify_link(grp, port)) @@ -1569,6 +1595,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, * Initialize the callback functions for this port. */ aggr_port_init_callbacks(port); + + last_attached = port; } if (link_state_changed) @@ -1585,18 +1613,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, return (0); bail: - grp->lg_closing = B_TRUE; - port = grp->lg_ports; - while (port != NULL) { - aggr_port_t *cport; - - cport = port->lp_next; - aggr_port_delete(port); - port = cport; - } - /* * Inform the lacp_rx thread to exit. */ @@ -1619,6 +1637,47 @@ bail: if (tid != 0) thread_join(tid); + if (mac_registered) { + (void) dls_devnet_destroy(grp->lg_mh, &tempid, B_TRUE); + (void) mac_disable(grp->lg_mh); + + if (last_attached != NULL) { + /* + * Detach and clean up ports added. + */ + mac_perim_enter_by_mh(grp->lg_mh, &mph); + + for (port = grp->lg_ports; ; port = port->lp_next) { + mac_perim_enter_by_mh(port->lp_mh, &pmph); + (void) aggr_grp_detach_port(grp, port); + mac_perim_exit(pmph); + + aggr_rem_pseudo_tx_group(port, + &grp->lg_tx_group); + + for (i = 0; i < grp->lg_rx_group_count; i++) { + aggr_rem_pseudo_rx_group(port, + &grp->lg_rx_groups[i]); + } + if (port == last_attached) + break; + } + + mac_perim_exit(mph); + } + + (void) mac_unregister(grp->lg_mh); + } + + port = grp->lg_ports; + while (port != NULL) { + aggr_port_t *cport; + + cport = port->lp_next; + aggr_port_delete(port); + port = cport; + } + kmem_free(grp->lg_tx_blocked_rings, (sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP)); rw_exit(&aggr_grp_lock); |