197364Stjr/*-
297364Stjr * Copyright (c) 2002 Tim J. Robbins.
397364Stjr * All rights reserved.
497364Stjr *
597364Stjr * Redistribution and use in source and binary forms, with or without
697364Stjr * modification, are permitted provided that the following conditions
797364Stjr * are met:
897364Stjr * 1. Redistributions of source code must retain the above copyright
997364Stjr *    notice, this list of conditions and the following disclaimer.
1097364Stjr * 2. Redistributions in binary form must reproduce the above copyright
1197364Stjr *    notice, this list of conditions and the following disclaimer in the
1297364Stjr *    documentation and/or other materials provided with the distribution.
1397364Stjr *
1497364Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1597364Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1697364Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1797364Stjr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1897364Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1997364Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2097364Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2197364Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2297364Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2397364Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2497364Stjr * SUCH DAMAGE.
2597364Stjr */
2697364Stjr
2797364Stjr/*
2897364Stjr * newgrp -- change to a new group
2997364Stjr */
3097364Stjr
3197364Stjr#include <sys/cdefs.h>
3297364Stjr__FBSDID("$FreeBSD$");
3397364Stjr
3497364Stjr#include <sys/types.h>
3597364Stjr
3697364Stjr#include <err.h>
37200462Sdelphij#include <errno.h>
3897364Stjr#include <grp.h>
3997364Stjr#include <libgen.h>
40200462Sdelphij#include <limits.h>
4197364Stjr#include <login_cap.h>
4297364Stjr#include <paths.h>
4397364Stjr#include <pwd.h>
4497364Stjr#include <stdio.h>
4597364Stjr#include <stdlib.h>
4697364Stjr#include <string.h>
4797364Stjr#include <unistd.h>
4897364Stjr
4997364Stjrstatic void	 addgroup(const char *grpname);
5097364Stjrstatic void	 doshell(void);
5197364Stjrstatic int	 inarray(gid_t, const gid_t[], int);
5297364Stjrstatic void	 loginshell(void);
5397364Stjrstatic void	 restoregrps(void);
5497364Stjrstatic void	 usage(void);
5597364Stjr
5697364Stjrstatic struct passwd *pwd;
5797364Stjrstatic uid_t euid;
5897364Stjr
5997364Stjrextern char **environ;
6097364Stjr
6197364Stjr/* Manipulate effective user ID. */
6297364Stjr#define PRIV_START do {				\
6397364Stjr		if (seteuid(euid) < 0)		\
6497364Stjr			err(1, "seteuid");	\
6597364Stjr	} while (0)
6697364Stjr#define PRIV_END do {				\
6797364Stjr		if (seteuid(getuid()) < 0)	\
6897364Stjr			err(1, "seteuid");	\
6997364Stjr	} while (0)
7097364Stjr
7197364Stjrint
7297364Stjrmain(int argc, char *argv[])
7397364Stjr{
7497364Stjr	int ch, login;
7597364Stjr
76246553Sdes	if ((euid = geteuid()) != 0)
77246553Sdes		warnx("need root permissions to function properly, check setuid bit");
7897364Stjr	if (seteuid(getuid()) < 0)
7997364Stjr		err(1, "seteuid");
8097364Stjr
8197364Stjr	if ((pwd = getpwuid(getuid())) == NULL)
8297364Stjr		errx(1, "unknown user");
8397364Stjr
8497364Stjr	login = 0;
8597364Stjr	while ((ch = getopt(argc, argv, "-l")) != -1) {
8697364Stjr		switch (ch) {
8797364Stjr		case '-':		/* Obsolescent */
8897364Stjr		case 'l':
8997364Stjr			login = 1;
9097364Stjr			break;
9197364Stjr		default:
9297364Stjr			usage();
9397364Stjr		}
9497364Stjr	}
9597364Stjr	argc -= optind;
9697364Stjr	argv += optind;
9797364Stjr
9897364Stjr	switch (argc) {
9997364Stjr	case 0:
10097364Stjr		restoregrps();
10197364Stjr		break;
10297364Stjr	case 1:
10397364Stjr		addgroup(*argv);
10497364Stjr		break;
10597364Stjr	default:
10697364Stjr		usage();
10797364Stjr	}
10897364Stjr
10997364Stjr	if (seteuid(euid) < 0)
11097364Stjr		err(1, "seteuid");
11197364Stjr	if (setuid(getuid()) < 0)
11297364Stjr		err(1, "setuid");
11397364Stjr
11497364Stjr	if (login)
11597364Stjr		loginshell();
11697364Stjr	else
11797364Stjr		doshell();
11897364Stjr
11997364Stjr	/*NOTREACHED*/
12097364Stjr	exit(1);
12197364Stjr}
12297364Stjr
12397364Stjrstatic void
12497364Stjrusage(void)
12597364Stjr{
12697364Stjr
12797364Stjr	fprintf(stderr, "usage: newgrp [-l] [group]\n");
12897364Stjr	exit(1);
12997364Stjr}
13097364Stjr
13197364Stjrstatic void
13297364Stjrrestoregrps(void)
13397364Stjr{
13497364Stjr	int initres, setres;
13597364Stjr
13697364Stjr	PRIV_START;
13797364Stjr	initres = initgroups(pwd->pw_name, pwd->pw_gid);
13897364Stjr	setres = setgid(pwd->pw_gid);
13997364Stjr	PRIV_END;
14097364Stjr
14197364Stjr	if (initres < 0)
14297364Stjr		warn("initgroups");
14397364Stjr	if (setres < 0)
144226274Sdelphij		warn("setgid");
14597364Stjr}
14697364Stjr
14797364Stjrstatic void
14897364Stjraddgroup(const char *grpname)
14997364Stjr{
150194494Sbrooks	gid_t *grps;
151194494Sbrooks	long lgid, ngrps_max;
15297364Stjr	int dbmember, i, ngrps;
15397364Stjr	gid_t egid;
15497364Stjr	struct group *grp;
155231994Skevlo	char *ep, *pass, *cryptpw;
15697364Stjr	char **p;
15797364Stjr
15897364Stjr	egid = getegid();
15997364Stjr
16097364Stjr	/* Try it as a group name, then a group id. */
16197364Stjr	if ((grp = getgrnam(grpname)) == NULL)
16297364Stjr		if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' ||
16397364Stjr		    (grp = getgrgid((gid_t)lgid)) == NULL ) {
16497364Stjr			warnx("%s: bad group name", grpname);
16597364Stjr			return;
16697364Stjr		}
16797364Stjr
16897364Stjr	/*
16997364Stjr	 * If the user is not a member of the requested group and the group
17097364Stjr	 * has a password, prompt and check it.
17197364Stjr	 */
17297364Stjr	dbmember = 0;
17397364Stjr	if (pwd->pw_gid == grp->gr_gid)
17497364Stjr		dbmember = 1;
17597364Stjr	for (p = grp->gr_mem; *p != NULL; p++)
17697364Stjr		if (strcmp(*p, pwd->pw_name) == 0) {
17797364Stjr			dbmember = 1;
17897364Stjr			break;
17997364Stjr		}
18097364Stjr	if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) {
18197364Stjr		pass = getpass("Password:");
182231994Skevlo		if (pass == NULL)
183231994Skevlo			return;
184231994Skevlo		cryptpw = crypt(pass, grp->gr_passwd);
185231994Skevlo		if (cryptpw == NULL || strcmp(grp->gr_passwd, cryptpw) != 0) {
18697364Stjr			fprintf(stderr, "Sorry\n");
18797364Stjr			return;
18897364Stjr		}
18997364Stjr	}
19097364Stjr
191194494Sbrooks	ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1;
192194494Sbrooks	if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL)
193194494Sbrooks		err(1, "malloc");
194194494Sbrooks	if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) {
19597364Stjr		warn("getgroups");
196229668Sghelmer		goto end;
19797364Stjr	}
19897364Stjr
19997364Stjr	/* Remove requested gid from supp. list if it exists. */
20097364Stjr	if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) {
20197364Stjr		for (i = 0; i < ngrps; i++)
20297364Stjr			if (grps[i] == grp->gr_gid)
20397364Stjr				break;
20497364Stjr		ngrps--;
20597364Stjr		memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t));
20697364Stjr		PRIV_START;
20797364Stjr		if (setgroups(ngrps, (const gid_t *)grps) < 0) {
20897364Stjr			PRIV_END;
20997364Stjr			warn("setgroups");
210229668Sghelmer			goto end;
21197364Stjr		}
21297364Stjr		PRIV_END;
21397364Stjr	}
21497364Stjr
21597364Stjr	PRIV_START;
21697364Stjr	if (setgid(grp->gr_gid)) {
21797364Stjr		PRIV_END;
21897364Stjr		warn("setgid");
219229668Sghelmer		goto end;
22097364Stjr	}
22197364Stjr	PRIV_END;
22297364Stjr	grps[0] = grp->gr_gid;
22397364Stjr
22497364Stjr	/* Add old effective gid to supp. list if it does not exist. */
22597364Stjr	if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) {
226226416Sdelphij		if (ngrps == ngrps_max)
22797364Stjr			warnx("too many groups");
22897364Stjr		else {
22997364Stjr			grps[ngrps++] = egid;
23097364Stjr			PRIV_START;
23197364Stjr			if (setgroups(ngrps, (const gid_t *)grps)) {
23297364Stjr				PRIV_END;
23397364Stjr				warn("setgroups");
234229668Sghelmer				goto end;
23597364Stjr			}
23697364Stjr			PRIV_END;
23797364Stjr		}
23897364Stjr	}
239229668Sghelmerend:
240194494Sbrooks	free(grps);
24197364Stjr}
24297364Stjr
24397364Stjrstatic int
24497364Stjrinarray(gid_t gid, const gid_t grps[], int ngrps)
24597364Stjr{
24697364Stjr	int i;
24797364Stjr
24897364Stjr	for (i = 0; i < ngrps; i++)
24997364Stjr		if (grps[i] == gid)
25097364Stjr			return (1);
25197364Stjr	return (0);
25297364Stjr}
25397364Stjr
25497364Stjr/*
25597364Stjr * Set the environment to what would be expected if the user logged in
25697364Stjr * again; this performs the same steps as su(1)'s -l option.
25797364Stjr */
25897364Stjrstatic void
25997364Stjrloginshell(void)
26097364Stjr{
26197364Stjr	char *args[2], **cleanenv, *term, *ticket;
26297364Stjr	const char *shell;
26397364Stjr	login_cap_t *lc;
26497364Stjr
26597364Stjr	shell = pwd->pw_shell;
26697364Stjr	if (*shell == '\0')
26797364Stjr		shell = _PATH_BSHELL;
26897364Stjr	if (chdir(pwd->pw_dir) < 0) {
26997364Stjr		warn("%s", pwd->pw_dir);
27097364Stjr		chdir("/");
27197364Stjr	}
27297364Stjr
27397364Stjr	term = getenv("TERM");
27497364Stjr	ticket = getenv("KRBTKFILE");
27597364Stjr
27697364Stjr	if ((cleanenv = calloc(20, sizeof(char *))) == NULL)
27797364Stjr		err(1, "calloc");
27897364Stjr	*cleanenv = NULL;
27997364Stjr	environ = cleanenv;
28097364Stjr
28197364Stjr	lc = login_getpwclass(pwd);
28297364Stjr	setusercontext(lc, pwd, pwd->pw_uid,
28397364Stjr	    LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
28497364Stjr	login_close(lc);
28597364Stjr	setenv("USER", pwd->pw_name, 1);
28697364Stjr	setenv("SHELL", shell, 1);
28797364Stjr	setenv("HOME", pwd->pw_dir, 1);
28897364Stjr	if (term != NULL)
28997364Stjr		setenv("TERM", term, 1);
29097364Stjr	if (ticket != NULL)
29197364Stjr		setenv("KRBTKFILE", ticket, 1);
29297364Stjr
29397364Stjr	if (asprintf(args, "-%s", basename(shell)) < 0)
29497364Stjr		err(1, "asprintf");
29597364Stjr	args[1] = NULL;
29697364Stjr
29797364Stjr	execv(shell, args);
29897364Stjr	err(1, "%s", shell);
29997364Stjr}
30097364Stjr
30197364Stjrstatic void
30297364Stjrdoshell(void)
30397364Stjr{
30497364Stjr	const char *shell;
30597364Stjr
30697364Stjr	shell = pwd->pw_shell;
30797364Stjr	if (*shell == '\0')
30897364Stjr		shell = _PATH_BSHELL;
309121741Sharti	execl(shell, basename(shell), (char *)NULL);
31097364Stjr	err(1, "%s", shell);
31197364Stjr}
312