pwd_mkdb.c revision 146755
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: head/usr.sbin/pwd_mkdb/pwd_mkdb.c 146755 2005-05-29 16:01:12Z charnier $");
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
71HASHINFO 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		for (cnt = 1; scan(fp, &pwd); ++cnt);
208		exit(0);
209	}
210
211	/* Open the temporary insecure password database. */
212	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
213	(void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
214	if (username) {
215		int use_version;
216
217		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
218		(void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
219
220		clean = FILE_INSECURE;
221		cp(buf2, buf, PERM_INSECURE);
222		dp = dbopen(buf,
223		    O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
224		if (dp == NULL)
225			error(buf);
226
227		clean = FILE_SECURE;
228		cp(sbuf2, sbuf, PERM_SECURE);
229		sdp = dbopen(sbuf,
230		    O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
231		if (sdp == NULL)
232			error(sbuf);
233
234		/*
235		 * Do some trouble to check if we should store this users
236		 * uid. Don't use getpwnam/getpwuid as that interferes
237		 * with NIS.
238		 */
239		pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
240		if (!pw_db)
241			error(_MP_DB);
242
243		key.data = verskey;
244		key.size = sizeof(verskey)-1;
245		if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
246			use_version = *(unsigned char *)data.data;
247		else
248			use_version = 3;
249		buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
250		len = strlen(username);
251
252		/* Only check that username fits in buffer */
253		memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
254		key.data = (u_char *)buf;
255		key.size = len + 1;
256		if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
257			p = (char *)data.data;
258
259			/* jump over pw_name and pw_passwd, to get to pw_uid */
260			while (*p++)
261				;
262			while (*p++)
263				;
264
265			buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
266			memmove(buf + 1, p, sizeof(store));
267			key.data = (u_char *)buf;
268			key.size = sizeof(store) + 1;
269
270			if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
271				/* First field of data.data holds pw_pwname */
272				if (!strcmp(data.data, username))
273					methoduid = 0;
274				else
275					methoduid = R_NOOVERWRITE;
276			} else {
277				methoduid = R_NOOVERWRITE;
278			}
279		} else {
280			methoduid = R_NOOVERWRITE;
281		}
282		if ((pw_db->close)(pw_db))
283			error("close pw_db");
284		method = 0;
285	} else {
286		dp = dbopen(buf,
287		    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
288		if (dp == NULL)
289			error(buf);
290		clean = FILE_INSECURE;
291
292		sdp = dbopen(sbuf,
293		    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
294		if (sdp == NULL)
295			error(sbuf);
296		clean = FILE_SECURE;
297
298		method = R_NOOVERWRITE;
299		methoduid = R_NOOVERWRITE;
300	}
301
302	/*
303	 * Open file for old password file.  Minor trickiness -- don't want to
304	 * chance the file already existing, since someone (stupidly) might
305	 * still be using this for permission checking.  So, open it first and
306	 * fdopen the resulting fd.  The resulting file should be readable by
307	 * everyone.
308	 */
309	if (makeold) {
310		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
311		if ((tfd = open(buf,
312		    O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
313			error(buf);
314		if ((oldfp = fdopen(tfd, "w")) == NULL)
315			error(buf);
316		clean = FILE_ORIG;
317	}
318
319	/*
320	 * The databases actually contain three copies of the original data.
321	 * Each password file entry is converted into a rough approximation
322	 * of a ``struct passwd'', with the strings placed inline.  This
323	 * object is then stored as the data for three separate keys.  The
324	 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
325	 * character.  The second key is the pw_uid field prepended by the
326	 * _PW_KEYBYUID character.  The third key is the line number in the
327	 * original file prepended by the _PW_KEYBYNUM character.  (The special
328	 * characters are prepended to ensure that the keys do not collide.)
329	 */
330	/* In order to transition this file into a machine-independent
331	 * form, we have to change the format of entries.  However, since
332	 * older binaries will still expect the old MD format entries, we
333	 * create those as usual and use versioned tags for the new entries.
334	 */
335	if (username == NULL) {
336		/* Do not add the VERSION tag when updating a single
337		 * user.  When operating on `old format' databases, this
338		 * would result in applications `seeing' only the updated
339		 * entries.
340		 */
341		key.data = verskey;
342		key.size = sizeof(verskey)-1;
343		data.data = &version;
344		data.size = 1;
345		if ((dp->put)(dp, &key, &data, 0) == -1)
346			error("put");
347		if ((dp->put)(sdp, &key, &data, 0) == -1)
348			error("put");
349	}
350	ypcnt = 1;
351	data.data = (u_char *)buf;
352	sdata.data = (u_char *)sbuf;
353	key.data = (u_char *)tbuf;
354	for (cnt = 1; scan(fp, &pwd); ++cnt) {
355		if (!is_comment &&
356		    (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
357			yp_enabled = 1;
358		if (is_comment)
359			--cnt;
360#define	COMPACT(e)	t = e; while ((*p++ = *t++));
361#define SCALAR(e)	store = htonl((uint32_t)(e));      \
362			memmove(p, &store, sizeof(store)); \
363			p += sizeof(store);
364#define	LSCALAR(e)	store = HTOL((uint32_t)(e));       \
365			memmove(p, &store, sizeof(store)); \
366			p += sizeof(store);
367#define	HTOL(e)		(openinfo.lorder == BYTE_ORDER ? \
368			(uint32_t)(e) : \
369			bswap32((uint32_t)(e)))
370		if (!is_comment &&
371		    (!username || (strcmp(username, pwd.pw_name) == 0))) {
372			/* Create insecure data. */
373			p = buf;
374			COMPACT(pwd.pw_name);
375			COMPACT("*");
376			SCALAR(pwd.pw_uid);
377			SCALAR(pwd.pw_gid);
378			SCALAR(pwd.pw_change);
379			COMPACT(pwd.pw_class);
380			COMPACT(pwd.pw_gecos);
381			COMPACT(pwd.pw_dir);
382			COMPACT(pwd.pw_shell);
383			SCALAR(pwd.pw_expire);
384			SCALAR(pwd.pw_fields);
385			data.size = p - buf;
386
387			/* Create secure data. */
388			p = sbuf;
389			COMPACT(pwd.pw_name);
390			COMPACT(pwd.pw_passwd);
391			SCALAR(pwd.pw_uid);
392			SCALAR(pwd.pw_gid);
393			SCALAR(pwd.pw_change);
394			COMPACT(pwd.pw_class);
395			COMPACT(pwd.pw_gecos);
396			COMPACT(pwd.pw_dir);
397			COMPACT(pwd.pw_shell);
398			SCALAR(pwd.pw_expire);
399			SCALAR(pwd.pw_fields);
400			sdata.size = p - sbuf;
401
402			/* Store insecure by name. */
403			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
404			len = strlen(pwd.pw_name);
405			memmove(tbuf + 1, pwd.pw_name, len);
406			key.size = len + 1;
407			if ((dp->put)(dp, &key, &data, method) == -1)
408				error("put");
409
410			/* Store insecure by number. */
411			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
412			store = htonl(cnt);
413			memmove(tbuf + 1, &store, sizeof(store));
414			key.size = sizeof(store) + 1;
415			if ((dp->put)(dp, &key, &data, method) == -1)
416				error("put");
417
418			/* Store insecure by uid. */
419			tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
420			store = htonl(pwd.pw_uid);
421			memmove(tbuf + 1, &store, sizeof(store));
422			key.size = sizeof(store) + 1;
423			if ((dp->put)(dp, &key, &data, methoduid) == -1)
424				error("put");
425
426			/* Store secure by name. */
427			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
428			len = strlen(pwd.pw_name);
429			memmove(tbuf + 1, pwd.pw_name, len);
430			key.size = len + 1;
431			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
432				error("put");
433
434			/* Store secure by number. */
435			tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
436			store = htonl(cnt);
437			memmove(tbuf + 1, &store, sizeof(store));
438			key.size = sizeof(store) + 1;
439			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
440				error("put");
441
442			/* Store secure by uid. */
443			tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
444			store = htonl(pwd.pw_uid);
445			memmove(tbuf + 1, &store, sizeof(store));
446			key.size = sizeof(store) + 1;
447			if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
448				error("put");
449
450			/* Store insecure and secure special plus and special minus */
451			if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
452				tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
453				store = htonl(ypcnt);
454				memmove(tbuf + 1, &store, sizeof(store));
455				ypcnt++;
456				key.size = sizeof(store) + 1;
457				if ((dp->put)(dp, &key, &data, method) == -1)
458					error("put");
459				if ((sdp->put)(sdp, &key, &sdata, method) == -1)
460					error("put");
461			}
462
463			/* Create insecure data. (legacy version) */
464			p = buf;
465			COMPACT(pwd.pw_name);
466			COMPACT("*");
467			LSCALAR(pwd.pw_uid);
468			LSCALAR(pwd.pw_gid);
469			LSCALAR(pwd.pw_change);
470			COMPACT(pwd.pw_class);
471			COMPACT(pwd.pw_gecos);
472			COMPACT(pwd.pw_dir);
473			COMPACT(pwd.pw_shell);
474			LSCALAR(pwd.pw_expire);
475			LSCALAR(pwd.pw_fields);
476			data.size = p - buf;
477
478			/* Create secure data. (legacy version) */
479			p = sbuf;
480			COMPACT(pwd.pw_name);
481			COMPACT(pwd.pw_passwd);
482			LSCALAR(pwd.pw_uid);
483			LSCALAR(pwd.pw_gid);
484			LSCALAR(pwd.pw_change);
485			COMPACT(pwd.pw_class);
486			COMPACT(pwd.pw_gecos);
487			COMPACT(pwd.pw_dir);
488			COMPACT(pwd.pw_shell);
489			LSCALAR(pwd.pw_expire);
490			LSCALAR(pwd.pw_fields);
491			sdata.size = p - sbuf;
492
493			/* Store insecure by name. */
494			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
495			len = strlen(pwd.pw_name);
496			memmove(tbuf + 1, pwd.pw_name, len);
497			key.size = len + 1;
498			if ((dp->put)(dp, &key, &data, method) == -1)
499				error("put");
500
501			/* Store insecure by number. */
502			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
503			store = HTOL(cnt);
504			memmove(tbuf + 1, &store, sizeof(store));
505			key.size = sizeof(store) + 1;
506			if ((dp->put)(dp, &key, &data, method) == -1)
507				error("put");
508
509			/* Store insecure by uid. */
510			tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
511			store = HTOL(pwd.pw_uid);
512			memmove(tbuf + 1, &store, sizeof(store));
513			key.size = sizeof(store) + 1;
514			if ((dp->put)(dp, &key, &data, methoduid) == -1)
515				error("put");
516
517			/* Store secure by name. */
518			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
519			len = strlen(pwd.pw_name);
520			memmove(tbuf + 1, pwd.pw_name, len);
521			key.size = len + 1;
522			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
523				error("put");
524
525			/* Store secure by number. */
526			tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
527			store = HTOL(cnt);
528			memmove(tbuf + 1, &store, sizeof(store));
529			key.size = sizeof(store) + 1;
530			if ((sdp->put)(sdp, &key, &sdata, method) == -1)
531				error("put");
532
533			/* Store secure by uid. */
534			tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
535			store = HTOL(pwd.pw_uid);
536			memmove(tbuf + 1, &store, sizeof(store));
537			key.size = sizeof(store) + 1;
538			if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
539				error("put");
540
541			/* Store insecure and secure special plus and special minus */
542			if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
543				tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
544				store = HTOL(ypcnt);
545				memmove(tbuf + 1, &store, sizeof(store));
546				ypcnt++;
547				key.size = sizeof(store) + 1;
548				if ((dp->put)(dp, &key, &data, method) == -1)
549					error("put");
550				if ((sdp->put)(sdp, &key, &sdata, method) == -1)
551					error("put");
552			}
553		}
554		/* Create original format password file entry */
555		if (is_comment && makeold){	/* copy comments */
556			if (fprintf(oldfp, "%s\n", line) < 0)
557				error("write old");
558		} else if (makeold) {
559			char uidstr[20];
560			char gidstr[20];
561
562			snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
563			snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
564
565			if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
566			    pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
567			    pwd.pw_fields & _PWF_GID ? gidstr : "",
568			    pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
569				error("write old");
570		}
571	}
572	/* If YP enabled, set flag. */
573	if (yp_enabled) {
574		buf[0] = yp_enabled + 2;
575		data.size = 1;
576		key.size = 1;
577		tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
578		if ((dp->put)(dp, &key, &data, method) == -1)
579			error("put");
580		if ((sdp->put)(sdp, &key, &data, method) == -1)
581			error("put");
582		tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
583		key.size = 1;
584		if ((dp->put)(dp, &key, &data, method) == -1)
585			error("put");
586		if ((sdp->put)(sdp, &key, &data, method) == -1)
587			error("put");
588	}
589
590	if ((dp->close)(dp) == -1)
591		error("close");
592	if ((sdp->close)(sdp) == -1)
593		error("close");
594	if (makeold) {
595		(void)fflush(oldfp);
596		if (fclose(oldfp) == EOF)
597			error("close old");
598	}
599
600	/* Set master.passwd permissions, in case caller forgot. */
601	(void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
602
603	/* Install as the real password files. */
604	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
605	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
606	mv(buf, buf2);
607	(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
608	(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
609	mv(buf, buf2);
610	if (makeold) {
611		(void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
612		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
613		mv(buf, buf2);
614	}
615	/*
616	 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
617	 * all use flock(2) on it to block other incarnations of themselves.
618	 * The rename means that everything is unlocked, as the original file
619	 * can no longer be accessed.
620	 */
621	(void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
622	mv(pname, buf);
623
624	/*
625	 * Close locked password file after rename()
626	 */
627	if (fclose(fp) == EOF)
628		error("close fp");
629
630	exit(0);
631}
632
633int
634scan(FILE * fp, struct passwd *pw)
635{
636	static int lcnt;
637	char *p;
638
639	if (!fgets(line, sizeof(line), fp))
640		return (0);
641	++lcnt;
642	/*
643	 * ``... if I swallow anything evil, put your fingers down my
644	 * throat...''
645	 *	-- The Who
646	 */
647	if (!(p = strchr(line, '\n'))) {
648		/*
649		 * XXX: This may also happen if the last line in a
650		 * file does not have a trailing newline.
651		 */
652		warnx("line #%d too long", lcnt);
653		goto fmt;
654
655	}
656	*p = '\0';
657
658	/*
659	 * Ignore comments: ^[ \t]*#
660	 */
661	for (p = line; *p != '\0'; p++)
662		if (*p != ' ' && *p != '\t')
663			break;
664	if (*p == '#' || *p == '\0') {
665		is_comment = 1;
666		return(1);
667	} else
668		is_comment = 0;
669
670	if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
671		warnx("at line #%d", lcnt);
672fmt:		errno = EFTYPE;	/* XXX */
673		error(pname);
674	}
675
676	return (1);
677}
678
679void
680cp(char *from, char *to, mode_t mode)
681{
682	static char buf[MAXBSIZE];
683	int from_fd, rcount, to_fd, wcount;
684
685	if ((from_fd = open(from, O_RDONLY, 0)) < 0)
686		error(from);
687	if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
688		error(to);
689	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
690		wcount = write(to_fd, buf, rcount);
691		if (rcount != wcount || wcount == -1) {
692			int sverrno = errno;
693
694			(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
695			errno = sverrno;
696			error(buf);
697		}
698	}
699	if (rcount < 0) {
700		int sverrno = errno;
701
702		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
703		errno = sverrno;
704		error(buf);
705	}
706}
707
708
709void
710mv(char *from, char *to)
711{
712	char buf[MAXPATHLEN];
713
714	if (rename(from, to)) {
715		int sverrno = errno;
716		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
717		errno = sverrno;
718		error(buf);
719	}
720}
721
722void
723error(const char *name)
724{
725
726	warn("%s", name);
727	cleanup();
728	exit(1);
729}
730
731void
732cleanup(void)
733{
734	char buf[MAXPATHLEN];
735
736	switch(clean) {
737	case FILE_ORIG:
738		(void)snprintf(buf, sizeof(buf), "%s.orig", pname);
739		(void)unlink(buf);
740		/* FALLTHROUGH */
741	case FILE_SECURE:
742		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
743		(void)unlink(buf);
744		/* FALLTHROUGH */
745	case FILE_INSECURE:
746		(void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
747		(void)unlink(buf);
748	}
749}
750
751static void
752usage(void)
753{
754
755	(void)fprintf(stderr,
756"usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n");
757	exit(1);
758}
759