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