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