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