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