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