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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char copyright[] =
33"@(#) Copyright (c) 1991, 1993, 1994\n\
34	The Regents of the University of California.  All rights reserved.\n";
35#endif /* not lint */
36
37#ifndef lint
38static char sccsid[] = "@(#)pwd_mkdb.c	8.5 (Berkeley) 4/20/94";
39#endif /* not lint */
40#endif
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD$");
44
45#include <sys/param.h>
46#include <sys/endian.h>
47#include <sys/stat.h>
48#include <arpa/inet.h>
49
50#include <db.h>
51#include <err.h>
52#include <errno.h>
53#include <fcntl.h>
54#include <limits.h>
55#include <pwd.h>
56#include <signal.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61
62#include "pw_scan.h"
63
64#define	INSECURE	1
65#define	SECURE		2
66#define	PERM_INSECURE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
67#define	PERM_SECURE	(S_IRUSR|S_IWUSR)
68#define LEGACY_VERSION(x)  _PW_VERSIONED(x, 3)
69#define CURRENT_VERSION(x) _PW_VERSIONED(x, 4)
70
71static HASHINFO openinfo = {
72	4096,		/* bsize */
73	32,		/* ffactor */
74	256,		/* nelem */
75	2048 * 1024,	/* cachesize */
76	NULL,		/* hash() */
77	BYTE_ORDER	/* lorder */
78};
79
80static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
81static struct passwd pwd;			/* password structure */
82static char *pname;				/* password file name */
83static char prefix[MAXPATHLEN];
84
85static int is_comment;	/* flag for comments */
86static char line[LINE_MAX];
87
88void	cleanup(void);
89void	error(const char *);
90void	cp(char *, char *, mode_t mode);
91void	mv(char *, char *);
92int	scan(FILE *, struct passwd *);
93static void	usage(void);
94
95int
96main(int argc, char *argv[])
97{
98	static char verskey[] = _PWD_VERSION_KEY;
99	char version = _PWD_CURRENT_VERSION;
100	DB *dp, *sdp, *pw_db;
101	DBT data, sdata, key;
102	FILE *fp, *oldfp;
103	sigset_t set;
104	int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
105	unsigned int len;
106	uint32_t store;
107	const char *t;
108	char *p;
109	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
110	char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
111	char buf2[MAXPATHLEN];
112	char sbuf2[MAXPATHLEN];
113	char *username;
114	u_int method, methoduid;
115	int Cflag, dflag, iflag;
116	int nblock = 0;
117
118	iflag = dflag = Cflag = 0;
119	strcpy(prefix, _PATH_PWD);
120	makeold = 0;
121	username = NULL;
122	oldfp = NULL;
123	while ((ch = getopt(argc, argv, "BCLNd:ips:u:v")) != -1)
124		switch(ch) {
125		case 'B':			/* big-endian output */
126			openinfo.lorder = BIG_ENDIAN;
127			break;
128		case 'C':                       /* verify only */
129			Cflag = 1;
130			break;
131		case 'L':			/* little-endian output */
132			openinfo.lorder = LITTLE_ENDIAN;
133			break;
134		case 'N':			/* do not wait for lock	*/
135			nblock = LOCK_NB;	/* will fail if locked */
136			break;
137		case 'd':
138			dflag++;
139			strlcpy(prefix, optarg, sizeof(prefix));
140			break;
141		case 'i':
142			iflag++;
143			break;
144		case 'p':			/* create V7 "file.orig" */
145			makeold = 1;
146			break;
147		case 's':			/* change default cachesize */
148			openinfo.cachesize = atoi(optarg) * 1024 * 1024;
149			break;
150		case 'u':			/* only update this record */
151			username = optarg;
152			break;
153		case 'v':                       /* backward compatible */
154			break;
155		default:
156			usage();
157		}
158	argc -= optind;
159	argv += optind;
160
161	if (argc != 1 || (username && (*username == '+' || *username == '-')))
162		usage();
163
164	/*
165	 * This could be changed to allow the user to interrupt.
166	 * Probably not worth the effort.
167	 */
168	sigemptyset(&set);
169	sigaddset(&set, SIGTSTP);
170	sigaddset(&set, SIGHUP);
171	sigaddset(&set, SIGINT);
172	sigaddset(&set, SIGQUIT);
173	sigaddset(&set, SIGTERM);
174	(void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
175
176	/* We don't care what the user wants. */
177	(void)umask(0);
178
179	pname = *argv;
180
181	/*
182	 * Open and lock the original password file.  We have to check
183	 * the hardlink count after we get the lock to handle any potential
184	 * unlink/rename race.
185	 *
186	 * This lock is necessary when someone runs pwd_mkdb manually, directly
187	 * on master.passwd, to handle the case where a user might try to
188	 * change his password while pwd_mkdb is running.
189	 */
190	for (;;) {
191		struct stat st;
192
193		if (!(fp = fopen(pname, "r")))
194			error(pname);
195		if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag))
196			error("flock");
197		if (fstat(fileno(fp), &st) < 0)
198			error(pname);
199		if (st.st_nlink != 0)
200			break;
201		fclose(fp);
202		fp = NULL;
203	}
204
205	/* check only if password database is valid */
206	if (Cflag) {
207		while (scan(fp, &pwd))
208			if (!is_comment && strlen(pwd.pw_name) >= MAXLOGNAME) {
209				warnx("%s: username too long", pwd.pw_name);
210				exit(1);
211			}
212		exit(0);
213	}
214
215	/* Open the temporary insecure password database. */
216	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
217	(void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
218	if (username) {
219		int use_version;
220
221		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
222		(void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
223
224		clean = FILE_INSECURE;
225		cp(buf2, buf, PERM_INSECURE);
226		dp = dbopen(buf,
227		    O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
228		if (dp == NULL)
229			error(buf);
230
231		clean = FILE_SECURE;
232		cp(sbuf2, sbuf, PERM_SECURE);
233		sdp = dbopen(sbuf,
234		    O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
235		if (sdp == NULL)
236			error(sbuf);
237
238		/*
239		 * Do some trouble to check if we should store this users
240		 * uid. Don't use getpwnam/getpwuid as that interferes
241		 * with NIS.
242		 */
243		pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
244		if (!pw_db)
245			error(_MP_DB);
246
247		key.data = verskey;
248		key.size = sizeof(verskey)-1;
249		if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
250			use_version = *(unsigned char *)data.data;
251		else
252			use_version = 3;
253		buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
254		len = strlen(username);
255
256		/* Only check that username fits in buffer */
257		memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
258		key.data = (u_char *)buf;
259		key.size = len + 1;
260		if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
261			p = (char *)data.data;
262
263			/* jump over pw_name and pw_passwd, to get to pw_uid */
264			while (*p++)
265				;
266			while (*p++)
267				;
268
269			buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
270			memmove(buf + 1, p, sizeof(store));
271			key.data = (u_char *)buf;
272			key.size = sizeof(store) + 1;
273
274			if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
275				/* First field of data.data holds pw_pwname */
276				if (!strcmp(data.data, username))
277					methoduid = 0;
278				else
279					methoduid = R_NOOVERWRITE;
280			} else {
281				methoduid = R_NOOVERWRITE;
282			}
283		} else {
284			methoduid = R_NOOVERWRITE;
285		}
286		if ((pw_db->close)(pw_db))
287			error("close pw_db");
288		method = 0;
289	} else {
290		dp = dbopen(buf,
291		    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
292		if (dp == NULL)
293			error(buf);
294		clean = FILE_INSECURE;
295
296		sdp = dbopen(sbuf,
297		    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
298		if (sdp == NULL)
299			error(sbuf);
300		clean = FILE_SECURE;
301
302		method = R_NOOVERWRITE;
303		methoduid = R_NOOVERWRITE;
304	}
305
306	/*
307	 * Open file for old password file.  Minor trickiness -- don't want to
308	 * chance the file already existing, since someone (stupidly) might
309	 * still be using this for permission checking.  So, open it first and
310	 * fdopen the resulting fd.  The resulting file should be readable by
311	 * everyone.
312	 */
313	if (makeold) {
314		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
315		if ((tfd = open(buf,
316		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
317			error(buf);
318		if ((oldfp = fdopen(tfd, "w")) == NULL)
319			error(buf);
320		clean = FILE_ORIG;
321	}
322
323	/*
324	 * The databases actually contain three copies of the original data.
325	 * Each password file entry is converted into a rough approximation
326	 * of a ``struct passwd'', with the strings placed inline.  This
327	 * object is then stored as the data for three separate keys.  The
328	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
329	 * character.  The second key is the pw_uid field prepended by the
330	 * _PW_KEYBYUID character.  The third key is the line number in the
331	 * original file prepended by the _PW_KEYBYNUM character.  (The special
332	 * characters are prepended to ensure that the keys do not collide.)
333	 */
334	/* In order to transition this file into a machine-independent
335	 * form, we have to change the format of entries.  However, since
336	 * older binaries will still expect the old MD format entries, we
337	 * create those as usual and use versioned tags for the new entries.
338	 */
339	if (username == NULL) {
340		/* Do not add the VERSION tag when updating a single
341		 * user.  When operating on `old format' databases, this
342		 * would result in applications `seeing' only the updated
343		 * entries.
344		 */
345		key.data = verskey;
346		key.size = sizeof(verskey)-1;
347		data.data = &version;
348		data.size = 1;
349		if ((dp->put)(dp, &key, &data, 0) == -1)
350			error("put");
351		if ((dp->put)(sdp, &key, &data, 0) == -1)
352			error("put");
353	}
354	ypcnt = 0;
355	data.data = (u_char *)buf;
356	sdata.data = (u_char *)sbuf;
357	key.data = (u_char *)tbuf;
358	for (cnt = 1; scan(fp, &pwd); ++cnt) {
359		if (!is_comment &&
360		    (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) {
361			yp_enabled = 1;
362			ypcnt++;
363		}
364		if (is_comment)
365			--cnt;
366#define	COMPACT(e)	t = e; while ((*p++ = *t++));
367#define SCALAR(e)	store = htonl((uint32_t)(e));      \
368			memmove(p, &store, sizeof(store)); \
369			p += sizeof(store);
370#define	LSCALAR(e)	store = HTOL((uint32_t)(e));       \
371			memmove(p, &store, sizeof(store)); \
372			p += sizeof(store);
373#define	HTOL(e)		(openinfo.lorder == BYTE_ORDER ? \
374			(uint32_t)(e) : \
375			bswap32((uint32_t)(e)))
376		if (!is_comment &&
377		    (!username || (strcmp(username, pwd.pw_name) == 0))) {
378			/* Create insecure data. */
379			p = buf;
380			COMPACT(pwd.pw_name);
381			COMPACT("*");
382			SCALAR(pwd.pw_uid);
383			SCALAR(pwd.pw_gid);
384			SCALAR(pwd.pw_change);
385			COMPACT(pwd.pw_class);
386			COMPACT(pwd.pw_gecos);
387			COMPACT(pwd.pw_dir);
388			COMPACT(pwd.pw_shell);
389			SCALAR(pwd.pw_expire);
390			SCALAR(pwd.pw_fields);
391			data.size = p - buf;
392
393			/* Create secure data. */
394			p = sbuf;
395			COMPACT(pwd.pw_name);
396			COMPACT(pwd.pw_passwd);
397			SCALAR(pwd.pw_uid);
398			SCALAR(pwd.pw_gid);
399			SCALAR(pwd.pw_change);
400			COMPACT(pwd.pw_class);
401			COMPACT(pwd.pw_gecos);
402			COMPACT(pwd.pw_dir);
403			COMPACT(pwd.pw_shell);
404			SCALAR(pwd.pw_expire);
405			SCALAR(pwd.pw_fields);
406			sdata.size = p - sbuf;
407
408			/* Store insecure by name. */
409			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
410			len = strlen(pwd.pw_name);
411			memmove(tbuf + 1, pwd.pw_name, len);
412			key.size = len + 1;
413			if ((dp->put)(dp, &key, &data, method) == -1)
414				error("put");
415
416			/* Store insecure by number. */
417			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
418			store = htonl(cnt);
419			memmove(tbuf + 1, &store, sizeof(store));
420			key.size = sizeof(store) + 1;
421			if ((dp->put)(dp, &key, &data, method) == -1)
422				error("put");
423
424			/* Store insecure by uid. */
425			tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
426			store = htonl(pwd.pw_uid);
427			memmove(tbuf + 1, &store, sizeof(store));
428			key.size = sizeof(store) + 1;
429			if ((dp->put)(dp, &key, &data, methoduid) == -1)
430				error("put");
431
432			/* Store secure by name. */
433			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
434			len = strlen(pwd.pw_name);
435			memmove(tbuf + 1, pwd.pw_name, len);
436			key.size = len + 1;
437			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
438				error("put");
439
440			/* Store secure by number. */
441			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
442			store = htonl(cnt);
443			memmove(tbuf + 1, &store, sizeof(store));
444			key.size = sizeof(store) + 1;
445			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
446				error("put");
447
448			/* Store secure by uid. */
449			tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
450			store = htonl(pwd.pw_uid);
451			memmove(tbuf + 1, &store, sizeof(store));
452			key.size = sizeof(store) + 1;
453			if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
454				error("put");
455
456			/* Store insecure and secure special plus and special minus */
457			if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
458				tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
459				store = htonl(ypcnt);
460				memmove(tbuf + 1, &store, sizeof(store));
461				key.size = sizeof(store) + 1;
462				if ((dp->put)(dp, &key, &data, method) == -1)
463					error("put");
464				if ((sdp->put)(sdp, &key, &sdata, method) == -1)
465					error("put");
466			}
467
468			/* Create insecure data. (legacy version) */
469			p = buf;
470			COMPACT(pwd.pw_name);
471			COMPACT("*");
472			LSCALAR(pwd.pw_uid);
473			LSCALAR(pwd.pw_gid);
474			LSCALAR(pwd.pw_change);
475			COMPACT(pwd.pw_class);
476			COMPACT(pwd.pw_gecos);
477			COMPACT(pwd.pw_dir);
478			COMPACT(pwd.pw_shell);
479			LSCALAR(pwd.pw_expire);
480			LSCALAR(pwd.pw_fields);
481			data.size = p - buf;
482
483			/* Create secure data. (legacy version) */
484			p = sbuf;
485			COMPACT(pwd.pw_name);
486			COMPACT(pwd.pw_passwd);
487			LSCALAR(pwd.pw_uid);
488			LSCALAR(pwd.pw_gid);
489			LSCALAR(pwd.pw_change);
490			COMPACT(pwd.pw_class);
491			COMPACT(pwd.pw_gecos);
492			COMPACT(pwd.pw_dir);
493			COMPACT(pwd.pw_shell);
494			LSCALAR(pwd.pw_expire);
495			LSCALAR(pwd.pw_fields);
496			sdata.size = p - sbuf;
497
498			/* Store insecure by name. */
499			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
500			len = strlen(pwd.pw_name);
501			memmove(tbuf + 1, pwd.pw_name, len);
502			key.size = len + 1;
503			if ((dp->put)(dp, &key, &data, method) == -1)
504				error("put");
505
506			/* Store insecure by number. */
507			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
508			store = HTOL(cnt);
509			memmove(tbuf + 1, &store, sizeof(store));
510			key.size = sizeof(store) + 1;
511			if ((dp->put)(dp, &key, &data, method) == -1)
512				error("put");
513
514			/* Store insecure by uid. */
515			tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
516			store = HTOL(pwd.pw_uid);
517			memmove(tbuf + 1, &store, sizeof(store));
518			key.size = sizeof(store) + 1;
519			if ((dp->put)(dp, &key, &data, methoduid) == -1)
520				error("put");
521
522			/* Store secure by name. */
523			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
524			len = strlen(pwd.pw_name);
525			memmove(tbuf + 1, pwd.pw_name, len);
526			key.size = len + 1;
527			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
528				error("put");
529
530			/* Store secure by number. */
531			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
532			store = HTOL(cnt);
533			memmove(tbuf + 1, &store, sizeof(store));
534			key.size = sizeof(store) + 1;
535			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
536				error("put");
537
538			/* Store secure by uid. */
539			tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
540			store = HTOL(pwd.pw_uid);
541			memmove(tbuf + 1, &store, sizeof(store));
542			key.size = sizeof(store) + 1;
543			if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
544				error("put");
545
546			/* Store insecure and secure special plus and special minus */
547			if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
548				tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
549				store = HTOL(ypcnt);
550				memmove(tbuf + 1, &store, sizeof(store));
551				key.size = sizeof(store) + 1;
552				if ((dp->put)(dp, &key, &data, method) == -1)
553					error("put");
554				if ((sdp->put)(sdp, &key, &sdata, method) == -1)
555					error("put");
556			}
557		}
558		/* Create original format password file entry */
559		if (is_comment && makeold){	/* copy comments */
560			if (fprintf(oldfp, "%s\n", line) < 0)
561				error("write old");
562		} else if (makeold) {
563			char uidstr[20];
564			char gidstr[20];
565
566			snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
567			snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
568
569			if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
570			    pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
571			    pwd.pw_fields & _PWF_GID ? gidstr : "",
572			    pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
573				error("write old");
574		}
575	}
576	/* If YP enabled, set flag. */
577	if (yp_enabled) {
578		buf[0] = yp_enabled + 2;
579		data.size = 1;
580		key.size = 1;
581		tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
582		if ((dp->put)(dp, &key, &data, method) == -1)
583			error("put");
584		if ((sdp->put)(sdp, &key, &data, method) == -1)
585			error("put");
586		tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
587		key.size = 1;
588		if ((dp->put)(dp, &key, &data, method) == -1)
589			error("put");
590		if ((sdp->put)(sdp, &key, &data, method) == -1)
591			error("put");
592	}
593
594	if ((dp->close)(dp) == -1)
595		error("close");
596	if ((sdp->close)(sdp) == -1)
597		error("close");
598	if (makeold) {
599		(void)fflush(oldfp);
600		if (fclose(oldfp) == EOF)
601			error("close old");
602	}
603
604	/* Set master.passwd permissions, in case caller forgot. */
605	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
606
607	/* Install as the real password files. */
608	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
609	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
610	mv(buf, buf2);
611	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
612	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
613	mv(buf, buf2);
614	if (makeold) {
615		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
616		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
617		mv(buf, buf2);
618	}
619	/*
620	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
621	 * all use flock(2) on it to block other incarnations of themselves.
622	 * The rename means that everything is unlocked, as the original file
623	 * can no longer be accessed.
624	 */
625	(void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
626	mv(pname, buf);
627
628	/*
629	 * Close locked password file after rename()
630	 */
631	if (fclose(fp) == EOF)
632		error("close fp");
633
634	exit(0);
635}
636
637int
638scan(FILE *fp, struct passwd *pw)
639{
640	static int lcnt;
641	size_t len;
642	char *p;
643
644	p = fgetln(fp, &len);
645	if (p == NULL)
646		return (0);
647	++lcnt;
648	/*
649	 * ``... if I swallow anything evil, put your fingers down my
650	 * throat...''
651	 *	-- The Who
652	 */
653	if (len > 0 && p[len - 1] == '\n')
654		len--;
655	if (len >= sizeof(line) - 1) {
656		warnx("line #%d too long", lcnt);
657		goto fmt;
658	}
659	memcpy(line, p, len);
660	line[len] = '\0';
661
662	/*
663	 * Ignore comments: ^[ \t]*#
664	 */
665	for (p = line; *p != '\0'; p++)
666		if (*p != ' ' && *p != '\t')
667			break;
668	if (*p == '#' || *p == '\0') {
669		is_comment = 1;
670		return(1);
671	} else
672		is_comment = 0;
673
674	if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
675		warnx("at line #%d", lcnt);
676fmt:		errno = EFTYPE;	/* XXX */
677		error(pname);
678	}
679
680	return (1);
681}
682
683void
684cp(char *from, char *to, mode_t mode)
685{
686	static char buf[MAXBSIZE];
687	int from_fd, rcount, to_fd, wcount;
688
689	if ((from_fd = open(from, O_RDONLY, 0)) < 0)
690		error(from);
691	if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
692		error(to);
693	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
694		wcount = write(to_fd, buf, rcount);
695		if (rcount != wcount || wcount == -1) {
696			int sverrno = errno;
697
698			(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
699			errno = sverrno;
700			error(buf);
701		}
702	}
703	if (rcount < 0) {
704		int sverrno = errno;
705
706		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
707		errno = sverrno;
708		error(buf);
709	}
710}
711
712
713void
714mv(char *from, char *to)
715{
716	char buf[MAXPATHLEN];
717
718	if (rename(from, to)) {
719		int sverrno = errno;
720		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
721		errno = sverrno;
722		error(buf);
723	}
724}
725
726void
727error(const char *name)
728{
729
730	warn("%s", name);
731	cleanup();
732	exit(1);
733}
734
735void
736cleanup(void)
737{
738	char buf[MAXPATHLEN];
739
740	switch(clean) {
741	case FILE_ORIG:
742		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
743		(void)unlink(buf);
744		/* FALLTHROUGH */
745	case FILE_SECURE:
746		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
747		(void)unlink(buf);
748		/* FALLTHROUGH */
749	case FILE_INSECURE:
750		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
751		(void)unlink(buf);
752	}
753}
754
755static void
756usage(void)
757{
758
759	(void)fprintf(stderr,
760"usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n");
761	exit(1);
762}
763