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