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