pwd_mkdb.c revision 1.36 1 /* $NetBSD: pwd_mkdb.c,v 1.36 2009/01/16 10:42:36 jmmv Exp $ */
2
3 /*
4 * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * Copyright (c) 1991, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58 /*
59 * Portions Copyright(C) 1994, Jason Downs. All rights reserved.
60 *
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
63 * are met:
64 * 1. Redistributions of source code must retain the above copyright
65 * notice, this list of conditions and the following disclaimer.
66 * 2. Redistributions in binary form must reproduce the above copyright
67 * notice, this list of conditions and the following disclaimer in the
68 * documentation and/or other materials provided with the distribution.
69 *
70 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
71 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
72 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
73 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
74 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
75 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
76 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
77 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
80 * SUCH DAMAGE.
81 */
82
83 #if HAVE_NBTOOL_CONFIG_H
84 #include "nbtool_config.h"
85 #endif
86
87 #include <sys/cdefs.h>
88 #if !defined(lint)
89 __COPYRIGHT("@(#) Copyright (c) 2000, 2009\
90 The NetBSD Foundation, Inc. All rights reserved.\
91 Copyright (c) 1991, 1993, 1994\
92 The Regents of the University of California. All rights reserved.");
93 __SCCSID("from: @(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94");
94 __RCSID("$NetBSD: pwd_mkdb.c,v 1.36 2009/01/16 10:42:36 jmmv Exp $");
95 #endif /* not lint */
96
97 #if HAVE_NBTOOL_CONFIG_H
98 #include "compat_pwd.h"
99 #else
100 #include <pwd.h>
101 #endif
102
103 #include <sys/param.h>
104 #include <sys/stat.h>
105
106 #include <db.h>
107 #include <err.h>
108 #include <errno.h>
109 #include <fcntl.h>
110 #include <limits.h>
111 #include <signal.h>
112 #include <stdio.h>
113 #include <stdlib.h>
114 #include <string.h>
115 #include <unistd.h>
116 #include <util.h>
117
118 #define MAX_CACHESIZE 8*1024*1024
119 #define MIN_CACHESIZE 2*1024*1024
120
121 #define PERM_INSECURE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
122 #define PERM_SECURE (S_IRUSR | S_IWUSR)
123
124 #if HAVE_NBTOOL_CONFIG_H
125 static const char __yp_token[] = "__YP!";
126 #else
127 /* Pull this out of the C library. */
128 extern const char __yp_token[];
129 #endif
130
131 HASHINFO openinfo = {
132 4096, /* bsize */
133 32, /* ffactor */
134 256, /* nelem */
135 0, /* cachesize */
136 NULL, /* hash() */
137 0 /* lorder */
138 };
139
140 #define FILE_INSECURE 0x01
141 #define FILE_SECURE 0x02
142 #define FILE_ORIG 0x04
143
144 static char *pname; /* password file name */
145 static char prefix[MAXPATHLEN];
146 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)];
147 static char pwd_db_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
148 static char pwd_Sdb_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
149 static int lorder = BYTE_ORDER;
150 static int clean;
151
152 void bailout(void);
153 void cp(const char *, const char *, mode_t);
154 int deldbent(DB *, const char *, int, void *);
155 void error(const char *);
156 int getdbent(DB *, const char *, int, void *, struct passwd **);
157 void inconsistancy(void);
158 void install(const char *, const char *);
159 int main(int, char **);
160 void putdbents(DB *, struct passwd *, const char *, int, const char *, int,
161 int, int);
162 void putyptoken(DB *, const char *);
163 void rm(const char *);
164 int scan(FILE *, struct passwd *, int *, int *);
165 void usage(void);
166 void wr_error(const char *);
167 void checkversion(DB *);
168 uint32_t getversion(void);
169 void setversion(DB *);
170
171 static __inline uint16_t swap16(uint16_t sw)
172 {
173 return ((sw & 0x00ff) << 8) |
174 ((sw & 0xff00) >> 8);
175 }
176
177 static __inline uint32_t swap32(uint32_t sw) {
178 return ((sw & 0x000000ff) << 24) |
179 ((sw & 0x0000ff00) << 8) |
180 ((sw & 0x00ff0000) >> 8) |
181 ((sw & 0xff000000) >> 24);
182 }
183
184 static __inline uint64_t swap64(uint64_t sw) {
185 return ((sw & 0x00000000000000ffULL) << 56) |
186 ((sw & 0x000000000000ff00ULL) << 40) |
187 ((sw & 0x0000000000ff0000ULL) >> 24) |
188 ((sw & 0x00000000ff000000ULL) << 8) |
189 ((sw & 0x000000ff00000000ULL) >> 8) |
190 ((sw & 0x0000ff0000000000ULL) >> 24) |
191 ((sw & 0x00ff000000000000ULL) >> 40) |
192 ((sw & 0xff00000000000000ULL) >> 56);
193 }
194
195 #define SWAP(sw) \
196 ((sizeof(sw) == 2 ? (typeof(sw))swap16((uint16_t)sw) : \
197 (sizeof(sw) == 4 ? (typeof(sw))swap32((uint32_t)sw) : \
198 (sizeof(sw) == 8 ? (typeof(sw))swap64((uint64_t)sw) : abort(), 0))))
199
200 int
201 main(int argc, char *argv[])
202 {
203 int ch, makeold, tfd, lineno, found, rv, hasyp, secureonly;
204 struct passwd pwd, *tpwd;
205 char *username;
206 DB *dp, *edp;
207 FILE *fp, *oldfp;
208 sigset_t set;
209 int dbflg, uid_dbflg, newuser, olduid, flags;
210 char buf[MAXPATHLEN];
211 struct stat st;
212 u_int cachesize;
213
214 prefix[0] = '\0';
215 makeold = 0;
216 oldfp = NULL;
217 username = NULL;
218 hasyp = 0;
219 secureonly = 0;
220 found = 0;
221 newuser = 0;
222 dp = NULL;
223 cachesize = 0;
224
225 while ((ch = getopt(argc, argv, "BLc:d:psu:v")) != -1)
226 switch (ch) {
227 case 'B': /* big-endian output */
228 lorder = BIG_ENDIAN;
229 break;
230 case 'L': /* little-endian output */
231 lorder = LITTLE_ENDIAN;
232 break;
233 case 'c':
234 cachesize = atoi(optarg) * 1024 * 1024;
235 break;
236 case 'd': /* set prefix */
237 strlcpy(prefix, optarg, sizeof(prefix));
238 break;
239 case 'p': /* create V7 "file.orig" */
240 makeold = 1;
241 break;
242 case 's': /* modify secure db only */
243 secureonly = 1;
244 break;
245 case 'u': /* modify one user only */
246 username = optarg;
247 break;
248 case 'v': /* backward compatible */
249 break;
250 case '?':
251 default:
252 usage();
253 }
254 argc -= optind;
255 argv += optind;
256
257 if (argc != 1)
258 usage();
259 if (username != NULL)
260 if (username[0] == '+' || username[0] == '-')
261 usage();
262 if (secureonly)
263 makeold = 0;
264
265 /*
266 * This could be changed to allow the user to interrupt.
267 * Probably not worth the effort.
268 */
269 sigemptyset(&set);
270 sigaddset(&set, SIGTSTP);
271 sigaddset(&set, SIGHUP);
272 sigaddset(&set, SIGINT);
273 sigaddset(&set, SIGQUIT);
274 sigaddset(&set, SIGTERM);
275 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
276
277 /* We don't care what the user wants. */
278 (void)umask(0);
279
280 if (username == NULL)
281 flags = O_RDWR | O_CREAT | O_EXCL;
282 else
283 flags = O_RDWR;
284
285 pname = *argv;
286 /* Open the original password file */
287 if ((fp = fopen(pname, "r")) == NULL)
288 error(pname);
289
290 openinfo.lorder = lorder;
291
292 if (fstat(fileno(fp), &st) == -1)
293 error(pname);
294
295 if (cachesize) {
296 openinfo.cachesize = cachesize;
297 } else {
298 /* Tweak openinfo values for large passwd files. */
299 cachesize = st.st_size * 20;
300 if (cachesize > MAX_CACHESIZE)
301 cachesize = MAX_CACHESIZE;
302 else if (cachesize < MIN_CACHESIZE)
303 cachesize = MIN_CACHESIZE;
304 openinfo.cachesize = cachesize;
305 }
306
307 /* Open the temporary insecure password database. */
308 if (!secureonly) {
309 (void)snprintf(pwd_db_tmp, sizeof(pwd_db_tmp), "%s%s.tmp",
310 prefix, _PATH_MP_DB);
311 if (username != NULL) {
312 snprintf(buf, sizeof(buf), "%s" _PATH_MP_DB, prefix);
313 cp(buf, pwd_db_tmp, PERM_INSECURE);
314 }
315 dp = dbopen(pwd_db_tmp, flags, PERM_INSECURE, DB_HASH,
316 &openinfo);
317 if (dp == NULL)
318 error(pwd_db_tmp);
319 clean |= FILE_INSECURE;
320 if (username != NULL)
321 checkversion(dp);
322 else
323 setversion(dp);
324 }
325
326 /* Open the temporary encrypted password database. */
327 (void)snprintf(pwd_Sdb_tmp, sizeof(pwd_Sdb_tmp), "%s%s.tmp", prefix,
328 _PATH_SMP_DB);
329 if (username != NULL) {
330 snprintf(buf, sizeof(buf), "%s" _PATH_SMP_DB, prefix);
331 cp(buf, pwd_Sdb_tmp, PERM_SECURE);
332 }
333 edp = dbopen(pwd_Sdb_tmp, flags, PERM_SECURE, DB_HASH, &openinfo);
334 if (!edp)
335 error(pwd_Sdb_tmp);
336 clean |= FILE_SECURE;
337 if (username != NULL)
338 checkversion(edp);
339 else
340 setversion(edp);
341
342 /*
343 * Open file for old password file. Minor trickiness -- don't want to
344 * chance the file already existing, since someone (stupidly) might
345 * still be using this for permission checking. So, open it first and
346 * fdopen the resulting fd. The resulting file should be readable by
347 * everyone.
348 */
349 if (makeold) {
350 (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig",
351 pname);
352 if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL,
353 PERM_INSECURE)) < 0)
354 error(oldpwdfile);
355 clean |= FILE_ORIG;
356 if ((oldfp = fdopen(tfd, "w")) == NULL)
357 error(oldpwdfile);
358 }
359
360 if (username != NULL) {
361 uid_dbflg = 0;
362 dbflg = 0;
363
364 /*
365 * Determine if this is a new entry.
366 */
367 if (getdbent(edp, pwd_Sdb_tmp, _PW_KEYBYNAME, username, &tpwd))
368 newuser = 1;
369 else {
370 newuser = 0;
371 olduid = tpwd->pw_uid;
372 }
373
374 } else {
375 uid_dbflg = R_NOOVERWRITE;
376 dbflg = R_NOOVERWRITE;
377 }
378
379 /*
380 * If we see something go by that looks like YP, we save a special
381 * pointer record, which if YP is enabled in the C lib, will speed
382 * things up.
383 */
384 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) {
385 /*
386 * Create original format password file entry.
387 */
388 if (makeold) {
389 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
390 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
391 pwd.pw_dir, pwd.pw_shell);
392 if (ferror(oldfp))
393 wr_error(oldpwdfile);
394 }
395
396 if (username == NULL) {
397 /* Look like YP? */
398 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')
399 hasyp++;
400
401 /* Warn about potentially unsafe uid/gid overrides. */
402 if (pwd.pw_name[0] == '+') {
403 if ((flags & _PASSWORD_NOUID) == 0 &&
404 pwd.pw_uid == 0)
405 warnx("line %d: superuser override "
406 "in YP inclusion", lineno);
407 if ((flags & _PASSWORD_NOGID) == 0 &&
408 pwd.pw_gid == 0)
409 warnx("line %d: wheel override "
410 "in YP inclusion", lineno);
411 }
412
413 /* Write the database entry out. */
414 if (!secureonly)
415 putdbents(dp, &pwd, "*", flags, pwd_db_tmp,
416 lineno, dbflg, uid_dbflg);
417 continue;
418 } else if (strcmp(username, pwd.pw_name) != 0)
419 continue;
420
421 if (found) {
422 warnx("user `%s' listed twice in password file",
423 username);
424 bailout();
425 }
426
427 /*
428 * Ensure that the text file and database agree on
429 * which line the record is from.
430 */
431 rv = getdbent(edp, pwd_Sdb_tmp, _PW_KEYBYNUM, &lineno, &tpwd);
432 if (newuser) {
433 if (rv == 0)
434 inconsistancy();
435 } else if (rv == -1 ||
436 strcmp(username, tpwd->pw_name) != 0)
437 inconsistancy();
438 else if (olduid != pwd.pw_uid) {
439 /*
440 * If we're changing UID, remove the BYUID
441 * record for the old UID only if it has the
442 * same username.
443 */
444 if (!getdbent(edp, pwd_Sdb_tmp, _PW_KEYBYUID, &olduid,
445 &tpwd)) {
446 if (strcmp(username, tpwd->pw_name) == 0) {
447 if (!secureonly)
448 deldbent(dp, pwd_db_tmp,
449 _PW_KEYBYUID, &olduid);
450 deldbent(edp, pwd_Sdb_tmp,
451 _PW_KEYBYUID, &olduid);
452 }
453 } else
454 inconsistancy();
455 }
456
457 /*
458 * If there's an existing BYUID record for the new UID and
459 * the username doesn't match then be sure not to overwrite
460 * it.
461 */
462 if (!getdbent(edp, pwd_Sdb_tmp, _PW_KEYBYUID, &pwd.pw_uid,
463 &tpwd))
464 if (strcmp(username, tpwd->pw_name) != 0)
465 uid_dbflg = R_NOOVERWRITE;
466
467 /* Write the database entries out */
468 if (!secureonly)
469 putdbents(dp, &pwd, "*", flags, pwd_db_tmp, lineno,
470 dbflg, uid_dbflg);
471 putdbents(edp, &pwd, pwd.pw_passwd, flags, pwd_Sdb_tmp,
472 lineno, dbflg, uid_dbflg);
473
474 found = 1;
475 if (!makeold)
476 break;
477 }
478
479 if (!secureonly) {
480 /* Store YP token if needed. */
481 if (hasyp)
482 putyptoken(dp, pwd_db_tmp);
483
484 /* Close the insecure database. */
485 if ((*dp->close)(dp) < 0)
486 wr_error(pwd_db_tmp);
487 }
488
489 /*
490 * If rebuilding the databases, we re-parse the text file and write
491 * the secure entries out in a separate pass.
492 */
493 if (username == NULL) {
494 rewind(fp);
495 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);)
496 putdbents(edp, &pwd, pwd.pw_passwd, flags, pwd_Sdb_tmp,
497 lineno, dbflg, uid_dbflg);
498
499 /* Store YP token if needed. */
500 if (hasyp)
501 putyptoken(edp, pwd_Sdb_tmp);
502 } else if (!found) {
503 warnx("user `%s' not found in password file", username);
504 bailout();
505 }
506
507 /* Close the secure database. */
508 if ((*edp->close)(edp) < 0)
509 wr_error(pwd_Sdb_tmp);
510
511 /* Install as the real password files. */
512 if (!secureonly)
513 install(pwd_db_tmp, _PATH_MP_DB);
514 install(pwd_Sdb_tmp, _PATH_SMP_DB);
515
516 /* Install the V7 password file. */
517 if (makeold) {
518 if (fflush(oldfp) == EOF)
519 wr_error(oldpwdfile);
520 if (fclose(oldfp) == EOF)
521 wr_error(oldpwdfile);
522 install(oldpwdfile, _PATH_PASSWD);
523 }
524
525 /* Set master.passwd permissions, in case caller forgot. */
526 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
527 if (fclose(fp) == EOF)
528 wr_error(pname);
529
530 /*
531 * Move the temporary master password file LAST -- chpass(1),
532 * passwd(1), vipw(8) and friends all use its existence to block
533 * other incarnations of themselves. The rename means that
534 * everything is unlocked, as the original file can no longer be
535 * accessed.
536 */
537 install(pname, _PATH_MASTERPASSWD);
538 exit(EXIT_SUCCESS);
539 /* NOTREACHED */
540 }
541
542 int
543 scan(FILE *fp, struct passwd *pw, int *flags, int *lineno)
544 {
545 static char line[LINE_MAX];
546 char *p;
547 int oflags;
548
549 if (fgets(line, sizeof(line), fp) == NULL)
550 return (0);
551 (*lineno)++;
552
553 /*
554 * ``... if I swallow anything evil, put your fingers down my
555 * throat...''
556 * -- The Who
557 */
558 if ((p = strchr(line, '\n')) == NULL) {
559 warnx("line too long");
560 errno = EFTYPE; /* XXX */
561 error(pname);
562 }
563 *p = '\0';
564 if (strcmp(line, "+") == 0)
565 strcpy(line, "+:::::::::"); /* pw_scan() can't handle "+" */
566 oflags = 0;
567 if (!pw_scan(line, pw, &oflags)) {
568 warnx("at line #%d", *lineno);
569 errno = EFTYPE; /* XXX */
570 error(pname);
571 }
572 *flags = oflags;
573
574 return (1);
575 }
576
577 void
578 install(const char *from, const char *to)
579 {
580 char buf[MAXPATHLEN];
581 char errbuf[BUFSIZ];
582 int sverrno;
583
584 snprintf(buf, sizeof(buf), "%s%s", prefix, to);
585 if (rename(from, buf)) {
586 sverrno = errno;
587 (void)snprintf(errbuf, sizeof(errbuf), "%s to %s", from, buf);
588 errno = sverrno;
589 error(errbuf);
590 }
591 }
592
593 void
594 rm(const char *victim)
595 {
596
597 if (unlink(victim) < 0)
598 warn("unlink(%s)", victim);
599 }
600
601 void
602 cp(const char *from, const char *to, mode_t mode)
603 {
604 static char buf[MAXBSIZE];
605 int from_fd, rcount, to_fd, wcount, sverrno;
606
607 if ((from_fd = open(from, O_RDONLY, 0)) < 0)
608 error(from);
609 if ((to_fd = open(to, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0)
610 error(to);
611 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
612 wcount = write(to_fd, buf, rcount);
613 if (rcount != wcount || wcount == -1) {
614 sverrno = errno;
615 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
616 errno = sverrno;
617 error(buf);
618 }
619 }
620
621 if (rcount < 0) {
622 sverrno = errno;
623 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
624 errno = sverrno;
625 error(buf);
626 }
627 }
628
629 void
630 wr_error(const char *str)
631 {
632 char errbuf[BUFSIZ];
633 int sverrno;
634
635 sverrno = errno;
636
637 (void)snprintf(errbuf, sizeof(errbuf),
638 "attempt to write %s failed", str);
639
640 errno = sverrno;
641 error(errbuf);
642 }
643
644 void
645 error(const char *str)
646 {
647
648 warn("%s", str);
649 bailout();
650 }
651
652 void
653 inconsistancy(void)
654 {
655
656 warnx("text files and databases are inconsistent");
657 warnx("re-build the databases without -u");
658 bailout();
659 }
660
661 void
662 bailout(void)
663 {
664
665 if ((clean & FILE_ORIG) != 0)
666 rm(oldpwdfile);
667 if ((clean & FILE_SECURE) != 0)
668 rm(pwd_Sdb_tmp);
669 if ((clean & FILE_INSECURE) != 0)
670 rm(pwd_db_tmp);
671
672 exit(EXIT_FAILURE);
673 }
674
675 /*
676 * Ensures that an existing database is up to date.
677 *
678 * Makes sure that the version number of an existing database matches the
679 * version number setversion() writes. If it does not, this function aborts
680 * execution because updating the database without fully regenerating it will
681 * leave it inconsistent.
682 */
683 void
684 checkversion(DB *dp)
685 {
686 DBT data, key;
687 int ret;
688
689 key.data = __UNCONST("VERSION");
690 key.size = strlen((const char *)key.data) + 1;
691
692 ret = (*dp->get)(dp, &key, &data, 0);
693 if (ret == -1) {
694 warnx("cannot get VERSION record from database");
695 bailout();
696 }
697
698 if (ret == 1 || *(int *)data.data != getversion()) {
699 warnx("databases are laid out according to an old version");
700 warnx("re-build the databases without -u");
701 bailout();
702 }
703 }
704
705 /*
706 * Returns the version number we write to and expect from databases.
707 */
708 uint32_t
709 getversion(void)
710 {
711 uint32_t version = sizeof(time_t) != sizeof(int32_t);
712 if (lorder != BYTE_ORDER)
713 version = SWAP(version);
714 return version;
715 }
716
717 void
718 setversion(DB *dp)
719 {
720 DBT data, key;
721 uint32_t version = getversion();
722
723 key.data = __UNCONST("VERSION");
724 key.size = strlen((const char *)key.data) + 1;
725
726 data.data = &version;
727 data.size = sizeof(uint32_t);
728
729 if ((*dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
730 wr_error("setversion");
731 }
732
733 /*
734 * Write entries to a database for a single user.
735 *
736 * The databases actually contain three copies of the original data. Each
737 * password file entry is converted into a rough approximation of a ``struct
738 * passwd'', with the strings placed inline. This object is then stored as
739 * the data for three separate keys. The first key * is the pw_name field
740 * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid
741 * field prepended by the _PW_KEYBYUID character. The third key is the line
742 * number in the original file prepended by the _PW_KEYBYNUM character.
743 * (The special characters are prepended to ensure that the keys do not
744 * collide.)
745 */
746 #define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';)
747
748 void
749 putdbents(DB *dp, struct passwd *pw, const char *passwd, int flags,
750 const char *fn, int lineno, int dbflg, int uid_dbflg)
751 {
752 struct passwd pwd;
753 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p;
754 DBT data, key;
755 const char *t;
756 u_int32_t x;
757 int len;
758
759 memcpy(&pwd, pw, sizeof(pwd));
760 data.data = (u_char *)buf;
761 key.data = (u_char *)tbuf;
762
763 if (lorder != BYTE_ORDER) {
764 pwd.pw_uid = SWAP(pwd.pw_uid);
765 pwd.pw_gid = SWAP(pwd.pw_gid);
766 pwd.pw_change = SWAP(pwd.pw_change);
767 pwd.pw_expire = SWAP(pwd.pw_expire);
768 }
769
770 /* Create insecure data. */
771 p = buf;
772 COMPACT(pwd.pw_name);
773 COMPACT(passwd);
774 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
775 p += sizeof(pwd.pw_uid);
776 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
777 p += sizeof(pwd.pw_gid);
778 memmove(p, &pwd.pw_change, sizeof(pwd.pw_change));
779 p += sizeof(pwd.pw_change);
780 COMPACT(pwd.pw_class);
781 COMPACT(pwd.pw_gecos);
782 COMPACT(pwd.pw_dir);
783 COMPACT(pwd.pw_shell);
784 memmove(p, &pwd.pw_expire, sizeof(pwd.pw_expire));
785 p += sizeof(pwd.pw_expire);
786 x = flags;
787 if (lorder != BYTE_ORDER)
788 x = SWAP(x);
789 memmove(p, &x, sizeof(x));
790 p += sizeof(flags);
791 data.size = p - buf;
792
793 /* Store insecure by name. */
794 tbuf[0] = _PW_KEYBYNAME;
795 len = strlen(pwd.pw_name);
796 memmove(tbuf + 1, pwd.pw_name, len);
797 key.size = len + 1;
798 if ((*dp->put)(dp, &key, &data, dbflg) == -1)
799 wr_error(fn);
800
801 /* Store insecure by number. */
802 tbuf[0] = _PW_KEYBYNUM;
803 x = lineno;
804 if (lorder != BYTE_ORDER)
805 x = SWAP(x);
806 memmove(tbuf + 1, &x, sizeof(x));
807 key.size = sizeof(x) + 1;
808 if ((*dp->put)(dp, &key, &data, dbflg) == -1)
809 wr_error(fn);
810
811 /* Store insecure by uid. */
812 tbuf[0] = _PW_KEYBYUID;
813 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
814 key.size = sizeof(pwd.pw_uid) + 1;
815 if ((*dp->put)(dp, &key, &data, uid_dbflg) == -1)
816 wr_error(fn);
817 }
818
819 int
820 deldbent(DB *dp, const char *fn, int type, void *keyp)
821 {
822 char tbuf[1024];
823 DBT key;
824 u_int32_t x;
825 int len, rv;
826
827 key.data = (u_char *)tbuf;
828
829 switch (tbuf[0] = type) {
830 case _PW_KEYBYNAME:
831 len = strlen((char *)keyp);
832 memcpy(tbuf + 1, keyp, len);
833 key.size = len + 1;
834 break;
835
836 case _PW_KEYBYNUM:
837 case _PW_KEYBYUID:
838 x = *(int *)keyp;
839 if (lorder != BYTE_ORDER)
840 x = SWAP(x);
841 memmove(tbuf + 1, &x, sizeof(x));
842 key.size = sizeof(x) + 1;
843 break;
844 }
845
846 if ((rv = (*dp->del)(dp, &key, 0)) == -1)
847 wr_error(fn);
848 return (rv);
849 }
850
851 int
852 getdbent(DB *dp, const char *fn, int type, void *keyp, struct passwd **tpwd)
853 {
854 static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)];
855 static struct passwd pwd;
856 char tbuf[1024], *p;
857 DBT key, data;
858 u_int32_t x;
859 int len, rv;
860
861 data.data = (u_char *)buf;
862 data.size = sizeof(buf);
863 key.data = (u_char *)tbuf;
864
865 switch (tbuf[0] = type) {
866 case _PW_KEYBYNAME:
867 len = strlen((char *)keyp);
868 memcpy(tbuf + 1, keyp, len);
869 key.size = len + 1;
870 break;
871
872 case _PW_KEYBYNUM:
873 case _PW_KEYBYUID:
874 x = *(int *)keyp;
875 if (lorder != BYTE_ORDER)
876 x = SWAP(x);
877 memmove(tbuf + 1, &x, sizeof(x));
878 key.size = sizeof(x) + 1;
879 break;
880 }
881
882 if ((rv = (*dp->get)(dp, &key, &data, 0)) == 1)
883 return (rv);
884 if (rv == -1)
885 error(pwd_Sdb_tmp);
886
887 p = (char *)data.data;
888
889 pwd.pw_name = p;
890 while (*p++ != '\0')
891 ;
892 pwd.pw_passwd = p;
893 while (*p++ != '\0')
894 ;
895
896 memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid));
897 p += sizeof(pwd.pw_uid);
898 memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid));
899 p += sizeof(pwd.pw_gid);
900 memcpy(&pwd.pw_change, p, sizeof(pwd.pw_change));
901 p += sizeof(pwd.pw_change);
902
903 pwd.pw_class = p;
904 while (*p++ != '\0')
905 ;
906 pwd.pw_gecos = p;
907 while (*p++ != '\0')
908 ;
909 pwd.pw_dir = p;
910 while (*p++ != '\0')
911 ;
912 pwd.pw_shell = p;
913 while (*p++ != '\0')
914 ;
915
916 memcpy(&pwd.pw_expire, p, sizeof(pwd.pw_expire));
917 p += sizeof(pwd.pw_expire);
918
919 if (lorder != BYTE_ORDER) {
920 pwd.pw_uid = SWAP(pwd.pw_uid);
921 pwd.pw_gid = SWAP(pwd.pw_gid);
922 pwd.pw_change = SWAP(pwd.pw_change);
923 pwd.pw_expire = SWAP(pwd.pw_expire);
924 }
925
926 *tpwd = &pwd;
927 return (0);
928 }
929
930 void
931 putyptoken(DB *dp, const char *fn)
932 {
933 DBT data, key;
934
935 key.data = (u_char *)__yp_token;
936 key.size = strlen(__yp_token);
937 data.data = (u_char *)NULL;
938 data.size = 0;
939
940 if ((*dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
941 wr_error(fn);
942 }
943
944 void
945 usage(void)
946 {
947
948 (void)fprintf(stderr,
949 "usage: pwd_mkdb [-BLps] [-c cachesize] [-d directory] [-u user] file\n");
950 exit(EXIT_FAILURE);
951 }
952