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