1 /* $NetBSD: pwd_mkdb.c,v 1.51 2010/01/10 19:04:35 snj Exp $ */ 2 3 /* 4 * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 1991, 1993, 1994 31 * The Regents of the University of California. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. Neither the name of the University nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 /* 59 * Portions Copyright(C) 1994, Jason Downs. All rights reserved. 60 * 61 * Redistribution and use in source and binary forms, with or without 62 * modification, are permitted provided that the following conditions 63 * are met: 64 * 1. Redistributions of source code must retain the above copyright 65 * notice, this list of conditions and the following disclaimer. 66 * 2. Redistributions in binary form must reproduce the above copyright 67 * notice, this list of conditions and the following disclaimer in the 68 * documentation and/or other materials provided with the distribution. 69 * 70 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 71 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 72 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 73 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 74 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 75 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 76 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 77 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 78 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 79 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 80 * SUCH DAMAGE. 81 */ 82 83 #if HAVE_NBTOOL_CONFIG_H 84 #include "nbtool_config.h" 85 #endif 86 87 #include <sys/cdefs.h> 88 #if !defined(lint) 89 __COPYRIGHT("@(#) Copyright (c) 2000, 2009\ 90 The NetBSD Foundation, Inc. All rights reserved.\ 91 Copyright (c) 1991, 1993, 1994\ 92 The Regents of the University of California. All rights reserved."); 93 __RCSID("$NetBSD: pwd_mkdb.c,v 1.51 2010/01/10 19:04:35 snj Exp $"); 94 #endif /* not lint */ 95 96 #if HAVE_NBTOOL_CONFIG_H 97 #include "compat_pwd.h" 98 #else 99 #include <pwd.h> 100 #endif 101 102 #include <sys/param.h> 103 #include <sys/stat.h> 104 #include <sys/types.h> 105 106 #ifndef HAVE_NBTOOL_CONFIG_H 107 #include <machine/bswap.h> 108 #endif 109 110 #include <db.h> 111 #include <err.h> 112 #include <errno.h> 113 #include <fcntl.h> 114 #include <limits.h> 115 #include <signal.h> 116 #include <stdio.h> 117 #include <stdlib.h> 118 #include <string.h> 119 #include <unistd.h> 120 #include <util.h> 121 122 #define MAX_CACHESIZE 8*1024*1024 123 #define MIN_CACHESIZE 2*1024*1024 124 125 #define PERM_INSECURE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 126 #define PERM_SECURE (S_IRUSR | S_IWUSR) 127 128 #if HAVE_NBTOOL_CONFIG_H 129 static const char __yp_token[] = "__YP!"; 130 #else 131 /* Pull this out of the C library. */ 132 extern const char __yp_token[]; 133 #endif 134 135 static HASHINFO openinfo = { 136 4096, /* bsize */ 137 32, /* ffactor */ 138 256, /* nelem */ 139 0, /* cachesize */ 140 NULL, /* hash() */ 141 0 /* lorder */ 142 }; 143 144 #define FILE_INSECURE 0x01 145 #define FILE_SECURE 0x02 146 #define FILE_ORIG 0x04 147 148 149 struct pwddb { 150 DB *db; 151 char dbname[MAX(MAXPATHLEN, LINE_MAX * 2)]; 152 const char *fname; 153 uint32_t rversion; 154 uint32_t wversion; 155 }; 156 157 static char *pname; /* password file name */ 158 static char prefix[MAXPATHLEN]; 159 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)]; 160 static int lorder = BYTE_ORDER; 161 static int clean; 162 static int verbose; 163 static int warning; 164 static struct pwddb sdb, idb; 165 166 167 void bailout(void) __attribute__((__noreturn__)); 168 void cp(const char *, const char *, mode_t); 169 void deldbent(struct pwddb *, int, void *); 170 void error(const char *); 171 int getdbent(struct pwddb *, int, void *, struct passwd **); 172 void inconsistency(void); 173 void install(const char *, const char *); 174 int main(int, char **); 175 void putdbents(struct pwddb *, struct passwd *, const char *, int, int, 176 u_int, u_int); 177 void putyptoken(struct pwddb *); 178 void rm(const char *); 179 int scan(FILE *, struct passwd *, int *, int *); 180 void usage(void) __attribute__((__noreturn__)); 181 void wr_error(const char *); 182 uint32_t getversion(const char *); 183 void setversion(struct pwddb *); 184 185 #ifndef __lint__ 186 #define SWAP(sw) \ 187 ((sizeof(sw) == 2 ? (typeof(sw))bswap16((uint16_t)sw) : \ 188 (sizeof(sw) == 4 ? (typeof(sw))bswap32((uint32_t)sw) : \ 189 (sizeof(sw) == 8 ? (typeof(sw))bswap64((uint64_t)sw) : (abort(), 0))))) 190 #else 191 #define SWAP(sw) sw 192 #endif 193 194 static void 195 closedb(struct pwddb *db) 196 { 197 if ((*db->db->close)(db->db) < 0) 198 wr_error(db->dbname); 199 } 200 201 static void 202 opendb(struct pwddb *db, const char *dbname, const char *username, 203 uint32_t req_version, int flags, mode_t perm) 204 { 205 char buf[MAXPATHLEN]; 206 207 (void)snprintf(db->dbname, sizeof(db->dbname), "%s%s.tmp", prefix, 208 dbname); 209 210 if (username != NULL) { 211 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, dbname); 212 cp(buf, db->dbname, perm); 213 } 214 215 db->db = dbopen(db->dbname, flags, perm, DB_HASH, &openinfo); 216 if (db->db == NULL) 217 error(db->dbname); 218 219 db->fname = dbname; 220 db->rversion = getversion(dbname); 221 if (req_version == ~0U) 222 db->wversion = db->rversion; 223 else 224 db->wversion = req_version; 225 226 if (warning && db->rversion == 0 && db->wversion == 0) { 227 warnx("Database %s is a version %u database.", 228 db->fname, db->rversion); 229 warnx("Use %s -V 1 to upgrade once you've recompiled " 230 "all your binaries.", getprogname()); 231 } 232 if (db->wversion != db->rversion) { 233 if (username != NULL) { 234 (void)fprintf(stderr, "%s: you cannot change a single " 235 "record from version %u to version %u\n", 236 getprogname(), db->rversion, db->wversion); 237 bailout(); 238 } else if (verbose) { 239 (void)fprintf(stderr, "%s: changing %s from version " 240 "%u to version %u\n", 241 getprogname(), db->fname, 242 db->rversion, db->wversion); 243 } 244 } else { 245 if (verbose) 246 (void)fprintf(stderr, "%s: %s version %u " 247 "requested %u\n", getprogname(), db->fname, 248 db->rversion, db->wversion); 249 } 250 251 setversion(db); 252 } 253 254 int 255 main(int argc, char *argv[]) 256 { 257 int ch, makeold, tfd, lineno, found, rv, hasyp, secureonly; 258 struct passwd pwd, *tpwd; 259 char *username; 260 FILE *fp, *oldfp; 261 sigset_t set; 262 u_int dbflg, uid_dbflg; 263 int newuser, olduid, flags; 264 struct stat st; 265 u_int cachesize; 266 uint32_t req_version; 267 268 prefix[0] = '\0'; 269 makeold = 0; 270 oldfp = NULL; 271 username = NULL; 272 hasyp = 0; 273 secureonly = 0; 274 found = 0; 275 newuser = 0; 276 cachesize = 0; 277 verbose = 0; 278 warning = 0; 279 req_version = ~0U; 280 281 while ((ch = getopt(argc, argv, "BLc:d:psu:V:vw")) != -1) 282 switch (ch) { 283 case 'B': /* big-endian output */ 284 lorder = BIG_ENDIAN; 285 break; 286 case 'L': /* little-endian output */ 287 lorder = LITTLE_ENDIAN; 288 break; 289 case 'c': 290 cachesize = atoi(optarg) * 1024 * 1024; 291 break; 292 case 'd': /* set prefix */ 293 (void)strlcpy(prefix, optarg, sizeof(prefix)); 294 break; 295 case 'p': /* create V7 "file.orig" */ 296 makeold = 1; 297 break; 298 case 's': /* modify secure db only */ 299 secureonly = 1; 300 break; 301 case 'u': /* modify one user only */ 302 username = optarg; 303 break; 304 case 'V': 305 req_version = (uint32_t)atoi(optarg); 306 if (req_version > 1) 307 err(1, "Unknown version %u\n", req_version); 308 break; 309 case 'v': 310 verbose++; 311 break; 312 case 'w': 313 warning++; 314 break; 315 case '?': 316 default: 317 usage(); 318 } 319 argc -= optind; 320 argv += optind; 321 322 if (argc != 1) 323 usage(); 324 if (username != NULL) 325 if (username[0] == '+' || username[0] == '-') 326 usage(); 327 if (secureonly) 328 makeold = 0; 329 330 /* 331 * This could be changed to allow the user to interrupt. 332 * Probably not worth the effort. 333 */ 334 (void)sigemptyset(&set); 335 (void)sigaddset(&set, SIGTSTP); 336 (void)sigaddset(&set, SIGHUP); 337 (void)sigaddset(&set, SIGINT); 338 (void)sigaddset(&set, SIGQUIT); 339 (void)sigaddset(&set, SIGTERM); 340 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); 341 342 /* We don't care what the user wants. */ 343 (void)umask(0); 344 345 if (username == NULL) 346 flags = O_RDWR | O_CREAT | O_EXCL; 347 else 348 flags = O_RDWR; 349 350 pname = *argv; 351 /* Open the original password file */ 352 if ((fp = fopen(pname, "r")) == NULL) 353 error(pname); 354 355 openinfo.lorder = lorder; 356 357 if (fstat(fileno(fp), &st) == -1) 358 error(pname); 359 360 if (cachesize) { 361 openinfo.cachesize = cachesize; 362 } else { 363 /* Tweak openinfo values for large passwd files. */ 364 cachesize = (u_int)(st.st_size * 20); 365 if (cachesize > MAX_CACHESIZE) 366 cachesize = MAX_CACHESIZE; 367 else if (cachesize < MIN_CACHESIZE) 368 cachesize = MIN_CACHESIZE; 369 openinfo.cachesize = cachesize; 370 } 371 372 /* Open the temporary insecure password database. */ 373 if (!secureonly) { 374 opendb(&idb, _PATH_MP_DB, username, req_version, 375 flags, PERM_INSECURE); 376 clean |= FILE_INSECURE; 377 } 378 379 380 /* Open the temporary encrypted password database. */ 381 opendb(&sdb, _PATH_SMP_DB, username, req_version, flags, PERM_SECURE); 382 clean |= FILE_SECURE; 383 384 /* 385 * Open file for old password file. Minor trickiness -- don't want to 386 * chance the file already existing, since someone (stupidly) might 387 * still be using this for permission checking. So, open it first and 388 * fdopen the resulting fd. The resulting file should be readable by 389 * everyone. 390 */ 391 if (makeold) { 392 (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig", 393 pname); 394 if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL, 395 PERM_INSECURE)) < 0) 396 error(oldpwdfile); 397 clean |= FILE_ORIG; 398 if ((oldfp = fdopen(tfd, "w")) == NULL) 399 error(oldpwdfile); 400 } 401 402 if (username != NULL) { 403 uid_dbflg = 0; 404 dbflg = 0; 405 406 /* 407 * Determine if this is a new entry. 408 */ 409 if (getdbent(&sdb, _PW_KEYBYNAME, username, &tpwd)) 410 newuser = 1; 411 else { 412 newuser = 0; 413 olduid = tpwd->pw_uid; 414 } 415 416 } else { 417 uid_dbflg = R_NOOVERWRITE; 418 dbflg = R_NOOVERWRITE; 419 } 420 421 /* 422 * If we see something go by that looks like YP, we save a special 423 * pointer record, which if YP is enabled in the C lib, will speed 424 * things up. 425 */ 426 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) { 427 /* 428 * Create original format password file entry. 429 */ 430 if (makeold) { 431 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n", 432 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, 433 pwd.pw_dir, pwd.pw_shell); 434 if (ferror(oldfp)) 435 wr_error(oldpwdfile); 436 } 437 438 if (username == NULL) { 439 /* Look like YP? */ 440 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') 441 hasyp++; 442 443 /* Warn about potentially unsafe uid/gid overrides. */ 444 if (pwd.pw_name[0] == '+') { 445 if ((flags & _PASSWORD_NOUID) == 0 && 446 pwd.pw_uid == 0) 447 warnx("line %d: superuser override " 448 "in YP inclusion", lineno); 449 if ((flags & _PASSWORD_NOGID) == 0 && 450 pwd.pw_gid == 0) 451 warnx("line %d: wheel override " 452 "in YP inclusion", lineno); 453 } 454 455 /* Write the database entry out. */ 456 if (!secureonly) 457 putdbents(&idb, &pwd, "*", flags, lineno, dbflg, 458 uid_dbflg); 459 continue; 460 } else if (strcmp(username, pwd.pw_name) != 0) 461 continue; 462 463 if (found) { 464 warnx("user `%s' listed twice in password file", 465 username); 466 bailout(); 467 } 468 469 /* 470 * Ensure that the text file and database agree on 471 * which line the record is from. 472 */ 473 rv = getdbent(&sdb, _PW_KEYBYNUM, &lineno, &tpwd); 474 if (newuser) { 475 if (rv == 0) 476 inconsistency(); 477 } else if (rv == 1 || strcmp(username, tpwd->pw_name) != 0) 478 inconsistency(); 479 else if ((uid_t)olduid != pwd.pw_uid) { 480 /* 481 * If we're changing UID, remove the BYUID 482 * record for the old UID only if it has the 483 * same username. 484 */ 485 if (!getdbent(&sdb, _PW_KEYBYUID, &olduid, &tpwd)) { 486 if (strcmp(username, tpwd->pw_name) == 0) { 487 if (!secureonly) 488 deldbent(&idb, _PW_KEYBYUID, 489 &olduid); 490 deldbent(&sdb, _PW_KEYBYUID, &olduid); 491 } 492 } else 493 inconsistency(); 494 } 495 496 /* 497 * If there's an existing BYUID record for the new UID and 498 * the username doesn't match then be sure not to overwrite 499 * it. 500 */ 501 if (!getdbent(&sdb, _PW_KEYBYUID, &pwd.pw_uid, &tpwd)) 502 if (strcmp(username, tpwd->pw_name) != 0) 503 uid_dbflg = R_NOOVERWRITE; 504 505 /* Write the database entries out */ 506 if (!secureonly) 507 putdbents(&idb, &pwd, "*", flags, lineno, dbflg, 508 uid_dbflg); 509 putdbents(&sdb, &pwd, pwd.pw_passwd, flags, lineno, dbflg, 510 uid_dbflg); 511 512 found = 1; 513 if (!makeold) 514 break; 515 } 516 517 if (!secureonly) { 518 /* Store YP token if needed. */ 519 if (hasyp) 520 putyptoken(&idb); 521 522 /* Close the insecure database. */ 523 closedb(&idb); 524 } 525 526 /* 527 * If rebuilding the databases, we re-parse the text file and write 528 * the secure entries out in a separate pass. 529 */ 530 if (username == NULL) { 531 rewind(fp); 532 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) 533 putdbents(&sdb, &pwd, pwd.pw_passwd, flags, 534 lineno, dbflg, uid_dbflg); 535 536 /* Store YP token if needed. */ 537 if (hasyp) 538 putyptoken(&sdb); 539 } else if (!found) { 540 warnx("user `%s' not found in password file", username); 541 bailout(); 542 } 543 544 /* Close the secure database. */ 545 closedb(&sdb); 546 547 /* Install as the real password files. */ 548 if (!secureonly) 549 install(idb.dbname, idb.fname); 550 install(sdb.dbname, sdb.fname); 551 552 /* Install the V7 password file. */ 553 if (makeold) { 554 if (fflush(oldfp) == EOF) 555 wr_error(oldpwdfile); 556 if (fclose(oldfp) == EOF) 557 wr_error(oldpwdfile); 558 install(oldpwdfile, _PATH_PASSWD); 559 } 560 561 /* Set master.passwd permissions, in case caller forgot. */ 562 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); 563 if (fclose(fp) == EOF) 564 wr_error(pname); 565 566 /* 567 * Move the temporary master password file LAST -- chpass(1), 568 * passwd(1), vipw(8) and friends all use its existence to block 569 * other incarnations of themselves. The rename means that 570 * everything is unlocked, as the original file can no longer be 571 * accessed. 572 */ 573 install(pname, _PATH_MASTERPASSWD); 574 exit(EXIT_SUCCESS); 575 /* NOTREACHED */ 576 } 577 578 int 579 scan(FILE *fp, struct passwd *pw, int *flags, int *lineno) 580 { 581 static char line[LINE_MAX]; 582 char *p; 583 int oflags; 584 585 if (fgets(line, (int)sizeof(line), fp) == NULL) 586 return (0); 587 (*lineno)++; 588 589 /* 590 * ``... if I swallow anything evil, put your fingers down my 591 * throat...'' 592 * -- The Who 593 */ 594 if ((p = strchr(line, '\n')) == NULL) { 595 warnx("line too long"); 596 errno = EFTYPE; /* XXX */ 597 error(pname); 598 } 599 *p = '\0'; 600 if (strcmp(line, "+") == 0) { 601 /* pw_scan() can't handle "+" */ 602 (void)strcpy(line, "+:::::::::"); 603 } 604 oflags = 0; 605 if (!pw_scan(line, pw, &oflags)) { 606 warnx("at line #%d", *lineno); 607 errno = EFTYPE; /* XXX */ 608 error(pname); 609 } 610 *flags = oflags; 611 612 return (1); 613 } 614 615 void 616 install(const char *from, const char *to) 617 { 618 char buf[MAXPATHLEN]; 619 char errbuf[BUFSIZ]; 620 int sverrno; 621 622 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, to); 623 if (rename(from, buf)) { 624 sverrno = errno; 625 (void)snprintf(errbuf, sizeof(errbuf), "%s to %s", from, buf); 626 errno = sverrno; 627 error(errbuf); 628 } 629 } 630 631 void 632 rm(const char *victim) 633 { 634 635 if (unlink(victim) < 0) 636 warn("unlink(%s)", victim); 637 } 638 639 void 640 cp(const char *from, const char *to, mode_t mode) 641 { 642 static char buf[MAXBSIZE]; 643 int from_fd, to_fd, sverrno; 644 ssize_t rcount, wcount; 645 646 if ((from_fd = open(from, O_RDONLY, 0)) < 0) 647 error(from); 648 if ((to_fd = open(to, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) 649 error(to); 650 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 651 wcount = write(to_fd, buf, (size_t)rcount); 652 if (rcount != wcount || wcount == -1) 653 goto on_error; 654 } 655 656 if (rcount < 0) 657 goto on_error; 658 close(from_fd); 659 if (close(to_fd)) 660 goto on_error; 661 return; 662 663 on_error: 664 sverrno = errno; 665 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 666 errno = sverrno; 667 error(buf); 668 } 669 670 void 671 wr_error(const char *str) 672 { 673 char errbuf[BUFSIZ]; 674 int sverrno; 675 676 sverrno = errno; 677 678 (void)snprintf(errbuf, sizeof(errbuf), 679 "attempt to write %s failed", str); 680 681 errno = sverrno; 682 error(errbuf); 683 } 684 685 void 686 error(const char *str) 687 { 688 689 warn("%s", str); 690 bailout(); 691 } 692 693 void 694 inconsistency(void) 695 { 696 697 warnx("text files and databases are inconsistent"); 698 warnx("re-build the databases without -u"); 699 bailout(); 700 } 701 702 void 703 bailout(void) 704 { 705 706 if ((clean & FILE_ORIG) != 0) 707 rm(oldpwdfile); 708 if ((clean & FILE_SECURE) != 0) 709 rm(sdb.dbname); 710 if ((clean & FILE_INSECURE) != 0) 711 rm(idb.dbname); 712 713 exit(EXIT_FAILURE); 714 } 715 716 uint32_t 717 getversion(const char *fname) 718 { 719 DBT data, key; 720 int ret; 721 uint32_t version = 0; 722 DB *db; 723 724 db = dbopen(fname, O_RDONLY, PERM_INSECURE, DB_HASH, NULL); 725 if (db == NULL) { 726 /* If we are building on a separate root, assume version 1 */ 727 if ((errno == EACCES || errno == ENOENT) && prefix[0]) 728 return 1; 729 warn("Cannot open database %s", fname); 730 bailout(); 731 } 732 key.data = __UNCONST("VERSION"); 733 key.size = strlen((const char *)key.data) + 1; 734 735 switch (ret = (*db->get)(db, &key, &data, 0)) { 736 case -1: /* Error */ 737 warn("Cannot get VERSION record from database"); 738 goto out; 739 case 0: 740 if (data.size != sizeof(version)) { 741 warnx("Bad VERSION record in database"); 742 goto out; 743 } 744 (void)memcpy(&version, data.data, sizeof(version)); 745 /*FALLTHROUGH*/ 746 case 1: 747 if (ret == 1) 748 warnx("Database %s has no version info", fname); 749 (*db->close)(db); 750 return version; 751 default: 752 warnx("internal error db->get returns %d", ret); 753 goto out; 754 } 755 out: 756 (*db->close)(db); 757 bailout(); 758 /*NOTREACHED*/ 759 } 760 761 void 762 setversion(struct pwddb *db) 763 { 764 DBT data, key; 765 key.data = __UNCONST("VERSION"); 766 key.size = strlen((const char *)key.data) + 1; 767 768 data.data = &db->wversion; 769 data.size = sizeof(uint32_t); 770 771 if ((*db->db->put)(db->db, &key, &data, 0) != 0) { 772 warn("Can't write VERSION record to %s", db->dbname); 773 bailout(); 774 } 775 } 776 777 778 /* 779 * Write entries to a database for a single user. 780 * 781 * The databases actually contain three copies of the original data. Each 782 * password file entry is converted into a rough approximation of a ``struct 783 * passwd'', with the strings placed inline. This object is then stored as 784 * the data for three separate keys. The first key * is the pw_name field 785 * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid 786 * field prepended by the _PW_KEYBYUID character. The third key is the line 787 * number in the original file prepended by the _PW_KEYBYNUM character. 788 * (The special characters are prepended to ensure that the keys do not 789 * collide.) 790 */ 791 #define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';) 792 793 void 794 putdbents(struct pwddb *db, struct passwd *pw, const char *passwd, int flags, 795 int lineno, u_int dbflg, u_int uid_dbflg) 796 { 797 struct passwd pwd; 798 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p; 799 DBT data, key; 800 const char *t; 801 u_int32_t x; 802 size_t len; 803 804 (void)memcpy(&pwd, pw, sizeof(pwd)); 805 data.data = (u_char *)buf; 806 key.data = (u_char *)tbuf; 807 808 if (lorder != BYTE_ORDER) { 809 pwd.pw_uid = SWAP(pwd.pw_uid); 810 pwd.pw_gid = SWAP(pwd.pw_gid); 811 } 812 813 #define WRITEPWTIMEVAR(pwvar) \ 814 do { \ 815 if (db->wversion == 0 && \ 816 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ 817 uint32_t tmp = (uint32_t)pwvar; \ 818 if (lorder != BYTE_ORDER) \ 819 tmp = SWAP(tmp); \ 820 (void)memmove(p, &tmp, sizeof(tmp)); \ 821 p += sizeof(tmp); \ 822 } else if (db->wversion == 1 && \ 823 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ 824 uint64_t tmp = pwvar; \ 825 if (lorder != BYTE_ORDER) \ 826 tmp = SWAP(tmp); \ 827 (void)memmove(p, &tmp, sizeof(tmp)); \ 828 p += sizeof(tmp); \ 829 } else { \ 830 if (lorder != BYTE_ORDER) \ 831 pwvar = SWAP(pwvar); \ 832 (void)memmove(p, &pwvar, sizeof(pwvar)); \ 833 p += sizeof(pwvar); \ 834 } \ 835 } while (/*CONSTCOND*/0) 836 837 /* Create insecure data. */ 838 p = buf; 839 COMPACT(pwd.pw_name); 840 COMPACT(passwd); 841 (void)memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid)); 842 p += sizeof(pwd.pw_uid); 843 (void)memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid)); 844 p += sizeof(pwd.pw_gid); 845 WRITEPWTIMEVAR(pwd.pw_change); 846 COMPACT(pwd.pw_class); 847 COMPACT(pwd.pw_gecos); 848 COMPACT(pwd.pw_dir); 849 COMPACT(pwd.pw_shell); 850 WRITEPWTIMEVAR(pwd.pw_expire); 851 x = flags; 852 if (lorder != BYTE_ORDER) 853 x = SWAP(x); 854 (void)memmove(p, &x, sizeof(x)); 855 p += sizeof(x); 856 data.size = p - buf; 857 858 /* Store insecure by name. */ 859 tbuf[0] = _PW_KEYBYNAME; 860 len = strlen(pwd.pw_name); 861 (void)memmove(tbuf + 1, pwd.pw_name, len); 862 key.size = len + 1; 863 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) 864 wr_error(db->dbname); 865 866 /* Store insecure by number. */ 867 tbuf[0] = _PW_KEYBYNUM; 868 x = lineno; 869 if (lorder != BYTE_ORDER) 870 x = SWAP(x); 871 (void)memmove(tbuf + 1, &x, sizeof(x)); 872 key.size = sizeof(x) + 1; 873 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) 874 wr_error(db->dbname); 875 876 /* Store insecure by uid. */ 877 tbuf[0] = _PW_KEYBYUID; 878 (void)memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 879 key.size = sizeof(pwd.pw_uid) + 1; 880 if ((*db->db->put)(db->db, &key, &data, uid_dbflg) == -1) 881 wr_error(db->dbname); 882 } 883 884 void 885 deldbent(struct pwddb *db, int type, void *keyp) 886 { 887 char tbuf[1024]; 888 DBT key; 889 u_int32_t x; 890 size_t len; 891 892 key.data = (u_char *)tbuf; 893 894 switch (tbuf[0] = type) { 895 case _PW_KEYBYNAME: 896 len = strlen((char *)keyp); 897 (void)memcpy(tbuf + 1, keyp, len); 898 key.size = len + 1; 899 break; 900 901 case _PW_KEYBYNUM: 902 case _PW_KEYBYUID: 903 x = *(int *)keyp; 904 if (lorder != BYTE_ORDER) 905 x = SWAP(x); 906 (void)memmove(tbuf + 1, &x, sizeof(x)); 907 key.size = sizeof(x) + 1; 908 break; 909 } 910 911 if ((*db->db->del)(db->db, &key, 0) == -1) 912 wr_error(db->dbname); 913 } 914 915 int 916 getdbent(struct pwddb *db, int type, void *keyp, struct passwd **tpwd) 917 { 918 static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)]; 919 static struct passwd pwd; 920 char tbuf[1024], *p; 921 DBT key, data; 922 u_int32_t x; 923 size_t len; 924 int rv; 925 926 data.data = (u_char *)buf; 927 data.size = sizeof(buf); 928 key.data = (u_char *)tbuf; 929 930 switch (tbuf[0] = type) { 931 case _PW_KEYBYNAME: 932 len = strlen((char *)keyp); 933 (void)memcpy(tbuf + 1, keyp, len); 934 key.size = len + 1; 935 break; 936 937 case _PW_KEYBYNUM: 938 case _PW_KEYBYUID: 939 x = *(int *)keyp; 940 if (lorder != BYTE_ORDER) 941 x = SWAP(x); 942 (void)memmove(tbuf + 1, &x, sizeof(x)); 943 key.size = sizeof(x) + 1; 944 break; 945 } 946 947 if ((rv = (*db->db->get)(db->db, &key, &data, 0)) == 1) 948 return (rv); 949 if (rv == -1) 950 error(db->dbname); 951 952 p = (char *)data.data; 953 954 pwd.pw_name = p; 955 while (*p++ != '\0') 956 continue; 957 pwd.pw_passwd = p; 958 while (*p++ != '\0') 959 continue; 960 961 (void)memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid)); 962 p += sizeof(pwd.pw_uid); 963 (void)memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid)); 964 p += sizeof(pwd.pw_gid); 965 966 #define READPWTIMEVAR(pwvar) \ 967 do { \ 968 if (db->rversion == 0 && \ 969 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ 970 uint32_t tmp; \ 971 (void)memcpy(&tmp, p, sizeof(tmp)); \ 972 p += sizeof(tmp); \ 973 if (lorder != BYTE_ORDER) \ 974 pwvar = SWAP(tmp); \ 975 else \ 976 pwvar = tmp; \ 977 } else if (db->rversion == 1 && \ 978 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ 979 uint64_t tmp; \ 980 (void)memcpy(&tmp, p, sizeof(tmp)); \ 981 p += sizeof(tmp); \ 982 if (lorder != BYTE_ORDER) \ 983 pwvar = (uint32_t)SWAP(tmp); \ 984 else \ 985 pwvar = (uint32_t)tmp; \ 986 } else { \ 987 (void)memcpy(&pwvar, p, sizeof(pwvar)); \ 988 p += sizeof(pwvar); \ 989 if (lorder != BYTE_ORDER) \ 990 pwvar = SWAP(pwvar); \ 991 } \ 992 } while (/*CONSTCOND*/0) 993 994 READPWTIMEVAR(pwd.pw_change); 995 996 pwd.pw_class = p; 997 while (*p++ != '\0') 998 continue; 999 pwd.pw_gecos = p; 1000 while (*p++ != '\0') 1001 continue; 1002 pwd.pw_dir = p; 1003 while (*p++ != '\0') 1004 continue; 1005 pwd.pw_shell = p; 1006 while (*p++ != '\0') 1007 continue; 1008 1009 READPWTIMEVAR(pwd.pw_expire); 1010 1011 if (lorder != BYTE_ORDER) { 1012 pwd.pw_uid = SWAP(pwd.pw_uid); 1013 pwd.pw_gid = SWAP(pwd.pw_gid); 1014 } 1015 1016 *tpwd = &pwd; 1017 return (0); 1018 } 1019 1020 void 1021 putyptoken(struct pwddb *db) 1022 { 1023 DBT data, key; 1024 1025 key.data = __UNCONST(__yp_token); 1026 key.size = strlen(__yp_token); 1027 data.data = (u_char *)NULL; 1028 data.size = 0; 1029 1030 if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) == -1) 1031 wr_error(db->dbname); 1032 } 1033 1034 void 1035 usage(void) 1036 { 1037 1038 (void)fprintf(stderr, 1039 "Usage: %s [-BLpsvw] [-c cachesize] [-d directory] [-u user] " 1040 "[-V version] file\n", 1041 getprogname()); 1042 exit(EXIT_FAILURE); 1043 } 1044