xref: /freebsd/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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 
79 void	cleanup __P((void));
80 void	error __P((char *));
81 void	mv __P((char *, char *));
82 int	scan __P((FILE *, struct passwd *));
83 void	usage __P((void));
84 
85 int
86 main(argc, argv)
87 	int argc;
88 	char *argv[];
89 {
90 	DB *dp, *edp;
91 	DBT data, key;
92 	FILE *fp, *oldfp;
93 	sigset_t set;
94 	int ch, cnt, len, makeold, tfd;
95 	char *p, *t;
96 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
97 
98 	makeold = 0;
99 	while ((ch = getopt(argc, argv, "pv")) != EOF)
100 		switch(ch) {
101 		case 'p':			/* create V7 "file.orig" */
102 			makeold = 1;
103 			break;
104 		case 'v':			/* backward compatible */
105 			break;
106 		case '?':
107 		default:
108 			usage();
109 		}
110 	argc -= optind;
111 	argv += optind;
112 
113 	if (argc != 1)
114 		usage();
115 
116 	/*
117 	 * This could be changed to allow the user to interrupt.
118 	 * Probably not worth the effort.
119 	 */
120 	sigemptyset(&set);
121 	sigaddset(&set, SIGTSTP);
122 	sigaddset(&set, SIGHUP);
123 	sigaddset(&set, SIGINT);
124 	sigaddset(&set, SIGQUIT);
125 	sigaddset(&set, SIGTERM);
126 	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
127 
128 	/* We don't care what the user wants. */
129 	(void)umask(0);
130 
131 	pname = *argv;
132 	/* Open the original password file */
133 	if (!(fp = fopen(pname, "r")))
134 		error(pname);
135 
136 	/* Open the temporary insecure password database. */
137 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
138 	dp = dbopen(buf,
139 	    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
140 	if (dp == NULL)
141 		error(buf);
142 	clean = FILE_INSECURE;
143 
144 	/*
145 	 * Open file for old password file.  Minor trickiness -- don't want to
146 	 * chance the file already existing, since someone (stupidly) might
147 	 * still be using this for permission checking.  So, open it first and
148 	 * fdopen the resulting fd.  The resulting file should be readable by
149 	 * everyone.
150 	 */
151 	if (makeold) {
152 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
153 		if ((tfd = open(buf,
154 		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
155 			error(buf);
156 		if ((oldfp = fdopen(tfd, "w")) == NULL)
157 			error(buf);
158 		clean = FILE_ORIG;
159 	}
160 
161 	/*
162 	 * The databases actually contain three copies of the original data.
163 	 * Each password file entry is converted into a rough approximation
164 	 * of a ``struct passwd'', with the strings placed inline.  This
165 	 * object is then stored as the data for three separate keys.  The
166 	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
167 	 * character.  The second key is the pw_uid field prepended by the
168 	 * _PW_KEYBYUID character.  The third key is the line number in the
169 	 * original file prepended by the _PW_KEYBYNUM character.  (The special
170 	 * characters are prepended to ensure that the keys do not collide.)
171 	 */
172 	data.data = (u_char *)buf;
173 	key.data = (u_char *)tbuf;
174 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
175 #define	COMPACT(e)	t = e; while (*p++ = *t++);
176 		/* Create insecure data. */
177 		p = buf;
178 		COMPACT(pwd.pw_name);
179 		COMPACT("*");
180 		memmove(p, &pwd.pw_uid, sizeof(int));
181 		p += sizeof(int);
182 		memmove(p, &pwd.pw_gid, sizeof(int));
183 		p += sizeof(int);
184 		memmove(p, &pwd.pw_change, sizeof(time_t));
185 		p += sizeof(time_t);
186 		COMPACT(pwd.pw_class);
187 		COMPACT(pwd.pw_gecos);
188 		COMPACT(pwd.pw_dir);
189 		COMPACT(pwd.pw_shell);
190 		memmove(p, &pwd.pw_expire, sizeof(time_t));
191 		p += sizeof(time_t);
192 		data.size = p - buf;
193 
194 		/* Store insecure by name. */
195 		tbuf[0] = _PW_KEYBYNAME;
196 		len = strlen(pwd.pw_name);
197 		memmove(tbuf + 1, pwd.pw_name, len);
198 		key.size = len + 1;
199 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
200 			error("put");
201 
202 		/* Store insecure by number. */
203 		tbuf[0] = _PW_KEYBYNUM;
204 		memmove(tbuf + 1, &cnt, sizeof(cnt));
205 		key.size = sizeof(cnt) + 1;
206 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
207 			error("put");
208 
209 		/* Store insecure by uid. */
210 		tbuf[0] = _PW_KEYBYUID;
211 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
212 		key.size = sizeof(pwd.pw_uid) + 1;
213 		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
214 			error("put");
215 
216 		/* Create original format password file entry */
217 		if (makeold)
218 			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
219 			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
220 			    pwd.pw_dir, pwd.pw_shell);
221 	}
222 	(void)(dp->close)(dp);
223 	if (makeold) {
224 		(void)fflush(oldfp);
225 		(void)fclose(oldfp);
226 	}
227 
228 	/* Open the temporary encrypted password database. */
229 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
230 	edp = dbopen(buf,
231 	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
232 	if (!edp)
233 		error(buf);
234 	clean = FILE_SECURE;
235 
236 	rewind(fp);
237 	for (cnt = 1; scan(fp, &pwd); ++cnt) {
238 
239 		/* Create secure data. */
240 		p = buf;
241 		COMPACT(pwd.pw_name);
242 		COMPACT(pwd.pw_passwd);
243 		memmove(p, &pwd.pw_uid, sizeof(int));
244 		p += sizeof(int);
245 		memmove(p, &pwd.pw_gid, sizeof(int));
246 		p += sizeof(int);
247 		memmove(p, &pwd.pw_change, sizeof(time_t));
248 		p += sizeof(time_t);
249 		COMPACT(pwd.pw_class);
250 		COMPACT(pwd.pw_gecos);
251 		COMPACT(pwd.pw_dir);
252 		COMPACT(pwd.pw_shell);
253 		memmove(p, &pwd.pw_expire, sizeof(time_t));
254 		p += sizeof(time_t);
255 		data.size = p - buf;
256 
257 		/* Store secure by name. */
258 		tbuf[0] = _PW_KEYBYNAME;
259 		len = strlen(pwd.pw_name);
260 		memmove(tbuf + 1, pwd.pw_name, len);
261 		key.size = len + 1;
262 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
263 			error("put");
264 
265 		/* Store secure by number. */
266 		tbuf[0] = _PW_KEYBYNUM;
267 		memmove(tbuf + 1, &cnt, sizeof(cnt));
268 		key.size = sizeof(cnt) + 1;
269 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
270 			error("put");
271 
272 		/* Store secure by uid. */
273 		tbuf[0] = _PW_KEYBYUID;
274 		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
275 		key.size = sizeof(pwd.pw_uid) + 1;
276 		if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
277 			error("put");
278 	}
279 
280 	(void)(edp->close)(edp);
281 
282 	/* Set master.passwd permissions, in case caller forgot. */
283 	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
284 	(void)fclose(fp);
285 
286 	/* Install as the real password files. */
287 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
288 	mv(buf, _PATH_MP_DB);
289 	(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
290 	mv(buf, _PATH_SMP_DB);
291 	if (makeold) {
292 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
293 		mv(buf, _PATH_PASSWD);
294 	}
295 	/*
296 	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
297 	 * all use flock(2) on it to block other incarnations of themselves.
298 	 * The rename means that everything is unlocked, as the original file
299 	 * can no longer be accessed.
300 	 */
301 	mv(pname, _PATH_MASTERPASSWD);
302 	exit(0);
303 }
304 
305 int
306 scan(fp, pw)
307 	FILE *fp;
308 	struct passwd *pw;
309 {
310 	static int lcnt;
311 	static char line[LINE_MAX];
312 	char *p;
313 
314 	if (!fgets(line, sizeof(line), fp))
315 		return (0);
316 	++lcnt;
317 	/*
318 	 * ``... if I swallow anything evil, put your fingers down my
319 	 * throat...''
320 	 *	-- The Who
321 	 */
322 	if (!(p = strchr(line, '\n'))) {
323 		warnx("line too long");
324 		goto fmt;
325 
326 	}
327 	*p = '\0';
328 	if (!pw_scan(line, pw)) {
329 		warnx("at line #%d", lcnt);
330 fmt:		errno = EFTYPE;	/* XXX */
331 		error(pname);
332 	}
333 
334 	return (1);
335 }
336 
337 void
338 mv(from, to)
339 	char *from, *to;
340 {
341 	char buf[MAXPATHLEN];
342 
343 	if (rename(from, to)) {
344 		int sverrno = errno;
345 		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
346 		errno = sverrno;
347 		error(buf);
348 	}
349 }
350 
351 void
352 error(name)
353 	char *name;
354 {
355 
356 	warn(name);
357 	cleanup();
358 	exit(1);
359 }
360 
361 void
362 cleanup()
363 {
364 	char buf[MAXPATHLEN];
365 
366 	switch(clean) {
367 	case FILE_ORIG:
368 		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
369 		(void)unlink(buf);
370 		/* FALLTHROUGH */
371 	case FILE_SECURE:
372 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
373 		(void)unlink(buf);
374 		/* FALLTHROUGH */
375 	case FILE_INSECURE:
376 		(void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
377 		(void)unlink(buf);
378 	}
379 }
380 
381 void
382 usage()
383 {
384 
385 	(void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
386 	exit(1);
387 }
388