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