1*47159Sbostic /*-
2*47159Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*47159Sbostic  * All rights reserved.
4*47159Sbostic  *
5*47159Sbostic  * %sccs.include.redist.c%
6*47159Sbostic  */
7*47159Sbostic 
8*47159Sbostic #ifndef lint
9*47159Sbostic char copyright[] =
10*47159Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
11*47159Sbostic  All rights reserved.\n";
12*47159Sbostic #endif /* not lint */
13*47159Sbostic 
14*47159Sbostic #ifndef lint
15*47159Sbostic static char sccsid[] = "@(#)pwd_mkdb.c	5.1 (Berkeley) 03/08/91";
16*47159Sbostic #endif /* not lint */
17*47159Sbostic 
18*47159Sbostic #include <sys/param.h>
19*47159Sbostic #include <sys/stat.h>
20*47159Sbostic #include <signal.h>
21*47159Sbostic #include <fcntl.h>
22*47159Sbostic #include <ndbm.h>
23*47159Sbostic #include <pwd.h>
24*47159Sbostic #include <errno.h>
25*47159Sbostic #include <limits.h>
26*47159Sbostic #include <stdio.h>
27*47159Sbostic #include <string.h>
28*47159Sbostic 
29*47159Sbostic #define	INSECURE	1
30*47159Sbostic #define	SECURE		2
31*47159Sbostic #define	PERM_INSECURE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
32*47159Sbostic #define	PERM_SECURE	(S_IRUSR|S_IWUSR)
33*47159Sbostic 
34*47159Sbostic char *progname = "pwd_mkdb";
35*47159Sbostic 
36*47159Sbostic static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
37*47159Sbostic static struct passwd pwd;			/* password structure */
38*47159Sbostic static char *pname;				/* password file name */
39*47159Sbostic 
40*47159Sbostic main(argc, argv)
41*47159Sbostic 	int argc;
42*47159Sbostic 	char **argv;
43*47159Sbostic {
44*47159Sbostic 	extern int optind;
45*47159Sbostic 	register int len, makeold;
46*47159Sbostic 	register char *p, *t;
47*47159Sbostic 	FILE *fp, *oldfp;
48*47159Sbostic 	DBM *dp, *edp;
49*47159Sbostic 	sigset_t set;
50*47159Sbostic 	datum key, data;
51*47159Sbostic 	int ch, cnt, tfd;
52*47159Sbostic 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
53*47159Sbostic 
54*47159Sbostic 	makeold = 0;
55*47159Sbostic 	while ((ch = getopt(argc, argv, "pv")) != EOF)
56*47159Sbostic 		switch(ch) {
57*47159Sbostic 		case 'p':			/* create V7 "file.orig" */
58*47159Sbostic 			makeold = 1;
59*47159Sbostic 			break;
60*47159Sbostic 		case 'v':			/* backward compatible */
61*47159Sbostic 			break;
62*47159Sbostic 		case '?':
63*47159Sbostic 		default:
64*47159Sbostic 			usage();
65*47159Sbostic 		}
66*47159Sbostic 	argc -= optind;
67*47159Sbostic 	argv += optind;
68*47159Sbostic 
69*47159Sbostic 	if (argc != 1)
70*47159Sbostic 		usage();
71*47159Sbostic 
72*47159Sbostic 	/*
73*47159Sbostic 	 * This could be done to allow the user to interrupt.  Probably
74*47159Sbostic 	 * not worth the effort.
75*47159Sbostic 	 */
76*47159Sbostic 	sigemptyset(&set);
77*47159Sbostic 	sigaddset(&set, SIGTSTP);
78*47159Sbostic 	sigaddset(&set, SIGHUP);
79*47159Sbostic 	sigaddset(&set, SIGINT);
80*47159Sbostic 	sigaddset(&set, SIGQUIT);
81*47159Sbostic 	sigaddset(&set, SIGTERM);
82*47159Sbostic 	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
83*47159Sbostic 
84*47159Sbostic 	pname = *argv;
85*47159Sbostic 	/* Open the original password file */
86*47159Sbostic 	if (!(fp = fopen(pname, "r")))
87*47159Sbostic 		error(pname);
88*47159Sbostic 
89*47159Sbostic 	/* Open the password database. */
90*47159Sbostic 	(void)sprintf(buf, "%s.tmp", _PATH_MP_DB);
91*47159Sbostic 	if (!(dp = dbm_open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)))
92*47159Sbostic 		error(buf);
93*47159Sbostic 	clean = FILE_INSECURE;
94*47159Sbostic 
95*47159Sbostic 	/* Open the encrypted password database. */
96*47159Sbostic 	(void)sprintf(buf, "%s.tmp", _PATH_SMP_DB);
97*47159Sbostic 	if (!(edp = dbm_open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_SECURE)))
98*47159Sbostic 		error(buf);
99*47159Sbostic 	clean = FILE_SECURE;
100*47159Sbostic 
101*47159Sbostic 	/*
102*47159Sbostic 	 * Open file for old password file.  Minor trickiness -- don't want to
103*47159Sbostic 	 * chance the file already existing, since someone (stupidly) might
104*47159Sbostic 	 * still be using this for permission checking.  So, open it first and
105*47159Sbostic 	 * fdopen the resulting fd.  Don't really care who reads it.
106*47159Sbostic 	 */
107*47159Sbostic 	if (makeold) {
108*47159Sbostic 		(void)sprintf(buf, "%s.orig", pname);
109*47159Sbostic 		if ((tfd = open(buf,
110*47159Sbostic 		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
111*47159Sbostic 			error(buf);
112*47159Sbostic 		if (!(oldfp = fdopen(tfd, "w")))
113*47159Sbostic 			error(buf);
114*47159Sbostic 		clean = FILE_ORIG;
115*47159Sbostic 	}
116*47159Sbostic 
117*47159Sbostic 	data.dptr = buf;
118*47159Sbostic 	key.dptr = tbuf;
119*47159Sbostic 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
120*47159Sbostic #define	COMPACT(e)	t = e; while (*p++ = *t++);
121*47159Sbostic 		/* Create insecure data. */
122*47159Sbostic 		p = buf;
123*47159Sbostic 		COMPACT(pwd.pw_name);
124*47159Sbostic 		COMPACT("*");
125*47159Sbostic 		bcopy((char *)&pwd.pw_uid, p, sizeof(int));
126*47159Sbostic 		p += sizeof(int);
127*47159Sbostic 		bcopy((char *)&pwd.pw_gid, p, sizeof(int));
128*47159Sbostic 		p += sizeof(int);
129*47159Sbostic 		bcopy((char *)&pwd.pw_change, p, sizeof(time_t));
130*47159Sbostic 		p += sizeof(time_t);
131*47159Sbostic 		COMPACT(pwd.pw_class);
132*47159Sbostic 		COMPACT(pwd.pw_gecos);
133*47159Sbostic 		COMPACT(pwd.pw_dir);
134*47159Sbostic 		COMPACT(pwd.pw_shell);
135*47159Sbostic 		bcopy((char *)&pwd.pw_expire, p, sizeof(time_t));
136*47159Sbostic 		p += sizeof(time_t);
137*47159Sbostic 		data.dsize = p - buf;
138*47159Sbostic 
139*47159Sbostic 		/* Store insecure by name. */
140*47159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
141*47159Sbostic 		len = strlen(pwd.pw_name);
142*47159Sbostic 		bcopy(pwd.pw_name, tbuf + 1, len);
143*47159Sbostic 		key.dsize = len + 1;
144*47159Sbostic 		if (dbm_store(dp, key, data, DBM_INSERT) < 0)
145*47159Sbostic 			error("dbm file");
146*47159Sbostic 
147*47159Sbostic 		/* Store insecure by number. */
148*47159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
149*47159Sbostic 		bcopy((char *)&cnt, tbuf + 1, sizeof(cnt));
150*47159Sbostic 		key.dsize = sizeof(cnt) + 1;
151*47159Sbostic 		if (dbm_store(dp, key, data, DBM_INSERT) < 0)
152*47159Sbostic 			error("dbm file");
153*47159Sbostic 
154*47159Sbostic 		/* Store insecure by uid. */
155*47159Sbostic 		tbuf[0] = _PW_KEYBYUID;
156*47159Sbostic 		bcopy((char *)&pwd.pw_uid, tbuf + 1, sizeof(pwd.pw_uid));
157*47159Sbostic 		key.dsize = sizeof(pwd.pw_uid) + 1;
158*47159Sbostic 		if (dbm_store(dp, key, data, DBM_INSERT) < 0)
159*47159Sbostic 			error("dbm file");
160*47159Sbostic 
161*47159Sbostic 		/* Create secure data. */
162*47159Sbostic 		p = buf;
163*47159Sbostic 		COMPACT(pwd.pw_name);
164*47159Sbostic 		COMPACT(pwd.pw_passwd);
165*47159Sbostic 		bcopy((char *)&pwd.pw_uid, p, sizeof(int));
166*47159Sbostic 		p += sizeof(int);
167*47159Sbostic 		bcopy((char *)&pwd.pw_gid, p, sizeof(int));
168*47159Sbostic 		p += sizeof(int);
169*47159Sbostic 		bcopy((char *)&pwd.pw_change, p, sizeof(time_t));
170*47159Sbostic 		p += sizeof(time_t);
171*47159Sbostic 		COMPACT(pwd.pw_class);
172*47159Sbostic 		COMPACT(pwd.pw_gecos);
173*47159Sbostic 		COMPACT(pwd.pw_dir);
174*47159Sbostic 		COMPACT(pwd.pw_shell);
175*47159Sbostic 		bcopy((char *)&pwd.pw_expire, p, sizeof(time_t));
176*47159Sbostic 		p += sizeof(time_t);
177*47159Sbostic 		data.dsize = p - buf;
178*47159Sbostic 
179*47159Sbostic 		/* Store secure by name. */
180*47159Sbostic 		tbuf[0] = _PW_KEYBYNAME;
181*47159Sbostic 		len = strlen(pwd.pw_name);
182*47159Sbostic 		bcopy(pwd.pw_name, tbuf + 1, len);
183*47159Sbostic 		key.dsize = len + 1;
184*47159Sbostic 		if (dbm_store(edp, key, data, DBM_INSERT) < 0)
185*47159Sbostic 			error("dbm file");
186*47159Sbostic 
187*47159Sbostic 		/* Store secure by number. */
188*47159Sbostic 		tbuf[0] = _PW_KEYBYNUM;
189*47159Sbostic 		bcopy((char *)&cnt, tbuf + 1, sizeof(cnt));
190*47159Sbostic 		key.dsize = sizeof(cnt) + 1;
191*47159Sbostic 		if (dbm_store(edp, key, data, DBM_INSERT) < 0)
192*47159Sbostic 			error("dbm file");
193*47159Sbostic 
194*47159Sbostic 		/* Store secure by uid. */
195*47159Sbostic 		tbuf[0] = _PW_KEYBYUID;
196*47159Sbostic 		bcopy((char *)&pwd.pw_uid, tbuf + 1, sizeof(pwd.pw_uid));
197*47159Sbostic 		key.dsize = sizeof(pwd.pw_uid) + 1;
198*47159Sbostic 		if (dbm_store(edp, key, data, DBM_INSERT) < 0)
199*47159Sbostic 			error("dbm file");
200*47159Sbostic 
201*47159Sbostic 		/* Create original format password file entry */
202*47159Sbostic 		if (makeold)
203*47159Sbostic 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
204*47159Sbostic 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
205*47159Sbostic 			    pwd.pw_dir, pwd.pw_shell);
206*47159Sbostic 	}
207*47159Sbostic 	(void)dbm_close(edp);
208*47159Sbostic 	(void)dbm_close(dp);
209*47159Sbostic 	if (makeold)
210*47159Sbostic 		(void)fclose(oldfp);
211*47159Sbostic 
212*47159Sbostic 	/* Set master.passwd permissions, in case caller forgot. */
213*47159Sbostic 	(void)fchmod(fp, S_IRUSR|S_IWUSR);
214*47159Sbostic 	(void)fclose(fp);
215*47159Sbostic 
216*47159Sbostic 	/* Install as the real password files. */
217*47159Sbostic 	(void)sprintf(buf, "%s.tmp", _PATH_MP_DB);
218*47159Sbostic 	mv(buf, _PATH_MP_DB);
219*47159Sbostic 	(void)sprintf(buf, "%s.tmp", _PATH_SMP_DB);
220*47159Sbostic 	mv(buf, _PATH_SMP_DB);
221*47159Sbostic 	if (makeold) {
222*47159Sbostic 		(void)sprintf(buf, "%s.orig", pname);
223*47159Sbostic 		mv(buf, _PATH_PASSWD);
224*47159Sbostic 	}
225*47159Sbostic 	/*
226*47159Sbostic 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
227*47159Sbostic 	 * all use flock(2) on it to block other incarnations of themselves.
228*47159Sbostic 	 * The rename means that everything is unlocked, as the original file
229*47159Sbostic 	 * can no longer be accessed.
230*47159Sbostic 	 */
231*47159Sbostic 	mv(pname, _PATH_MASTERPASSWD);
232*47159Sbostic 	exit(0);
233*47159Sbostic }
234*47159Sbostic 
235*47159Sbostic scan(fp, pw)
236*47159Sbostic 	FILE *fp;
237*47159Sbostic 	struct passwd *pw;
238*47159Sbostic {
239*47159Sbostic 	static int lcnt;
240*47159Sbostic 	static char line[LINE_MAX];
241*47159Sbostic 	char *p;
242*47159Sbostic 
243*47159Sbostic 	if (!fgets(line, sizeof(line), fp))
244*47159Sbostic 		return(0);
245*47159Sbostic 	++lcnt;
246*47159Sbostic 	/*
247*47159Sbostic 	 * ``... if I swallow anything evil, put your fingers down my
248*47159Sbostic 	 * throat...''
249*47159Sbostic 	 *	-- The Who
250*47159Sbostic 	 */
251*47159Sbostic 	if (!(p = index(line, '\n'))) {
252*47159Sbostic 		(void)fprintf(stderr, "pwd_mkdb: line too long\n");
253*47159Sbostic 		goto fmt;
254*47159Sbostic 
255*47159Sbostic 	}
256*47159Sbostic 	*p = '\0';
257*47159Sbostic 	if (!pw_scan(line, pw)) {
258*47159Sbostic 		(void)fprintf(stderr, "pwd_mkdb: at line #%d.\n", lcnt);
259*47159Sbostic fmt:		errno = EFTYPE;
260*47159Sbostic 		error(pname);
261*47159Sbostic 		exit(1);
262*47159Sbostic 	}
263*47159Sbostic }
264*47159Sbostic 
265*47159Sbostic mv(from, to)
266*47159Sbostic 	char *from, *to;
267*47159Sbostic {
268*47159Sbostic 	int sverrno;
269*47159Sbostic 	char buf[MAXPATHLEN];
270*47159Sbostic 
271*47159Sbostic 	if (rename(from, to)) {
272*47159Sbostic 		sverrno = errno;
273*47159Sbostic 		(void)sprintf(buf, "%s to %s", from, to);
274*47159Sbostic 		errno = sverrno;
275*47159Sbostic 		error(buf);
276*47159Sbostic 	}
277*47159Sbostic }
278*47159Sbostic 
279*47159Sbostic cleanup()
280*47159Sbostic {
281*47159Sbostic 	char buf[MAXPATHLEN];
282*47159Sbostic 
283*47159Sbostic 	switch(clean) {
284*47159Sbostic 	case FILE_ORIG:
285*47159Sbostic 		(void)sprintf(buf, "%s.orig", pname);
286*47159Sbostic 		(void)unlink(buf);
287*47159Sbostic 		/* FALLTHROUGH */
288*47159Sbostic 	case FILE_SECURE:
289*47159Sbostic 		(void)sprintf(buf, "%s.tmp", _PATH_SMP_DB);
290*47159Sbostic 		(void)unlink(buf);
291*47159Sbostic 		/* FALLTHROUGH */
292*47159Sbostic 	case FILE_INSECURE:
293*47159Sbostic 		(void)sprintf(buf, "%s.tmp", _PATH_MP_DB);
294*47159Sbostic 		(void)unlink(buf);
295*47159Sbostic 	}
296*47159Sbostic }
297*47159Sbostic 
298*47159Sbostic error(name)
299*47159Sbostic 	char *name;
300*47159Sbostic {
301*47159Sbostic 	(void)fprintf(stderr, "pwd_mkdb: %s: %s\n", name, strerror(errno));
302*47159Sbostic 	cleanup();
303*47159Sbostic 	exit(1);
304*47159Sbostic }
305*47159Sbostic 
306*47159Sbostic usage()
307*47159Sbostic {
308*47159Sbostic 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
309*47159Sbostic 	exit(1);
310*47159Sbostic }
311