xref: /minix3/usr.bin/newgrp/grutil.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: grutil.c,v 1.4 2014/06/23 06:57:31 shm Exp $	*/
25c007436SBen Gras 
35c007436SBen Gras /*-
45c007436SBen Gras  * Copyright (c) 2007 The NetBSD Foundation, Inc.
55c007436SBen Gras  * All rights reserved.
65c007436SBen Gras  *
75c007436SBen Gras  * This code is derived from software contributed to The NetBSD Foundation
85c007436SBen Gras  * by Brian Ginsbach.
95c007436SBen Gras  *
105c007436SBen Gras  * Redistribution and use in source and binary forms, with or without
115c007436SBen Gras  * modification, are permitted provided that the following conditions
125c007436SBen Gras  * are met:
135c007436SBen Gras  * 1. Redistributions of source code must retain the above copyright
145c007436SBen Gras  *    notice, this list of conditions and the following disclaimer.
155c007436SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
165c007436SBen Gras  *    notice, this list of conditions and the following disclaimer in the
175c007436SBen Gras  *    documentation and/or other materials provided with the distribution.
185c007436SBen Gras  *
195c007436SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205c007436SBen Gras  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215c007436SBen Gras  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225c007436SBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235c007436SBen Gras  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245c007436SBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255c007436SBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265c007436SBen Gras  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275c007436SBen Gras  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285c007436SBen Gras  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295c007436SBen Gras  * POSSIBILITY OF SUCH DAMAGE.
305c007436SBen Gras  */
315c007436SBen Gras #include <sys/cdefs.h>
32*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: grutil.c,v 1.4 2014/06/23 06:57:31 shm Exp $");
335c007436SBen Gras 
345c007436SBen Gras #include <sys/param.h>
355c007436SBen Gras #include <err.h>
365c007436SBen Gras #include <errno.h>
375c007436SBen Gras #include <grp.h>
385c007436SBen Gras #include <pwd.h>
395c007436SBen Gras #include <stdio.h>
405c007436SBen Gras #include <stdlib.h>
415c007436SBen Gras #include <string.h>
425c007436SBen Gras #include <unistd.h>
435c007436SBen Gras #include <util.h>
445c007436SBen Gras 
455c007436SBen Gras #ifdef LOGIN_CAP
465c007436SBen Gras #include <login_cap.h>
475c007436SBen Gras #endif
485c007436SBen Gras 
495c007436SBen Gras #include "grutil.h"
505c007436SBen Gras 
515c007436SBen Gras typedef enum {
525c007436SBen Gras 	ADDGRP_NOERROR		= 0,	/* must be zero */
535c007436SBen Gras 	ADDGRP_EMALLOC		= 1,
545c007436SBen Gras 	ADDGRP_EGETGROUPS	= 2,
555c007436SBen Gras 	ADDGRP_ESETGROUPS	= 3
565c007436SBen Gras } addgrp_ret_t;
575c007436SBen Gras 
585c007436SBen Gras static void
free_groups(void * groups)595c007436SBen Gras free_groups(void *groups)
605c007436SBen Gras {
615c007436SBen Gras 	int oerrno;
625c007436SBen Gras 
635c007436SBen Gras 	oerrno = errno;
645c007436SBen Gras 	free(groups);
655c007436SBen Gras 	errno = oerrno;
665c007436SBen Gras }
675c007436SBen Gras 
685c007436SBen Gras static addgrp_ret_t
alloc_groups(int * ngroups,gid_t ** groups,int * ngroupsmax)695c007436SBen Gras alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax)
705c007436SBen Gras {
715c007436SBen Gras 	*ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX);
725c007436SBen Gras 	if (*ngroupsmax < 0)
735c007436SBen Gras 		*ngroupsmax = NGROUPS_MAX;
745c007436SBen Gras 
755c007436SBen Gras 	*groups = malloc(*ngroupsmax * sizeof(**groups));
765c007436SBen Gras 	if (*groups == NULL)
775c007436SBen Gras 		return ADDGRP_EMALLOC;
785c007436SBen Gras 
795c007436SBen Gras 	*ngroups = getgroups(*ngroupsmax, *groups);
805c007436SBen Gras 	if (*ngroups == -1) {
815c007436SBen Gras 		free_groups(*groups);
825c007436SBen Gras 		return ADDGRP_ESETGROUPS;
835c007436SBen Gras 	}
845c007436SBen Gras 	return ADDGRP_NOERROR;
855c007436SBen Gras }
865c007436SBen Gras 
875c007436SBen Gras static addgrp_ret_t
addgid(gid_t * groups,int ngroups,int ngroupsmax,gid_t gid,int makespace)885c007436SBen Gras addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace)
895c007436SBen Gras {
905c007436SBen Gras 	int i;
915c007436SBen Gras 
925c007436SBen Gras 	/* search for gid in supplemental group list */
935c007436SBen Gras 	for (i = 0; i < ngroups && groups[i] != gid; i++)
945c007436SBen Gras 		continue;
955c007436SBen Gras 
965c007436SBen Gras 	/* add the gid to the supplemental group list */
975c007436SBen Gras 	if (i == ngroups) {
985c007436SBen Gras 		if (ngroups < ngroupsmax)
995c007436SBen Gras 			groups[ngroups++] = gid;
1005c007436SBen Gras 		else {	/*
1015c007436SBen Gras 			 * setgroups(2) will fail with errno = EINVAL
1025c007436SBen Gras 			 * if ngroups > nmaxgroups.  If makespace is
1035c007436SBen Gras 			 * set, replace the last group with the new
1045c007436SBen Gras 			 * one.  Otherwise, fail the way setgroups(2)
1055c007436SBen Gras 			 * would if we passed the larger groups array.
1065c007436SBen Gras 			 */
1075c007436SBen Gras 			if (makespace) {
1085c007436SBen Gras 				/*
1095c007436SBen Gras 				 * Find a slot that doesn't contain
1105c007436SBen Gras 				 * the primary group.
1115c007436SBen Gras 				 */
1125c007436SBen Gras 				struct passwd *pwd;
1135c007436SBen Gras 				gid_t pgid;
1145c007436SBen Gras 				pwd = getpwuid(getuid());
1155c007436SBen Gras 				if (pwd == NULL)
1165c007436SBen Gras 					goto error;
1175c007436SBen Gras 				pgid = pwd->pw_gid;
1185c007436SBen Gras 				for (i = ngroupsmax - 1; i >= 0; i--)
1195c007436SBen Gras 					if (groups[i] != pgid)
1205c007436SBen Gras 						break;
1215c007436SBen Gras 				if (i < 0)
1225c007436SBen Gras 					goto error;
1235c007436SBen Gras 				groups[i] = gid;
1245c007436SBen Gras 			}
1255c007436SBen Gras 			else {
1265c007436SBen Gras 		error:
1275c007436SBen Gras 				errno = EINVAL;
1285c007436SBen Gras 				return ADDGRP_ESETGROUPS;
1295c007436SBen Gras 			}
1305c007436SBen Gras 		}
1315c007436SBen Gras 		if (setgroups(ngroups, groups) < 0)
1325c007436SBen Gras 			return ADDGRP_ESETGROUPS;
1335c007436SBen Gras 	}
1345c007436SBen Gras 	return ADDGRP_NOERROR;
1355c007436SBen Gras }
1365c007436SBen Gras 
1375c007436SBen Gras static addgrp_ret_t
addgrp(gid_t newgid,int makespace)1385c007436SBen Gras addgrp(gid_t newgid, int makespace)
1395c007436SBen Gras {
14084d9c625SLionel Sambuc 	int ngroups, ngroupsmax;
14184d9c625SLionel Sambuc 	addgrp_ret_t rval;
1425c007436SBen Gras 	gid_t *groups;
1435c007436SBen Gras 	gid_t oldgid;
1445c007436SBen Gras 
1455c007436SBen Gras 	oldgid = getgid();
1465c007436SBen Gras 	if (oldgid == newgid) /* nothing to do */
1475c007436SBen Gras 		return ADDGRP_NOERROR;
1485c007436SBen Gras 
1495c007436SBen Gras 	rval = alloc_groups(&ngroups, &groups, &ngroupsmax);
150*0a6a1f1dSLionel Sambuc 	if (rval != ADDGRP_NOERROR)
1515c007436SBen Gras 		return rval;
1525c007436SBen Gras 
1535c007436SBen Gras 	/*
1545c007436SBen Gras 	 * BSD based systems normally have the egid in the supplemental
1555c007436SBen Gras 	 * group list.
1565c007436SBen Gras 	 */
1575c007436SBen Gras #if (defined(BSD) && BSD >= 199306)
1585c007436SBen Gras 	/*
1595c007436SBen Gras 	 * According to POSIX/XPG6:
1605c007436SBen Gras 	 * On system where the egid is normally in the supplemental group list
1615c007436SBen Gras 	 * (or whenever the old egid actually is in the supplemental group
1625c007436SBen Gras 	 * list):
1635c007436SBen Gras 	 *	o If the new egid is in the supplemental group list,
1645c007436SBen Gras 	 *	  just change the egid.
1655c007436SBen Gras 	 *	o If the new egid is not in the supplemental group list,
1665c007436SBen Gras 	 *	  add the new egid to the list if there is room.
1675c007436SBen Gras 	 */
1685c007436SBen Gras 
1695c007436SBen Gras 	rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace);
1705c007436SBen Gras #else
1715c007436SBen Gras 	/*
1725c007436SBen Gras 	 * According to POSIX/XPG6:
1735c007436SBen Gras 	 * On systems where the egid is not normally in the supplemental group
1745c007436SBen Gras 	 * list (or whenever the old egid is not in the supplemental group
1755c007436SBen Gras 	 * list):
1765c007436SBen Gras 	 *	o If the new egid is in the supplemental group list, delete
1775c007436SBen Gras 	 *	  it from the list.
1785c007436SBen Gras 	 *	o If the old egid is not in the supplemental group list,
1795c007436SBen Gras 	 *	  add the old egid to the list if there is room.
1805c007436SBen Gras 	 */
1815c007436SBen Gras 	{
1825c007436SBen Gras 		int i;
1835c007436SBen Gras 
1845c007436SBen Gras 		/* search for new egid in supplemental group list */
1855c007436SBen Gras 		for (i = 0; i < ngroups && groups[i] != newgid; i++)
1865c007436SBen Gras 			continue;
1875c007436SBen Gras 
1885c007436SBen Gras 		/* remove new egid from supplemental group list */
1895c007436SBen Gras 		if (i != ngroups)
1905c007436SBen Gras 			for (--ngroups; i < ngroups; i++)
1915c007436SBen Gras 				groups[i] = groups[i + 1];
1925c007436SBen Gras 
1935c007436SBen Gras 		rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace);
1945c007436SBen Gras 	}
1955c007436SBen Gras #endif
1965c007436SBen Gras 	free_groups(groups);
1975c007436SBen Gras 	return rval;
1985c007436SBen Gras }
1995c007436SBen Gras 
2005c007436SBen Gras /*
2015c007436SBen Gras  * If newgrp fails, it returns (gid_t)-1 and the errno variable is
2025c007436SBen Gras  * set to:
2035c007436SBen Gras  *	[EINVAL]	Unknown group.
2045c007436SBen Gras  *	[EPERM]		Bad password.
2055c007436SBen Gras  */
2065c007436SBen Gras static gid_t
newgrp(const char * gname,struct passwd * pwd,uid_t ruid,const char * prompt)2075c007436SBen Gras newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
2085c007436SBen Gras {
2095c007436SBen Gras 	struct group *grp;
2105c007436SBen Gras 	char **ap;
2115c007436SBen Gras 	char *p;
2125c007436SBen Gras 	gid_t *groups;
2135c007436SBen Gras 	int ngroups, ngroupsmax;
2145c007436SBen Gras 
2155c007436SBen Gras 	if (gname == NULL)
2165c007436SBen Gras 		return pwd->pw_gid;
2175c007436SBen Gras 
2185c007436SBen Gras 	grp = getgrnam(gname);
2195c007436SBen Gras 
2205c007436SBen Gras #ifdef GRUTIL_ACCEPT_GROUP_NUMBERS
2215c007436SBen Gras 	if (grp == NULL) {
2225c007436SBen Gras 		gid_t gid;
2235c007436SBen Gras 		if (*gname != '-') {
2245c007436SBen Gras 		    gid = (gid_t)strtol(gname, &p, 10);
2255c007436SBen Gras 		    if (*p == '\0')
2265c007436SBen Gras 			    grp = getgrgid(gid);
2275c007436SBen Gras 		}
2285c007436SBen Gras 	}
2295c007436SBen Gras #endif
2305c007436SBen Gras 	if (grp == NULL) {
2315c007436SBen Gras 		errno = EINVAL;
2325c007436SBen Gras 		return (gid_t)-1;
2335c007436SBen Gras 	}
2345c007436SBen Gras 
2355c007436SBen Gras 	if (ruid == 0 || pwd->pw_gid == grp->gr_gid)
2365c007436SBen Gras 		return grp->gr_gid;
2375c007436SBen Gras 
238*0a6a1f1dSLionel Sambuc 	if (alloc_groups(&ngroups, &groups, &ngroupsmax) == ADDGRP_NOERROR) {
2395c007436SBen Gras 		int i;
2405c007436SBen Gras 		for (i = 0; i < ngroups; i++)
2415c007436SBen Gras 			if (groups[i] == grp->gr_gid) {
2425c007436SBen Gras 				free_groups(groups);
2435c007436SBen Gras 				return grp->gr_gid;
2445c007436SBen Gras 			}
2455c007436SBen Gras 		free_groups(groups);
2465c007436SBen Gras 	}
2475c007436SBen Gras 
2485c007436SBen Gras 	/*
2495c007436SBen Gras 	 * Check the group membership list in case the groups[] array
2505c007436SBen Gras 	 * was maxed out or the user has been added to it since login.
2515c007436SBen Gras 	 */
2525c007436SBen Gras 	for (ap = grp->gr_mem; *ap != NULL; ap++)
2535c007436SBen Gras 		if (strcmp(*ap, pwd->pw_name) == 0)
2545c007436SBen Gras 			return grp->gr_gid;
2555c007436SBen Gras 
2565c007436SBen Gras 	if (*grp->gr_passwd != '\0') {
2575c007436SBen Gras 		p = getpass(prompt);
2585c007436SBen Gras 		if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) {
2595c007436SBen Gras 			(void)memset(p, '\0', _PASSWORD_LEN);
2605c007436SBen Gras 			return grp->gr_gid;
2615c007436SBen Gras 		}
2625c007436SBen Gras 		(void)memset(p, '\0', _PASSWORD_LEN);
2635c007436SBen Gras 	}
2645c007436SBen Gras 
2655c007436SBen Gras 	errno = EPERM;
2665c007436SBen Gras 	return (gid_t)-1;
2675c007436SBen Gras }
2685c007436SBen Gras 
2695c007436SBen Gras #ifdef GRUTIL_SETGROUPS_MAKESPACE
2705c007436SBen Gras # define ADDGRP_MAKESPACE	1
2715c007436SBen Gras #else
2725c007436SBen Gras # define ADDGRP_MAKESPACE	0
2735c007436SBen Gras #endif
2745c007436SBen Gras 
2755c007436SBen Gras #ifdef GRUTIL_ALLOW_GROUP_ERRORS
2765c007436SBen Gras # define maybe_exit(e)
2775c007436SBen Gras #else
2785c007436SBen Gras # define maybe_exit(e)	exit(e);
2795c007436SBen Gras #endif
2805c007436SBen Gras 
2815c007436SBen Gras void
addgroup(login_cap_t * lc,const char * gname,struct passwd * pwd,uid_t ruid,const char * prompt)2825c007436SBen Gras addgroup(
2835c007436SBen Gras #ifdef LOGIN_CAP
2845c007436SBen Gras     login_cap_t *lc,
2855c007436SBen Gras #endif
2865c007436SBen Gras     const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
2875c007436SBen Gras {
2885c007436SBen Gras 	pwd->pw_gid = newgrp(gname, pwd, ruid, prompt);
2895c007436SBen Gras 	if (pwd->pw_gid == (gid_t)-1) {
2905c007436SBen Gras 		switch (errno) {
2915c007436SBen Gras 		case EINVAL:
2925c007436SBen Gras 			warnx("Unknown group `%s'", gname);
2935c007436SBen Gras 			maybe_exit(EXIT_FAILURE);
2945c007436SBen Gras 			break;
2955c007436SBen Gras 		case EPERM:	/* password failure */
2965c007436SBen Gras 			warnx("Sorry");
2975c007436SBen Gras 			maybe_exit(EXIT_FAILURE);
2985c007436SBen Gras 			break;
2995c007436SBen Gras 		default: /* XXX - should never happen */
3005c007436SBen Gras 			err(EXIT_FAILURE, "unknown error");
3015c007436SBen Gras 			break;
3025c007436SBen Gras 		}
3035c007436SBen Gras 		pwd->pw_gid = getgid();
3045c007436SBen Gras 	}
3055c007436SBen Gras 
3065c007436SBen Gras 	switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) {
3075c007436SBen Gras 	case ADDGRP_NOERROR:
3085c007436SBen Gras 		break;
3095c007436SBen Gras 	case ADDGRP_EMALLOC:
3105c007436SBen Gras 		err(EXIT_FAILURE, "malloc");
3115c007436SBen Gras 		break;
3125c007436SBen Gras 	case ADDGRP_EGETGROUPS:
3135c007436SBen Gras 		err(EXIT_FAILURE, "getgroups");
3145c007436SBen Gras 		break;
3155c007436SBen Gras 	case ADDGRP_ESETGROUPS:
3165c007436SBen Gras 		switch(errno) {
3175c007436SBen Gras 		case EINVAL:
3185c007436SBen Gras 			warnx("setgroups: ngroups > ngroupsmax");
3195c007436SBen Gras 			maybe_exit(EXIT_FAILURE);
3205c007436SBen Gras 			break;
3215c007436SBen Gras 		case EPERM:
3225c007436SBen Gras 		case EFAULT:
3235c007436SBen Gras 		default:
3245c007436SBen Gras 			warn("setgroups");
3255c007436SBen Gras 			maybe_exit(EXIT_FAILURE);
3265c007436SBen Gras 			break;
3275c007436SBen Gras 		}
3285c007436SBen Gras 		break;
3295c007436SBen Gras 	}
3305c007436SBen Gras 
3315c007436SBen Gras #ifdef LOGIN_CAP
3325c007436SBen Gras 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGID) == -1)
3335c007436SBen Gras 		err(EXIT_FAILURE, "setting user context");
3345c007436SBen Gras #else
3355c007436SBen Gras 	if (setgid(pwd->pw_gid) == -1)
3365c007436SBen Gras 		err(EXIT_FAILURE, "setgid");
3375c007436SBen Gras #endif
3385c007436SBen Gras }
339