147159Sbostic /*-
266650Spendry  * Copyright (c) 1991, 1993, 1994
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[] =
1066650Spendry "@(#) Copyright (c) 1991, 1993, 1994\n\
1161863Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1247159Sbostic #endif /* not lint */
1347159Sbostic 
1447159Sbostic #ifndef lint
15*67042Sbostic static char sccsid[] = "@(#)pwd_mkdb.c	8.5 (Berkeley) 04/20/94";
1647159Sbostic #endif /* not lint */
1747159Sbostic 
1847159Sbostic #include <sys/param.h>
1947159Sbostic #include <sys/stat.h>
2060596Sbostic 
2147161Sbostic #include <db.h>
2266580Spendry #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 
3366580Spendry #include "pw_scan.h"
3466580Spendry 
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
main(argc,argv)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;
6866580Spendry 	int ch, cnt, len, makeold, tfd;
6966580Spendry 	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 
102*67042Sbostic 	/* We don't care what the user wants. */
103*67042Sbostic 	(void)umask(0);
104*67042Sbostic 
10547159Sbostic 	pname = *argv;
10647159Sbostic 	/* Open the original password file */
10747159Sbostic 	if (!(fp = fopen(pname, "r")))
10847159Sbostic 		error(pname);
10947159Sbostic 
11047161Sbostic 	/* Open the temporary insecure password database. */
11160596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
11260596Sbostic 	dp = dbopen(buf,
11360596Sbostic 	    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
11460596Sbostic 	if (dp == NULL)
11547159Sbostic 		error(buf);
11647159Sbostic 	clean = FILE_INSECURE;
11747159Sbostic 
11847159Sbostic 	/*
11947159Sbostic 	 * Open file for old password file.  Minor trickiness -- don't want to
12047159Sbostic 	 * chance the file already existing, since someone (stupidly) might
12147159Sbostic 	 * still be using this for permission checking.  So, open it first and
12266816Sbostic 	 * fdopen the resulting fd.  The resulting file should be readable by
123*67042Sbostic 	 * everyone.
12447159Sbostic 	 */
12547159Sbostic 	if (makeold) {
12660596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
12747159Sbostic 		if ((tfd = open(buf,
12847159Sbostic 		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
12947159Sbostic 			error(buf);
13060596Sbostic 		if ((oldfp = fdopen(tfd, "w")) == NULL)
13147159Sbostic 			error(buf);
13247159Sbostic 		clean = FILE_ORIG;
13347159Sbostic 	}
13447159Sbostic 
13549253Sbostic 	/*
13649253Sbostic 	 * The databases actually contain three copies of the original data.
13749253Sbostic 	 * Each password file entry is converted into a rough approximation
13849253Sbostic 	 * of a ``struct passwd'', with the strings placed inline.  This
13949253Sbostic 	 * object is then stored as the data for three separate keys.  The
14049253Sbostic 	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
14149253Sbostic 	 * character.  The second key is the pw_uid field prepended by the
14249253Sbostic 	 * _PW_KEYBYUID character.  The third key is the line number in the
14349253Sbostic 	 * original file prepended by the _PW_KEYBYNUM character.  (The special
14449253Sbostic 	 * characters are prepended to ensure that the keys do not collide.)
14549253Sbostic 	 */
14647161Sbostic 	data.data = (u_char *)buf;
14747161Sbostic 	key.data = (u_char *)tbuf;
14847159Sbostic 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
14947159Sbostic #define	COMPACT(e)	t = e; while (*p++ = *t++);
15047159Sbostic 		/* Create insecure data. */
15147159Sbostic 		p = buf;
15247159Sbostic 		COMPACT(pwd.pw_name);
15347159Sbostic 		COMPACT("*");
15460596Sbostic 		memmove(p, &pwd.pw_uid, sizeof(int));
15547159Sbostic 		p += sizeof(int);
15660596Sbostic 		memmove(p, &pwd.pw_gid, sizeof(int));
15747159Sbostic 		p += sizeof(int);
15860596Sbostic 		memmove(p, &pwd.pw_change, sizeof(time_t));
15947159Sbostic 		p += sizeof(time_t);
16047159Sbostic 		COMPACT(pwd.pw_class);
16147159Sbostic 		COMPACT(pwd.pw_gecos);
16247159Sbostic 		COMPACT(pwd.pw_dir);
16347159Sbostic 		COMPACT(pwd.pw_shell);
16460596Sbostic 		memmove(p, &pwd.pw_expire, sizeof(time_t));
16547159Sbostic 		p += sizeof(time_t);
16647161Sbostic 		data.size = p - buf;
16747159Sbostic 
16847159Sbostic 		/* Store insecure by name. */
16947159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
17047159Sbostic 		len = strlen(pwd.pw_name);
17160596Sbostic 		memmove(tbuf + 1, pwd.pw_name, len);
17247161Sbostic 		key.size = len + 1;
17347161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
17447161Sbostic 			error("put");
17547159Sbostic 
17647159Sbostic 		/* Store insecure by number. */
17747159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
17860596Sbostic 		memmove(tbuf + 1, &cnt, sizeof(cnt));
17947161Sbostic 		key.size = sizeof(cnt) + 1;
18047161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
18147161Sbostic 			error("put");
18247159Sbostic 
18347159Sbostic 		/* Store insecure by uid. */
18447159Sbostic 		tbuf[0] = _PW_KEYBYUID;
18560596Sbostic 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
18647161Sbostic 		key.size = sizeof(pwd.pw_uid) + 1;
18747161Sbostic 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
18847161Sbostic 			error("put");
18947159Sbostic 
19060596Sbostic 		/* Create original format password file entry */
19160596Sbostic 		if (makeold)
19260596Sbostic 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
19360596Sbostic 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
19460596Sbostic 			    pwd.pw_dir, pwd.pw_shell);
19560596Sbostic 	}
19660596Sbostic 	(void)(dp->close)(dp);
19760596Sbostic 	if (makeold) {
19860596Sbostic 		(void)fflush(oldfp);
19960596Sbostic 		(void)fclose(oldfp);
20060596Sbostic 	}
20160596Sbostic 
20260596Sbostic 	/* Open the temporary encrypted password database. */
20360596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
20460596Sbostic 	edp = dbopen(buf,
20560596Sbostic 	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
20660596Sbostic 	if (!edp)
20760596Sbostic 		error(buf);
20860596Sbostic 	clean = FILE_SECURE;
20960596Sbostic 
21060596Sbostic 	rewind(fp);
21160596Sbostic 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
21260596Sbostic 
21347159Sbostic 		/* Create secure data. */
21447159Sbostic 		p = buf;
21547159Sbostic 		COMPACT(pwd.pw_name);
21647159Sbostic 		COMPACT(pwd.pw_passwd);
21760596Sbostic 		memmove(p, &pwd.pw_uid, sizeof(int));
21847159Sbostic 		p += sizeof(int);
21960596Sbostic 		memmove(p, &pwd.pw_gid, sizeof(int));
22047159Sbostic 		p += sizeof(int);
22160596Sbostic 		memmove(p, &pwd.pw_change, sizeof(time_t));
22247159Sbostic 		p += sizeof(time_t);
22347159Sbostic 		COMPACT(pwd.pw_class);
22447159Sbostic 		COMPACT(pwd.pw_gecos);
22547159Sbostic 		COMPACT(pwd.pw_dir);
22647159Sbostic 		COMPACT(pwd.pw_shell);
22760596Sbostic 		memmove(p, &pwd.pw_expire, sizeof(time_t));
22847159Sbostic 		p += sizeof(time_t);
22947161Sbostic 		data.size = p - buf;
23047159Sbostic 
23147159Sbostic 		/* Store secure by name. */
23247159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
23347159Sbostic 		len = strlen(pwd.pw_name);
23460596Sbostic 		memmove(tbuf + 1, pwd.pw_name, len);
23547161Sbostic 		key.size = len + 1;
23647161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
23747161Sbostic 			error("put");
23847159Sbostic 
23947159Sbostic 		/* Store secure by number. */
24047159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
24160596Sbostic 		memmove(tbuf + 1, &cnt, sizeof(cnt));
24247161Sbostic 		key.size = sizeof(cnt) + 1;
24347161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
24447161Sbostic 			error("put");
24547159Sbostic 
24647159Sbostic 		/* Store secure by uid. */
24747159Sbostic 		tbuf[0] = _PW_KEYBYUID;
24860596Sbostic 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
24947161Sbostic 		key.size = sizeof(pwd.pw_uid) + 1;
25047161Sbostic 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
25147161Sbostic 			error("put");
25260596Sbostic 	}
25347159Sbostic 
25447161Sbostic 	(void)(edp->close)(edp);
25547159Sbostic 
25647159Sbostic 	/* Set master.passwd permissions, in case caller forgot. */
25747162Sbostic 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
25847159Sbostic 	(void)fclose(fp);
25947159Sbostic 
26047159Sbostic 	/* Install as the real password files. */
26160596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
26247159Sbostic 	mv(buf, _PATH_MP_DB);
26360596Sbostic 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
26447159Sbostic 	mv(buf, _PATH_SMP_DB);
26547159Sbostic 	if (makeold) {
26660596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
26747159Sbostic 		mv(buf, _PATH_PASSWD);
26847159Sbostic 	}
26947159Sbostic 	/*
27047159Sbostic 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
27147159Sbostic 	 * all use flock(2) on it to block other incarnations of themselves.
27247159Sbostic 	 * The rename means that everything is unlocked, as the original file
27347159Sbostic 	 * can no longer be accessed.
27447159Sbostic 	 */
27547159Sbostic 	mv(pname, _PATH_MASTERPASSWD);
27647159Sbostic 	exit(0);
27747159Sbostic }
27847159Sbostic 
27960596Sbostic int
scan(fp,pw)28047159Sbostic scan(fp, pw)
28147159Sbostic 	FILE *fp;
28247159Sbostic 	struct passwd *pw;
28347159Sbostic {
28447159Sbostic 	static int lcnt;
28547159Sbostic 	static char line[LINE_MAX];
28647159Sbostic 	char *p;
28747159Sbostic 
28847159Sbostic 	if (!fgets(line, sizeof(line), fp))
28966580Spendry 		return (0);
29047159Sbostic 	++lcnt;
29147159Sbostic 	/*
29247159Sbostic 	 * ``... if I swallow anything evil, put your fingers down my
29347159Sbostic 	 * throat...''
29447159Sbostic 	 *	-- The Who
29547159Sbostic 	 */
29660596Sbostic 	if (!(p = strchr(line, '\n'))) {
29766580Spendry 		warnx("line too long");
29847159Sbostic 		goto fmt;
29947159Sbostic 
30047159Sbostic 	}
30147159Sbostic 	*p = '\0';
30247159Sbostic 	if (!pw_scan(line, pw)) {
30366580Spendry 		warnx("at line #%d", lcnt);
30466580Spendry fmt:		errno = EFTYPE;	/* XXX */
30547159Sbostic 		error(pname);
30647159Sbostic 	}
30766580Spendry 
30866580Spendry 	return (1);
30947159Sbostic }
31047159Sbostic 
31160596Sbostic void
mv(from,to)31247159Sbostic mv(from, to)
31347159Sbostic 	char *from, *to;
31447159Sbostic {
31547159Sbostic 	char buf[MAXPATHLEN];
31647159Sbostic 
31747159Sbostic 	if (rename(from, to)) {
31866580Spendry 		int sverrno = errno;
31960596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
32047159Sbostic 		errno = sverrno;
32147159Sbostic 		error(buf);
32247159Sbostic 	}
32347159Sbostic }
32447159Sbostic 
32560596Sbostic void
error(name)32647161Sbostic error(name)
32747161Sbostic 	char *name;
32847161Sbostic {
32966580Spendry 
33066580Spendry 	warn(name);
33147161Sbostic 	cleanup();
33247161Sbostic 	exit(1);
33347161Sbostic }
33447161Sbostic 
33560596Sbostic void
cleanup()33647159Sbostic cleanup()
33747159Sbostic {
33847159Sbostic 	char buf[MAXPATHLEN];
33947159Sbostic 
34047159Sbostic 	switch(clean) {
34147159Sbostic 	case FILE_ORIG:
34260596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
34347159Sbostic 		(void)unlink(buf);
34447159Sbostic 		/* FALLTHROUGH */
34547159Sbostic 	case FILE_SECURE:
34660596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
34747159Sbostic 		(void)unlink(buf);
34847159Sbostic 		/* FALLTHROUGH */
34947159Sbostic 	case FILE_INSECURE:
35060596Sbostic 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
35147159Sbostic 		(void)unlink(buf);
35247159Sbostic 	}
35347159Sbostic }
35447159Sbostic 
35560596Sbostic void
usage()35647159Sbostic usage()
35747159Sbostic {
35866580Spendry 
35947159Sbostic 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
36047159Sbostic 	exit(1);
36147159Sbostic }
362