1/*- 2 * Copyright (c) 2002 Tim J. Robbins. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27/* 28 * newgrp -- change to a new group 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: src/usr.bin/newgrp/newgrp.c,v 1.5 2009/12/13 03:14:06 delphij Exp $"); 33 34#include <sys/types.h> 35 36#include <err.h> 37#include <errno.h> 38#include <grp.h> 39#include <libgen.h> 40#include <limits.h> 41#ifndef __APPLE__ 42#include <login_cap.h> 43#endif /* !__APPLE__ */ 44#ifdef __APPLE__ 45#include <membership.h> 46#endif /* __APPLE__ */ 47#include <pwd.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <unistd.h> 52#ifdef __APPLE__ 53#include <paths.h> 54#endif /* __APPLE__ */ 55static void addgroup(const char *grpname); 56static void doshell(void); 57static int inarray(gid_t, const gid_t[], int); 58static void loginshell(void); 59static void restoregrps(void); 60static void usage(void); 61 62static struct passwd *pwd; 63static uid_t euid; 64 65extern char **environ; 66 67/* Manipulate effective user ID. */ 68#define PRIV_START do { \ 69 if (seteuid(euid) < 0) \ 70 err(1, "seteuid"); \ 71 } while (0) 72#define PRIV_END do { \ 73 if (seteuid(getuid()) < 0) \ 74 err(1, "seteuid"); \ 75 } while (0) 76 77int 78main(int argc, char *argv[]) 79{ 80 int ch, login; 81 82 euid = geteuid(); 83 if (seteuid(getuid()) < 0) 84 err(1, "seteuid"); 85 86 if ((pwd = getpwuid(getuid())) == NULL) 87 errx(1, "unknown user"); 88 89 login = 0; 90 while ((ch = getopt(argc, argv, "-l")) != -1) { 91 switch (ch) { 92 case '-': /* Obsolescent */ 93 case 'l': 94 login = 1; 95 break; 96 default: 97 usage(); 98 } 99 } 100 argc -= optind; 101 argv += optind; 102 103 switch (argc) { 104 case 0: 105 restoregrps(); 106 break; 107 case 1: 108 addgroup(*argv); 109 break; 110 default: 111 usage(); 112 } 113 114 if (seteuid(euid) < 0) 115 err(1, "seteuid"); 116 if (setuid(getuid()) < 0) 117 err(1, "setuid"); 118 119 if (login) 120 loginshell(); 121 else 122 doshell(); 123 124 /*NOTREACHED*/ 125 exit(1); 126} 127 128static void 129usage(void) 130{ 131 132 fprintf(stderr, "usage: newgrp [-l] [group]\n"); 133 exit(1); 134} 135 136static void 137restoregrps(void) 138{ 139 int initres, setres; 140 141 PRIV_START; 142 initres = initgroups(pwd->pw_name, pwd->pw_gid); 143 setres = setgid(pwd->pw_gid); 144 PRIV_END; 145 146 if (initres < 0) 147 warn("initgroups"); 148 if (setres < 0) 149 warn("setgid"); 150} 151 152static void 153addgroup(const char *grpname) 154{ 155 gid_t *grps; 156 long lgid, ngrps_max; 157 int dbmember, i, ngrps; 158 gid_t egid; 159 struct group *grp; 160 char *ep, *pass; 161 char **p; 162 char *grp_passwd; 163#ifdef __APPLE__ 164 uuid_t user_uuid; 165 uuid_t group_uuid; 166 int status; 167#endif 168 169 egid = getegid(); 170 171 /* Try it as a group name, then a group id. */ 172 if ((grp = getgrnam(grpname)) == NULL) 173 if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' || 174 (grp = getgrgid((gid_t)lgid)) == NULL ) { 175 warnx("%s: bad group name", grpname); 176 return; 177 } 178 179#ifdef __APPLE__ 180 status = mbr_uid_to_uuid(pwd->pw_uid, user_uuid); 181 if (status) 182 errc(1, status, "mbr_uid_to_uuid"); 183 184 status = mbr_gid_to_uuid(grp->gr_gid, group_uuid); 185 if (status) 186 errc(1, status, "mbr_gid_to_uuid"); 187 188 status = mbr_check_membership(user_uuid, group_uuid, &dbmember); 189 if (status) 190 errc(1, status, "mbr_check_membership"); 191#else 192 /* 193 * If the user is not a member of the requested group and the group 194 * has a password, prompt and check it. 195 */ 196 dbmember = 0; 197 if (pwd->pw_gid == grp->gr_gid) 198 dbmember = 1; 199 for (p = grp->gr_mem; *p != NULL; p++) 200 if (strcmp(*p, pwd->pw_name) == 0) { 201 dbmember = 1; 202 break; 203 } 204#endif 205 206 grp_passwd = grp->gr_passwd; 207 if ((grp_passwd == NULL) || (grp_passwd[0] == '\0')) 208 grp_passwd = "*"; 209 if (!dbmember && getuid() != 0) { 210 pass = getpass("Password:"); 211 if (pass == NULL || 212 strcmp(grp_passwd, crypt(pass, grp_passwd)) != 0) { 213 fprintf(stderr, "Sorry\n"); 214 return; 215 } 216 } 217 218 ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1; 219 if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL) 220 err(1, "malloc"); 221 if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) { 222 warn("getgroups"); 223 goto end; 224 } 225 226 /* Remove requested gid from supp. list if it exists. */ 227 if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) { 228 for (i = 0; i < ngrps; i++) 229 if (grps[i] == grp->gr_gid) 230 break; 231 ngrps--; 232 memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t)); 233 PRIV_START; 234 if (setgroups(ngrps, (const gid_t *)grps) < 0) { 235 PRIV_END; 236 warn("setgroups"); 237 goto end; 238 } 239 PRIV_END; 240 } 241 242 PRIV_START; 243 if (setgid(grp->gr_gid)) { 244 PRIV_END; 245 warn("setgid"); 246 goto end; 247 } 248 PRIV_END; 249 grps[0] = grp->gr_gid; 250 251 /* Add old effective gid to supp. list if it does not exist. */ 252 if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) { 253 if (ngrps + 1 >= ngrps_max) 254 warnx("too many groups"); 255 else { 256 grps[ngrps++] = egid; 257 PRIV_START; 258 if (setgroups(ngrps, (const gid_t *)grps)) { 259 PRIV_END; 260 warn("setgroups"); 261 goto end; 262 } 263 PRIV_END; 264 } 265 } 266 267end: 268 free(grps); 269} 270 271static int 272inarray(gid_t gid, const gid_t grps[], int ngrps) 273{ 274 int i; 275 276 for (i = 0; i < ngrps; i++) 277 if (grps[i] == gid) 278 return (1); 279 return (0); 280} 281 282/* 283 * Set the environment to what would be expected if the user logged in 284 * again; this performs the same steps as su(1)'s -l option. 285 */ 286static void 287loginshell(void) 288{ 289 char *args[2], **cleanenv, *term, *ticket; 290 const char *shell; 291 char *prog, progbuf[PATH_MAX]; 292#ifndef __APPLE__ 293 login_cap_t *lc; 294#endif /* !__APPLE__ */ 295 shell = pwd->pw_shell; 296 if (*shell == '\0') 297 shell = _PATH_BSHELL; 298 if (chdir(pwd->pw_dir) < 0) { 299 warn("%s", pwd->pw_dir); 300 chdir("/"); 301 } 302 303 term = getenv("TERM"); 304 ticket = getenv("KRBTKFILE"); 305 306 if ((cleanenv = calloc(20, sizeof(char *))) == NULL) 307 err(1, "calloc"); 308 *cleanenv = NULL; 309 environ = cleanenv; 310#ifndef __APPLE__ 311 lc = login_getpwclass(pwd); 312 setusercontext(lc, pwd, pwd->pw_uid, 313 LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 314 login_close(lc); 315#endif /* !__APPLE__ */ 316 setenv("USER", pwd->pw_name, 1); 317 setenv("SHELL", shell, 1); 318 setenv("HOME", pwd->pw_dir, 1); 319 if (term != NULL) 320 setenv("TERM", term, 1); 321 if (ticket != NULL) 322 setenv("KRBTKFILE", ticket, 1); 323 324 strlcpy(progbuf, shell, sizeof(progbuf)); 325 prog = basename(progbuf); 326 327 if (asprintf(args, "-%s", prog) < 0) 328 err(1, "asprintf"); 329 args[1] = NULL; 330 331 execv(shell, args); 332 err(1, "%s", shell); 333} 334 335static void 336doshell(void) 337{ 338 const char *shell; 339 char *prog, progbuf[PATH_MAX]; 340 341 shell = pwd->pw_shell; 342 if (*shell == '\0') 343 shell = _PATH_BSHELL; 344 345 strlcpy(progbuf, shell, sizeof(progbuf)); 346 prog = basename(progbuf); 347 348 execl(shell, prog, (char *)NULL); 349 err(1, "%s", shell); 350} 351