pwd_mkdb.c revision 1.42 1 /* $NetBSD: pwd_mkdb.c,v 1.42 2009/06/18 21:59:24 christos 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.42 2009/06/18 21:59:24 christos 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 #include <sys/types.h>
106
107 #ifndef HAVE_NBTOOL_CONFIG_H
108 #include <machine/bswap.h>
109 #endif
110
111 #include <db.h>
112 #include <err.h>
113 #include <errno.h>
114 #include <fcntl.h>
115 #include <limits.h>
116 #include <signal.h>
117 #include <stdio.h>
118 #include <stdlib.h>
119 #include <string.h>
120 #include <unistd.h>
121 #include <util.h>
122
123 #define MAX_CACHESIZE 8*1024*1024
124 #define MIN_CACHESIZE 2*1024*1024
125
126 #define PERM_INSECURE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
127 #define PERM_SECURE (S_IRUSR | S_IWUSR)
128
129 #if HAVE_NBTOOL_CONFIG_H
130 static const char __yp_token[] = "__YP!";
131 #else
132 /* Pull this out of the C library. */
133 extern const char __yp_token[];
134 #endif
135
136 static HASHINFO openinfo = {
137 4096, /* bsize */
138 32, /* ffactor */
139 256, /* nelem */
140 0, /* cachesize */
141 NULL, /* hash() */
142 0 /* lorder */
143 };
144
145 #define FILE_INSECURE 0x01
146 #define FILE_SECURE 0x02
147 #define FILE_ORIG 0x04
148
149
150 struct pwddb {
151 DB *db;
152 char dbname[MAX(MAXPATHLEN, LINE_MAX * 2)];
153 const char *fname;
154 uint32_t rversion;
155 uint32_t wversion;
156 };
157
158 static char *pname; /* password file name */
159 static char prefix[MAXPATHLEN];
160 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)];
161 static int lorder = BYTE_ORDER;
162 static int clean;
163 static int verbose;
164 static int warning;
165 static struct pwddb sdb, idb;
166
167
168 void bailout(void) __attribute__((__noreturn__));
169 void cp(const char *, const char *, mode_t);
170 int deldbent(struct pwddb *, int, void *);
171 void error(const char *);
172 int getdbent(struct pwddb *, int, void *, struct passwd **);
173 void inconsistancy(void);
174 void install(const char *, const char *);
175 int main(int, char **);
176 void putdbents(struct pwddb *, struct passwd *, const char *, int, int, int,
177 int);
178 void putyptoken(struct pwddb *);
179 void rm(const char *);
180 int scan(FILE *, struct passwd *, int *, int *);
181 void usage(void) __attribute__((__noreturn__));
182 void wr_error(const char *);
183 void checkversion(struct pwddb *);
184 uint32_t getversion(const char *);
185 void setversion(struct pwddb *);
186
187 #define SWAP(sw) \
188 ((sizeof(sw) == 2 ? (typeof(sw))bswap16((uint16_t)sw) : \
189 (sizeof(sw) == 4 ? (typeof(sw))bswap32((uint32_t)sw) : \
190 (sizeof(sw) == 8 ? (typeof(sw))bswap64((uint64_t)sw) : (abort(), 0)))))
191
192 static void
193 closedb(struct pwddb *db)
194 {
195 if ((*db->db->close)(db->db) < 0)
196 wr_error(db->dbname);
197 }
198
199 static void
200 opendb(struct pwddb *db, const char *dbname, const char *username,
201 uint32_t req_version, int flags, int perm)
202 {
203 char buf[MAXPATHLEN];
204
205 (void)snprintf(db->dbname, sizeof(db->dbname), "%s%s.tmp", prefix,
206 dbname);
207
208 if (username != NULL) {
209 snprintf(buf, sizeof(buf), "%s%s", prefix, dbname);
210 cp(buf, db->dbname, perm);
211 }
212
213 db->db = dbopen(db->dbname, flags, perm, DB_HASH, &openinfo);
214 if (db->db == NULL)
215 error(db->dbname);
216
217 db->fname = dbname;
218 db->rversion = getversion(dbname);
219 if (req_version == ~0U)
220 db->wversion = db->rversion;
221 else
222 db->wversion = req_version;
223
224 if (warning && db->rversion == 0 && db->wversion == 0) {
225 warnx("Database %s is a version %u database.",
226 db->fname, db->rversion);
227 warnx("Use %s -V 1 to upgrade once you've recompiled "
228 "all your binaries.", getprogname());
229 }
230 if (verbose)
231 fprintf(stderr, "%s: %s version %u requested %u\n",
232 getprogname(), db->fname, db->rversion, db->wversion);
233
234 if (db->wversion != db->rversion) {
235 if (username != NULL)
236 checkversion(db);
237 else if (verbose) {
238 fprintf(stderr, "%s: changing %s from version "
239 "%u to version %u\n",
240 getprogname(), db->fname,
241 db->rversion, db->wversion);
242 }
243 }
244 setversion(db);
245 }
246
247 int
248 main(int argc, char *argv[])
249 {
250 int ch, makeold, tfd, lineno, found, rv, hasyp, secureonly;
251 struct passwd pwd, *tpwd;
252 char *username;
253 FILE *fp, *oldfp;
254 sigset_t set;
255 int dbflg, uid_dbflg, newuser, olduid, flags;
256 struct stat st;
257 u_int cachesize;
258 uint32_t req_version;
259
260 prefix[0] = '\0';
261 makeold = 0;
262 oldfp = NULL;
263 username = NULL;
264 hasyp = 0;
265 secureonly = 0;
266 found = 0;
267 newuser = 0;
268 cachesize = 0;
269 verbose = 0;
270 warning = 0;
271 req_version = ~0U;
272
273 while ((ch = getopt(argc, argv, "BLc:d:psu:V:vw")) != -1)
274 switch (ch) {
275 case 'B': /* big-endian output */
276 lorder = BIG_ENDIAN;
277 break;
278 case 'L': /* little-endian output */
279 lorder = LITTLE_ENDIAN;
280 break;
281 case 'c':
282 cachesize = atoi(optarg) * 1024 * 1024;
283 break;
284 case 'd': /* set prefix */
285 strlcpy(prefix, optarg, sizeof(prefix));
286 break;
287 case 'p': /* create V7 "file.orig" */
288 makeold = 1;
289 break;
290 case 's': /* modify secure db only */
291 secureonly = 1;
292 break;
293 case 'u': /* modify one user only */
294 username = optarg;
295 break;
296 case 'V':
297 req_version = (uint32_t)atoi(optarg);
298 if (req_version > 1)
299 err(1, "Unknown version %u\n", req_version);
300 break;
301 case 'v':
302 verbose++;
303 break;
304 case 'w':
305 warning++;
306 break;
307 case '?':
308 default:
309 usage();
310 }
311 argc -= optind;
312 argv += optind;
313
314 if (argc != 1)
315 usage();
316 if (username != NULL)
317 if (username[0] == '+' || username[0] == '-')
318 usage();
319 if (secureonly)
320 makeold = 0;
321
322 /*
323 * This could be changed to allow the user to interrupt.
324 * Probably not worth the effort.
325 */
326 sigemptyset(&set);
327 sigaddset(&set, SIGTSTP);
328 sigaddset(&set, SIGHUP);
329 sigaddset(&set, SIGINT);
330 sigaddset(&set, SIGQUIT);
331 sigaddset(&set, SIGTERM);
332 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
333
334 /* We don't care what the user wants. */
335 (void)umask(0);
336
337 if (username == NULL)
338 flags = O_RDWR | O_CREAT | O_EXCL;
339 else
340 flags = O_RDWR;
341
342 pname = *argv;
343 /* Open the original password file */
344 if ((fp = fopen(pname, "r")) == NULL)
345 error(pname);
346
347 openinfo.lorder = lorder;
348
349 if (fstat(fileno(fp), &st) == -1)
350 error(pname);
351
352 if (cachesize) {
353 openinfo.cachesize = cachesize;
354 } else {
355 /* Tweak openinfo values for large passwd files. */
356 cachesize = st.st_size * 20;
357 if (cachesize > MAX_CACHESIZE)
358 cachesize = MAX_CACHESIZE;
359 else if (cachesize < MIN_CACHESIZE)
360 cachesize = MIN_CACHESIZE;
361 openinfo.cachesize = cachesize;
362 }
363
364 /* Open the temporary insecure password database. */
365 if (!secureonly) {
366 opendb(&idb, _PATH_MP_DB, username, req_version,
367 flags, PERM_INSECURE);
368 clean |= FILE_INSECURE;
369 }
370
371
372 /* Open the temporary encrypted password database. */
373 opendb(&sdb, _PATH_SMP_DB, username, req_version, flags, PERM_SECURE);
374 clean |= FILE_SECURE;
375
376 /*
377 * Open file for old password file. Minor trickiness -- don't want to
378 * chance the file already existing, since someone (stupidly) might
379 * still be using this for permission checking. So, open it first and
380 * fdopen the resulting fd. The resulting file should be readable by
381 * everyone.
382 */
383 if (makeold) {
384 (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig",
385 pname);
386 if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL,
387 PERM_INSECURE)) < 0)
388 error(oldpwdfile);
389 clean |= FILE_ORIG;
390 if ((oldfp = fdopen(tfd, "w")) == NULL)
391 error(oldpwdfile);
392 }
393
394 if (username != NULL) {
395 uid_dbflg = 0;
396 dbflg = 0;
397
398 /*
399 * Determine if this is a new entry.
400 */
401 if (getdbent(&sdb, _PW_KEYBYNAME, username, &tpwd))
402 newuser = 1;
403 else {
404 newuser = 0;
405 olduid = tpwd->pw_uid;
406 }
407
408 } else {
409 uid_dbflg = R_NOOVERWRITE;
410 dbflg = R_NOOVERWRITE;
411 }
412
413 /*
414 * If we see something go by that looks like YP, we save a special
415 * pointer record, which if YP is enabled in the C lib, will speed
416 * things up.
417 */
418 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) {
419 /*
420 * Create original format password file entry.
421 */
422 if (makeold) {
423 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
424 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
425 pwd.pw_dir, pwd.pw_shell);
426 if (ferror(oldfp))
427 wr_error(oldpwdfile);
428 }
429
430 if (username == NULL) {
431 /* Look like YP? */
432 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')
433 hasyp++;
434
435 /* Warn about potentially unsafe uid/gid overrides. */
436 if (pwd.pw_name[0] == '+') {
437 if ((flags & _PASSWORD_NOUID) == 0 &&
438 pwd.pw_uid == 0)
439 warnx("line %d: superuser override "
440 "in YP inclusion", lineno);
441 if ((flags & _PASSWORD_NOGID) == 0 &&
442 pwd.pw_gid == 0)
443 warnx("line %d: wheel override "
444 "in YP inclusion", lineno);
445 }
446
447 /* Write the database entry out. */
448 if (!secureonly)
449 putdbents(&idb, &pwd, "*", flags, lineno, dbflg,
450 uid_dbflg);
451 continue;
452 } else if (strcmp(username, pwd.pw_name) != 0)
453 continue;
454
455 if (found) {
456 warnx("user `%s' listed twice in password file",
457 username);
458 bailout();
459 }
460
461 /*
462 * Ensure that the text file and database agree on
463 * which line the record is from.
464 */
465 rv = getdbent(&sdb, _PW_KEYBYNUM, &lineno, &tpwd);
466 if (newuser) {
467 if (rv == 0)
468 inconsistancy();
469 } else if (rv == -1 ||
470 strcmp(username, tpwd->pw_name) != 0)
471 inconsistancy();
472 else if ((uid_t)olduid != pwd.pw_uid) {
473 /*
474 * If we're changing UID, remove the BYUID
475 * record for the old UID only if it has the
476 * same username.
477 */
478 if (!getdbent(&sdb, _PW_KEYBYUID, &olduid, &tpwd)) {
479 if (strcmp(username, tpwd->pw_name) == 0) {
480 if (!secureonly)
481 deldbent(&idb, _PW_KEYBYUID,
482 &olduid);
483 deldbent(&sdb, _PW_KEYBYUID, &olduid);
484 }
485 } else
486 inconsistancy();
487 }
488
489 /*
490 * If there's an existing BYUID record for the new UID and
491 * the username doesn't match then be sure not to overwrite
492 * it.
493 */
494 if (!getdbent(&sdb, _PW_KEYBYUID, &pwd.pw_uid, &tpwd))
495 if (strcmp(username, tpwd->pw_name) != 0)
496 uid_dbflg = R_NOOVERWRITE;
497
498 /* Write the database entries out */
499 if (!secureonly)
500 putdbents(&idb, &pwd, "*", flags, lineno, dbflg,
501 uid_dbflg);
502 putdbents(&sdb, &pwd, pwd.pw_passwd, flags, lineno, dbflg,
503 uid_dbflg);
504
505 found = 1;
506 if (!makeold)
507 break;
508 }
509
510 if (!secureonly) {
511 /* Store YP token if needed. */
512 if (hasyp)
513 putyptoken(&idb);
514
515 /* Close the insecure database. */
516 closedb(&idb);
517 }
518
519 /*
520 * If rebuilding the databases, we re-parse the text file and write
521 * the secure entries out in a separate pass.
522 */
523 if (username == NULL) {
524 rewind(fp);
525 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);)
526 putdbents(&sdb, &pwd, pwd.pw_passwd, flags,
527 lineno, dbflg, uid_dbflg);
528
529 /* Store YP token if needed. */
530 if (hasyp)
531 putyptoken(&sdb);
532 } else if (!found) {
533 warnx("user `%s' not found in password file", username);
534 bailout();
535 }
536
537 /* Close the secure database. */
538 closedb(&sdb);
539
540 /* Install as the real password files. */
541 if (!secureonly)
542 install(idb.dbname, idb.fname);
543 install(sdb.dbname, sdb.fname);
544
545 /* Install the V7 password file. */
546 if (makeold) {
547 if (fflush(oldfp) == EOF)
548 wr_error(oldpwdfile);
549 if (fclose(oldfp) == EOF)
550 wr_error(oldpwdfile);
551 install(oldpwdfile, _PATH_PASSWD);
552 }
553
554 /* Set master.passwd permissions, in case caller forgot. */
555 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
556 if (fclose(fp) == EOF)
557 wr_error(pname);
558
559 /*
560 * Move the temporary master password file LAST -- chpass(1),
561 * passwd(1), vipw(8) and friends all use its existence to block
562 * other incarnations of themselves. The rename means that
563 * everything is unlocked, as the original file can no longer be
564 * accessed.
565 */
566 install(pname, _PATH_MASTERPASSWD);
567 exit(EXIT_SUCCESS);
568 /* NOTREACHED */
569 }
570
571 int
572 scan(FILE *fp, struct passwd *pw, int *flags, int *lineno)
573 {
574 static char line[LINE_MAX];
575 char *p;
576 int oflags;
577
578 if (fgets(line, sizeof(line), fp) == NULL)
579 return (0);
580 (*lineno)++;
581
582 /*
583 * ``... if I swallow anything evil, put your fingers down my
584 * throat...''
585 * -- The Who
586 */
587 if ((p = strchr(line, '\n')) == NULL) {
588 warnx("line too long");
589 errno = EFTYPE; /* XXX */
590 error(pname);
591 }
592 *p = '\0';
593 if (strcmp(line, "+") == 0)
594 strcpy(line, "+:::::::::"); /* pw_scan() can't handle "+" */
595 oflags = 0;
596 if (!pw_scan(line, pw, &oflags)) {
597 warnx("at line #%d", *lineno);
598 errno = EFTYPE; /* XXX */
599 error(pname);
600 }
601 *flags = oflags;
602
603 return (1);
604 }
605
606 void
607 install(const char *from, const char *to)
608 {
609 char buf[MAXPATHLEN];
610 char errbuf[BUFSIZ];
611 int sverrno;
612
613 snprintf(buf, sizeof(buf), "%s%s", prefix, to);
614 if (rename(from, buf)) {
615 sverrno = errno;
616 (void)snprintf(errbuf, sizeof(errbuf), "%s to %s", from, buf);
617 errno = sverrno;
618 error(errbuf);
619 }
620 }
621
622 void
623 rm(const char *victim)
624 {
625
626 if (unlink(victim) < 0)
627 warn("unlink(%s)", victim);
628 }
629
630 void
631 cp(const char *from, const char *to, mode_t mode)
632 {
633 static char buf[MAXBSIZE];
634 int from_fd, rcount, to_fd, wcount, sverrno;
635
636 if ((from_fd = open(from, O_RDONLY, 0)) < 0)
637 error(from);
638 if ((to_fd = open(to, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0)
639 error(to);
640 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
641 wcount = write(to_fd, buf, rcount);
642 if (rcount != wcount || wcount == -1) {
643 sverrno = errno;
644 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
645 errno = sverrno;
646 error(buf);
647 }
648 }
649
650 if (rcount < 0) {
651 sverrno = errno;
652 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
653 errno = sverrno;
654 error(buf);
655 }
656 }
657
658 void
659 wr_error(const char *str)
660 {
661 char errbuf[BUFSIZ];
662 int sverrno;
663
664 sverrno = errno;
665
666 (void)snprintf(errbuf, sizeof(errbuf),
667 "attempt to write %s failed", str);
668
669 errno = sverrno;
670 error(errbuf);
671 }
672
673 void
674 error(const char *str)
675 {
676
677 warn("%s", str);
678 bailout();
679 }
680
681 void
682 inconsistancy(void)
683 {
684
685 warnx("text files and databases are inconsistent");
686 warnx("re-build the databases without -u");
687 bailout();
688 }
689
690 void
691 bailout(void)
692 {
693
694 if ((clean & FILE_ORIG) != 0)
695 rm(oldpwdfile);
696 if ((clean & FILE_SECURE) != 0)
697 rm(sdb.dbname);
698 if ((clean & FILE_INSECURE) != 0)
699 rm(idb.dbname);
700
701 exit(EXIT_FAILURE);
702 }
703
704 uint32_t
705 getversion(const char *fname)
706 {
707 DBT data, key;
708 int ret;
709 uint32_t version = 0;
710 DB *db;
711
712 db = dbopen(fname, O_RDONLY, PERM_INSECURE, DB_HASH, NULL);
713 if (db == NULL) {
714 warn("Cannot open database %s", fname);
715 bailout();
716 }
717 key.data = __UNCONST("VERSION");
718 key.size = strlen((const char *)key.data) + 1;
719
720 switch (ret = (*db->get)(db, &key, &data, 0)) {
721 case -1: /* Error */
722 warn("Cannot get VERSION record from database");
723 goto out;
724 case 0:
725 if (data.size != sizeof(version)) {
726 warnx("Bad VERSION record in database");
727 goto out;
728 }
729 memcpy(&version, data.data, sizeof(version));
730 /*FALLTHROUGH*/
731 case 1:
732 if (ret == 1)
733 warnx("Database %s has no version info", fname);
734 (*db->close)(db);
735 return version;
736 default:
737 warnx("internal error db->get returns %d", ret);
738 goto out;
739 }
740 out:
741 (*db->close)(db);
742 bailout();
743 }
744
745 void
746 setversion(struct pwddb *db)
747 {
748 DBT data, key;
749 key.data = __UNCONST("VERSION");
750 key.size = strlen((const char *)key.data) + 1;
751
752 data.data = &db->wversion;
753 data.size = sizeof(uint32_t);
754
755 if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) != 0) {
756 warn("Can't write VERSION record to %s", db->dbname);
757 bailout();
758 }
759 }
760
761 void
762 checkversion(struct pwddb *db)
763 {
764 (void)fprintf(stderr, "%s: you cannot change a single "
765 "record from version %u to version %u\n", getprogname(),
766 db->rversion, db->wversion);
767 bailout();
768 }
769
770 /*
771 * Write entries to a database for a single user.
772 *
773 * The databases actually contain three copies of the original data. Each
774 * password file entry is converted into a rough approximation of a ``struct
775 * passwd'', with the strings placed inline. This object is then stored as
776 * the data for three separate keys. The first key * is the pw_name field
777 * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid
778 * field prepended by the _PW_KEYBYUID character. The third key is the line
779 * number in the original file prepended by the _PW_KEYBYNUM character.
780 * (The special characters are prepended to ensure that the keys do not
781 * collide.)
782 */
783 #define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';)
784
785 void
786 putdbents(struct pwddb *db, struct passwd *pw, const char *passwd, int flags,
787 int lineno, int dbflg, int uid_dbflg)
788 {
789 struct passwd pwd;
790 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p;
791 DBT data, key;
792 const char *t;
793 u_int32_t x;
794 int len;
795
796 memcpy(&pwd, pw, sizeof(pwd));
797 data.data = (u_char *)buf;
798 key.data = (u_char *)tbuf;
799
800 if (lorder != BYTE_ORDER) {
801 pwd.pw_uid = SWAP(pwd.pw_uid);
802 pwd.pw_gid = SWAP(pwd.pw_gid);
803 }
804
805 #define WRITEPWTIMEVAR(pwvar) \
806 do { \
807 if (db->wversion == 0 && \
808 sizeof(pwvar) == sizeof(uint64_t)) { \
809 uint32_t tmp = (uint32_t)pwvar; \
810 if (lorder != BYTE_ORDER) \
811 tmp = SWAP(tmp); \
812 memmove(p, &tmp, sizeof(tmp)); \
813 p += sizeof(tmp); \
814 } else if (db->wversion == 1 && \
815 sizeof(pwvar) == sizeof(uint32_t)) { \
816 uint64_t tmp = pwvar; \
817 if (lorder != BYTE_ORDER) \
818 tmp = SWAP(tmp); \
819 memmove(p, &tmp, sizeof(tmp)); \
820 p += sizeof(tmp); \
821 } else { \
822 if (lorder != BYTE_ORDER) \
823 pwvar = SWAP(pwvar); \
824 memmove(p, &pwvar, sizeof(pwvar)); \
825 p += sizeof(pwvar); \
826 } \
827 } while (/*CONSTCOND*/0)
828
829 /* Create insecure data. */
830 p = buf;
831 COMPACT(pwd.pw_name);
832 COMPACT(passwd);
833 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
834 p += sizeof(pwd.pw_uid);
835 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
836 p += sizeof(pwd.pw_gid);
837 WRITEPWTIMEVAR(pwd.pw_change);
838 COMPACT(pwd.pw_class);
839 COMPACT(pwd.pw_gecos);
840 COMPACT(pwd.pw_dir);
841 COMPACT(pwd.pw_shell);
842 WRITEPWTIMEVAR(pwd.pw_expire);
843 x = flags;
844 if (lorder != BYTE_ORDER)
845 x = SWAP(x);
846 memmove(p, &x, sizeof(x));
847 p += sizeof(flags);
848 data.size = p - buf;
849
850 /* Store insecure by name. */
851 tbuf[0] = _PW_KEYBYNAME;
852 len = strlen(pwd.pw_name);
853 memmove(tbuf + 1, pwd.pw_name, len);
854 key.size = len + 1;
855 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1)
856 wr_error(db->dbname);
857
858 /* Store insecure by number. */
859 tbuf[0] = _PW_KEYBYNUM;
860 x = lineno;
861 if (lorder != BYTE_ORDER)
862 x = SWAP(x);
863 memmove(tbuf + 1, &x, sizeof(x));
864 key.size = sizeof(x) + 1;
865 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1)
866 wr_error(db->dbname);
867
868 /* Store insecure by uid. */
869 tbuf[0] = _PW_KEYBYUID;
870 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
871 key.size = sizeof(pwd.pw_uid) + 1;
872 if ((*db->db->put)(db->db, &key, &data, uid_dbflg) == -1)
873 wr_error(db->dbname);
874 }
875
876 int
877 deldbent(struct pwddb *db, int type, void *keyp)
878 {
879 char tbuf[1024];
880 DBT key;
881 u_int32_t x;
882 int len, rv;
883
884 key.data = (u_char *)tbuf;
885
886 switch (tbuf[0] = type) {
887 case _PW_KEYBYNAME:
888 len = strlen((char *)keyp);
889 memcpy(tbuf + 1, keyp, len);
890 key.size = len + 1;
891 break;
892
893 case _PW_KEYBYNUM:
894 case _PW_KEYBYUID:
895 x = *(int *)keyp;
896 if (lorder != BYTE_ORDER)
897 x = SWAP(x);
898 memmove(tbuf + 1, &x, sizeof(x));
899 key.size = sizeof(x) + 1;
900 break;
901 }
902
903 if ((rv = (*db->db->del)(db->db, &key, 0)) == -1)
904 wr_error(db->dbname);
905 return (rv);
906 }
907
908 int
909 getdbent(struct pwddb *db, int type, void *keyp, struct passwd **tpwd)
910 {
911 static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)];
912 static struct passwd pwd;
913 char tbuf[1024], *p;
914 DBT key, data;
915 u_int32_t x;
916 int len, rv;
917
918 data.data = (u_char *)buf;
919 data.size = sizeof(buf);
920 key.data = (u_char *)tbuf;
921
922 switch (tbuf[0] = type) {
923 case _PW_KEYBYNAME:
924 len = strlen((char *)keyp);
925 memcpy(tbuf + 1, keyp, len);
926 key.size = len + 1;
927 break;
928
929 case _PW_KEYBYNUM:
930 case _PW_KEYBYUID:
931 x = *(int *)keyp;
932 if (lorder != BYTE_ORDER)
933 x = SWAP(x);
934 memmove(tbuf + 1, &x, sizeof(x));
935 key.size = sizeof(x) + 1;
936 break;
937 }
938
939 if ((rv = (*db->db->get)(db->db, &key, &data, 0)) == 1)
940 return (rv);
941 if (rv == -1)
942 error(db->dbname);
943
944 p = (char *)data.data;
945
946 pwd.pw_name = p;
947 while (*p++ != '\0')
948 continue;
949 pwd.pw_passwd = p;
950 while (*p++ != '\0')
951 continue;
952
953 memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid));
954 p += sizeof(pwd.pw_uid);
955 memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid));
956 p += sizeof(pwd.pw_gid);
957
958 #define READPWTIMEVAR(pwvar) \
959 do { \
960 if (db->rversion == 0 && \
961 sizeof(pwvar) == sizeof(uint64_t)) { \
962 uint32_t tmp; \
963 memcpy(&tmp, p, sizeof(tmp)); \
964 p += sizeof(tmp); \
965 if (lorder != BYTE_ORDER) \
966 pwvar = SWAP(tmp); \
967 else \
968 pwvar = tmp; \
969 } else if (db->rversion == 1 && \
970 sizeof(pwvar) == sizeof(uint32_t)) { \
971 uint64_t tmp; \
972 memcpy(&tmp, p, sizeof(tmp)); \
973 p += sizeof(tmp); \
974 if (lorder != BYTE_ORDER) \
975 pwvar = (uint32_t)SWAP(tmp); \
976 else \
977 pwvar = (uint32_t)tmp; \
978 } else { \
979 memcpy(&pwvar, p, sizeof(pwvar)); \
980 p += sizeof(pwvar); \
981 if (lorder != BYTE_ORDER) \
982 pwvar = SWAP(pwvar); \
983 } \
984 } while (/*CONSTCOND*/0)
985
986 READPWTIMEVAR(pwd.pw_change);
987
988 pwd.pw_class = p;
989 while (*p++ != '\0')
990 continue;
991 pwd.pw_gecos = p;
992 while (*p++ != '\0')
993 continue;
994 pwd.pw_dir = p;
995 while (*p++ != '\0')
996 continue;
997 pwd.pw_shell = p;
998 while (*p++ != '\0')
999 continue;
1000
1001 READPWTIMEVAR(pwd.pw_expire);
1002
1003 if (lorder != BYTE_ORDER) {
1004 pwd.pw_uid = SWAP(pwd.pw_uid);
1005 pwd.pw_gid = SWAP(pwd.pw_gid);
1006 }
1007
1008 *tpwd = &pwd;
1009 return (0);
1010 }
1011
1012 void
1013 putyptoken(struct pwddb *db)
1014 {
1015 DBT data, key;
1016
1017 key.data = __UNCONST(__yp_token);
1018 key.size = strlen(__yp_token);
1019 data.data = (u_char *)NULL;
1020 data.size = 0;
1021
1022 if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) == -1)
1023 wr_error(db->dbname);
1024 }
1025
1026 void
1027 usage(void)
1028 {
1029
1030 (void)fprintf(stderr,
1031 "Usage: %s [-BLpsvw] [-c cachesize] [-d directory] [-u user] "
1032 "[-V version] file\n",
1033 getprogname());
1034 exit(EXIT_FAILURE);
1035 }
1036