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