xref: /original-bsd/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision e58c8952)
1 /*-
2  * Copyright (c) 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1991, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)pwd_mkdb.c	8.4 (Berkeley) 04/16/94";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 
21 #include <db.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <pwd.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "pw_scan.h"
34 
35 #define	INSECURE	1
36 #define	SECURE		2
37 #define	PERM_INSECURE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
38 #define	PERM_SECURE	(S_IRUSR|S_IWUSR)
39 
40 HASHINFO openinfo = {
41 	4096,		/* bsize */
42 	32,		/* ffactor */
43 	256,		/* nelem */
44 	2048 * 1024,	/* cachesize */
45 	NULL,		/* hash() */
46 	0		/* lorder */
47 };
48 
49 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
50 static struct passwd pwd;			/* password structure */
51 static char *pname;				/* password file name */
52 
53 void	cleanup __P((void));
54 void	error __P((char *));
55 void	mv __P((char *, char *));
56 int	scan __P((FILE *, struct passwd *));
57 void	usage __P((void));
58 
59 int
60 main(argc, argv)
61 	int argc;
62 	char *argv[];
63 {
64 	DB *dp, *edp;
65 	DBT data, key;
66 	FILE *fp, *oldfp;
67 	sigset_t set;
68 	int ch, cnt, len, makeold, tfd;
69 	char *p, *t;
70 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
71 
72 	makeold = 0;
73 	while ((ch = getopt(argc, argv, "pv")) != EOF)
74 		switch(ch) {
75 		case 'p':			/* create V7 "file.orig" */
76 			makeold = 1;
77 			break;
78 		case 'v':			/* backward compatible */
79 			break;
80 		case '?':
81 		default:
82 			usage();
83 		}
84 	argc -= optind;
85 	argv += optind;
86 
87 	if (argc != 1)
88 		usage();
89 
90 	/*
91 	 * This could be changed to allow the user to interrupt.
92 	 * Probably not worth the effort.
93 	 */
94 	sigemptyset(&set);
95 	sigaddset(&set, SIGTSTP);
96 	sigaddset(&set, SIGHUP);
97 	sigaddset(&set, SIGINT);
98 	sigaddset(&set, SIGQUIT);
99 	sigaddset(&set, SIGTERM);
100 	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
101 
102 	pname = *argv;
103 	/* Open the original password file */
104 	if (!(fp = fopen(pname, "r")))
105 		error(pname);
106 
107 	/* Open the temporary insecure password database. */
108 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
109 	dp = dbopen(buf,
110 	    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
111 	if (dp == NULL)
112 		error(buf);
113 	clean = FILE_INSECURE;
114 
115 	/*
116 	 * Open file for old password file.  Minor trickiness -- don't want to
117 	 * chance the file already existing, since someone (stupidly) might
118 	 * still be using this for permission checking.  So, open it first and
119 	 * fdopen the resulting fd.  The resulting file should be readable by
120 	 * everyone, don't trust the user's umask.
121 	 */
122 	if (makeold) {
123 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
124 		if ((tfd = open(buf,
125 		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
126 			error(buf);
127 		(void)fchmod(tfd, PERM_INSECURE);
128 		if ((oldfp = fdopen(tfd, "w")) == NULL)
129 			error(buf);
130 		clean = FILE_ORIG;
131 	}
132 
133 	/*
134 	 * The databases actually contain three copies of the original data.
135 	 * Each password file entry is converted into a rough approximation
136 	 * of a ``struct passwd'', with the strings placed inline.  This
137 	 * object is then stored as the data for three separate keys.  The
138 	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
139 	 * character.  The second key is the pw_uid field prepended by the
140 	 * _PW_KEYBYUID character.  The third key is the line number in the
141 	 * original file prepended by the _PW_KEYBYNUM character.  (The special
142 	 * characters are prepended to ensure that the keys do not collide.)
143 	 */
144 	data.data = (u_char *)buf;
145 	key.data = (u_char *)tbuf;
146 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
147 #define	COMPACT(e)	t = e; while (*p++ = *t++);
148 		/* Create insecure data. */
149 		p = buf;
150 		COMPACT(pwd.pw_name);
151 		COMPACT("*");
152 		memmove(p, &pwd.pw_uid, sizeof(int));
153 		p += sizeof(int);
154 		memmove(p, &pwd.pw_gid, sizeof(int));
155 		p += sizeof(int);
156 		memmove(p, &pwd.pw_change, sizeof(time_t));
157 		p += sizeof(time_t);
158 		COMPACT(pwd.pw_class);
159 		COMPACT(pwd.pw_gecos);
160 		COMPACT(pwd.pw_dir);
161 		COMPACT(pwd.pw_shell);
162 		memmove(p, &pwd.pw_expire, sizeof(time_t));
163 		p += sizeof(time_t);
164 		data.size = p - buf;
165 
166 		/* Store insecure by name. */
167 		tbuf[0] = _PW_KEYBYNAME;
168 		len = strlen(pwd.pw_name);
169 		memmove(tbuf + 1, pwd.pw_name, len);
170 		key.size = len + 1;
171 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
172 			error("put");
173 
174 		/* Store insecure by number. */
175 		tbuf[0] = _PW_KEYBYNUM;
176 		memmove(tbuf + 1, &cnt, sizeof(cnt));
177 		key.size = sizeof(cnt) + 1;
178 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
179 			error("put");
180 
181 		/* Store insecure by uid. */
182 		tbuf[0] = _PW_KEYBYUID;
183 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
184 		key.size = sizeof(pwd.pw_uid) + 1;
185 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
186 			error("put");
187 
188 		/* Create original format password file entry */
189 		if (makeold)
190 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
191 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
192 			    pwd.pw_dir, pwd.pw_shell);
193 	}
194 	(void)(dp->close)(dp);
195 	if (makeold) {
196 		(void)fflush(oldfp);
197 		(void)fclose(oldfp);
198 	}
199 
200 	/* Open the temporary encrypted password database. */
201 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
202 	edp = dbopen(buf,
203 	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
204 	if (!edp)
205 		error(buf);
206 	clean = FILE_SECURE;
207 
208 	rewind(fp);
209 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
210 
211 		/* Create secure data. */
212 		p = buf;
213 		COMPACT(pwd.pw_name);
214 		COMPACT(pwd.pw_passwd);
215 		memmove(p, &pwd.pw_uid, sizeof(int));
216 		p += sizeof(int);
217 		memmove(p, &pwd.pw_gid, sizeof(int));
218 		p += sizeof(int);
219 		memmove(p, &pwd.pw_change, sizeof(time_t));
220 		p += sizeof(time_t);
221 		COMPACT(pwd.pw_class);
222 		COMPACT(pwd.pw_gecos);
223 		COMPACT(pwd.pw_dir);
224 		COMPACT(pwd.pw_shell);
225 		memmove(p, &pwd.pw_expire, sizeof(time_t));
226 		p += sizeof(time_t);
227 		data.size = p - buf;
228 
229 		/* Store secure by name. */
230 		tbuf[0] = _PW_KEYBYNAME;
231 		len = strlen(pwd.pw_name);
232 		memmove(tbuf + 1, pwd.pw_name, len);
233 		key.size = len + 1;
234 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
235 			error("put");
236 
237 		/* Store secure by number. */
238 		tbuf[0] = _PW_KEYBYNUM;
239 		memmove(tbuf + 1, &cnt, sizeof(cnt));
240 		key.size = sizeof(cnt) + 1;
241 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
242 			error("put");
243 
244 		/* Store secure by uid. */
245 		tbuf[0] = _PW_KEYBYUID;
246 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
247 		key.size = sizeof(pwd.pw_uid) + 1;
248 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
249 			error("put");
250 	}
251 
252 	(void)(edp->close)(edp);
253 
254 	/* Set master.passwd permissions, in case caller forgot. */
255 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
256 	(void)fclose(fp);
257 
258 	/* Install as the real password files. */
259 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
260 	mv(buf, _PATH_MP_DB);
261 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
262 	mv(buf, _PATH_SMP_DB);
263 	if (makeold) {
264 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
265 		mv(buf, _PATH_PASSWD);
266 	}
267 	/*
268 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
269 	 * all use flock(2) on it to block other incarnations of themselves.
270 	 * The rename means that everything is unlocked, as the original file
271 	 * can no longer be accessed.
272 	 */
273 	mv(pname, _PATH_MASTERPASSWD);
274 	exit(0);
275 }
276 
277 int
278 scan(fp, pw)
279 	FILE *fp;
280 	struct passwd *pw;
281 {
282 	static int lcnt;
283 	static char line[LINE_MAX];
284 	char *p;
285 
286 	if (!fgets(line, sizeof(line), fp))
287 		return (0);
288 	++lcnt;
289 	/*
290 	 * ``... if I swallow anything evil, put your fingers down my
291 	 * throat...''
292 	 *	-- The Who
293 	 */
294 	if (!(p = strchr(line, '\n'))) {
295 		warnx("line too long");
296 		goto fmt;
297 
298 	}
299 	*p = '\0';
300 	if (!pw_scan(line, pw)) {
301 		warnx("at line #%d", lcnt);
302 fmt:		errno = EFTYPE;	/* XXX */
303 		error(pname);
304 	}
305 
306 	return (1);
307 }
308 
309 void
310 mv(from, to)
311 	char *from, *to;
312 {
313 	char buf[MAXPATHLEN];
314 
315 	if (rename(from, to)) {
316 		int sverrno = errno;
317 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
318 		errno = sverrno;
319 		error(buf);
320 	}
321 }
322 
323 void
324 error(name)
325 	char *name;
326 {
327 
328 	warn(name);
329 	cleanup();
330 	exit(1);
331 }
332 
333 void
334 cleanup()
335 {
336 	char buf[MAXPATHLEN];
337 
338 	switch(clean) {
339 	case FILE_ORIG:
340 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
341 		(void)unlink(buf);
342 		/* FALLTHROUGH */
343 	case FILE_SECURE:
344 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
345 		(void)unlink(buf);
346 		/* FALLTHROUGH */
347 	case FILE_INSECURE:
348 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
349 		(void)unlink(buf);
350 	}
351 }
352 
353 void
354 usage()
355 {
356 
357 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
358 	exit(1);
359 }
360