1 /* $OpenBSD: passwd.c,v 1.42 2003/06/26 16:34:42 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 #if defined(LIBC_SCCS) && !defined(lint) 33 static const char rcsid[] = "$OpenBSD: passwd.c,v 1.42 2003/06/26 16:34:42 deraadt Exp $"; 34 #endif /* LIBC_SCCS and not lint */ 35 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <sys/time.h> 39 #include <sys/resource.h> 40 #include <sys/wait.h> 41 42 #include <fcntl.h> 43 #include <unistd.h> 44 #include <stdlib.h> 45 #include <stdio.h> 46 #include <string.h> 47 #include <ctype.h> 48 #include <pwd.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <paths.h> 52 #include <signal.h> 53 #include <limits.h> 54 55 #include "util.h" 56 57 #define NUM_OPTIONS 2 /* Number of hardcoded defaults */ 58 59 static void pw_cont(int sig); 60 61 static const char options[NUM_OPTIONS][2][80] = { 62 {"localcipher", "blowfish,4"}, 63 {"ypcipher", "old"} 64 }; 65 66 static char pw_defdir[] = "/etc"; 67 static char *pw_dir = pw_defdir; 68 static char *pw_lck; 69 70 static void trim_whitespace(char *); 71 static int read_line(FILE *, char *, int); 72 static const char *pw_default(const char *); 73 74 /* Removes head and/or tail spaces. */ 75 static void 76 trim_whitespace(char *line) 77 { 78 char *p; 79 80 /* Remove leading spaces */ 81 p = line; 82 while (isspace(*p)) 83 p++; 84 (void) memmove(line, p, strlen(p) + 1); 85 86 /* Remove trailing spaces */ 87 p = line + strlen(line) - 1; 88 while (isspace(*p)) 89 p--; 90 *(p + 1) = '\0'; 91 } 92 93 94 /* Get one line, remove spaces from front and tail */ 95 static int 96 read_line(FILE *fp, char *line, int max) 97 { 98 char *p; 99 100 /* Read one line of config */ 101 if (fgets(line, max, fp) == 0) 102 return 0; 103 if (!(p = strchr(line, '\n'))) { 104 warnx("line too long"); 105 return 0; 106 } 107 *p = '\0'; 108 109 /* Remove comments */ 110 if ((p = strchr(line, '#'))) 111 *p = '\0'; 112 113 trim_whitespace(line); 114 return 1; 115 } 116 117 118 static const char * 119 pw_default(const char *option) 120 { 121 int i; 122 123 for (i = 0; i < NUM_OPTIONS; i++) 124 if (!strcmp(options[i][0], option)) 125 return options[i][1]; 126 return NULL; 127 } 128 129 char * 130 pw_file(const char *nm) 131 { 132 const char *p = strrchr(nm, '/'); 133 char *new_nm; 134 135 if (p) 136 p++; 137 else 138 p = nm; 139 140 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1) 141 return NULL; 142 return new_nm; 143 } 144 145 146 /* 147 * Retrieve password information from the /etc/passwd.conf file, 148 * at the moment this is only for choosing the cipher to use. 149 * It could easily be used for other authentication methods as 150 * well. 151 */ 152 void 153 pw_getconf(char *data, size_t max, const char *key, const char *option) 154 { 155 FILE *fp; 156 char line[LINE_MAX]; 157 static char result[LINE_MAX]; 158 char *p; 159 int got = 0; 160 int found = 0; 161 162 result[0] = '\0'; 163 164 p = pw_file(_PATH_PASSWDCONF); 165 if (!p || (fp = fopen(p, "r")) == NULL) { 166 if (p) 167 free(p); 168 if ((p = (char *)pw_default(option))) { 169 strncpy(data, p, max - 1); 170 data[max - 1] = '\0'; 171 } else 172 data[0] = '\0'; 173 return; 174 } 175 free(p); 176 177 while (!found && (got || read_line(fp, line, LINE_MAX))) { 178 got = 0; 179 if (strncmp(key, line, strlen(key)) || 180 line[strlen(key)] != ':') 181 continue; 182 183 /* Now we found our specified key */ 184 while (read_line(fp, line, LINE_MAX)) { 185 char *p2; 186 187 /* Leaving key field */ 188 if (strchr(line, ':')) { 189 got = 1; 190 break; 191 } 192 p2 = line; 193 if (!(p = strsep(&p2, "=")) || p2 == NULL) 194 continue; 195 trim_whitespace(p); 196 if (!strncmp(p, option, strlen(option))) { 197 trim_whitespace(p2); 198 strlcpy(result, p2, sizeof result); 199 found = 1; 200 break; 201 } 202 } 203 } 204 fclose(fp); 205 206 /* 207 * If we got no result and were looking for a default 208 * value, try hard coded defaults. 209 */ 210 211 if (!strlen(result) && !strcmp(key,"default") && 212 (p = (char *)pw_default(option))) 213 strncpy(data, p, max - 1); 214 else 215 strncpy(data, result, max - 1); 216 data[max - 1] = '\0'; 217 } 218 219 220 void 221 pw_setdir(const char *dir) 222 { 223 char *p; 224 225 if (strcmp (dir, pw_dir) == 0) 226 return; 227 if (pw_dir != pw_defdir) 228 free(pw_dir); 229 pw_dir = strdup(dir); 230 if (pw_lck) { 231 p = pw_file(pw_lck); 232 free(pw_lck); 233 pw_lck = p; 234 } 235 } 236 237 238 int 239 pw_lock(int retries) 240 { 241 int i, fd; 242 mode_t old_mode; 243 int save_errno; 244 245 if (!pw_lck) 246 return (-1); 247 /* Acquire the lock file. */ 248 old_mode = umask(0); 249 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600); 250 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 251 sleep(1); 252 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600); 253 } 254 save_errno = errno; 255 if (fd != -1 && fcntl(fd, F_SETFD, 1) == -1) { 256 close(fd); 257 fd = -1; 258 } 259 (void) umask(old_mode); 260 errno = save_errno; 261 return (fd); 262 } 263 264 int 265 pw_mkdb(char *username, int flags) 266 { 267 int pstat, ac; 268 pid_t pid; 269 char *av[8]; 270 struct stat sb; 271 272 if (pw_lck == NULL) 273 return(-1); 274 275 /* A zero length passwd file is never ok */ 276 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) { 277 warnx("%s is zero length", pw_lck); 278 return (-1); 279 } 280 281 ac = 0; 282 av[ac++] = "pwd_mkdb"; 283 av[ac++] = "-d"; 284 av[ac++] = pw_dir; 285 if (flags & _PASSWORD_SECUREONLY) 286 av[ac++] = "-s"; 287 else if (!(flags & _PASSWORD_OMITV7)) 288 av[ac++] = "-p"; 289 if (username) { 290 av[ac++] = "-u"; 291 av[ac++] = username; 292 } 293 av[ac++] = pw_lck; 294 av[ac] = NULL; 295 296 pid = vfork(); 297 if (pid == -1) 298 return (-1); 299 if (pid == 0) { 300 if (pw_lck) 301 execv(_PATH_PWD_MKDB, av); 302 _exit(1); 303 } 304 pid = waitpid(pid, &pstat, 0); 305 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 306 return (-1); 307 return (0); 308 } 309 310 int 311 pw_abort(void) 312 { 313 return (pw_lck ? unlink(pw_lck) : -1); 314 } 315 316 /* Everything below this point is intended for the convenience of programs 317 * which allow a user to interactively edit the passwd file. Errors in the 318 * routines below will cause the process to abort. */ 319 320 static pid_t editpid = -1; 321 322 static void 323 pw_cont(int signo) 324 { 325 int save_errno = errno; 326 327 if (editpid != -1) 328 kill(editpid, signo); 329 errno = save_errno; 330 } 331 332 void 333 pw_init(void) 334 { 335 struct rlimit rlim; 336 337 /* Unlimited resource limits. */ 338 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 339 (void)setrlimit(RLIMIT_CPU, &rlim); 340 (void)setrlimit(RLIMIT_FSIZE, &rlim); 341 (void)setrlimit(RLIMIT_STACK, &rlim); 342 (void)setrlimit(RLIMIT_DATA, &rlim); 343 (void)setrlimit(RLIMIT_RSS, &rlim); 344 345 /* Don't drop core (not really necessary, but GP's). */ 346 rlim.rlim_cur = rlim.rlim_max = 0; 347 (void)setrlimit(RLIMIT_CORE, &rlim); 348 349 /* Turn off signals. */ 350 (void)signal(SIGALRM, SIG_IGN); 351 (void)signal(SIGHUP, SIG_IGN); 352 (void)signal(SIGINT, SIG_IGN); 353 (void)signal(SIGPIPE, SIG_IGN); 354 (void)signal(SIGQUIT, SIG_IGN); 355 (void)signal(SIGTERM, SIG_IGN); 356 (void)signal(SIGCONT, pw_cont); 357 358 if (!pw_lck) 359 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK); 360 } 361 362 void 363 pw_edit(int notsetuid, const char *filename) 364 { 365 int pstat; 366 char *p; 367 char * volatile editor; 368 char *argp[] = {"sh", "-c", NULL, NULL}; 369 370 if (!filename) { 371 filename = pw_lck; 372 if (!filename) 373 return; 374 } 375 376 if ((editor = getenv("EDITOR")) == NULL) 377 editor = _PATH_VI; 378 379 if (asprintf(&p, "%s %s", editor, filename) == -1) 380 return; 381 argp[2] = p; 382 383 switch (editpid = vfork()) { 384 case -1: /* error */ 385 free(p); 386 return; 387 case 0: /* child */ 388 if (notsetuid) { 389 setgid(getgid()); 390 setuid(getuid()); 391 } 392 execv(_PATH_BSHELL, argp); 393 _exit(127); 394 } 395 396 free(p); 397 for (;;) { 398 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 399 if (editpid == -1) 400 pw_error(editor, 1, 1); 401 else if (WIFSTOPPED(pstat)) 402 raise(WSTOPSIG(pstat)); 403 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 404 break; 405 else 406 pw_error(editor, 1, 1); 407 } 408 editpid = -1; 409 } 410 411 void 412 pw_prompt(void) 413 { 414 int first, c; 415 416 (void)printf("re-edit the password file? [y]: "); 417 (void)fflush(stdout); 418 first = c = getchar(); 419 while (c != '\n' && c != EOF) 420 c = getchar(); 421 if (first == 'n') 422 pw_error(NULL, 0, 0); 423 } 424 425 void 426 pw_copy(int ffd, int tfd, struct passwd *pw) 427 { 428 FILE *from, *to; 429 int done; 430 char *p, buf[8192]; 431 char *master = pw_file(_PATH_MASTERPASSWD); 432 433 if (!master) 434 pw_error(NULL, 0, 1); 435 if (!(from = fdopen(ffd, "r"))) 436 pw_error(master, 1, 1); 437 if (!(to = fdopen(tfd, "w"))) 438 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1); 439 440 for (done = 0; fgets(buf, sizeof(buf), from);) { 441 if (!strchr(buf, '\n')) { 442 warnx("%s: line too long", master); 443 pw_error(NULL, 0, 1); 444 } 445 if (done) { 446 (void)fprintf(to, "%s", buf); 447 if (ferror(to)) 448 goto err; 449 continue; 450 } 451 if (!(p = strchr(buf, ':'))) { 452 warnx("%s: corrupted entry", master); 453 pw_error(NULL, 0, 1); 454 } 455 *p = '\0'; 456 if (strcmp(buf, pw->pw_name)) { 457 *p = ':'; 458 (void)fprintf(to, "%s", buf); 459 if (ferror(to)) 460 goto err; 461 continue; 462 } 463 (void)fprintf(to, "%s:%s:%u:%u:%s:%d:%d:%s:%s:%s\n", 464 pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid, 465 (u_int)pw->pw_gid, pw->pw_class, pw->pw_change, 466 pw->pw_expire, pw->pw_gecos, pw->pw_dir, 467 pw->pw_shell); 468 done = 1; 469 if (ferror(to)) 470 goto err; 471 } 472 if (!done) 473 (void)fprintf(to, "%s:%s:%d:%d:%s:%d:%d:%s:%s:%s\n", 474 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 475 pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos, 476 pw->pw_dir, pw->pw_shell); 477 478 if (ferror(to)) 479 err: 480 pw_error(NULL, 0, 1); 481 free(master); 482 (void)fclose(to); 483 } 484 485 int 486 pw_scan(char *bp, struct passwd *pw, int *flags) 487 { 488 u_long id; 489 int root; 490 char *p, *sh, *p2; 491 492 if (flags != (int *)NULL) 493 *flags = 0; 494 495 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */ 496 goto fmt; 497 pw->pw_name = p; 498 root = !strcmp(pw->pw_name, "root"); 499 500 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ 501 goto fmt; 502 503 if (!(p = strsep(&bp, ":"))) /* uid */ 504 goto fmt; 505 id = strtoul(p, &p2, 10); 506 if (root && id) { 507 warnx("root uid should be 0"); 508 return (0); 509 } 510 if (*p2 != '\0') { 511 warnx("illegal uid field"); 512 return (0); 513 } 514 if (id > UID_MAX) { 515 /* errno is set to ERANGE by strtoul(3) */ 516 warnx("uid greater than %u", UID_MAX-1); 517 return (0); 518 } 519 pw->pw_uid = (uid_t)id; 520 if ((*p == '\0') && (flags != (int *)NULL)) 521 *flags |= _PASSWORD_NOUID; 522 523 if (!(p = strsep(&bp, ":"))) /* gid */ 524 goto fmt; 525 id = strtoul(p, &p2, 10); 526 if (*p2 != '\0') { 527 warnx("illegal gid field"); 528 return (0); 529 } 530 if (id > UID_MAX) { 531 /* errno is set to ERANGE by strtoul(3) */ 532 warnx("gid greater than %u", UID_MAX-1); 533 return (0); 534 } 535 pw->pw_gid = (gid_t)id; 536 if ((*p == '\0') && (flags != (int *)NULL)) 537 *flags |= _PASSWORD_NOGID; 538 539 pw->pw_class = strsep(&bp, ":"); /* class */ 540 if (!(p = strsep(&bp, ":"))) /* change */ 541 goto fmt; 542 pw->pw_change = atol(p); 543 if ((*p == '\0') && (flags != (int *)NULL)) 544 *flags |= _PASSWORD_NOCHG; 545 if (!(p = strsep(&bp, ":"))) /* expire */ 546 goto fmt; 547 pw->pw_expire = atol(p); 548 if ((*p == '\0') && (flags != (int *)NULL)) 549 *flags |= _PASSWORD_NOEXP; 550 pw->pw_gecos = strsep(&bp, ":"); /* gecos */ 551 pw->pw_dir = strsep(&bp, ":"); /* directory */ 552 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ 553 goto fmt; 554 555 p = pw->pw_shell; 556 if (root && *p) { /* empty == /bin/sh */ 557 for (setusershell();;) { 558 if (!(sh = getusershell())) { 559 warnx("warning, unknown root shell"); 560 break; 561 } 562 if (!strcmp(p, sh)) 563 break; 564 } 565 endusershell(); 566 } 567 568 if ((p = strsep(&bp, ":"))) { /* too many */ 569 fmt: warnx("corrupted entry"); 570 return (0); 571 } 572 573 return (1); 574 } 575 576 __dead void 577 pw_error(const char *name, int err, int eval) 578 { 579 char *master = pw_file(_PATH_MASTERPASSWD); 580 581 if (err) { 582 if (name) 583 warn("%s", name); 584 else 585 warn(NULL); 586 } 587 if (master) { 588 warnx("%s: unchanged", master); 589 free(master); 590 } 591 592 pw_abort(); 593 exit(eval); 594 } 595