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