Home | History | Annotate | Line # | Download | only in pwd_mkdb
pwd_mkdb.c revision 1.19
      1 /*	$NetBSD: pwd_mkdb.c,v 1.19 2000/08/07 13:19:27 ad Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1991, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  * Portions Copyright(C) 1994, Jason Downs.  All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #ifndef lint
     39 __COPYRIGHT("@(#) Copyright (c) 2000\n\
     40 	The NetBSD Foundation, Inc.  All rights reserved.\n\
     41 Copyright (c) 1991, 1993, 1994\n\
     42 	The Regents of the University of California.  All rights reserved.\n");
     43 __SCCSID("from: @(#)pwd_mkdb.c	8.5 (Berkeley) 4/20/94");
     44 __RCSID("$NetBSD: pwd_mkdb.c,v 1.19 2000/08/07 13:19:27 ad Exp $");
     45 #endif /* not lint */
     46 
     47 #include <sys/param.h>
     48 #include <sys/stat.h>
     49 
     50 #include <db.h>
     51 #include <err.h>
     52 #include <errno.h>
     53 #include <fcntl.h>
     54 #include <limits.h>
     55 #include <pwd.h>
     56 #include <signal.h>
     57 #include <stdio.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <unistd.h>
     61 #include <util.h>
     62 
     63 #define	INSECURE	1
     64 #define	SECURE		2
     65 #define	PERM_INSECURE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
     66 #define	PERM_SECURE	(S_IRUSR | S_IWUSR)
     67 
     68 /* pull this out of the C library. */
     69 extern const char __yp_token[];
     70 
     71 HASHINFO openinfo = {
     72 	4096,		/* bsize */
     73 	32,		/* ffactor */
     74 	256,		/* nelem */
     75 	2048 * 1024,	/* cachesize */
     76 	NULL,		/* hash() */
     77 	0		/* lorder */
     78 };
     79 
     80 static enum { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
     81 static char *pname;				/* password file name */
     82 static char prefix[MAXPATHLEN];
     83 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)];
     84 static char pwd_db_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
     85 static char pwd_Sdb_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
     86 static int lorder = BYTE_ORDER;
     87 
     88 void	cleanup(void);
     89 void	error(const char *);
     90 void	wr_error(const char *);
     91 int	main(int, char **);
     92 void	install(const char *, const char *);
     93 void	rm(const char *);
     94 int	scan(FILE *, struct passwd *, int *, int *);
     95 void	usage(void);
     96 void	putdbent(DB *, struct passwd *, const char *, int, const char *, int);
     97 void	putyptoken(DB *dp, const char *fn);
     98 
     99 int
    100 main(int argc, char *argv[])
    101 {
    102 	DB *dp, *edp;
    103 	FILE *fp, *oldfp;
    104 	sigset_t set;
    105 	int ch, makeold, tfd, hasyp, flags, lineno;
    106 	struct passwd pwd;
    107 
    108 	hasyp = 0;
    109 	oldfp = NULL;
    110 	strcpy(prefix, "/");
    111 	makeold = 0;
    112 
    113 	while ((ch = getopt(argc, argv, "d:pvBL")) != -1)
    114 		switch(ch) {
    115 		case 'd':			/* set prefix */
    116 			strncpy(prefix, optarg, sizeof(prefix));
    117 			prefix[sizeof(prefix)-1] = '\0';
    118 			break;
    119 		case 'p':			/* create V7 "file.orig" */
    120 			makeold = 1;
    121 			break;
    122 		case 'v':			/* backward compatible */
    123 			break;
    124 		case 'B':			/* big-endian output */
    125 			lorder = BIG_ENDIAN;
    126 			break;
    127 		case 'L':			/* little-endian output */
    128 			lorder = LITTLE_ENDIAN;
    129 			break;
    130 		case '?':
    131 		default:
    132 			usage();
    133 		}
    134 	argc -= optind;
    135 	argv += optind;
    136 
    137 	if (argc != 1)
    138 		usage();
    139 
    140 	/*
    141 	 * This could be changed to allow the user to interrupt.
    142 	 * Probably not worth the effort.
    143 	 */
    144 	sigemptyset(&set);
    145 	sigaddset(&set, SIGTSTP);
    146 	sigaddset(&set, SIGHUP);
    147 	sigaddset(&set, SIGINT);
    148 	sigaddset(&set, SIGQUIT);
    149 	sigaddset(&set, SIGTERM);
    150 	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
    151 
    152 	/* We don't care what the user wants. */
    153 	(void)umask(0);
    154 
    155 	pname = *argv;
    156 	/* Open the original password file */
    157 	if ((fp = fopen(pname, "r")) == NULL)
    158 		error(pname);
    159 
    160 	openinfo.lorder = lorder;
    161 
    162 	/* Open the temporary insecure password database. */
    163 	(void)snprintf(pwd_db_tmp, sizeof(pwd_db_tmp), "%s%s.tmp", prefix,
    164 	    _PATH_MP_DB);
    165 	dp = dbopen(pwd_db_tmp, O_RDWR | O_CREAT | O_EXCL, PERM_INSECURE,
    166 	    DB_HASH, &openinfo);
    167 	if (dp == NULL)
    168 		error(pwd_db_tmp);
    169 	clean = FILE_INSECURE;
    170 
    171 	/*
    172 	 * Open file for old password file.  Minor trickiness -- don't want to
    173 	 * chance the file already existing, since someone (stupidly) might
    174 	 * still be using this for permission checking.  So, open it first and
    175 	 * fdopen the resulting fd.  The resulting file should be readable by
    176 	 * everyone.
    177 	 */
    178 	if (makeold) {
    179 		(void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig",
    180 		    pname);
    181 		if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL,
    182 		    PERM_INSECURE)) < 0)
    183 			error(oldpwdfile);
    184 		if ((oldfp = fdopen(tfd, "w")) == NULL)
    185 			error(oldpwdfile);
    186 		clean = FILE_ORIG;
    187 	}
    188 
    189 	/*
    190 	 * If we see something go by that looks like YP, we save a special
    191 	 * pointer record, which if YP is enabled in the C lib, will speed
    192 	 * things up.
    193 	 */
    194 	for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) {
    195 		/* look like YP? */
    196 		if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')
    197 			hasyp++;
    198 
    199 		/*
    200 		 * Warn about potentially unsafe uid/gid overrides.
    201 		 */
    202 		if (pwd.pw_name[0] == '+') {
    203 			if ((flags & _PASSWORD_NOUID) == 0 && pwd.pw_uid == 0)
    204 				warnx(
    205 				"line %d: superuser override in YP inclusion",
    206 				    lineno);
    207 			if ((flags & _PASSWORD_NOGID) == 0 && pwd.pw_gid == 0)
    208 				warnx(
    209 				    "line %d: wheel override in YP inclusion",
    210 				    lineno);
    211 		}
    212 
    213 		putdbent(dp, &pwd, "*", flags, pwd_db_tmp, lineno);
    214 
    215 		/* Create original format password file entry */
    216 		if (makeold) {
    217 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
    218 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
    219 			    pwd.pw_dir, pwd.pw_shell);
    220 			if (ferror(oldfp))
    221 				wr_error(oldpwdfile);
    222 		}
    223 	}
    224 
    225 	/* Store YP token, if needed. */
    226 	if (hasyp)
    227 		putyptoken(dp, pwd_db_tmp);
    228 
    229 	if ((dp->close)(dp) < 0)
    230 		wr_error(pwd_db_tmp);
    231 	if (makeold) {
    232 		if (fflush(oldfp) == EOF)
    233 			wr_error(oldpwdfile);
    234 		if (fclose(oldfp) == EOF)
    235 			wr_error(oldpwdfile);
    236 	}
    237 
    238 	/* Open the temporary encrypted password database. */
    239 	(void)snprintf(pwd_Sdb_tmp, sizeof(pwd_Sdb_tmp), "%s%s.tmp", prefix,
    240 		_PATH_SMP_DB);
    241 	edp = dbopen(pwd_Sdb_tmp, O_RDWR | O_CREAT | O_EXCL, PERM_SECURE,
    242 	   DB_HASH, &openinfo);
    243 	if (!edp)
    244 		error(pwd_Sdb_tmp);
    245 	clean = FILE_SECURE;
    246 
    247 	rewind(fp);
    248 	for (lineno = 0; scan(fp, &pwd, &flags, &lineno);)
    249 		putdbent(edp, &pwd, pwd.pw_passwd, flags, pwd_Sdb_tmp,
    250 		    lineno);
    251 
    252 	/* Store YP token, if needed. */
    253 	if (hasyp)
    254 		putyptoken(edp, pwd_Sdb_tmp);
    255 
    256 	if ((edp->close)(edp) < 0)
    257 		wr_error(_PATH_SMP_DB);
    258 
    259 	/* Set master.passwd permissions, in case caller forgot. */
    260 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
    261 	if (fclose(fp) == EOF)
    262 		wr_error(pname);
    263 
    264 	/* Install as the real password files. */
    265 	install(pwd_db_tmp, _PATH_MP_DB);
    266 	install(pwd_Sdb_tmp, _PATH_SMP_DB);
    267 	if (makeold)
    268 		install(oldpwdfile, _PATH_PASSWD);
    269 
    270 	/*
    271 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
    272 	 * all use flock(2) on it to block other incarnations of themselves.
    273 	 * The rename means that everything is unlocked, as the original
    274 	 * file can no longer be accessed.
    275 	 */
    276 	install(pname, _PATH_MASTERPASSWD);
    277 	exit(EXIT_SUCCESS);
    278 	/* NOTREACHED */
    279 }
    280 
    281 int
    282 scan(FILE *fp, struct passwd *pw, int *flags, int *lineno)
    283 {
    284 	static char line[LINE_MAX];
    285 	char *p;
    286 	int oflags;
    287 
    288 	if (fgets(line, sizeof(line), fp) == NULL)
    289 		return (0);
    290 	(*lineno)++;
    291 
    292 	/*
    293 	 * ``... if I swallow anything evil, put your fingers down my
    294 	 * throat...''
    295 	 *	-- The Who
    296 	 */
    297 	if ((p = strchr(line, '\n')) == NULL) {
    298 		warnx("line too long");
    299 		errno = EFTYPE;	/* XXX */
    300 		error(pname);
    301 	}
    302 	*p = '\0';
    303 	if (strcmp(line, "+") == 0)
    304 		strcpy(line, "+:::::::::");	/* pw_scan() can't handle "+" */
    305 	oflags = 0;
    306 	if (!pw_scan(line, pw, &oflags)) {
    307 		warnx("at line #%d", *lineno);
    308 		errno = EFTYPE;	/* XXX */
    309 		error(pname);
    310 	}
    311 	*flags = oflags;
    312 
    313 	return (1);
    314 }
    315 
    316 void
    317 install(const char *from, const char *to)
    318 {
    319 	char buf[MAXPATHLEN];
    320 	int sverrno;
    321 
    322 	snprintf(buf, sizeof(buf), "%s%s", prefix, to);
    323 	if (rename(from, buf)) {
    324 		sverrno = errno;
    325 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, buf);
    326 		errno = sverrno;
    327 		error(buf);
    328 	}
    329 }
    330 
    331 void
    332 wr_error(const char *str)
    333 {
    334 	char errbuf[BUFSIZ];
    335 	int sverrno;
    336 
    337 	sverrno = errno;
    338 
    339 	(void)snprintf(errbuf, sizeof(errbuf),
    340 		"attempt to write %s failed", str);
    341 
    342 	errno = sverrno;
    343 	error(errbuf);
    344 }
    345 
    346 void
    347 error(const char *str)
    348 {
    349 
    350 	warn("%s", str);
    351 	cleanup();
    352 #ifdef think_about_this_a_while_longer
    353 	fprintf(stderr, "NOTE: possible inconsistencies between "
    354 	    "text files and databases\n"
    355 	    "re-run pwd_mkdb when you have fixed the problem.\n");
    356 #endif
    357 	exit(EXIT_FAILURE);
    358 }
    359 
    360 void
    361 rm(const char *victim)
    362 {
    363 
    364 	if (unlink(victim) < 0)
    365 		warn("unlink(%s)", victim);
    366 }
    367 
    368 void
    369 cleanup(void)
    370 {
    371 
    372 	switch(clean) {
    373 	case FILE_ORIG:
    374 		rm(oldpwdfile);
    375 		/* FALLTHROUGH */
    376 	case FILE_SECURE:
    377 		rm(pwd_Sdb_tmp);
    378 		/* FALLTHROUGH */
    379 	case FILE_INSECURE:
    380 		rm(pwd_db_tmp);
    381 		/* FALLTHROUGH */
    382 	}
    383 }
    384 
    385 /*
    386  * Write entries to a database for a single user.
    387  *
    388  * The databases actually contain three copies of the original data.  Each
    389  * password file entry is converted into a rough approximation of a ``struct
    390  * passwd'', with the strings placed inline.  This object is then stored as
    391  * the data for three separate keys.  The first key * is the pw_name field
    392  * prepended by the _PW_KEYBYNAME character.  The second key is the pw_uid
    393  * field prepended by the _PW_KEYBYUID character.  The third key is the line
    394  * number in the original file prepended by the _PW_KEYBYNUM character.
    395  * (The special characters are prepended to ensure that the keys do not
    396  * collide.)
    397  */
    398 #define	COMPACT(e)	for (t = e; (*p++ = *t++) != '\0';)
    399 
    400 void
    401 putdbent(DB *dp, struct passwd *pw, const char *passwd, int flags,
    402 	 const char *fn, int lineno)
    403 {
    404 	struct passwd pwd;
    405 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p;
    406 	DBT data, key;
    407 	const char *t;
    408 	u_int32_t x;
    409 	int len;
    410 
    411 	memcpy(&pwd, pw, sizeof(pwd));
    412 	data.data = (u_char *)buf;
    413 	key.data = (u_char *)tbuf;
    414 
    415 	if (lorder != BYTE_ORDER) {
    416 		M_32_SWAP(pwd.pw_uid);
    417 		M_32_SWAP(pwd.pw_gid);
    418 		M_32_SWAP(pwd.pw_change);
    419 		M_32_SWAP(pwd.pw_expire);
    420 		M_32_SWAP(flags);
    421 	}
    422 
    423 	/* Create insecure data. */
    424 	p = buf;
    425 	COMPACT(pwd.pw_name);
    426 	COMPACT(passwd);
    427 	memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
    428 	p += sizeof(pwd.pw_uid);
    429 	memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
    430 	p += sizeof(pwd.pw_gid);
    431 	memmove(p, &pwd.pw_change, sizeof(pwd.pw_change));
    432 	p += sizeof(pwd.pw_change);
    433 	COMPACT(pwd.pw_class);
    434 	COMPACT(pwd.pw_gecos);
    435 	COMPACT(pwd.pw_dir);
    436 	COMPACT(pwd.pw_shell);
    437 	memmove(p, &pwd.pw_expire, sizeof(pwd.pw_expire));
    438 	p += sizeof(pwd.pw_expire);
    439 	x = flags;
    440 	if (lorder != BYTE_ORDER)
    441 		M_32_SWAP(x);
    442 	memmove(p, &x, sizeof(x));
    443 	p += sizeof(flags);
    444 	data.size = p - buf;
    445 
    446 	/* Store insecure by name. */
    447 	tbuf[0] = _PW_KEYBYNAME;
    448 	len = strlen(pwd.pw_name);
    449 	memmove(tbuf + 1, pwd.pw_name, len);
    450 	key.size = len + 1;
    451 	if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
    452 		wr_error(fn);
    453 
    454 	/* Store insecure by number. */
    455 	tbuf[0] = _PW_KEYBYNUM;
    456 	x = lineno;
    457 	if (lorder != BYTE_ORDER)
    458 		M_32_SWAP(x);
    459 	memmove(tbuf + 1, &x, sizeof(x));
    460 	key.size = sizeof(x) + 1;
    461 	if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
    462 		wr_error(fn);
    463 
    464 	/* Store insecure by uid. */
    465 	tbuf[0] = _PW_KEYBYUID;
    466 	memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
    467 	key.size = sizeof(pwd.pw_uid) + 1;
    468 	if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
    469 		wr_error(fn);
    470 }
    471 
    472 void
    473 putyptoken(DB *dp, const char *fn)
    474 {
    475 	DBT data, key;
    476 
    477 	key.data = (u_char *)__yp_token;
    478 	key.size = strlen(__yp_token);
    479 	data.data = (u_char *)NULL;
    480 	data.size = 0;
    481 
    482 	if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
    483 		wr_error(fn);
    484 }
    485 
    486 void
    487 usage(void)
    488 {
    489 
    490 	(void)fprintf(stderr, "usage: pwd_mkdb [-pBL] [-d directory] file\n");
    491 	exit(EXIT_FAILURE);
    492 }
    493