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