1 /* $NetBSD: pwd_mkdb.c,v 1.52 2010/08/18 08:06:39 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.52 2010/08/18 08:06:39 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 #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 131 static const char __yp_token[] = "__YP!"; 132 #else 133 /* Pull this out of the C library. */ 134 extern const char __yp_token[]; 135 #endif 136 137 static 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 151 struct 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 159 static char *pname; /* password file name */ 160 static char prefix[MAXPATHLEN]; 161 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)]; 162 static int lorder = BYTE_ORDER; 163 static int logsyslog; 164 static int clean; 165 static int verbose; 166 static int warning; 167 static struct pwddb sdb, idb; 168 169 170 void bailout(void) __attribute__((__noreturn__)); 171 void cp(const char *, const char *, mode_t); 172 void deldbent(struct pwddb *, int, void *); 173 void mkpw_error(const char *, ...); 174 void mkpw_warning(const char *, ...); 175 int getdbent(struct pwddb *, int, void *, struct passwd **); 176 void inconsistency(void); 177 void install(const char *, const char *); 178 int main(int, char **); 179 void putdbents(struct pwddb *, struct passwd *, const char *, int, int, 180 u_int, u_int); 181 void putyptoken(struct pwddb *); 182 void rm(const char *); 183 int scan(FILE *, struct passwd *, int *, int *); 184 void usage(void) __attribute__((__noreturn__)); 185 void wr_error(const char *); 186 uint32_t getversion(const char *); 187 void 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 198 static void 199 closedb(struct pwddb *db) 200 { 201 if ((*db->db->close)(db->db) < 0) 202 wr_error(db->dbname); 203 } 204 205 static void 206 opendb(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 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 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, (sigset_t *)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 586 int 587 scan(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 621 void 622 install(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 631 void 632 rm(const char *victim) 633 { 634 635 if (unlink(victim) < 0) 636 warn("unlink(%s)", victim); 637 } 638 639 void 640 cp(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 mkpw_error("Cannot open `%s'", to); 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 goto on_error; 654 } 655 656 if (rcount < 0) 657 goto on_error; 658 close(from_fd); 659 if (close(to_fd)) 660 goto on_error; 661 return; 662 663 on_error: 664 mkpw_error("Cannot copy `%s' to `%s'", from, to); 665 } 666 667 void 668 wr_error(const char *str) 669 { 670 mkpw_error("Cannot write `%s'", str); 671 } 672 673 void 674 mkpw_error(const char *fmt, ...) 675 { 676 va_list ap; 677 va_start(ap, fmt); 678 if (logsyslog) { 679 int sverrno = errno; 680 char efmt[BUFSIZ]; 681 snprintf(efmt, sizeof(efmt), "%s (%%m)", fmt); 682 errno = sverrno; 683 vsyslog(LOG_ERR, efmt, ap); 684 } else 685 vwarn(fmt, ap); 686 va_end(ap); 687 bailout(); 688 } 689 690 void 691 mkpw_warning(const char *fmt, ...) 692 { 693 va_list ap; 694 va_start(ap, fmt); 695 if (logsyslog) 696 vsyslog(LOG_WARNING, fmt, ap); 697 else 698 vwarnx(fmt, ap); 699 va_end(ap); 700 } 701 702 void 703 inconsistency(void) 704 { 705 706 mkpw_warning("text files and databases are inconsistent"); 707 mkpw_warning("re-build the databases without -u"); 708 bailout(); 709 } 710 711 void 712 bailout(void) 713 { 714 715 if ((clean & FILE_ORIG) != 0) 716 rm(oldpwdfile); 717 if ((clean & FILE_SECURE) != 0) 718 rm(sdb.dbname); 719 if ((clean & FILE_INSECURE) != 0) 720 rm(idb.dbname); 721 722 exit(EXIT_FAILURE); 723 } 724 725 uint32_t 726 getversion(const char *fname) 727 { 728 DBT data, key; 729 int ret; 730 uint32_t version = 0; 731 DB *db; 732 733 db = dbopen(fname, O_RDONLY, PERM_INSECURE, DB_HASH, NULL); 734 if (db == NULL) { 735 /* If we are building on a separate root, assume version 1 */ 736 if ((errno == EACCES || errno == ENOENT) && prefix[0]) 737 return 1; 738 mkpw_warning("Cannot open database `%s'", fname); 739 bailout(); 740 } 741 key.data = __UNCONST("VERSION"); 742 key.size = strlen((const char *)key.data) + 1; 743 744 switch (ret = (*db->get)(db, &key, &data, 0)) { 745 case -1: /* Error */ 746 mkpw_warning("Cannot get VERSION record from database `%s'", 747 fname); 748 goto out; 749 case 0: 750 if (data.size != sizeof(version)) { 751 mkpw_warning("Bad VERSION record in database `%s'", fname); 752 goto out; 753 } 754 (void)memcpy(&version, data.data, sizeof(version)); 755 /*FALLTHROUGH*/ 756 case 1: 757 if (ret == 1) 758 mkpw_warning("Database `%s' has no version info", 759 fname); 760 (*db->close)(db); 761 return version; 762 default: 763 mkpw_warning("internal mkpw_error db->get returns %d", ret); 764 goto out; 765 } 766 out: 767 (*db->close)(db); 768 bailout(); 769 /*NOTREACHED*/ 770 } 771 772 void 773 setversion(struct pwddb *db) 774 { 775 DBT data, key; 776 key.data = __UNCONST("VERSION"); 777 key.size = strlen((const char *)key.data) + 1; 778 779 data.data = &db->wversion; 780 data.size = sizeof(uint32_t); 781 782 if ((*db->db->put)(db->db, &key, &data, 0) != 0) { 783 mkpw_warning("Can't write VERSION record to `%s'", db->dbname); 784 bailout(); 785 } 786 } 787 788 789 /* 790 * Write entries to a database for a single user. 791 * 792 * The databases actually contain three copies of the original data. Each 793 * password file entry is converted into a rough approximation of a ``struct 794 * passwd'', with the strings placed inline. This object is then stored as 795 * the data for three separate keys. The first key * is the pw_name field 796 * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid 797 * field prepended by the _PW_KEYBYUID character. The third key is the line 798 * number in the original file prepended by the _PW_KEYBYNUM character. 799 * (The special characters are prepended to ensure that the keys do not 800 * collide.) 801 */ 802 #define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';) 803 804 void 805 putdbents(struct pwddb *db, struct passwd *pw, const char *passwd, int flags, 806 int lineno, u_int dbflg, u_int uid_dbflg) 807 { 808 struct passwd pwd; 809 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p; 810 DBT data, key; 811 const char *t; 812 u_int32_t x; 813 size_t len; 814 815 (void)memcpy(&pwd, pw, sizeof(pwd)); 816 data.data = (u_char *)buf; 817 key.data = (u_char *)tbuf; 818 819 if (lorder != BYTE_ORDER) { 820 pwd.pw_uid = SWAP(pwd.pw_uid); 821 pwd.pw_gid = SWAP(pwd.pw_gid); 822 } 823 824 #define WRITEPWTIMEVAR(pwvar) \ 825 do { \ 826 if (db->wversion == 0 && \ 827 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ 828 uint32_t tmp = (uint32_t)pwvar; \ 829 if (lorder != BYTE_ORDER) \ 830 tmp = SWAP(tmp); \ 831 (void)memmove(p, &tmp, sizeof(tmp)); \ 832 p += sizeof(tmp); \ 833 } else if (db->wversion == 1 && \ 834 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ 835 uint64_t tmp = pwvar; \ 836 if (lorder != BYTE_ORDER) \ 837 tmp = SWAP(tmp); \ 838 (void)memmove(p, &tmp, sizeof(tmp)); \ 839 p += sizeof(tmp); \ 840 } else { \ 841 if (lorder != BYTE_ORDER) \ 842 pwvar = SWAP(pwvar); \ 843 (void)memmove(p, &pwvar, sizeof(pwvar)); \ 844 p += sizeof(pwvar); \ 845 } \ 846 } while (/*CONSTCOND*/0) 847 848 /* Create insecure data. */ 849 p = buf; 850 COMPACT(pwd.pw_name); 851 COMPACT(passwd); 852 (void)memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid)); 853 p += sizeof(pwd.pw_uid); 854 (void)memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid)); 855 p += sizeof(pwd.pw_gid); 856 WRITEPWTIMEVAR(pwd.pw_change); 857 COMPACT(pwd.pw_class); 858 COMPACT(pwd.pw_gecos); 859 COMPACT(pwd.pw_dir); 860 COMPACT(pwd.pw_shell); 861 WRITEPWTIMEVAR(pwd.pw_expire); 862 x = flags; 863 if (lorder != BYTE_ORDER) 864 x = SWAP(x); 865 (void)memmove(p, &x, sizeof(x)); 866 p += sizeof(x); 867 data.size = p - buf; 868 869 /* Store insecure by name. */ 870 tbuf[0] = _PW_KEYBYNAME; 871 len = strlen(pwd.pw_name); 872 (void)memmove(tbuf + 1, pwd.pw_name, len); 873 key.size = len + 1; 874 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) 875 wr_error(db->dbname); 876 877 /* Store insecure by number. */ 878 tbuf[0] = _PW_KEYBYNUM; 879 x = lineno; 880 if (lorder != BYTE_ORDER) 881 x = SWAP(x); 882 (void)memmove(tbuf + 1, &x, sizeof(x)); 883 key.size = sizeof(x) + 1; 884 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) 885 wr_error(db->dbname); 886 887 /* Store insecure by uid. */ 888 tbuf[0] = _PW_KEYBYUID; 889 (void)memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 890 key.size = sizeof(pwd.pw_uid) + 1; 891 if ((*db->db->put)(db->db, &key, &data, uid_dbflg) == -1) 892 wr_error(db->dbname); 893 } 894 895 void 896 deldbent(struct pwddb *db, int type, void *keyp) 897 { 898 char tbuf[1024]; 899 DBT key; 900 u_int32_t x; 901 size_t len; 902 903 key.data = (u_char *)tbuf; 904 905 switch (tbuf[0] = type) { 906 case _PW_KEYBYNAME: 907 len = strlen((char *)keyp); 908 (void)memcpy(tbuf + 1, keyp, len); 909 key.size = len + 1; 910 break; 911 912 case _PW_KEYBYNUM: 913 case _PW_KEYBYUID: 914 x = *(int *)keyp; 915 if (lorder != BYTE_ORDER) 916 x = SWAP(x); 917 (void)memmove(tbuf + 1, &x, sizeof(x)); 918 key.size = sizeof(x) + 1; 919 break; 920 } 921 922 if ((*db->db->del)(db->db, &key, 0) == -1) 923 wr_error(db->dbname); 924 } 925 926 int 927 getdbent(struct pwddb *db, int type, void *keyp, struct passwd **tpwd) 928 { 929 static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)]; 930 static struct passwd pwd; 931 char tbuf[1024], *p; 932 DBT key, data; 933 u_int32_t x; 934 size_t len; 935 int rv; 936 937 data.data = (u_char *)buf; 938 data.size = sizeof(buf); 939 key.data = (u_char *)tbuf; 940 941 switch (tbuf[0] = type) { 942 case _PW_KEYBYNAME: 943 len = strlen((char *)keyp); 944 (void)memcpy(tbuf + 1, keyp, len); 945 key.size = len + 1; 946 break; 947 948 case _PW_KEYBYNUM: 949 case _PW_KEYBYUID: 950 x = *(int *)keyp; 951 if (lorder != BYTE_ORDER) 952 x = SWAP(x); 953 (void)memmove(tbuf + 1, &x, sizeof(x)); 954 key.size = sizeof(x) + 1; 955 break; 956 } 957 958 if ((rv = (*db->db->get)(db->db, &key, &data, 0)) == 1) 959 return (rv); 960 if (rv == -1) 961 mkpw_error("Error getting record from `%s'", db->dbname); 962 963 p = (char *)data.data; 964 965 pwd.pw_name = p; 966 while (*p++ != '\0') 967 continue; 968 pwd.pw_passwd = p; 969 while (*p++ != '\0') 970 continue; 971 972 (void)memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid)); 973 p += sizeof(pwd.pw_uid); 974 (void)memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid)); 975 p += sizeof(pwd.pw_gid); 976 977 #define READPWTIMEVAR(pwvar) \ 978 do { \ 979 if (db->rversion == 0 && \ 980 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ 981 uint32_t tmp; \ 982 (void)memcpy(&tmp, p, sizeof(tmp)); \ 983 p += sizeof(tmp); \ 984 if (lorder != BYTE_ORDER) \ 985 pwvar = SWAP(tmp); \ 986 else \ 987 pwvar = tmp; \ 988 } else if (db->rversion == 1 && \ 989 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ 990 uint64_t tmp; \ 991 (void)memcpy(&tmp, p, sizeof(tmp)); \ 992 p += sizeof(tmp); \ 993 if (lorder != BYTE_ORDER) \ 994 pwvar = (uint32_t)SWAP(tmp); \ 995 else \ 996 pwvar = (uint32_t)tmp; \ 997 } else { \ 998 (void)memcpy(&pwvar, p, sizeof(pwvar)); \ 999 p += sizeof(pwvar); \ 1000 if (lorder != BYTE_ORDER) \ 1001 pwvar = SWAP(pwvar); \ 1002 } \ 1003 } while (/*CONSTCOND*/0) 1004 1005 READPWTIMEVAR(pwd.pw_change); 1006 1007 pwd.pw_class = p; 1008 while (*p++ != '\0') 1009 continue; 1010 pwd.pw_gecos = p; 1011 while (*p++ != '\0') 1012 continue; 1013 pwd.pw_dir = p; 1014 while (*p++ != '\0') 1015 continue; 1016 pwd.pw_shell = p; 1017 while (*p++ != '\0') 1018 continue; 1019 1020 READPWTIMEVAR(pwd.pw_expire); 1021 1022 if (lorder != BYTE_ORDER) { 1023 pwd.pw_uid = SWAP(pwd.pw_uid); 1024 pwd.pw_gid = SWAP(pwd.pw_gid); 1025 } 1026 1027 *tpwd = &pwd; 1028 return (0); 1029 } 1030 1031 void 1032 putyptoken(struct pwddb *db) 1033 { 1034 DBT data, key; 1035 1036 key.data = __UNCONST(__yp_token); 1037 key.size = strlen(__yp_token); 1038 data.data = (u_char *)NULL; 1039 data.size = 0; 1040 1041 if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) == -1) 1042 wr_error(db->dbname); 1043 } 1044 1045 void 1046 usage(void) 1047 { 1048 1049 (void)fprintf(stderr, 1050 "Usage: %s [-BLlpsvw] [-c cachesize] [-d directory] [-u user] " 1051 "[-V version] file\n", 1052 getprogname()); 1053 exit(EXIT_FAILURE); 1054 } 1055