147159Sbostic /*-
261863Sbostic  * Copyright (c) 1991, 1993
361863Sbostic  *	The Regents of the University of California.  All rights reserved.
447159Sbostic  *
547159Sbostic  * %sccs.include.redist.c%
647159Sbostic  */
747159Sbostic 
847159Sbostic #ifndef lint
961863Sbostic static char copyright[] =
1061863Sbostic "@(#) Copyright (c) 1991, 1993\n\
1161863Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1247159Sbostic #endif /* not lint */
1347159Sbostic 
1447159Sbostic #ifndef lint
15*66580Spendry static char sccsid[] = "@(#)pwd_mkdb.c	8.2 (Berkeley) 04/01/94";
1647159Sbostic #endif /* not lint */
1747159Sbostic 
1847159Sbostic #include <sys/param.h>
1947159Sbostic #include <sys/stat.h>
2060596Sbostic 
2147161Sbostic #include <db.h>
22*66580Spendry #include <err.h>
2347159Sbostic #include <errno.h>
2460596Sbostic #include <fcntl.h>
2547159Sbostic #include <limits.h>
2660596Sbostic #include <pwd.h>
2760596Sbostic #include <signal.h>
2847159Sbostic #include <stdio.h>
2960596Sbostic #include <stdlib.h>
3047159Sbostic #include <string.h>
3160596Sbostic #include <unistd.h>
3247159Sbostic 
33*66580Spendry #include "pw_scan.h"
34*66580Spendry 
3547159Sbostic #define	INSECURE	1
3647159Sbostic #define	SECURE		2
3747159Sbostic #define	PERM_INSECURE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
3847159Sbostic #define	PERM_SECURE	(S_IRUSR|S_IWUSR)
3947159Sbostic 
4060596Sbostic HASHINFO openinfo = {
4160596Sbostic 	4096,		/* bsize */
4260596Sbostic 	32,		/* ffactor */
4360596Sbostic 	256,		/* nelem */
4460596Sbostic 	2048 * 1024,	/* cachesize */
4560596Sbostic 	NULL,		/* hash() */
4660596Sbostic 	0		/* lorder */
4760596Sbostic };
4860596Sbostic 
4947159Sbostic static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
5047159Sbostic static struct passwd pwd;			/* password structure */
5147159Sbostic static char *pname;				/* password file name */
5247159Sbostic 
5360596Sbostic void	cleanup __P((void));
5460596Sbostic void	error __P((char *));
5560596Sbostic void	mv __P((char *, char *));
5660596Sbostic int	scan __P((FILE *, struct passwd *));
5760596Sbostic void	usage __P((void));
5860596Sbostic 
5960596Sbostic int
6047159Sbostic main(argc, argv)
6147159Sbostic 	int argc;
6260596Sbostic 	char *argv[];
6347159Sbostic {
6460596Sbostic 	DB *dp, *edp;
6560596Sbostic 	DBT data, key;
6647159Sbostic 	FILE *fp, *oldfp;
6747159Sbostic 	sigset_t set;
68*66580Spendry 	int ch, cnt, len, makeold, tfd;
69*66580Spendry 	char *p, *t;
7047159Sbostic 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
7147159Sbostic 
7247159Sbostic 	makeold = 0;
7347159Sbostic 	while ((ch = getopt(argc, argv, "pv")) != EOF)
7447159Sbostic 		switch(ch) {
7547159Sbostic 		case 'p':			/* create V7 "file.orig" */
7647159Sbostic 			makeold = 1;
7747159Sbostic 			break;
7847159Sbostic 		case 'v':			/* backward compatible */
7947159Sbostic 			break;
8047159Sbostic 		case '?':
8147159Sbostic 		default:
8247159Sbostic 			usage();
8347159Sbostic 		}
8447159Sbostic 	argc -= optind;
8547159Sbostic 	argv += optind;
8647159Sbostic 
8747159Sbostic 	if (argc != 1)
8847159Sbostic 		usage();
8947159Sbostic 
9047159Sbostic 	/*
9160596Sbostic 	 * This could be changed to allow the user to interrupt.
9260596Sbostic 	 * Probably not worth the effort.
9347159Sbostic 	 */
9447159Sbostic 	sigemptyset(&set);
9547159Sbostic 	sigaddset(&set, SIGTSTP);
9647159Sbostic 	sigaddset(&set, SIGHUP);
9747159Sbostic 	sigaddset(&set, SIGINT);
9847159Sbostic 	sigaddset(&set, SIGQUIT);
9947159Sbostic 	sigaddset(&set, SIGTERM);
10047159Sbostic 	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
10147159Sbostic 
10247159Sbostic 	pname = *argv;
10347159Sbostic 	/* Open the original password file */
10447159Sbostic 	if (!(fp = fopen(pname, "r")))
10547159Sbostic 		error(pname);
10647159Sbostic 
10747161Sbostic 	/* Open the temporary insecure password database. */
10860596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
10960596Sbostic 	dp = dbopen(buf,
11060596Sbostic 	    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
11160596Sbostic 	if (dp == NULL)
11247159Sbostic 		error(buf);
11347159Sbostic 	clean = FILE_INSECURE;
11447159Sbostic 
11547159Sbostic 	/*
11647159Sbostic 	 * Open file for old password file.  Minor trickiness -- don't want to
11747159Sbostic 	 * chance the file already existing, since someone (stupidly) might
11847159Sbostic 	 * still be using this for permission checking.  So, open it first and
11947159Sbostic 	 * fdopen the resulting fd.  Don't really care who reads it.
12047159Sbostic 	 */
12147159Sbostic 	if (makeold) {
12260596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
12347159Sbostic 		if ((tfd = open(buf,
12447159Sbostic 		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
12547159Sbostic 			error(buf);
12660596Sbostic 		if ((oldfp = fdopen(tfd, "w")) == NULL)
12747159Sbostic 			error(buf);
12847159Sbostic 		clean = FILE_ORIG;
12947159Sbostic 	}
13047159Sbostic 
13149253Sbostic 	/*
13249253Sbostic 	 * The databases actually contain three copies of the original data.
13349253Sbostic 	 * Each password file entry is converted into a rough approximation
13449253Sbostic 	 * of a ``struct passwd'', with the strings placed inline.  This
13549253Sbostic 	 * object is then stored as the data for three separate keys.  The
13649253Sbostic 	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
13749253Sbostic 	 * character.  The second key is the pw_uid field prepended by the
13849253Sbostic 	 * _PW_KEYBYUID character.  The third key is the line number in the
13949253Sbostic 	 * original file prepended by the _PW_KEYBYNUM character.  (The special
14049253Sbostic 	 * characters are prepended to ensure that the keys do not collide.)
14149253Sbostic 	 */
14247161Sbostic 	data.data = (u_char *)buf;
14347161Sbostic 	key.data = (u_char *)tbuf;
14447159Sbostic 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
14547159Sbostic #define	COMPACT(e)	t = e; while (*p++ = *t++);
14647159Sbostic 		/* Create insecure data. */
14747159Sbostic 		p = buf;
14847159Sbostic 		COMPACT(pwd.pw_name);
14947159Sbostic 		COMPACT("*");
15060596Sbostic 		memmove(p, &pwd.pw_uid, sizeof(int));
15147159Sbostic 		p += sizeof(int);
15260596Sbostic 		memmove(p, &pwd.pw_gid, sizeof(int));
15347159Sbostic 		p += sizeof(int);
15460596Sbostic 		memmove(p, &pwd.pw_change, sizeof(time_t));
15547159Sbostic 		p += sizeof(time_t);
15647159Sbostic 		COMPACT(pwd.pw_class);
15747159Sbostic 		COMPACT(pwd.pw_gecos);
15847159Sbostic 		COMPACT(pwd.pw_dir);
15947159Sbostic 		COMPACT(pwd.pw_shell);
16060596Sbostic 		memmove(p, &pwd.pw_expire, sizeof(time_t));
16147159Sbostic 		p += sizeof(time_t);
16247161Sbostic 		data.size = p - buf;
16347159Sbostic 
16447159Sbostic 		/* Store insecure by name. */
16547159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
16647159Sbostic 		len = strlen(pwd.pw_name);
16760596Sbostic 		memmove(tbuf + 1, pwd.pw_name, len);
16847161Sbostic 		key.size = len + 1;
16947161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
17047161Sbostic 			error("put");
17147159Sbostic 
17247159Sbostic 		/* Store insecure by number. */
17347159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
17460596Sbostic 		memmove(tbuf + 1, &cnt, sizeof(cnt));
17547161Sbostic 		key.size = sizeof(cnt) + 1;
17647161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
17747161Sbostic 			error("put");
17847159Sbostic 
17947159Sbostic 		/* Store insecure by uid. */
18047159Sbostic 		tbuf[0] = _PW_KEYBYUID;
18160596Sbostic 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
18247161Sbostic 		key.size = sizeof(pwd.pw_uid) + 1;
18347161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
18447161Sbostic 			error("put");
18547159Sbostic 
18660596Sbostic 		/* Create original format password file entry */
18760596Sbostic 		if (makeold)
18860596Sbostic 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
18960596Sbostic 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
19060596Sbostic 			    pwd.pw_dir, pwd.pw_shell);
19160596Sbostic 	}
19260596Sbostic 	(void)(dp->close)(dp);
19360596Sbostic 	if (makeold) {
19460596Sbostic 		(void)fflush(oldfp);
19560596Sbostic 		(void)fclose(oldfp);
19660596Sbostic 	}
19760596Sbostic 
19860596Sbostic 	/* Open the temporary encrypted password database. */
19960596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
20060596Sbostic 	edp = dbopen(buf,
20160596Sbostic 	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
20260596Sbostic 	if (!edp)
20360596Sbostic 		error(buf);
20460596Sbostic 	clean = FILE_SECURE;
20560596Sbostic 
20660596Sbostic 	rewind(fp);
20760596Sbostic 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
20860596Sbostic 
20947159Sbostic 		/* Create secure data. */
21047159Sbostic 		p = buf;
21147159Sbostic 		COMPACT(pwd.pw_name);
21247159Sbostic 		COMPACT(pwd.pw_passwd);
21360596Sbostic 		memmove(p, &pwd.pw_uid, sizeof(int));
21447159Sbostic 		p += sizeof(int);
21560596Sbostic 		memmove(p, &pwd.pw_gid, sizeof(int));
21647159Sbostic 		p += sizeof(int);
21760596Sbostic 		memmove(p, &pwd.pw_change, sizeof(time_t));
21847159Sbostic 		p += sizeof(time_t);
21947159Sbostic 		COMPACT(pwd.pw_class);
22047159Sbostic 		COMPACT(pwd.pw_gecos);
22147159Sbostic 		COMPACT(pwd.pw_dir);
22247159Sbostic 		COMPACT(pwd.pw_shell);
22360596Sbostic 		memmove(p, &pwd.pw_expire, sizeof(time_t));
22447159Sbostic 		p += sizeof(time_t);
22547161Sbostic 		data.size = p - buf;
22647159Sbostic 
22747159Sbostic 		/* Store secure by name. */
22847159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
22947159Sbostic 		len = strlen(pwd.pw_name);
23060596Sbostic 		memmove(tbuf + 1, pwd.pw_name, len);
23147161Sbostic 		key.size = len + 1;
23247161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
23347161Sbostic 			error("put");
23447159Sbostic 
23547159Sbostic 		/* Store secure by number. */
23647159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
23760596Sbostic 		memmove(tbuf + 1, &cnt, sizeof(cnt));
23847161Sbostic 		key.size = sizeof(cnt) + 1;
23947161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
24047161Sbostic 			error("put");
24147159Sbostic 
24247159Sbostic 		/* Store secure by uid. */
24347159Sbostic 		tbuf[0] = _PW_KEYBYUID;
24460596Sbostic 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
24547161Sbostic 		key.size = sizeof(pwd.pw_uid) + 1;
24647161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
24747161Sbostic 			error("put");
24860596Sbostic 	}
24947159Sbostic 
25047161Sbostic 	(void)(edp->close)(edp);
25147159Sbostic 
25247159Sbostic 	/* Set master.passwd permissions, in case caller forgot. */
25347162Sbostic 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
25447159Sbostic 	(void)fclose(fp);
25547159Sbostic 
25647159Sbostic 	/* Install as the real password files. */
25760596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
25847159Sbostic 	mv(buf, _PATH_MP_DB);
25960596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
26047159Sbostic 	mv(buf, _PATH_SMP_DB);
26147159Sbostic 	if (makeold) {
26260596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
26347159Sbostic 		mv(buf, _PATH_PASSWD);
26447159Sbostic 	}
26547159Sbostic 	/*
26647159Sbostic 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
26747159Sbostic 	 * all use flock(2) on it to block other incarnations of themselves.
26847159Sbostic 	 * The rename means that everything is unlocked, as the original file
26947159Sbostic 	 * can no longer be accessed.
27047159Sbostic 	 */
27147159Sbostic 	mv(pname, _PATH_MASTERPASSWD);
27247159Sbostic 	exit(0);
27347159Sbostic }
27447159Sbostic 
27560596Sbostic int
27647159Sbostic scan(fp, pw)
27747159Sbostic 	FILE *fp;
27847159Sbostic 	struct passwd *pw;
27947159Sbostic {
28047159Sbostic 	static int lcnt;
28147159Sbostic 	static char line[LINE_MAX];
28247159Sbostic 	char *p;
28347159Sbostic 
28447159Sbostic 	if (!fgets(line, sizeof(line), fp))
285*66580Spendry 		return (0);
28647159Sbostic 	++lcnt;
28747159Sbostic 	/*
28847159Sbostic 	 * ``... if I swallow anything evil, put your fingers down my
28947159Sbostic 	 * throat...''
29047159Sbostic 	 *	-- The Who
29147159Sbostic 	 */
29260596Sbostic 	if (!(p = strchr(line, '\n'))) {
293*66580Spendry 		warnx("line too long");
29447159Sbostic 		goto fmt;
29547159Sbostic 
29647159Sbostic 	}
29747159Sbostic 	*p = '\0';
29847159Sbostic 	if (!pw_scan(line, pw)) {
299*66580Spendry 		warnx("at line #%d", lcnt);
300*66580Spendry fmt:		errno = EFTYPE;	/* XXX */
30147159Sbostic 		error(pname);
30247159Sbostic 	}
303*66580Spendry 
304*66580Spendry 	return (1);
30547159Sbostic }
30647159Sbostic 
30760596Sbostic void
30847159Sbostic mv(from, to)
30947159Sbostic 	char *from, *to;
31047159Sbostic {
31147159Sbostic 	char buf[MAXPATHLEN];
31247159Sbostic 
31347159Sbostic 	if (rename(from, to)) {
314*66580Spendry 		int sverrno = errno;
31560596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
31647159Sbostic 		errno = sverrno;
31747159Sbostic 		error(buf);
31847159Sbostic 	}
31947159Sbostic }
32047159Sbostic 
32160596Sbostic void
32247161Sbostic error(name)
32347161Sbostic 	char *name;
32447161Sbostic {
325*66580Spendry 
326*66580Spendry 	warn(name);
32747161Sbostic 	cleanup();
32847161Sbostic 	exit(1);
32947161Sbostic }
33047161Sbostic 
33160596Sbostic void
33247159Sbostic cleanup()
33347159Sbostic {
33447159Sbostic 	char buf[MAXPATHLEN];
33547159Sbostic 
33647159Sbostic 	switch(clean) {
33747159Sbostic 	case FILE_ORIG:
33860596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
33947159Sbostic 		(void)unlink(buf);
34047159Sbostic 		/* FALLTHROUGH */
34147159Sbostic 	case FILE_SECURE:
34260596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
34347159Sbostic 		(void)unlink(buf);
34447159Sbostic 		/* FALLTHROUGH */
34547159Sbostic 	case FILE_INSECURE:
34660596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
34747159Sbostic 		(void)unlink(buf);
34847159Sbostic 	}
34947159Sbostic }
35047159Sbostic 
35160596Sbostic void
35247159Sbostic usage()
35347159Sbostic {
354*66580Spendry 
35547159Sbostic 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
35647159Sbostic 	exit(1);
35747159Sbostic }
358