diff --git a/usr.bin/newgrp/Makefile b/usr.bin/newgrp/Makefile index 392044fb306a..0cec2a059c0a 100644 --- a/usr.bin/newgrp/Makefile +++ b/usr.bin/newgrp/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2007/10/17 21:07:49 christos Exp $ +# $NetBSD: Makefile,v 1.3 2007/10/27 15:36:21 christos Exp $ # .include @@ -9,8 +9,6 @@ BINOWN= root BINMODE=4555 WARNS= 4 -.PATH.c: ${.CURDIR}/../su -CPPFLAGS+=-I${.CURDIR}/../su CPPFLAGS+=-DGRUTIL_ACCEPT_GROUP_NUMBERS CPPFLAGS+=-DGRUTIL_ALLOW_GROUP_ERRORS # for POSIX.1 compliance CPPFLAGS+=-DLOGIN_CAP diff --git a/usr.bin/newgrp/grutil.c b/usr.bin/newgrp/grutil.c new file mode 100644 index 000000000000..688b0c688361 --- /dev/null +++ b/usr.bin/newgrp/grutil.c @@ -0,0 +1,344 @@ +/* $NetBSD: grutil.c,v 1.1 2007/10/27 15:36:21 christos Exp $ */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: grutil.c,v 1.1 2007/10/27 15:36:21 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOGIN_CAP +#include +#endif + +#include "grutil.h" + +typedef enum { + ADDGRP_NOERROR = 0, /* must be zero */ + ADDGRP_EMALLOC = 1, + ADDGRP_EGETGROUPS = 2, + ADDGRP_ESETGROUPS = 3 +} addgrp_ret_t; + +static void +free_groups(void *groups) +{ + int oerrno; + + oerrno = errno; + free(groups); + errno = oerrno; +} + +static addgrp_ret_t +alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax) +{ + *ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX); + if (*ngroupsmax < 0) + *ngroupsmax = NGROUPS_MAX; + + *groups = malloc(*ngroupsmax * sizeof(**groups)); + if (*groups == NULL) + return ADDGRP_EMALLOC; + + *ngroups = getgroups(*ngroupsmax, *groups); + if (*ngroups == -1) { + free_groups(*groups); + return ADDGRP_ESETGROUPS; + } + return ADDGRP_NOERROR; +} + +static addgrp_ret_t +addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace) +{ + int i; + + /* search for gid in supplemental group list */ + for (i = 0; i < ngroups && groups[i] != gid; i++) + continue; + + /* add the gid to the supplemental group list */ + if (i == ngroups) { + if (ngroups < ngroupsmax) + groups[ngroups++] = gid; + else { /* + * setgroups(2) will fail with errno = EINVAL + * if ngroups > nmaxgroups. If makespace is + * set, replace the last group with the new + * one. Otherwise, fail the way setgroups(2) + * would if we passed the larger groups array. + */ + if (makespace) { + /* + * Find a slot that doesn't contain + * the primary group. + */ + struct passwd *pwd; + gid_t pgid; + pwd = getpwuid(getuid()); + if (pwd == NULL) + goto error; + pgid = pwd->pw_gid; + for (i = ngroupsmax - 1; i >= 0; i--) + if (groups[i] != pgid) + break; + if (i < 0) + goto error; + groups[i] = gid; + } + else { + error: + errno = EINVAL; + return ADDGRP_ESETGROUPS; + } + } + if (setgroups(ngroups, groups) < 0) + return ADDGRP_ESETGROUPS; + } + return ADDGRP_NOERROR; +} + +static addgrp_ret_t +addgrp(gid_t newgid, int makespace) +{ + int ngroups, ngroupsmax, rval; + gid_t *groups; + gid_t oldgid; + + oldgid = getgid(); + if (oldgid == newgid) /* nothing to do */ + return ADDGRP_NOERROR; + + rval = alloc_groups(&ngroups, &groups, &ngroupsmax); + if (rval != 0) + return rval; + + /* + * BSD based systems normally have the egid in the supplemental + * group list. + */ +#if (defined(BSD) && BSD >= 199306) + /* + * According to POSIX/XPG6: + * On system where the egid is normally in the supplemental group list + * (or whenever the old egid actually is in the supplemental group + * list): + * o If the new egid is in the supplemental group list, + * just change the egid. + * o If the new egid is not in the supplemental group list, + * add the new egid to the list if there is room. + */ + + rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace); +#else + /* + * According to POSIX/XPG6: + * On systems where the egid is not normally in the supplemental group + * list (or whenever the old egid is not in the supplemental group + * list): + * o If the new egid is in the supplemental group list, delete + * it from the list. + * o If the old egid is not in the supplemental group list, + * add the old egid to the list if there is room. + */ + { + int i; + + /* search for new egid in supplemental group list */ + for (i = 0; i < ngroups && groups[i] != newgid; i++) + continue; + + /* remove new egid from supplemental group list */ + if (i != ngroups) + for (--ngroups; i < ngroups; i++) + groups[i] = groups[i + 1]; + + rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace); + } +#endif + free_groups(groups); + return rval; +} + +/* + * If newgrp fails, it returns (gid_t)-1 and the errno variable is + * set to: + * [EINVAL] Unknown group. + * [EPERM] Bad password. + */ +static gid_t +newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt) +{ + struct group *grp; + char **ap; + char *p; + gid_t *groups; + int ngroups, ngroupsmax; + + if (gname == NULL) + return pwd->pw_gid; + + grp = getgrnam(gname); + +#ifdef GRUTIL_ACCEPT_GROUP_NUMBERS + if (grp == NULL) { + gid_t gid; + if (*gname != '-') { + gid = (gid_t)strtol(gname, &p, 10); + if (*p == '\0') + grp = getgrgid(gid); + } + } +#endif + if (grp == NULL) { + errno = EINVAL; + return (gid_t)-1; + } + + if (ruid == 0 || pwd->pw_gid == grp->gr_gid) + return grp->gr_gid; + + if (alloc_groups(&ngroups, &groups, &ngroupsmax) == 0) { + int i; + for (i = 0; i < ngroups; i++) + if (groups[i] == grp->gr_gid) { + free_groups(groups); + return grp->gr_gid; + } + free_groups(groups); + } + + /* + * Check the group membership list in case the groups[] array + * was maxed out or the user has been added to it since login. + */ + for (ap = grp->gr_mem; *ap != NULL; ap++) + if (strcmp(*ap, pwd->pw_name) == 0) + return grp->gr_gid; + + if (*grp->gr_passwd != '\0') { + p = getpass(prompt); + if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) { + (void)memset(p, '\0', _PASSWORD_LEN); + return grp->gr_gid; + } + (void)memset(p, '\0', _PASSWORD_LEN); + } + + errno = EPERM; + return (gid_t)-1; +} + +#ifdef GRUTIL_SETGROUPS_MAKESPACE +# define ADDGRP_MAKESPACE 1 +#else +# define ADDGRP_MAKESPACE 0 +#endif + +#ifdef GRUTIL_ALLOW_GROUP_ERRORS +# define maybe_exit(e) +#else +# define maybe_exit(e) exit(e); +#endif + +void +addgroup( +#ifdef LOGIN_CAP + login_cap_t *lc, +#endif + const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt) +{ + pwd->pw_gid = newgrp(gname, pwd, ruid, prompt); + if (pwd->pw_gid == (gid_t)-1) { + switch (errno) { + case EINVAL: + warnx("Unknown group `%s'", gname); + maybe_exit(EXIT_FAILURE); + break; + case EPERM: /* password failure */ + warnx("Sorry"); + maybe_exit(EXIT_FAILURE); + break; + default: /* XXX - should never happen */ + err(EXIT_FAILURE, "unknown error"); + break; + } + pwd->pw_gid = getgid(); + } + + switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) { + case ADDGRP_NOERROR: + break; + case ADDGRP_EMALLOC: + err(EXIT_FAILURE, "malloc"); + break; + case ADDGRP_EGETGROUPS: + err(EXIT_FAILURE, "getgroups"); + break; + case ADDGRP_ESETGROUPS: + switch(errno) { + case EINVAL: + warnx("setgroups: ngroups > ngroupsmax"); + maybe_exit(EXIT_FAILURE); + break; + case EPERM: + case EFAULT: + default: + warn("setgroups"); + maybe_exit(EXIT_FAILURE); + break; + } + break; + } + +#ifdef LOGIN_CAP + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGID) == -1) + err(EXIT_FAILURE, "setting user context"); +#else + if (setgid(pwd->pw_gid) == -1) + err(EXIT_FAILURE, "setgid"); +#endif +} diff --git a/usr.bin/newgrp/grutil.h b/usr.bin/newgrp/grutil.h new file mode 100644 index 000000000000..5bdf34cd2999 --- /dev/null +++ b/usr.bin/newgrp/grutil.h @@ -0,0 +1,47 @@ +/* $NetBSD: grutil.h,v 1.1 2007/10/27 15:36:21 christos Exp $ */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _GRUTIL_H_ +#define _GRUTIL_H_ + +void addgroup( +#ifdef LOGIN_CAP + login_cap_t *, +#endif + const char *, struct passwd *, uid_t, const char *); + +#endif /* _GRUTIL_H_ */