1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)pw_util.c 8.3 (Berkeley) 4/2/94 37 * $FreeBSD: head/lib/libutil/pw_util.c 244744 2012-12-27 20:24:44Z bapt $ 38 */ 39 40 /* 41 * This file is used by all the "password" programs; vipw(8), chpass(1), 42 * and passwd(1). 43 */ 44 45 #include <sys/param.h> 46 #include <sys/errno.h> 47 #include <sys/time.h> 48 #include <sys/resource.h> 49 #include <sys/stat.h> 50 #include <sys/wait.h> 51 52 #include <ctype.h> 53 #include <err.h> 54 #include <fcntl.h> 55 #include <inttypes.h> 56 #include <libgen.h> 57 #include <paths.h> 58 #include <pwd.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #include <libutil.h> 65 66 static pid_t editpid = -1; 67 static int lockfd = -1; 68 static char masterpasswd[PATH_MAX]; 69 static char passwd_dir[PATH_MAX]; 70 static char tempname[PATH_MAX]; 71 static int initialized; 72 73 #if 0 74 void 75 pw_cont(int sig) 76 { 77 78 if (editpid != -1) 79 kill(editpid, sig); 80 } 81 #endif 82 83 /* 84 * Initialize statics and set limits, signals & umask to try to avoid 85 * interruptions, crashes etc. that might expose passord data. 86 */ 87 int 88 pw_init(const char *dir, const char *master) 89 { 90 #if 0 91 struct rlimit rlim; 92 #endif 93 94 if (dir == NULL) { 95 strcpy(passwd_dir, _PATH_ETC); 96 } else { 97 if (strlen(dir) >= sizeof(passwd_dir)) { 98 errno = ENAMETOOLONG; 99 return (-1); 100 } 101 strcpy(passwd_dir, dir); 102 } 103 104 if (master == NULL) { 105 if (dir == NULL) { 106 strcpy(masterpasswd, _PATH_MASTERPASSWD); 107 } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", 108 passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { 109 errno = ENAMETOOLONG; 110 return (-1); 111 } 112 } else { 113 if (strlen(master) >= sizeof(masterpasswd)) { 114 errno = ENAMETOOLONG; 115 return (-1); 116 } 117 strcpy(masterpasswd, master); 118 } 119 120 /* 121 * The code that follows is extremely disruptive to the calling 122 * process, and is therefore disabled until someone can conceive 123 * of a realistic scenario where it would fend off a compromise. 124 * Race conditions concerning the temporary files can be guarded 125 * against in other ways than masking signals (by checking stat(2) 126 * results after creation). 127 */ 128 #if 0 129 /* Unlimited resource limits. */ 130 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 131 (void)setrlimit(RLIMIT_CPU, &rlim); 132 (void)setrlimit(RLIMIT_FSIZE, &rlim); 133 (void)setrlimit(RLIMIT_STACK, &rlim); 134 (void)setrlimit(RLIMIT_DATA, &rlim); 135 (void)setrlimit(RLIMIT_RSS, &rlim); 136 137 /* Don't drop core (not really necessary, but GP's). */ 138 rlim.rlim_cur = rlim.rlim_max = 0; 139 (void)setrlimit(RLIMIT_CORE, &rlim); 140 141 /* Turn off signals. */ 142 (void)signal(SIGALRM, SIG_IGN); 143 (void)signal(SIGHUP, SIG_IGN); 144 (void)signal(SIGINT, SIG_IGN); 145 (void)signal(SIGPIPE, SIG_IGN); 146 (void)signal(SIGQUIT, SIG_IGN); 147 (void)signal(SIGTERM, SIG_IGN); 148 (void)signal(SIGCONT, pw_cont); 149 150 /* Create with exact permissions. */ 151 (void)umask(0); 152 #endif 153 initialized = 1; 154 return (0); 155 } 156 157 /* 158 * Lock the master password file. 159 */ 160 int 161 pw_lock(void) 162 { 163 164 if (*masterpasswd == '\0') 165 return (-1); 166 167 /* 168 * If the master password file doesn't exist, the system is hosed. 169 * Might as well try to build one. Set the close-on-exec bit so 170 * that users can't get at the encrypted passwords while editing. 171 * Open should allow flock'ing the file; see 4.4BSD. XXX 172 */ 173 for (;;) { 174 struct stat st; 175 176 lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 177 if (lockfd == -1) { 178 if (errno == EWOULDBLOCK) { 179 errx(1, "the password db file is busy"); 180 } else { 181 err(1, "could not lock the passwd file: "); 182 } 183 } 184 185 /* 186 * If the password file was replaced while we were trying to 187 * get the lock, our hardlink count will be 0 and we have to 188 * close and retry. 189 */ 190 if (fstat(lockfd, &st) == -1) 191 err(1, "fstat() failed: "); 192 if (st.st_nlink != 0) 193 break; 194 close(lockfd); 195 lockfd = -1; 196 } 197 return (lockfd); 198 } 199 200 /* 201 * Create and open a presumably safe temp file for editing the password 202 * data, and copy the master password file into it. 203 */ 204 int 205 pw_tmp(int mfd) 206 { 207 char buf[8192]; 208 ssize_t nr; 209 const char *p; 210 int tfd; 211 212 if (*masterpasswd == '\0') 213 return (-1); 214 if ((p = strrchr(masterpasswd, '/'))) 215 ++p; 216 else 217 p = masterpasswd; 218 if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 219 (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 220 errno = ENAMETOOLONG; 221 return (-1); 222 } 223 if ((tfd = mkstemp(tempname)) == -1) 224 return (-1); 225 if (mfd != -1) { 226 while ((nr = read(mfd, buf, sizeof(buf))) > 0) 227 if (write(tfd, buf, (size_t)nr) != nr) 228 break; 229 if (nr != 0) { 230 unlink(tempname); 231 *tempname = '\0'; 232 close(tfd); 233 return (-1); 234 } 235 } 236 return (tfd); 237 } 238 239 /* 240 * Regenerate the password database. 241 */ 242 int 243 pw_mkdb(const char *user) 244 { 245 int pstat; 246 pid_t pid; 247 248 (void)fflush(stderr); 249 switch ((pid = fork())) { 250 case -1: 251 return (-1); 252 case 0: 253 /* child */ 254 if (user == NULL) 255 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 256 "-d", passwd_dir, tempname, NULL); 257 else 258 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 259 "-d", passwd_dir, "-u", user, tempname, 260 NULL); 261 _exit(1); 262 /* NOTREACHED */ 263 default: 264 /* parent */ 265 break; 266 } 267 if (waitpid(pid, &pstat, 0) == -1) 268 return (-1); 269 if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 270 return (0); 271 errno = 0; 272 return (-1); 273 } 274 275 /* 276 * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 277 * if it was not. 278 */ 279 int 280 pw_edit(int notsetuid) 281 { 282 struct sigaction sa, sa_int, sa_quit; 283 sigset_t oldsigset, nsigset; 284 struct stat st1, st2; 285 const char *editor; 286 int pstat; 287 288 if ((editor = getenv("EDITOR")) == NULL) 289 editor = _PATH_VI; 290 if (stat(tempname, &st1) == -1) 291 return (-1); 292 sa.sa_handler = SIG_IGN; 293 sigemptyset(&sa.sa_mask); 294 sa.sa_flags = 0; 295 sigaction(SIGINT, &sa, &sa_int); 296 sigaction(SIGQUIT, &sa, &sa_quit); 297 sigemptyset(&nsigset); 298 sigaddset(&nsigset, SIGCHLD); 299 sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); 300 switch ((editpid = fork())) { 301 case -1: 302 return (-1); 303 case 0: 304 sigaction(SIGINT, &sa_int, NULL); 305 sigaction(SIGQUIT, &sa_quit, NULL); 306 sigprocmask(SIG_SETMASK, &oldsigset, NULL); 307 if (notsetuid) { 308 (void)setgid(getgid()); 309 (void)setuid(getuid()); 310 } 311 errno = 0; 312 execlp(editor, basename(strdup(editor)), tempname, NULL); 313 _exit(errno); 314 default: 315 /* parent */ 316 break; 317 } 318 for (;;) { 319 if (waitpid(editpid, &pstat, WUNTRACED) == -1) { 320 if (errno == EINTR) 321 continue; 322 unlink(tempname); 323 editpid = -1; 324 break; 325 } else if (WIFSTOPPED(pstat)) { 326 raise(WSTOPSIG(pstat)); 327 } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 328 editpid = -1; 329 break; 330 } else { 331 unlink(tempname); 332 editpid = -1; 333 break; 334 } 335 } 336 sigaction(SIGINT, &sa_int, NULL); 337 sigaction(SIGQUIT, &sa_quit, NULL); 338 sigprocmask(SIG_SETMASK, &oldsigset, NULL); 339 if (stat(tempname, &st2) == -1) 340 return (-1); 341 return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || 342 st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); 343 } 344 345 /* 346 * Clean up. Preserve errno for the caller's convenience. 347 */ 348 void 349 pw_fini(void) 350 { 351 int serrno, status; 352 353 if (!initialized) 354 return; 355 initialized = 0; 356 serrno = errno; 357 if (editpid != -1) { 358 kill(editpid, SIGTERM); 359 kill(editpid, SIGCONT); 360 waitpid(editpid, &status, 0); 361 editpid = -1; 362 } 363 if (*tempname != '\0') { 364 unlink(tempname); 365 *tempname = '\0'; 366 } 367 if (lockfd != -1) 368 close(lockfd); 369 errno = serrno; 370 } 371 372 /* 373 * Compares two struct pwds. 374 */ 375 int 376 pw_equal(const struct passwd *pw1, const struct passwd *pw2) 377 { 378 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 379 pw1->pw_uid == pw2->pw_uid && 380 pw1->pw_gid == pw2->pw_gid && 381 strcmp(pw1->pw_class, pw2->pw_class) == 0 && 382 pw1->pw_change == pw2->pw_change && 383 pw1->pw_expire == pw2->pw_expire && 384 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 385 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 386 strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 387 } 388 389 /* 390 * Make a passwd line out of a struct passwd. 391 */ 392 char * 393 pw_make(const struct passwd *pw) 394 { 395 char *line; 396 397 asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 398 pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 399 pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 400 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 401 return (line); 402 } 403 404 /* 405 * Make a passwd line (in v7 format) out of a struct passwd 406 */ 407 char * 408 pw_make_v7(const struct passwd *pw) 409 { 410 char *line; 411 412 asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, 413 (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 414 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 415 return (line); 416 } 417 418 /* 419 * Copy password file from one descriptor to another, replacing, deleting 420 * or adding a single record on the way. 421 */ 422 int 423 pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 424 { 425 char buf[8192], *end, *line, *p, *q, *r, t; 426 struct passwd *fpw; 427 const struct passwd *spw; 428 size_t len; 429 int eof, readlen; 430 431 if (old_pw == NULL && pw == NULL) 432 return (-1); 433 434 spw = old_pw; 435 /* deleting a user */ 436 if (pw == NULL) { 437 line = NULL; 438 } else { 439 if ((line = pw_make(pw)) == NULL) 440 return (-1); 441 } 442 443 /* adding a user */ 444 if (spw == NULL) 445 spw = pw; 446 447 eof = 0; 448 len = 0; 449 p = q = end = buf; 450 for (;;) { 451 /* find the end of the current line */ 452 for (p = q; q < end && *q != '\0'; ++q) 453 if (*q == '\n') 454 break; 455 456 /* if we don't have a complete line, fill up the buffer */ 457 if (q >= end) { 458 if (eof) 459 break; 460 if ((size_t)(q - p) >= sizeof(buf)) { 461 warnx("passwd line too long"); 462 errno = EINVAL; /* hack */ 463 goto err; 464 } 465 if (p < end) { 466 q = memmove(buf, p, end - p); 467 end -= p - buf; 468 } else { 469 p = q = end = buf; 470 } 471 readlen = read(ffd, end, sizeof(buf) - (end - buf)); 472 if (readlen == -1) 473 goto err; 474 else 475 len = (size_t)readlen; 476 if (len == 0 && p == buf) 477 break; 478 end += len; 479 len = end - buf; 480 if (len < (ssize_t)sizeof(buf)) { 481 eof = 1; 482 if (len > 0 && buf[len - 1] != '\n') 483 ++len, *end++ = '\n'; 484 } 485 continue; 486 } 487 488 /* is it a blank line or a comment? */ 489 for (r = p; r < q && isspace(*r); ++r) 490 /* nothing */ ; 491 if (r == q || *r == '#') { 492 /* yep */ 493 if (write(tfd, p, q - p + 1) != q - p + 1) 494 goto err; 495 ++q; 496 continue; 497 } 498 499 /* is it the one we're looking for? */ 500 501 t = *q; 502 *q = '\0'; 503 504 fpw = pw_scan(r, PWSCAN_MASTER); 505 506 /* 507 * fpw is either the struct passwd for the current line, 508 * or NULL if the line is malformed. 509 */ 510 511 *q = t; 512 if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { 513 /* nope */ 514 if (fpw != NULL) 515 free(fpw); 516 if (write(tfd, p, q - p + 1) != q - p + 1) 517 goto err; 518 ++q; 519 continue; 520 } 521 if (old_pw && !pw_equal(fpw, old_pw)) { 522 warnx("entry inconsistent"); 523 free(fpw); 524 errno = EINVAL; /* hack */ 525 goto err; 526 } 527 free(fpw); 528 529 /* it is, replace or remove it */ 530 if (line != NULL) { 531 len = strlen(line); 532 if (write(tfd, line, len) != (int)len) 533 goto err; 534 } else { 535 /* when removed, avoid the \n */ 536 q++; 537 } 538 /* we're done, just copy the rest over */ 539 for (;;) { 540 if (write(tfd, q, end - q) != end - q) 541 goto err; 542 q = buf; 543 readlen = read(ffd, buf, sizeof(buf)); 544 if (readlen == 0) 545 break; 546 else 547 len = (size_t)readlen; 548 if (readlen == -1) 549 goto err; 550 end = buf + len; 551 } 552 goto done; 553 } 554 555 /* if we got here, we didn't find the old entry */ 556 if (line == NULL) { 557 errno = ENOENT; 558 goto err; 559 } 560 len = strlen(line); 561 if ((size_t)write(tfd, line, len) != len || 562 write(tfd, "\n", 1) != 1) 563 goto err; 564 done: 565 if (line != NULL) 566 free(line); 567 return (0); 568 err: 569 if (line != NULL) 570 free(line); 571 return (-1); 572 } 573 574 /* 575 * Return the current value of tempname. 576 */ 577 const char * 578 pw_tempname(void) 579 { 580 581 return (tempname); 582 } 583 584 /* 585 * Duplicate a struct passwd. 586 */ 587 struct passwd * 588 pw_dup(const struct passwd *pw) 589 { 590 char *dst; 591 struct passwd *npw; 592 ssize_t len; 593 594 len = sizeof(*npw); 595 if (pw->pw_name != NULL) 596 len += strlen(pw->pw_name) + 1; 597 if (pw->pw_passwd != NULL) 598 len += strlen(pw->pw_passwd) + 1; 599 if (pw->pw_class != NULL) 600 len += strlen(pw->pw_class) + 1; 601 if (pw->pw_gecos != NULL) 602 len += strlen(pw->pw_gecos) + 1; 603 if (pw->pw_dir != NULL) 604 len += strlen(pw->pw_dir) + 1; 605 if (pw->pw_shell != NULL) 606 len += strlen(pw->pw_shell) + 1; 607 if ((npw = malloc((size_t)len)) == NULL) 608 return (NULL); 609 memcpy(npw, pw, sizeof(*npw)); 610 dst = (char *)npw + sizeof(*npw); 611 if (pw->pw_name != NULL) { 612 npw->pw_name = dst; 613 dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 614 } 615 if (pw->pw_passwd != NULL) { 616 npw->pw_passwd = dst; 617 dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 618 } 619 if (pw->pw_class != NULL) { 620 npw->pw_class = dst; 621 dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 622 } 623 if (pw->pw_gecos != NULL) { 624 npw->pw_gecos = dst; 625 dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 626 } 627 if (pw->pw_dir != NULL) { 628 npw->pw_dir = dst; 629 dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 630 } 631 if (pw->pw_shell != NULL) { 632 npw->pw_shell = dst; 633 dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 634 } 635 return (npw); 636 } 637 638 #include "pw_scan.h" 639 640 /* 641 * Wrapper around an internal libc function 642 */ 643 struct passwd * 644 pw_scan(const char *line, int flags) 645 { 646 struct passwd pw, *ret; 647 char *bp; 648 649 if ((bp = strdup(line)) == NULL) 650 return (NULL); 651 if (!__pw_scan(bp, &pw, flags)) { 652 free(bp); 653 return (NULL); 654 } 655 ret = pw_dup(&pw); 656 free(bp); 657 return (ret); 658 } 659