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