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