xref: /freebsd/usr.bin/newgrp/newgrp.c (revision 5e3934b15a2741b2de6b217e77dc9d798d740804)
129f48d62STim J. Robbins /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
429f48d62STim J. Robbins  * Copyright (c) 2002 Tim J. Robbins.
529f48d62STim J. Robbins  * All rights reserved.
629f48d62STim J. Robbins  *
729f48d62STim J. Robbins  * Redistribution and use in source and binary forms, with or without
829f48d62STim J. Robbins  * modification, are permitted provided that the following conditions
929f48d62STim J. Robbins  * are met:
1029f48d62STim J. Robbins  * 1. Redistributions of source code must retain the above copyright
1129f48d62STim J. Robbins  *    notice, this list of conditions and the following disclaimer.
1229f48d62STim J. Robbins  * 2. Redistributions in binary form must reproduce the above copyright
1329f48d62STim J. Robbins  *    notice, this list of conditions and the following disclaimer in the
1429f48d62STim J. Robbins  *    documentation and/or other materials provided with the distribution.
1529f48d62STim J. Robbins  *
1629f48d62STim J. Robbins  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1729f48d62STim J. Robbins  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1829f48d62STim J. Robbins  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1929f48d62STim J. Robbins  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2029f48d62STim J. Robbins  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2129f48d62STim J. Robbins  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2229f48d62STim J. Robbins  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2329f48d62STim J. Robbins  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2429f48d62STim J. Robbins  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2529f48d62STim J. Robbins  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2629f48d62STim J. Robbins  * SUCH DAMAGE.
2729f48d62STim J. Robbins  */
2829f48d62STim J. Robbins 
2929f48d62STim J. Robbins /*
3029f48d62STim J. Robbins  * newgrp -- change to a new group
3129f48d62STim J. Robbins  */
3229f48d62STim J. Robbins 
3329f48d62STim J. Robbins #include <sys/types.h>
3429f48d62STim J. Robbins 
3529f48d62STim J. Robbins #include <err.h>
36821df508SXin LI #include <errno.h>
3729f48d62STim J. Robbins #include <grp.h>
38821df508SXin LI #include <limits.h>
3929f48d62STim J. Robbins #include <login_cap.h>
4029f48d62STim J. Robbins #include <paths.h>
4129f48d62STim J. Robbins #include <pwd.h>
4229f48d62STim J. Robbins #include <stdio.h>
4329f48d62STim J. Robbins #include <stdlib.h>
4429f48d62STim J. Robbins #include <string.h>
4529f48d62STim J. Robbins #include <unistd.h>
4629f48d62STim J. Robbins 
4729f48d62STim J. Robbins static void	 addgroup(const char *grpname);
4829f48d62STim J. Robbins static void	 doshell(void);
4929f48d62STim J. Robbins static int	 inarray(gid_t, const gid_t[], int);
5029f48d62STim J. Robbins static void	 loginshell(void);
5129f48d62STim J. Robbins static void	 restoregrps(void);
5229f48d62STim J. Robbins static void	 usage(void);
5329f48d62STim J. Robbins 
5429f48d62STim J. Robbins static struct passwd *pwd;
5529f48d62STim J. Robbins static uid_t euid;
5629f48d62STim J. Robbins 
5729f48d62STim J. Robbins extern char **environ;
5829f48d62STim J. Robbins 
5929f48d62STim J. Robbins /* Manipulate effective user ID. */
6029f48d62STim J. Robbins #define PRIV_START do {				\
6129f48d62STim J. Robbins 		if (seteuid(euid) < 0)		\
6229f48d62STim J. Robbins 			err(1, "seteuid");	\
6329f48d62STim J. Robbins 	} while (0)
6429f48d62STim J. Robbins #define PRIV_END do {				\
6529f48d62STim J. Robbins 		if (seteuid(getuid()) < 0)	\
6629f48d62STim J. Robbins 			err(1, "seteuid");	\
6729f48d62STim J. Robbins 	} while (0)
6829f48d62STim J. Robbins 
6929f48d62STim J. Robbins int
main(int argc,char * argv[])7029f48d62STim J. Robbins main(int argc, char *argv[])
7129f48d62STim J. Robbins {
7229f48d62STim J. Robbins 	int ch, login;
7329f48d62STim J. Robbins 
7430c4fc7cSDag-Erling Smørgrav 	if ((euid = geteuid()) != 0)
7530c4fc7cSDag-Erling Smørgrav 		warnx("need root permissions to function properly, check setuid bit");
7629f48d62STim J. Robbins 	if (seteuid(getuid()) < 0)
7729f48d62STim J. Robbins 		err(1, "seteuid");
7829f48d62STim J. Robbins 
7929f48d62STim J. Robbins 	if ((pwd = getpwuid(getuid())) == NULL)
8029f48d62STim J. Robbins 		errx(1, "unknown user");
8129f48d62STim J. Robbins 
8229f48d62STim J. Robbins 	login = 0;
8329f48d62STim J. Robbins 	while ((ch = getopt(argc, argv, "-l")) != -1) {
8429f48d62STim J. Robbins 		switch (ch) {
8529f48d62STim J. Robbins 		case '-':		/* Obsolescent */
8629f48d62STim J. Robbins 		case 'l':
8729f48d62STim J. Robbins 			login = 1;
8829f48d62STim J. Robbins 			break;
8929f48d62STim J. Robbins 		default:
9029f48d62STim J. Robbins 			usage();
9129f48d62STim J. Robbins 		}
9229f48d62STim J. Robbins 	}
9329f48d62STim J. Robbins 	argc -= optind;
9429f48d62STim J. Robbins 	argv += optind;
9529f48d62STim J. Robbins 
9629f48d62STim J. Robbins 	switch (argc) {
9729f48d62STim J. Robbins 	case 0:
9829f48d62STim J. Robbins 		restoregrps();
9929f48d62STim J. Robbins 		break;
10029f48d62STim J. Robbins 	case 1:
10129f48d62STim J. Robbins 		addgroup(*argv);
10229f48d62STim J. Robbins 		break;
10329f48d62STim J. Robbins 	default:
10429f48d62STim J. Robbins 		usage();
10529f48d62STim J. Robbins 	}
10629f48d62STim J. Robbins 
10729f48d62STim J. Robbins 	if (seteuid(euid) < 0)
10829f48d62STim J. Robbins 		err(1, "seteuid");
10929f48d62STim J. Robbins 	if (setuid(getuid()) < 0)
11029f48d62STim J. Robbins 		err(1, "setuid");
11129f48d62STim J. Robbins 
11229f48d62STim J. Robbins 	if (login)
11329f48d62STim J. Robbins 		loginshell();
11429f48d62STim J. Robbins 	else
11529f48d62STim J. Robbins 		doshell();
11629f48d62STim J. Robbins 
11729f48d62STim J. Robbins 	/*NOTREACHED*/
11829f48d62STim J. Robbins 	exit(1);
11929f48d62STim J. Robbins }
12029f48d62STim J. Robbins 
12129f48d62STim J. Robbins static void
usage(void)12229f48d62STim J. Robbins usage(void)
12329f48d62STim J. Robbins {
12429f48d62STim J. Robbins 
12529f48d62STim J. Robbins 	fprintf(stderr, "usage: newgrp [-l] [group]\n");
12629f48d62STim J. Robbins 	exit(1);
12729f48d62STim J. Robbins }
12829f48d62STim J. Robbins 
12929f48d62STim J. Robbins static void
restoregrps(void)13029f48d62STim J. Robbins restoregrps(void)
13129f48d62STim J. Robbins {
13229f48d62STim J. Robbins 	int initres, setres;
13329f48d62STim J. Robbins 
13429f48d62STim J. Robbins 	PRIV_START;
13529f48d62STim J. Robbins 	initres = initgroups(pwd->pw_name, pwd->pw_gid);
13629f48d62STim J. Robbins 	setres = setgid(pwd->pw_gid);
13729f48d62STim J. Robbins 	PRIV_END;
13829f48d62STim J. Robbins 
13929f48d62STim J. Robbins 	if (initres < 0)
14029f48d62STim J. Robbins 		warn("initgroups");
14129f48d62STim J. Robbins 	if (setres < 0)
1426a99195fSXin LI 		warn("setgid");
14329f48d62STim J. Robbins }
14429f48d62STim J. Robbins 
14529f48d62STim J. Robbins static void
addgroup(const char * grpname)14629f48d62STim J. Robbins addgroup(const char *grpname)
14729f48d62STim J. Robbins {
14854404cfbSBrooks Davis 	gid_t *grps;
14954404cfbSBrooks Davis 	long lgid, ngrps_max;
15029f48d62STim J. Robbins 	int dbmember, i, ngrps;
15129f48d62STim J. Robbins 	gid_t egid;
15229f48d62STim J. Robbins 	struct group *grp;
15329dcf726SKevin Lo 	char *ep, *pass, *cryptpw;
15429f48d62STim J. Robbins 	char **p;
15529f48d62STim J. Robbins 
15629f48d62STim J. Robbins 	egid = getegid();
15729f48d62STim J. Robbins 
15829f48d62STim J. Robbins 	/* Try it as a group name, then a group id. */
15929f48d62STim J. Robbins 	if ((grp = getgrnam(grpname)) == NULL)
16029f48d62STim J. Robbins 		if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' ||
16129f48d62STim J. Robbins 		    (grp = getgrgid((gid_t)lgid)) == NULL ) {
16229f48d62STim J. Robbins 			warnx("%s: bad group name", grpname);
16329f48d62STim J. Robbins 			return;
16429f48d62STim J. Robbins 		}
16529f48d62STim J. Robbins 
16629f48d62STim J. Robbins 	/*
16729f48d62STim J. Robbins 	 * If the user is not a member of the requested group and the group
16829f48d62STim J. Robbins 	 * has a password, prompt and check it.
16929f48d62STim J. Robbins 	 */
17029f48d62STim J. Robbins 	dbmember = 0;
17129f48d62STim J. Robbins 	if (pwd->pw_gid == grp->gr_gid)
17229f48d62STim J. Robbins 		dbmember = 1;
17329f48d62STim J. Robbins 	for (p = grp->gr_mem; *p != NULL; p++)
17429f48d62STim J. Robbins 		if (strcmp(*p, pwd->pw_name) == 0) {
17529f48d62STim J. Robbins 			dbmember = 1;
17629f48d62STim J. Robbins 			break;
17729f48d62STim J. Robbins 		}
17829f48d62STim J. Robbins 	if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) {
17929f48d62STim J. Robbins 		pass = getpass("Password:");
18029dcf726SKevin Lo 		if (pass == NULL)
18129dcf726SKevin Lo 			return;
18229dcf726SKevin Lo 		cryptpw = crypt(pass, grp->gr_passwd);
18329dcf726SKevin Lo 		if (cryptpw == NULL || strcmp(grp->gr_passwd, cryptpw) != 0) {
18429f48d62STim J. Robbins 			fprintf(stderr, "Sorry\n");
18529f48d62STim J. Robbins 			return;
18629f48d62STim J. Robbins 		}
18729f48d62STim J. Robbins 	}
18829f48d62STim J. Robbins 
18954404cfbSBrooks Davis 	ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1;
19054404cfbSBrooks Davis 	if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL)
19154404cfbSBrooks Davis 		err(1, "malloc");
19254404cfbSBrooks Davis 	if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) {
19329f48d62STim J. Robbins 		warn("getgroups");
194532b7268SGuy Helmer 		goto end;
19529f48d62STim J. Robbins 	}
19629f48d62STim J. Robbins 
19729f48d62STim J. Robbins 	/* Remove requested gid from supp. list if it exists. */
19829f48d62STim J. Robbins 	if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) {
19929f48d62STim J. Robbins 		for (i = 0; i < ngrps; i++)
20029f48d62STim J. Robbins 			if (grps[i] == grp->gr_gid)
20129f48d62STim J. Robbins 				break;
20229f48d62STim J. Robbins 		ngrps--;
20329f48d62STim J. Robbins 		memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t));
20429f48d62STim J. Robbins 		PRIV_START;
20529f48d62STim J. Robbins 		if (setgroups(ngrps, (const gid_t *)grps) < 0) {
20629f48d62STim J. Robbins 			PRIV_END;
20729f48d62STim J. Robbins 			warn("setgroups");
208532b7268SGuy Helmer 			goto end;
20929f48d62STim J. Robbins 		}
21029f48d62STim J. Robbins 		PRIV_END;
21129f48d62STim J. Robbins 	}
21229f48d62STim J. Robbins 
21329f48d62STim J. Robbins 	PRIV_START;
21429f48d62STim J. Robbins 	if (setgid(grp->gr_gid)) {
21529f48d62STim J. Robbins 		PRIV_END;
21629f48d62STim J. Robbins 		warn("setgid");
217532b7268SGuy Helmer 		goto end;
21829f48d62STim J. Robbins 	}
21929f48d62STim J. Robbins 	PRIV_END;
22029f48d62STim J. Robbins 	grps[0] = grp->gr_gid;
22129f48d62STim J. Robbins 
22229f48d62STim J. Robbins 	/* Add old effective gid to supp. list if it does not exist. */
22329f48d62STim J. Robbins 	if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) {
22441cbfdc9SXin LI 		if (ngrps == ngrps_max)
22529f48d62STim J. Robbins 			warnx("too many groups");
22629f48d62STim J. Robbins 		else {
22729f48d62STim J. Robbins 			grps[ngrps++] = egid;
22829f48d62STim J. Robbins 			PRIV_START;
22929f48d62STim J. Robbins 			if (setgroups(ngrps, (const gid_t *)grps)) {
23029f48d62STim J. Robbins 				PRIV_END;
23129f48d62STim J. Robbins 				warn("setgroups");
232532b7268SGuy Helmer 				goto end;
23329f48d62STim J. Robbins 			}
23429f48d62STim J. Robbins 			PRIV_END;
23529f48d62STim J. Robbins 		}
23629f48d62STim J. Robbins 	}
237532b7268SGuy Helmer end:
23854404cfbSBrooks Davis 	free(grps);
23929f48d62STim J. Robbins }
24029f48d62STim J. Robbins 
24129f48d62STim J. Robbins static int
inarray(gid_t gid,const gid_t grps[],int ngrps)24229f48d62STim J. Robbins inarray(gid_t gid, const gid_t grps[], int ngrps)
24329f48d62STim J. Robbins {
24429f48d62STim J. Robbins 	int i;
24529f48d62STim J. Robbins 
24629f48d62STim J. Robbins 	for (i = 0; i < ngrps; i++)
24729f48d62STim J. Robbins 		if (grps[i] == gid)
24829f48d62STim J. Robbins 			return (1);
24929f48d62STim J. Robbins 	return (0);
25029f48d62STim J. Robbins }
25129f48d62STim J. Robbins 
25229f48d62STim J. Robbins /*
25329f48d62STim J. Robbins  * Set the environment to what would be expected if the user logged in
25429f48d62STim J. Robbins  * again; this performs the same steps as su(1)'s -l option.
25529f48d62STim J. Robbins  */
25629f48d62STim J. Robbins static void
loginshell(void)25729f48d62STim J. Robbins loginshell(void)
25829f48d62STim J. Robbins {
25929f48d62STim J. Robbins 	char *args[2], **cleanenv, *term, *ticket;
26029f48d62STim J. Robbins 	const char *shell;
26129f48d62STim J. Robbins 	login_cap_t *lc;
26229f48d62STim J. Robbins 
26329f48d62STim J. Robbins 	shell = pwd->pw_shell;
26429f48d62STim J. Robbins 	if (*shell == '\0')
26529f48d62STim J. Robbins 		shell = _PATH_BSHELL;
26629f48d62STim J. Robbins 	if (chdir(pwd->pw_dir) < 0) {
26729f48d62STim J. Robbins 		warn("%s", pwd->pw_dir);
26829f48d62STim J. Robbins 		chdir("/");
26929f48d62STim J. Robbins 	}
27029f48d62STim J. Robbins 
27129f48d62STim J. Robbins 	term = getenv("TERM");
27229f48d62STim J. Robbins 	ticket = getenv("KRBTKFILE");
27329f48d62STim J. Robbins 
27429f48d62STim J. Robbins 	if ((cleanenv = calloc(20, sizeof(char *))) == NULL)
27529f48d62STim J. Robbins 		err(1, "calloc");
27629f48d62STim J. Robbins 	*cleanenv = NULL;
27729f48d62STim J. Robbins 	environ = cleanenv;
27829f48d62STim J. Robbins 
27929f48d62STim J. Robbins 	lc = login_getpwclass(pwd);
28029f48d62STim J. Robbins 	setusercontext(lc, pwd, pwd->pw_uid,
28129f48d62STim J. Robbins 	    LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
28229f48d62STim J. Robbins 	login_close(lc);
28329f48d62STim J. Robbins 	setenv("USER", pwd->pw_name, 1);
28429f48d62STim J. Robbins 	setenv("SHELL", shell, 1);
28529f48d62STim J. Robbins 	setenv("HOME", pwd->pw_dir, 1);
28629f48d62STim J. Robbins 	if (term != NULL)
28729f48d62STim J. Robbins 		setenv("TERM", term, 1);
28829f48d62STim J. Robbins 	if (ticket != NULL)
28929f48d62STim J. Robbins 		setenv("KRBTKFILE", ticket, 1);
29029f48d62STim J. Robbins 
291e33d251eSEd Schouten 	if (asprintf(args, "-%s", shell) < 0)
29229f48d62STim J. Robbins 		err(1, "asprintf");
29329f48d62STim J. Robbins 	args[1] = NULL;
29429f48d62STim J. Robbins 
29529f48d62STim J. Robbins 	execv(shell, args);
29629f48d62STim J. Robbins 	err(1, "%s", shell);
29729f48d62STim J. Robbins }
29829f48d62STim J. Robbins 
29929f48d62STim J. Robbins static void
doshell(void)30029f48d62STim J. Robbins doshell(void)
30129f48d62STim J. Robbins {
30229f48d62STim J. Robbins 	const char *shell;
30329f48d62STim J. Robbins 
30429f48d62STim J. Robbins 	shell = pwd->pw_shell;
30529f48d62STim J. Robbins 	if (*shell == '\0')
30629f48d62STim J. Robbins 		shell = _PATH_BSHELL;
307e33d251eSEd Schouten 	execl(shell, shell, (char *)NULL);
30829f48d62STim J. Robbins 	err(1, "%s", shell);
30929f48d62STim J. Robbins }
310