pwd_mkdb.c revision 17125
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
35static 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
41static 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
66HASHINFO openinfo = {
67	4096,		/* bsize */
68	32,		/* ffactor */
69	256,		/* nelem */
70	2048 * 1024,	/* cachesize */
71	NULL,		/* hash() */
72	0		/* lorder */
73};
74
75static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
76static struct passwd pwd;			/* password structure */
77static char *pname;				/* password file name */
78static char prefix[MAXPATHLEN];
79
80void	cleanup __P((void));
81void	error __P((char *));
82void	cp __P((char *, char *, mode_t mode));
83void	mv __P((char *, char *));
84int	scan __P((FILE *, struct passwd *));
85void	usage __P((void));
86
87int
88main(argc, argv)
89	int argc;
90	char *argv[];
91{
92	DB *dp, *sdp, *pw_db;
93	DBT data, sdata, key;
94	FILE *fp, *oldfp;
95	sigset_t set;
96	int ch, cnt, ypcnt, len, makeold, tfd, yp_enabled = 0;
97	char *p, *t;
98	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
99	char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
100	char buf2[MAXPATHLEN];
101	char sbuf2[MAXPATHLEN];
102	char *username;
103	u_int method, methoduid;
104
105	strcpy(prefix, _PATH_PWD);
106	makeold = 0;
107	username = NULL;
108	while ((ch = getopt(argc, argv, "d:pu:v")) != EOF)
109		switch(ch) {
110		case 'd':
111			strcpy(prefix, optarg);
112			break;
113		case 'p':			/* create V7 "file.orig" */
114			makeold = 1;
115			break;
116		case 'u':			/* only update this record */
117			username = optarg;
118			break;
119		case 'v':                       /* backward compatible */
120			break;
121		case '?':
122		default:
123			usage();
124		}
125	argc -= optind;
126	argv += optind;
127
128	if (argc != 1 || (username && (*username == '+' || *username == '-')))
129		usage();
130
131	/*
132	 * This could be changed to allow the user to interrupt.
133	 * Probably not worth the effort.
134	 */
135	sigemptyset(&set);
136	sigaddset(&set, SIGTSTP);
137	sigaddset(&set, SIGHUP);
138	sigaddset(&set, SIGINT);
139	sigaddset(&set, SIGQUIT);
140	sigaddset(&set, SIGTERM);
141	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
142
143	/* We don't care what the user wants. */
144	(void)umask(0);
145
146	pname = *argv;
147	/* Open the original password file */
148	if (!(fp = fopen(pname, "r")))
149		error(pname);
150
151	/* Open the temporary insecure password database. */
152	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
153	(void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
154	if (username) {
155		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
156		(void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
157
158		clean = FILE_INSECURE;
159		cp(buf2, buf, PERM_INSECURE);
160		dp = dbopen(buf,
161		    O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
162		if (dp == NULL)
163			error(buf);
164
165		clean = FILE_SECURE;
166		cp(sbuf2, sbuf, PERM_SECURE);
167		sdp = dbopen(sbuf,
168		    O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
169		if (sdp == NULL)
170			error(sbuf);
171
172		/*
173		 * Do some trouble to check if we should store this users
174		 * uid. Don't use getpwnam/getpwuid as that interferes
175		 * with NIS.
176		 */
177		pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
178		if (!pw_db)
179			error(_MP_DB);
180		buf[0] = _PW_KEYBYNAME;
181		len = strlen(username);
182
183		/* Only check that username fits in buffer */
184		memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
185		key.data = (u_char *)buf;
186		key.size = len + 1;
187		if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
188			p = (char *)data.data;
189
190			/* jump over pw_name and pw_passwd, to get to pw_uid */
191			while (*p++)
192				;
193			while (*p++)
194				;
195
196			buf[0] = _PW_KEYBYUID;
197			memmove(buf + 1, p, sizeof(int));
198			key.data = (u_char *)buf;
199			key.size = sizeof(int) + 1;
200
201			if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
202				/* First field of data.data holds pw_pwname */
203				if (!strcmp(data.data, username))
204					methoduid = 0;
205				else
206					methoduid = R_NOOVERWRITE;
207			} else {
208				methoduid = R_NOOVERWRITE;
209			}
210		} else {
211			methoduid = R_NOOVERWRITE;
212		}
213		(void)(pw_db->close)(pw_db);
214		method = 0;
215	} else {
216		dp = dbopen(buf,
217		    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
218		if (dp == NULL)
219			error(buf);
220		clean = FILE_INSECURE;
221
222		sdp = dbopen(sbuf,
223		    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
224		if (sdp == NULL)
225			error(sbuf);
226		clean = FILE_SECURE;
227
228		method = R_NOOVERWRITE;
229		methoduid = R_NOOVERWRITE;
230	}
231
232	/*
233	 * Open file for old password file.  Minor trickiness -- don't want to
234	 * chance the file already existing, since someone (stupidly) might
235	 * still be using this for permission checking.  So, open it first and
236	 * fdopen the resulting fd.  The resulting file should be readable by
237	 * everyone.
238	 */
239	if (makeold) {
240		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
241		if ((tfd = open(buf,
242		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
243			error(buf);
244		if ((oldfp = fdopen(tfd, "w")) == NULL)
245			error(buf);
246		clean = FILE_ORIG;
247	}
248
249	/*
250	 * The databases actually contain three copies of the original data.
251	 * Each password file entry is converted into a rough approximation
252	 * of a ``struct passwd'', with the strings placed inline.  This
253	 * object is then stored as the data for three separate keys.  The
254	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
255	 * character.  The second key is the pw_uid field prepended by the
256	 * _PW_KEYBYUID character.  The third key is the line number in the
257	 * original file prepended by the _PW_KEYBYNUM character.  (The special
258	 * characters are prepended to ensure that the keys do not collide.)
259	 */
260	ypcnt = 1;
261	data.data = (u_char *)buf;
262	sdata.data = (u_char *)sbuf;
263	key.data = (u_char *)tbuf;
264	for (cnt = 1; scan(fp, &pwd); ++cnt) {
265		if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')
266			yp_enabled = 1;
267#define	COMPACT(e)	t = e; while (*p++ = *t++);
268		if (!username || (strcmp(username, pwd.pw_name) == 0)) {
269			/* Create insecure data. */
270			p = buf;
271			COMPACT(pwd.pw_name);
272			COMPACT("*");
273			memmove(p, &pwd.pw_uid, sizeof(int));
274			p += sizeof(int);
275			memmove(p, &pwd.pw_gid, sizeof(int));
276			p += sizeof(int);
277			memmove(p, &pwd.pw_change, sizeof(time_t));
278			p += sizeof(time_t);
279			COMPACT(pwd.pw_class);
280			COMPACT(pwd.pw_gecos);
281			COMPACT(pwd.pw_dir);
282			COMPACT(pwd.pw_shell);
283			memmove(p, &pwd.pw_expire, sizeof(time_t));
284			p += sizeof(time_t);
285			memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
286			p += sizeof pwd.pw_fields;
287			data.size = p - buf;
288
289			/* Create secure data. */
290			p = sbuf;
291			COMPACT(pwd.pw_name);
292			COMPACT(pwd.pw_passwd);
293			memmove(p, &pwd.pw_uid, sizeof(int));
294			p += sizeof(int);
295			memmove(p, &pwd.pw_gid, sizeof(int));
296			p += sizeof(int);
297			memmove(p, &pwd.pw_change, sizeof(time_t));
298			p += sizeof(time_t);
299			COMPACT(pwd.pw_class);
300			COMPACT(pwd.pw_gecos);
301			COMPACT(pwd.pw_dir);
302			COMPACT(pwd.pw_shell);
303			memmove(p, &pwd.pw_expire, sizeof(time_t));
304			p += sizeof(time_t);
305			memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
306			p += sizeof pwd.pw_fields;
307			sdata.size = p - sbuf;
308
309			/* Store insecure by name. */
310			tbuf[0] = _PW_KEYBYNAME;
311			len = strlen(pwd.pw_name);
312			memmove(tbuf + 1, pwd.pw_name, len);
313			key.size = len + 1;
314			if ((dp->put)(dp, &key, &data, method) == -1)
315				error("put");
316
317			/* Store insecure by number. */
318			tbuf[0] = _PW_KEYBYNUM;
319			memmove(tbuf + 1, &cnt, sizeof(cnt));
320			key.size = sizeof(cnt) + 1;
321			if ((dp->put)(dp, &key, &data, method) == -1)
322				error("put");
323
324			/* Store insecure by uid. */
325			tbuf[0] = _PW_KEYBYUID;
326			memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
327			key.size = sizeof(pwd.pw_uid) + 1;
328			if ((dp->put)(dp, &key, &data, methoduid) == -1)
329				error("put");
330
331			/* Store secure by name. */
332			tbuf[0] = _PW_KEYBYNAME;
333			len = strlen(pwd.pw_name);
334			memmove(tbuf + 1, pwd.pw_name, len);
335			key.size = len + 1;
336			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
337				error("put");
338
339			/* Store secure by number. */
340			tbuf[0] = _PW_KEYBYNUM;
341			memmove(tbuf + 1, &cnt, sizeof(cnt));
342			key.size = sizeof(cnt) + 1;
343			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
344				error("put");
345
346			/* Store secure by uid. */
347			tbuf[0] = _PW_KEYBYUID;
348			memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
349			key.size = sizeof(pwd.pw_uid) + 1;
350			if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
351				error("put");
352
353			/* Store insecure and secure special plus and special minus */
354			if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
355				tbuf[0] = _PW_KEYYPBYNUM;
356				memmove(tbuf + 1, &ypcnt, sizeof(cnt));
357				ypcnt++;
358				key.size = sizeof(cnt) + 1;
359				if ((dp->put)(dp, &key, &data, method) == -1)
360					error("put");
361				if ((sdp->put)(sdp, &key, &sdata, method) == -1)
362					error("put");
363			}
364		}
365		/* Create original format password file entry */
366		if (makeold)
367			(void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
368			    pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
369			    pwd.pw_dir, pwd.pw_shell);
370	}
371	/* If YP enabled, set flag. */
372	if (yp_enabled) {
373		buf[0] = yp_enabled + 2;
374		data.size = 1;
375		tbuf[0] = _PW_KEYYPENABLED;
376		key.size = 1;
377		if ((dp->put)(dp, &key, &data, method) == -1)
378			error("put");
379		if ((sdp->put)(sdp, &key, &data, method) == -1)
380			error("put");
381	}
382
383	(void)(dp->close)(dp);
384	(void)(sdp->close)(sdp);
385	if (makeold) {
386		(void)fflush(oldfp);
387		(void)fclose(oldfp);
388	}
389
390	/* Set master.passwd permissions, in case caller forgot. */
391	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
392	(void)fclose(fp);
393
394	/* Install as the real password files. */
395	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
396	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
397	mv(buf, buf2);
398	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
399	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
400	mv(buf, buf2);
401	if (makeold) {
402		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
403		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
404		mv(buf, buf2);
405	}
406	/*
407	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
408	 * all use flock(2) on it to block other incarnations of themselves.
409	 * The rename means that everything is unlocked, as the original file
410	 * can no longer be accessed.
411	 */
412	(void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
413	mv(pname, buf);
414	exit(0);
415}
416
417int
418scan(fp, pw)
419	FILE *fp;
420	struct passwd *pw;
421{
422	static int lcnt;
423	static char line[LINE_MAX];
424	char *p;
425
426	if (!fgets(line, sizeof(line), fp))
427		return (0);
428	++lcnt;
429	/*
430	 * ``... if I swallow anything evil, put your fingers down my
431	 * throat...''
432	 *	-- The Who
433	 */
434	if (!(p = strchr(line, '\n'))) {
435		warnx("line too long");
436		goto fmt;
437
438	}
439	*p = '\0';
440	if (!pw_scan(line, pw)) {
441		warnx("at line #%d", lcnt);
442fmt:		errno = EFTYPE;	/* XXX */
443		error(pname);
444	}
445
446	return (1);
447}
448
449void
450cp(from, to, mode)
451	char *from, *to;
452	mode_t mode;
453{
454	static char buf[MAXBSIZE];
455	int from_fd, rcount, to_fd, wcount;
456
457	if ((from_fd = open(from, O_RDONLY, 0)) < 0)
458		error(from);
459	if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
460		error(to);
461	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
462		wcount = write(to_fd, buf, rcount);
463		if (rcount != wcount || wcount == -1) {
464			int sverrno = errno;
465
466			(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
467			errno = sverrno;
468			error(buf);
469		}
470	}
471	if (rcount < 0) {
472		int sverrno = errno;
473
474		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
475		errno = sverrno;
476		error(buf);
477	}
478}
479
480
481void
482mv(from, to)
483	char *from, *to;
484{
485	char buf[MAXPATHLEN];
486
487	if (rename(from, to)) {
488		int sverrno = errno;
489		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
490		errno = sverrno;
491		error(buf);
492	}
493}
494
495void
496error(name)
497	char *name;
498{
499
500	warn(name);
501	cleanup();
502	exit(1);
503}
504
505void
506cleanup()
507{
508	char buf[MAXPATHLEN];
509
510	switch(clean) {
511	case FILE_ORIG:
512		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
513		(void)unlink(buf);
514		/* FALLTHROUGH */
515	case FILE_SECURE:
516		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
517		(void)unlink(buf);
518		/* FALLTHROUGH */
519	case FILE_INSECURE:
520		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
521		(void)unlink(buf);
522	}
523}
524
525void
526usage()
527{
528
529	(void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d <dest dir>] [-u <local username>] file\n");
530	exit(1);
531}
532