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