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