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