1 /* $OpenBSD: passwd.c,v 1.56 2019/06/28 13:32:43 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 1993, 1994, 1995 5 * The Regents of the University of California. 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 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <sys/resource.h> 36 #include <sys/wait.h> 37 38 #include <fcntl.h> 39 #include <unistd.h> 40 #include <stdlib.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <pwd.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <paths.h> 48 #include <signal.h> 49 #include <limits.h> 50 51 #include "util.h" 52 53 static char pw_defdir[] = "/etc"; 54 static char *pw_dir = pw_defdir; 55 static char *pw_lck; 56 57 char * 58 pw_file(const char *nm) 59 { 60 const char *p = strrchr(nm, '/'); 61 char *new_nm; 62 63 if (p) 64 p++; 65 else 66 p = nm; 67 68 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1) 69 return NULL; 70 return new_nm; 71 } 72 73 void 74 pw_setdir(const char *dir) 75 { 76 char *p; 77 78 if (strcmp (dir, pw_dir) == 0) 79 return; 80 if (pw_dir != pw_defdir) 81 free(pw_dir); 82 pw_dir = strdup(dir); 83 if (pw_lck) { 84 p = pw_file(pw_lck); 85 free(pw_lck); 86 pw_lck = p; 87 } 88 } 89 90 91 int 92 pw_lock(int retries) 93 { 94 int i, fd; 95 mode_t old_mode; 96 97 if (!pw_lck) { 98 errno = EINVAL; 99 return (-1); 100 } 101 /* Acquire the lock file. */ 102 old_mode = umask(0); 103 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); 104 for (i = 0; i < retries && fd == -1 && errno == EEXIST; i++) { 105 sleep(1); 106 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); 107 } 108 (void) umask(old_mode); 109 return (fd); 110 } 111 112 int 113 pw_mkdb(char *username, int flags) 114 { 115 int pstat, ac; 116 pid_t pid; 117 char *av[8]; 118 struct stat sb; 119 120 if (pw_lck == NULL) 121 return(-1); 122 123 /* A zero length passwd file is never ok */ 124 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) { 125 warnx("%s is zero length", pw_lck); 126 return (-1); 127 } 128 129 ac = 0; 130 av[ac++] = "pwd_mkdb"; 131 av[ac++] = "-d"; 132 av[ac++] = pw_dir; 133 if (flags & _PASSWORD_SECUREONLY) 134 av[ac++] = "-s"; 135 else if (!(flags & _PASSWORD_OMITV7)) 136 av[ac++] = "-p"; 137 if (username) { 138 av[ac++] = "-u"; 139 av[ac++] = username; 140 } 141 av[ac++] = pw_lck; 142 av[ac] = NULL; 143 144 pid = vfork(); 145 if (pid == -1) 146 return (-1); 147 if (pid == 0) { 148 if (pw_lck) 149 execv(_PATH_PWD_MKDB, av); 150 _exit(1); 151 } 152 pid = waitpid(pid, &pstat, 0); 153 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 154 return (-1); 155 return (0); 156 } 157 158 int 159 pw_abort(void) 160 { 161 return (pw_lck ? unlink(pw_lck) : -1); 162 } 163 164 /* Everything below this point is intended for the convenience of programs 165 * which allow a user to interactively edit the passwd file. Errors in the 166 * routines below will cause the process to abort. */ 167 168 static pid_t editpid = -1; 169 170 static void 171 pw_cont(int signo) 172 { 173 int save_errno = errno; 174 175 if (editpid != -1) 176 kill(editpid, signo); 177 errno = save_errno; 178 } 179 180 void 181 pw_init(void) 182 { 183 struct rlimit rlim; 184 185 /* Unlimited resource limits. */ 186 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 187 (void)setrlimit(RLIMIT_CPU, &rlim); 188 (void)setrlimit(RLIMIT_FSIZE, &rlim); 189 (void)setrlimit(RLIMIT_STACK, &rlim); 190 (void)setrlimit(RLIMIT_DATA, &rlim); 191 (void)setrlimit(RLIMIT_RSS, &rlim); 192 193 /* Don't drop core (not really necessary, but GP's). */ 194 rlim.rlim_cur = rlim.rlim_max = 0; 195 (void)setrlimit(RLIMIT_CORE, &rlim); 196 197 /* Turn off signals. */ 198 (void)signal(SIGALRM, SIG_IGN); 199 (void)signal(SIGHUP, SIG_IGN); 200 (void)signal(SIGINT, SIG_IGN); 201 (void)signal(SIGPIPE, SIG_IGN); 202 (void)signal(SIGQUIT, SIG_IGN); 203 (void)signal(SIGTERM, SIG_IGN); 204 (void)signal(SIGCONT, pw_cont); 205 206 if (!pw_lck) 207 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK); 208 } 209 210 void 211 pw_edit(int notsetuid, const char *filename) 212 { 213 int pstat; 214 char *p; 215 char *editor; 216 char *argp[] = {"sh", "-c", NULL, NULL}; 217 218 if (!filename) { 219 filename = pw_lck; 220 if (!filename) 221 return; 222 } 223 224 if ((editor = getenv("EDITOR")) == NULL) 225 editor = _PATH_VI; 226 227 if (asprintf(&p, "%s %s", editor, filename) == -1) 228 return; 229 argp[2] = p; 230 231 switch (editpid = vfork()) { 232 case -1: /* error */ 233 free(p); 234 return; 235 case 0: /* child */ 236 if (notsetuid) { 237 setgid(getgid()); 238 setuid(getuid()); 239 } 240 execv(_PATH_BSHELL, argp); 241 _exit(127); 242 } 243 244 free(p); 245 for (;;) { 246 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 247 if (editpid == -1) 248 pw_error(editor, 1, 1); 249 else if (WIFSTOPPED(pstat)) 250 raise(WSTOPSIG(pstat)); 251 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 252 break; 253 else 254 pw_error(editor, 1, 1); 255 } 256 editpid = -1; 257 } 258 259 void 260 pw_prompt(void) 261 { 262 int first, c; 263 264 (void)printf("re-edit the password file? [y]: "); 265 (void)fflush(stdout); 266 first = c = getchar(); 267 while (c != '\n' && c != EOF) 268 c = getchar(); 269 switch (first) { 270 case EOF: 271 putchar('\n'); 272 /* FALLTHROUGH */ 273 case 'n': 274 case 'N': 275 pw_error(NULL, 0, 0); 276 break; 277 } 278 } 279 280 static int 281 pw_equal(const struct passwd *pw1, const struct passwd *pw2) 282 { 283 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 284 pw1->pw_uid == pw2->pw_uid && 285 pw1->pw_gid == pw2->pw_gid && 286 strcmp(pw1->pw_class, pw2->pw_class) == 0 && 287 pw1->pw_change == pw2->pw_change && 288 pw1->pw_expire == pw2->pw_expire && 289 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 290 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 291 strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 292 } 293 294 static int 295 pw_write_entry(FILE *to, const struct passwd *pw) 296 { 297 char gidstr[16], uidstr[16]; 298 299 /* Preserve gid/uid -1 */ 300 if (pw->pw_gid == (gid_t)-1) 301 strlcpy(gidstr, "-1", sizeof(gidstr)); 302 else 303 snprintf(gidstr, sizeof(gidstr), "%u", (u_int)pw->pw_gid); 304 305 if (pw->pw_uid == (uid_t)-1) 306 strlcpy(uidstr, "-1", sizeof(uidstr)); 307 else 308 snprintf(uidstr, sizeof(uidstr), "%u", (u_int)pw->pw_uid); 309 310 return fprintf(to, "%s:%s:%s:%s:%s:%lld:%lld:%s:%s:%s\n", 311 pw->pw_name, pw->pw_passwd, uidstr, gidstr, pw->pw_class, 312 (long long)pw->pw_change, (long long)pw->pw_expire, 313 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 314 } 315 316 void 317 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw) 318 { 319 struct passwd tpw; 320 FILE *from, *to; 321 int done; 322 char *p, *ep, buf[8192]; 323 char *master = pw_file(_PATH_MASTERPASSWD); 324 325 if (!master) 326 pw_error(NULL, 0, 1); 327 if (!(from = fdopen(ffd, "r"))) 328 pw_error(master, 1, 1); 329 if (!(to = fdopen(tfd, "w"))) 330 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1); 331 332 for (done = 0; fgets(buf, (int)sizeof(buf), from);) { 333 if ((ep = strchr(buf, '\n')) == NULL) { 334 warnx("%s: line too long", master); 335 pw_error(NULL, 0, 1); 336 } 337 if (done) { 338 if (fputs(buf, to)) 339 goto fail; 340 continue; 341 } 342 if (!(p = strchr(buf, ':'))) { 343 warnx("%s: corrupted entry", master); 344 pw_error(NULL, 0, 1); 345 } 346 *p = '\0'; 347 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) { 348 *p = ':'; 349 if (fputs(buf, to)) 350 goto fail; 351 continue; 352 } 353 if (opw != NULL) { 354 *p = ':'; 355 *ep = '\0'; 356 if (!pw_scan(buf, &tpw, NULL)) 357 pw_error(NULL, 0, 1); 358 if (!pw_equal(&tpw, opw)) { 359 warnx("%s: inconsistent entry", master); 360 pw_error(NULL, 0, 1); 361 } 362 } 363 if (pw_write_entry(to, pw) == -1) 364 goto fail; 365 done = 1; 366 } 367 if (!done && pw_write_entry(to, pw) == -1) 368 goto fail; 369 370 if (ferror(to) || fflush(to)) 371 fail: 372 pw_error(NULL, 0, 1); 373 free(master); 374 (void)fclose(to); 375 } 376 377 int 378 pw_scan(char *bp, struct passwd *pw, int *flags) 379 { 380 int root; 381 char *p, *sh; 382 const char *errstr; 383 384 if (flags != NULL) 385 *flags = 0; 386 387 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */ 388 goto fmt; 389 pw->pw_name = p; 390 root = !strcmp(pw->pw_name, "root"); 391 392 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ 393 goto fmt; 394 395 if (!(p = strsep(&bp, ":"))) /* uid */ 396 goto fmt; 397 pw->pw_uid = strtonum(p, -1, UID_MAX, &errstr); 398 if (errstr != NULL) { 399 if (*p != '\0') { 400 warnx("uid is %s", errstr); 401 return (0); 402 } 403 if (flags != NULL) 404 *flags |= _PASSWORD_NOUID; 405 } 406 if (root && pw->pw_uid) { 407 warnx("root uid should be 0"); 408 return (0); 409 } 410 411 if (!(p = strsep(&bp, ":"))) /* gid */ 412 goto fmt; 413 pw->pw_gid = strtonum(p, -1, GID_MAX, &errstr); 414 if (errstr != NULL) { 415 if (*p != '\0') { 416 warnx("gid is %s", errstr); 417 return (0); 418 } 419 if (flags != NULL) 420 *flags |= _PASSWORD_NOGID; 421 } 422 423 pw->pw_class = strsep(&bp, ":"); /* class */ 424 if (!(p = strsep(&bp, ":"))) /* change */ 425 goto fmt; 426 pw->pw_change = atoll(p); 427 if ((*p == '\0') && (flags != (int *)NULL)) 428 *flags |= _PASSWORD_NOCHG; 429 if (!(p = strsep(&bp, ":"))) /* expire */ 430 goto fmt; 431 pw->pw_expire = atoll(p); 432 if ((*p == '\0') && (flags != (int *)NULL)) 433 *flags |= _PASSWORD_NOEXP; 434 pw->pw_gecos = strsep(&bp, ":"); /* gecos */ 435 pw->pw_dir = strsep(&bp, ":"); /* directory */ 436 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ 437 goto fmt; 438 439 p = pw->pw_shell; 440 if (root && *p) { /* empty == /bin/sh */ 441 for (setusershell();;) { 442 if (!(sh = getusershell())) { 443 warnx("warning, unknown root shell"); 444 break; 445 } 446 if (!strcmp(p, sh)) 447 break; 448 } 449 endusershell(); 450 } 451 452 if ((p = strsep(&bp, ":"))) { /* too many */ 453 fmt: warnx("corrupted entry"); 454 return (0); 455 } 456 457 return (1); 458 } 459 460 __dead void 461 pw_error(const char *name, int error, int eval) 462 { 463 char *master = pw_file(_PATH_MASTERPASSWD); 464 465 if (error) { 466 if (name) 467 warn("%s", name); 468 else 469 warn(NULL); 470 } 471 if (master) { 472 warnx("%s: unchanged", master); 473 free(master); 474 } 475 476 pw_abort(); 477 exit(eval); 478 } 479