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