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