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