newgrp.c revision 200420
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: head/usr.bin/newgrp/newgrp.c 200420 2009-12-11 23:35:38Z delphij $"); 33 34#include <sys/types.h> 35 36#include <err.h> 37#include <grp.h> 38#include <libgen.h> 39#include <login_cap.h> 40#include <paths.h> 41#include <pwd.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46 47static void addgroup(const char *grpname); 48static void doshell(void); 49static int inarray(gid_t, const gid_t[], int); 50static void loginshell(void); 51static void restoregrps(void); 52static void usage(void); 53 54static struct passwd *pwd; 55static uid_t euid; 56 57extern char **environ; 58 59/* Manipulate effective user ID. */ 60#define PRIV_START do { \ 61 if (seteuid(euid) < 0) \ 62 err(1, "seteuid"); \ 63 } while (0) 64#define PRIV_END do { \ 65 if (seteuid(getuid()) < 0) \ 66 err(1, "seteuid"); \ 67 } while (0) 68 69int 70main(int argc, char *argv[]) 71{ 72 int ch, login; 73 74 euid = geteuid(); 75 if (seteuid(getuid()) < 0) 76 err(1, "seteuid"); 77 78 if ((pwd = getpwuid(getuid())) == NULL) 79 errx(1, "unknown user"); 80 81 login = 0; 82 while ((ch = getopt(argc, argv, "-l")) != -1) { 83 switch (ch) { 84 case '-': /* Obsolescent */ 85 case 'l': 86 login = 1; 87 break; 88 default: 89 usage(); 90 } 91 } 92 argc -= optind; 93 argv += optind; 94 95 switch (argc) { 96 case 0: 97 restoregrps(); 98 break; 99 case 1: 100 addgroup(*argv); 101 break; 102 default: 103 usage(); 104 } 105 106 if (seteuid(euid) < 0) 107 err(1, "seteuid"); 108 if (setuid(getuid()) < 0) 109 err(1, "setuid"); 110 111 if (login) 112 loginshell(); 113 else 114 doshell(); 115 116 /*NOTREACHED*/ 117 exit(1); 118} 119 120static void 121usage(void) 122{ 123 124 fprintf(stderr, "usage: newgrp [-l] [group]\n"); 125 exit(1); 126} 127 128static void 129restoregrps(void) 130{ 131 int initres, setres; 132 133 PRIV_START; 134 initres = initgroups(pwd->pw_name, pwd->pw_gid); 135 setres = setgid(pwd->pw_gid); 136 PRIV_END; 137 138 if (initres < 0) 139 warn("initgroups"); 140 if (setres < 0) 141 warn("setgroups"); 142} 143 144static void 145addgroup(const char *grpname) 146{ 147 gid_t *grps; 148 long lgid, ngrps_max; 149 int dbmember, i, ngrps; 150 gid_t egid; 151 struct group *grp; 152 char *ep, *pass; 153 char **p; 154 155 egid = getegid(); 156 157 /* Try it as a group name, then a group id. */ 158 if ((grp = getgrnam(grpname)) == NULL) 159 if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' || 160 (grp = getgrgid((gid_t)lgid)) == NULL ) { 161 warnx("%s: bad group name", grpname); 162 return; 163 } 164 165 /* 166 * If the user is not a member of the requested group and the group 167 * has a password, prompt and check it. 168 */ 169 dbmember = 0; 170 if (pwd->pw_gid == grp->gr_gid) 171 dbmember = 1; 172 for (p = grp->gr_mem; *p != NULL; p++) 173 if (strcmp(*p, pwd->pw_name) == 0) { 174 dbmember = 1; 175 break; 176 } 177 if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) { 178 pass = getpass("Password:"); 179 if (pass == NULL || 180 strcmp(grp->gr_passwd, crypt(pass, grp->gr_passwd)) != 0) { 181 fprintf(stderr, "Sorry\n"); 182 return; 183 } 184 } 185 186 ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1; 187 if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL) 188 err(1, "malloc"); 189 if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) { 190 warn("getgroups"); 191 return; 192 } 193 194 /* Remove requested gid from supp. list if it exists. */ 195 if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) { 196 for (i = 0; i < ngrps; i++) 197 if (grps[i] == grp->gr_gid) 198 break; 199 ngrps--; 200 memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t)); 201 PRIV_START; 202 if (setgroups(ngrps, (const gid_t *)grps) < 0) { 203 PRIV_END; 204 warn("setgroups"); 205 return; 206 } 207 PRIV_END; 208 } 209 210 PRIV_START; 211 if (setgid(grp->gr_gid)) { 212 PRIV_END; 213 warn("setgid"); 214 return; 215 } 216 PRIV_END; 217 grps[0] = grp->gr_gid; 218 219 /* Add old effective gid to supp. list if it does not exist. */ 220 if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) { 221 if (ngrps == ngrps_max) 222 warnx("too many groups"); 223 else { 224 grps[ngrps++] = egid; 225 PRIV_START; 226 if (setgroups(ngrps, (const gid_t *)grps)) { 227 PRIV_END; 228 warn("setgroups"); 229 return; 230 } 231 PRIV_END; 232 } 233 } 234 235 free(grps); 236} 237 238static int 239inarray(gid_t gid, const gid_t grps[], int ngrps) 240{ 241 int i; 242 243 for (i = 0; i < ngrps; i++) 244 if (grps[i] == gid) 245 return (1); 246 return (0); 247} 248 249/* 250 * Set the environment to what would be expected if the user logged in 251 * again; this performs the same steps as su(1)'s -l option. 252 */ 253static void 254loginshell(void) 255{ 256 char *args[2], **cleanenv, *term, *ticket; 257 const char *shell; 258 login_cap_t *lc; 259 260 shell = pwd->pw_shell; 261 if (*shell == '\0') 262 shell = _PATH_BSHELL; 263 if (chdir(pwd->pw_dir) < 0) { 264 warn("%s", pwd->pw_dir); 265 chdir("/"); 266 } 267 268 term = getenv("TERM"); 269 ticket = getenv("KRBTKFILE"); 270 271 if ((cleanenv = calloc(20, sizeof(char *))) == NULL) 272 err(1, "calloc"); 273 *cleanenv = NULL; 274 environ = cleanenv; 275 276 lc = login_getpwclass(pwd); 277 setusercontext(lc, pwd, pwd->pw_uid, 278 LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 279 login_close(lc); 280 setenv("USER", pwd->pw_name, 1); 281 setenv("SHELL", shell, 1); 282 setenv("HOME", pwd->pw_dir, 1); 283 if (term != NULL) 284 setenv("TERM", term, 1); 285 if (ticket != NULL) 286 setenv("KRBTKFILE", ticket, 1); 287 288 if (asprintf(args, "-%s", basename(shell)) < 0) 289 err(1, "asprintf"); 290 args[1] = NULL; 291 292 execv(shell, args); 293 err(1, "%s", shell); 294} 295 296static void 297doshell(void) 298{ 299 const char *shell; 300 301 shell = pwd->pw_shell; 302 if (*shell == '\0') 303 shell = _PATH_BSHELL; 304 execl(shell, basename(shell), (char *)NULL); 305 err(1, "%s", shell); 306} 307