xref: /freebsd/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
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, 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 	data.data = (u_char *)buf;
179 	key.data = (u_char *)tbuf;
180 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
181 		if(pwd.pw_name[0] == '+') {
182 			if(pwd.pw_name[1] && !yp_enabled) {
183 				yp_enabled = 1;
184 			} else if(!pwd.pw_name[1]) {
185 				yp_enabled = -1;
186 			}
187 		}
188 #define	COMPACT(e)	t = e; while (*p++ = *t++);
189 		/* Create insecure data. */
190 		p = buf;
191 		COMPACT(pwd.pw_name);
192 		COMPACT("*");
193 		memmove(p, &pwd.pw_uid, sizeof(int));
194 		p += sizeof(int);
195 		memmove(p, &pwd.pw_gid, sizeof(int));
196 		p += sizeof(int);
197 		memmove(p, &pwd.pw_change, sizeof(time_t));
198 		p += sizeof(time_t);
199 		COMPACT(pwd.pw_class);
200 		COMPACT(pwd.pw_gecos);
201 		COMPACT(pwd.pw_dir);
202 		COMPACT(pwd.pw_shell);
203 		memmove(p, &pwd.pw_expire, sizeof(time_t));
204 		p += sizeof(time_t);
205 		memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
206 		p += sizeof pwd.pw_fields;
207 		data.size = p - buf;
208 
209 		/* Store insecure by name. */
210 		tbuf[0] = _PW_KEYBYNAME;
211 		len = strlen(pwd.pw_name);
212 		memmove(tbuf + 1, pwd.pw_name, len);
213 		key.size = len + 1;
214 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
215 			error("put");
216 
217 		/* Store insecure by number. */
218 		tbuf[0] = _PW_KEYBYNUM;
219 		memmove(tbuf + 1, &cnt, sizeof(cnt));
220 		key.size = sizeof(cnt) + 1;
221 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
222 			error("put");
223 
224 		/* Store insecure by uid. */
225 		tbuf[0] = _PW_KEYBYUID;
226 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
227 		key.size = sizeof(pwd.pw_uid) + 1;
228 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
229 			error("put");
230 
231 		/* Create original format password file entry */
232 		if (makeold)
233 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
234 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
235 			    pwd.pw_dir, pwd.pw_shell);
236 	}
237 	/* If YP enabled, set flag. */
238 	if(yp_enabled) {
239 		buf[0] = yp_enabled + 2;
240 		data.size = 1;
241 		tbuf[0] = _PW_KEYYPENABLED;
242 		key.size = 1;
243 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
244 			error("put");
245 	}
246 
247 	(void)(dp->close)(dp);
248 	if (makeold) {
249 		(void)fflush(oldfp);
250 		(void)fclose(oldfp);
251 	}
252 
253 	/* Open the temporary encrypted password database. */
254 	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
255 	edp = dbopen(buf,
256 	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
257 	if (!edp)
258 		error(buf);
259 	clean = FILE_SECURE;
260 
261 	rewind(fp);
262 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
263 
264 		/* Create secure data. */
265 		p = buf;
266 		COMPACT(pwd.pw_name);
267 		COMPACT(pwd.pw_passwd);
268 		memmove(p, &pwd.pw_uid, sizeof(int));
269 		p += sizeof(int);
270 		memmove(p, &pwd.pw_gid, sizeof(int));
271 		p += sizeof(int);
272 		memmove(p, &pwd.pw_change, sizeof(time_t));
273 		p += sizeof(time_t);
274 		COMPACT(pwd.pw_class);
275 		COMPACT(pwd.pw_gecos);
276 		COMPACT(pwd.pw_dir);
277 		COMPACT(pwd.pw_shell);
278 		memmove(p, &pwd.pw_expire, sizeof(time_t));
279 		p += sizeof(time_t);
280 		memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
281 		p += sizeof pwd.pw_fields;
282 		data.size = p - buf;
283 
284 		/* Store secure by name. */
285 		tbuf[0] = _PW_KEYBYNAME;
286 		len = strlen(pwd.pw_name);
287 		memmove(tbuf + 1, pwd.pw_name, len);
288 		key.size = len + 1;
289 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
290 			error("put");
291 
292 		/* Store secure by number. */
293 		tbuf[0] = _PW_KEYBYNUM;
294 		memmove(tbuf + 1, &cnt, sizeof(cnt));
295 		key.size = sizeof(cnt) + 1;
296 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
297 			error("put");
298 
299 		/* Store secure by uid. */
300 		tbuf[0] = _PW_KEYBYUID;
301 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
302 		key.size = sizeof(pwd.pw_uid) + 1;
303 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
304 			error("put");
305 	}
306 	/* If YP enabled, set flag. */
307 	if(yp_enabled) {
308 		buf[0] = yp_enabled + 2;
309 		data.size = 1;
310 		tbuf[0] = _PW_KEYYPENABLED;
311 		key.size = 1;
312 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
313 			error("put");
314 	}
315 
316 	(void)(edp->close)(edp);
317 
318 	/* Set master.passwd permissions, in case caller forgot. */
319 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
320 	(void)fclose(fp);
321 
322 	/* Install as the real password files. */
323 	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
324 	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
325 	mv(buf, buf2);
326 	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
327 	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
328 	mv(buf, buf2);
329 	if (makeold) {
330 		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
331 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
332 		mv(buf, buf2);
333 	}
334 	/*
335 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
336 	 * all use flock(2) on it to block other incarnations of themselves.
337 	 * The rename means that everything is unlocked, as the original file
338 	 * can no longer be accessed.
339 	 */
340 	(void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
341 	mv(pname, buf);
342 	exit(0);
343 }
344 
345 int
346 scan(fp, pw)
347 	FILE *fp;
348 	struct passwd *pw;
349 {
350 	static int lcnt;
351 	static char line[LINE_MAX];
352 	char *p;
353 
354 	if (!fgets(line, sizeof(line), fp))
355 		return (0);
356 	++lcnt;
357 	/*
358 	 * ``... if I swallow anything evil, put your fingers down my
359 	 * throat...''
360 	 *	-- The Who
361 	 */
362 	if (!(p = strchr(line, '\n'))) {
363 		warnx("line too long");
364 		goto fmt;
365 
366 	}
367 	*p = '\0';
368 	if (!pw_scan(line, pw)) {
369 		warnx("at line #%d", lcnt);
370 fmt:		errno = EFTYPE;	/* XXX */
371 		error(pname);
372 	}
373 
374 	return (1);
375 }
376 
377 void
378 mv(from, to)
379 	char *from, *to;
380 {
381 	char buf[MAXPATHLEN];
382 
383 	if (rename(from, to)) {
384 		int sverrno = errno;
385 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
386 		errno = sverrno;
387 		error(buf);
388 	}
389 }
390 
391 void
392 error(name)
393 	char *name;
394 {
395 
396 	warn(name);
397 	cleanup();
398 	exit(1);
399 }
400 
401 void
402 cleanup()
403 {
404 	char buf[MAXPATHLEN];
405 
406 	switch(clean) {
407 	case FILE_ORIG:
408 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
409 		(void)unlink(buf);
410 		/* FALLTHROUGH */
411 	case FILE_SECURE:
412 		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
413 		(void)unlink(buf);
414 		/* FALLTHROUGH */
415 	case FILE_INSECURE:
416 		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
417 		(void)unlink(buf);
418 	}
419 }
420 
421 void
422 usage()
423 {
424 
425 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d <dest dir>] file\n");
426 	exit(1);
427 }
428