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