tests: print error locations verbosely
[minix.git] / usr.sbin / pwd_mkdb / pwd_mkdb.c
blobd07f06ab4095d74f50db1458b9d7a87944edee48
1 /* $NetBSD: pwd_mkdb.c,v 1.53 2011/01/04 10:01:51 wiz Exp $ */
3 /*
4 * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
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.
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.
30 * Copyright (c) 1991, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
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.
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.
59 * Portions Copyright(C) 1994, Jason Downs. All rights reserved.
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.
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.
83 #if HAVE_NBTOOL_CONFIG_H
84 #include "nbtool_config.h"
85 #endif
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 __RCSID("$NetBSD: pwd_mkdb.c,v 1.53 2011/01/04 10:01:51 wiz Exp $");
94 #endif /* not lint */
96 #if HAVE_NBTOOL_CONFIG_H
97 #include "compat_pwd.h"
98 #else
99 #include <pwd.h>
100 #endif
102 #include <sys/param.h>
103 #include <sys/stat.h>
104 #include <sys/types.h>
106 #ifndef HAVE_NBTOOL_CONFIG_H
107 #include <machine/bswap.h>
108 #endif
110 #include <db.h>
111 #include <err.h>
112 #include <errno.h>
113 #include <fcntl.h>
114 #include <syslog.h>
115 #include <limits.h>
116 #include <signal.h>
117 #include <stdio.h>
118 #include <stdlib.h>
119 #include <stdarg.h>
120 #include <string.h>
121 #include <unistd.h>
122 #include <util.h>
124 #define MAX_CACHESIZE 8*1024*1024
125 #define MIN_CACHESIZE 2*1024*1024
127 #define PERM_INSECURE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
128 #define PERM_SECURE (S_IRUSR | S_IWUSR)
130 #if HAVE_NBTOOL_CONFIG_H
131 static const char __yp_token[] = "__YP!";
132 #else
133 /* Pull this out of the C library. */
134 extern const char __yp_token[];
135 #endif
137 static HASHINFO openinfo = {
138 4096, /* bsize */
139 32, /* ffactor */
140 256, /* nelem */
141 0, /* cachesize */
142 NULL, /* hash() */
143 0 /* lorder */
146 #define FILE_INSECURE 0x01
147 #define FILE_SECURE 0x02
148 #define FILE_ORIG 0x04
151 struct pwddb {
152 DB *db;
153 char dbname[MAX(MAXPATHLEN, LINE_MAX * 2)];
154 const char *fname;
155 uint32_t rversion;
156 uint32_t wversion;
159 static char *pname; /* password file name */
160 static char prefix[MAXPATHLEN];
161 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)];
162 static int lorder = BYTE_ORDER;
163 static int logsyslog;
164 static int clean;
165 static int verbose;
166 static int warning;
167 static struct pwddb sdb, idb;
170 void bailout(void) __attribute__((__noreturn__));
171 void cp(const char *, const char *, mode_t);
172 void deldbent(struct pwddb *, int, void *);
173 void mkpw_error(const char *, ...);
174 void mkpw_warning(const char *, ...);
175 int getdbent(struct pwddb *, int, void *, struct passwd **);
176 void inconsistency(void);
177 void install(const char *, const char *);
178 int main(int, char **);
179 void putdbents(struct pwddb *, struct passwd *, const char *, int, int,
180 u_int, u_int);
181 void putyptoken(struct pwddb *);
182 void rm(const char *);
183 int scan(FILE *, struct passwd *, int *, int *);
184 void usage(void) __attribute__((__noreturn__));
185 void wr_error(const char *);
186 uint32_t getversion(const char *);
187 void setversion(struct pwddb *);
189 #ifndef __lint__
190 #define SWAP(sw) \
191 ((sizeof(sw) == 2 ? (typeof(sw))bswap16((uint16_t)sw) : \
192 (sizeof(sw) == 4 ? (typeof(sw))bswap32((uint32_t)sw) : \
193 (sizeof(sw) == 8 ? (typeof(sw))bswap64((uint64_t)sw) : (abort(), 0)))))
194 #else
195 #define SWAP(sw) sw
196 #endif
198 static void
199 closedb(struct pwddb *db)
201 if ((*db->db->close)(db->db) < 0)
202 wr_error(db->dbname);
205 static void
206 opendb(struct pwddb *db, const char *dbname, const char *username,
207 uint32_t req_version, int flags, mode_t perm)
209 char buf[MAXPATHLEN];
211 (void)snprintf(db->dbname, sizeof(db->dbname), "%s%s.tmp", prefix,
212 dbname);
214 if (username != NULL) {
215 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, dbname);
216 cp(buf, db->dbname, perm);
219 db->db = dbopen(db->dbname, flags, perm, DB_HASH, &openinfo);
220 if (db->db == NULL)
221 mkpw_error("Cannot open `%s'", db->dbname);
223 db->fname = dbname;
224 db->rversion = getversion(dbname);
225 if (req_version == ~0U)
226 db->wversion = db->rversion;
227 else
228 db->wversion = req_version;
230 if (warning && db->rversion == 0 && db->wversion == 0) {
231 mkpw_warning("Database %s is a version %u database.",
232 db->fname, db->rversion);
233 mkpw_warning("Use %s -V 1 to upgrade once you've recompiled "
234 "all your binaries.", getprogname());
236 if (db->wversion != db->rversion) {
237 if (username != NULL) {
238 mkpw_warning("You cannot change a single "
239 "record from version %u to version %u\n",
240 db->rversion, db->wversion);
241 bailout();
242 } else if (verbose) {
243 mkpw_warning("Changing %s from version %u to version %u",
244 db->fname, db->rversion, db->wversion);
246 } else {
247 if (verbose)
248 mkpw_warning("File `%s' version %u requested %u",
249 db->fname, db->rversion, db->wversion);
252 setversion(db);
256 main(int argc, char *argv[])
258 int ch, makeold, tfd, lineno, found, rv, hasyp, secureonly;
259 struct passwd pwd, *tpwd;
260 char *username;
261 FILE *fp, *oldfp;
262 sigset_t set;
263 u_int dbflg, uid_dbflg;
264 int newuser, olduid, flags;
265 struct stat st;
266 u_int cachesize;
267 uint32_t req_version;
269 prefix[0] = '\0';
270 makeold = 0;
271 oldfp = NULL;
272 username = NULL;
273 hasyp = 0;
274 secureonly = 0;
275 found = 0;
276 newuser = 0;
277 cachesize = 0;
278 verbose = 0;
279 warning = 0;
280 logsyslog = 0;
281 req_version = ~0U;
283 while ((ch = getopt(argc, argv, "BLc:d:lpsu:V:vw")) != -1)
284 switch (ch) {
285 case 'B': /* big-endian output */
286 lorder = BIG_ENDIAN;
287 break;
288 case 'L': /* little-endian output */
289 lorder = LITTLE_ENDIAN;
290 break;
291 case 'c':
292 cachesize = atoi(optarg) * 1024 * 1024;
293 break;
294 case 'd': /* set prefix */
295 (void)strlcpy(prefix, optarg, sizeof(prefix));
296 break;
297 case 'l':
298 openlog(getprogname(), LOG_PID, LOG_AUTH);
299 logsyslog = 1;
300 break;
301 case 'p': /* create V7 "file.orig" */
302 makeold = 1;
303 break;
304 case 's': /* modify secure db only */
305 secureonly = 1;
306 break;
307 case 'u': /* modify one user only */
308 username = optarg;
309 break;
310 case 'V':
311 req_version = (uint32_t)atoi(optarg);
312 if (req_version > 1) {
313 mkpw_warning("Unknown version %u", req_version);
314 return EXIT_FAILURE;
316 break;
317 case 'v':
318 verbose++;
319 break;
320 case 'w':
321 warning++;
322 break;
323 case '?':
324 default:
325 usage();
327 argc -= optind;
328 argv += optind;
330 if (argc != 1)
331 usage();
332 if (username != NULL)
333 if (username[0] == '+' || username[0] == '-')
334 usage();
335 if (secureonly)
336 makeold = 0;
339 * This could be changed to allow the user to interrupt.
340 * Probably not worth the effort.
342 (void)sigemptyset(&set);
343 (void)sigaddset(&set, SIGTSTP);
344 (void)sigaddset(&set, SIGHUP);
345 (void)sigaddset(&set, SIGINT);
346 (void)sigaddset(&set, SIGQUIT);
347 (void)sigaddset(&set, SIGTERM);
348 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
350 /* We don't care what the user wants. */
351 (void)umask(0);
353 if (username == NULL)
354 flags = O_RDWR | O_CREAT | O_EXCL;
355 else
356 flags = O_RDWR;
358 pname = *argv;
359 /* Open the original password file */
360 if ((fp = fopen(pname, "r")) == NULL)
361 mkpw_error("Cannot open `%s'", pname);
363 openinfo.lorder = lorder;
365 if (fstat(fileno(fp), &st) == -1)
366 mkpw_error("Cannot stat `%s'", pname);
368 if (cachesize) {
369 openinfo.cachesize = cachesize;
370 } else {
371 /* Tweak openinfo values for large passwd files. */
372 cachesize = (u_int)(st.st_size * 20);
373 if (cachesize > MAX_CACHESIZE)
374 cachesize = MAX_CACHESIZE;
375 else if (cachesize < MIN_CACHESIZE)
376 cachesize = MIN_CACHESIZE;
377 openinfo.cachesize = cachesize;
380 /* Open the temporary insecure password database. */
381 if (!secureonly) {
382 opendb(&idb, _PATH_MP_DB, username, req_version,
383 flags, PERM_INSECURE);
384 clean |= FILE_INSECURE;
388 /* Open the temporary encrypted password database. */
389 opendb(&sdb, _PATH_SMP_DB, username, req_version, flags, PERM_SECURE);
390 clean |= FILE_SECURE;
393 * Open file for old password file. Minor trickiness -- don't want to
394 * chance the file already existing, since someone (stupidly) might
395 * still be using this for permission checking. So, open it first and
396 * fdopen the resulting fd. The resulting file should be readable by
397 * everyone.
399 if (makeold) {
400 (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig",
401 pname);
402 if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL,
403 PERM_INSECURE)) < 0)
404 mkpw_error("Cannot create `%s'", oldpwdfile);
405 clean |= FILE_ORIG;
406 if ((oldfp = fdopen(tfd, "w")) == NULL)
407 mkpw_error("Cannot fdopen `%s'", oldpwdfile);
410 if (username != NULL) {
411 uid_dbflg = 0;
412 dbflg = 0;
415 * Determine if this is a new entry.
417 if (getdbent(&sdb, _PW_KEYBYNAME, username, &tpwd))
418 newuser = 1;
419 else {
420 newuser = 0;
421 olduid = tpwd->pw_uid;
424 } else {
425 uid_dbflg = R_NOOVERWRITE;
426 dbflg = R_NOOVERWRITE;
430 * If we see something go by that looks like YP, we save a special
431 * pointer record, which if YP is enabled in the C lib, will speed
432 * things up.
434 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) {
436 * Create original format password file entry.
438 if (makeold) {
439 #ifdef __minix
440 (void)fprintf(oldfp, "%s:##%s:%d:%d:%s:%s:%s\n",
441 pwd.pw_name, pwd.pw_name, pwd.pw_uid, pwd.pw_gid,
442 pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell);
443 #else
444 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
445 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
446 pwd.pw_dir, pwd.pw_shell);
447 #endif
448 if (ferror(oldfp))
449 wr_error(oldpwdfile);
452 if (username == NULL) {
453 /* Look like YP? */
454 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')
455 hasyp++;
457 /* Warn about potentially unsafe uid/gid overrides. */
458 if (pwd.pw_name[0] == '+') {
459 if ((flags & _PASSWORD_NOUID) == 0 &&
460 pwd.pw_uid == 0)
461 mkpw_warning("line %d: superuser "
462 "override in YP inclusion", lineno);
463 if ((flags & _PASSWORD_NOGID) == 0 &&
464 pwd.pw_gid == 0)
465 mkpw_warning("line %d: wheel override "
466 "in YP inclusion", lineno);
469 /* Write the database entry out. */
470 if (!secureonly)
471 putdbents(&idb, &pwd, "*", flags, lineno, dbflg,
472 uid_dbflg);
473 continue;
474 } else if (strcmp(username, pwd.pw_name) != 0)
475 continue;
477 if (found) {
478 mkpw_warning("user `%s' listed twice in password file",
479 username);
480 bailout();
484 * Ensure that the text file and database agree on
485 * which line the record is from.
487 rv = getdbent(&sdb, _PW_KEYBYNUM, &lineno, &tpwd);
488 if (newuser) {
489 if (rv == 0)
490 inconsistency();
491 } else if (rv == 1 || strcmp(username, tpwd->pw_name) != 0)
492 inconsistency();
493 else if ((uid_t)olduid != pwd.pw_uid) {
495 * If we're changing UID, remove the BYUID
496 * record for the old UID only if it has the
497 * same username.
499 if (!getdbent(&sdb, _PW_KEYBYUID, &olduid, &tpwd)) {
500 if (strcmp(username, tpwd->pw_name) == 0) {
501 if (!secureonly)
502 deldbent(&idb, _PW_KEYBYUID,
503 &olduid);
504 deldbent(&sdb, _PW_KEYBYUID, &olduid);
506 } else
507 inconsistency();
511 * If there's an existing BYUID record for the new UID and
512 * the username doesn't match then be sure not to overwrite
513 * it.
515 if (!getdbent(&sdb, _PW_KEYBYUID, &pwd.pw_uid, &tpwd))
516 if (strcmp(username, tpwd->pw_name) != 0)
517 uid_dbflg = R_NOOVERWRITE;
519 /* Write the database entries out */
520 if (!secureonly)
521 putdbents(&idb, &pwd, "*", flags, lineno, dbflg,
522 uid_dbflg);
523 putdbents(&sdb, &pwd, pwd.pw_passwd, flags, lineno, dbflg,
524 uid_dbflg);
526 found = 1;
527 if (!makeold)
528 break;
531 if (!secureonly) {
532 /* Store YP token if needed. */
533 if (hasyp)
534 putyptoken(&idb);
536 /* Close the insecure database. */
537 closedb(&idb);
541 * If rebuilding the databases, we re-parse the text file and write
542 * the secure entries out in a separate pass.
544 if (username == NULL) {
545 rewind(fp);
546 for (lineno = 0; scan(fp, &pwd, &flags, &lineno);)
547 putdbents(&sdb, &pwd, pwd.pw_passwd, flags,
548 lineno, dbflg, uid_dbflg);
550 /* Store YP token if needed. */
551 if (hasyp)
552 putyptoken(&sdb);
553 } else if (!found) {
554 mkpw_warning("user `%s' not found in password file", username);
555 bailout();
558 /* Close the secure database. */
559 closedb(&sdb);
561 /* Install as the real password files. */
562 if (!secureonly)
563 install(idb.dbname, idb.fname);
564 install(sdb.dbname, sdb.fname);
566 /* Install the V7 password file. */
567 if (makeold) {
568 if (fflush(oldfp) == EOF)
569 wr_error(oldpwdfile);
570 if (fclose(oldfp) == EOF)
571 wr_error(oldpwdfile);
572 install(oldpwdfile, _PATH_PASSWD);
575 /* Set master.passwd permissions, in case caller forgot. */
576 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
577 if (fclose(fp) == EOF)
578 wr_error(pname);
581 * Move the temporary master password file LAST -- chpass(1),
582 * passwd(1), vipw(8) and friends all use its existence to block
583 * other incarnations of themselves. The rename means that
584 * everything is unlocked, as the original file can no longer be
585 * accessed.
587 install(pname, _PATH_MASTERPASSWD);
588 exit(EXIT_SUCCESS);
589 /* NOTREACHED */
593 scan(FILE *fp, struct passwd *pw, int *flags, int *lineno)
595 static char line[LINE_MAX];
596 char *p;
597 int oflags;
599 if (fgets(line, (int)sizeof(line), fp) == NULL)
600 return (0);
601 (*lineno)++;
604 * ``... if I swallow anything evil, put your fingers down my
605 * throat...''
606 * -- The Who
608 if ((p = strchr(line, '\n')) == NULL) {
609 errno = EFTYPE; /* XXX */
610 mkpw_error("%s, %d: line too long", pname, *lineno);
612 *p = '\0';
613 if (strcmp(line, "+") == 0) {
614 /* pw_scan() can't handle "+" */
615 (void)strcpy(line, "+:::::::::");
617 oflags = 0;
618 if (!pw_scan(line, pw, &oflags)) {
619 errno = EFTYPE; /* XXX */
620 mkpw_error("%s, %d: Syntax mkpw_error", pname, *lineno);
622 *flags = oflags;
624 return (1);
627 void
628 install(const char *from, const char *to)
630 char buf[MAXPATHLEN];
632 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, to);
633 if (rename(from, buf))
634 mkpw_error("Cannot rename `%s' to `%s'", from, buf);
637 void
638 rm(const char *victim)
641 if (unlink(victim) < 0)
642 warn("unlink(%s)", victim);
645 void
646 cp(const char *from, const char *to, mode_t mode)
648 static char buf[MAXBSIZE];
649 int from_fd, to_fd;
650 ssize_t rcount, wcount;
652 if ((from_fd = open(from, O_RDONLY, 0)) < 0)
653 mkpw_error("Cannot open `%s'", from);
654 if ((to_fd = open(to, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
655 (void)close(from_fd);
656 mkpw_error("Cannot open `%s'", to);
658 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
659 wcount = write(to_fd, buf, (size_t)rcount);
660 if (rcount != wcount || wcount == -1) {
661 (void)close(from_fd);
662 (void)close(to_fd);
663 goto on_error;
667 close(from_fd);
668 if (close(to_fd))
669 goto on_error;
670 if (rcount < 0)
671 goto on_error;
672 return;
674 on_error:
675 mkpw_error("Cannot copy `%s' to `%s'", from, to);
678 void
679 wr_error(const char *str)
681 mkpw_error("Cannot write `%s'", str);
684 void
685 mkpw_error(const char *fmt, ...)
687 va_list ap;
688 va_start(ap, fmt);
689 if (logsyslog) {
690 int sverrno = errno;
691 char efmt[BUFSIZ];
692 snprintf(efmt, sizeof(efmt), "%s (%%m)", fmt);
693 errno = sverrno;
694 vsyslog(LOG_ERR, efmt, ap);
695 } else
696 vwarn(fmt, ap);
697 va_end(ap);
698 bailout();
701 void
702 mkpw_warning(const char *fmt, ...)
704 va_list ap;
705 va_start(ap, fmt);
706 if (logsyslog)
707 vsyslog(LOG_WARNING, fmt, ap);
708 else
709 vwarnx(fmt, ap);
710 va_end(ap);
713 void
714 inconsistency(void)
717 mkpw_warning("text files and databases are inconsistent");
718 mkpw_warning("re-build the databases without -u");
719 bailout();
722 void
723 bailout(void)
726 if ((clean & FILE_ORIG) != 0)
727 rm(oldpwdfile);
728 if ((clean & FILE_SECURE) != 0)
729 rm(sdb.dbname);
730 if ((clean & FILE_INSECURE) != 0)
731 rm(idb.dbname);
733 exit(EXIT_FAILURE);
736 uint32_t
737 getversion(const char *fname)
739 DBT data, key;
740 int ret;
741 uint32_t version = 0;
742 DB *db;
744 db = dbopen(fname, O_RDONLY, PERM_INSECURE, DB_HASH, NULL);
745 if (db == NULL) {
746 /* If we are building on a separate root, assume version 1 */
747 if ((errno == EACCES || errno == ENOENT) && prefix[0])
748 return 1;
749 mkpw_warning("Cannot open database `%s'", fname);
750 bailout();
752 key.data = __UNCONST("VERSION");
753 key.size = strlen((const char *)key.data) + 1;
755 switch (ret = (*db->get)(db, &key, &data, 0)) {
756 case -1: /* Error */
757 mkpw_warning("Cannot get VERSION record from database `%s'",
758 fname);
759 goto out;
760 case 0:
761 if (data.size != sizeof(version)) {
762 mkpw_warning("Bad VERSION record in database `%s'", fname);
763 goto out;
765 (void)memcpy(&version, data.data, sizeof(version));
766 /*FALLTHROUGH*/
767 case 1:
768 if (ret == 1)
769 mkpw_warning("Database `%s' has no version info",
770 fname);
771 (*db->close)(db);
772 return version;
773 default:
774 mkpw_warning("internal mkpw_error db->get returns %d", ret);
775 goto out;
777 out:
778 (*db->close)(db);
779 bailout();
780 /*NOTREACHED*/
783 void
784 setversion(struct pwddb *db)
786 DBT data, key;
787 key.data = __UNCONST("VERSION");
788 key.size = strlen((const char *)key.data) + 1;
790 data.data = &db->wversion;
791 data.size = sizeof(uint32_t);
793 if ((*db->db->put)(db->db, &key, &data, 0) != 0) {
794 mkpw_warning("Can't write VERSION record to `%s'", db->dbname);
795 bailout();
801 * Write entries to a database for a single user.
803 * The databases actually contain three copies of the original data. Each
804 * password file entry is converted into a rough approximation of a ``struct
805 * passwd'', with the strings placed inline. This object is then stored as
806 * the data for three separate keys. The first key * is the pw_name field
807 * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid
808 * field prepended by the _PW_KEYBYUID character. The third key is the line
809 * number in the original file prepended by the _PW_KEYBYNUM character.
810 * (The special characters are prepended to ensure that the keys do not
811 * collide.)
813 #define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';)
815 void
816 putdbents(struct pwddb *db, struct passwd *pw, const char *passwd, int flags,
817 int lineno, u_int dbflg, u_int uid_dbflg)
819 struct passwd pwd;
820 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p;
821 DBT data, key;
822 const char *t;
823 u_int32_t x;
824 size_t len;
826 (void)memcpy(&pwd, pw, sizeof(pwd));
827 data.data = (u_char *)buf;
828 key.data = (u_char *)tbuf;
830 if (lorder != BYTE_ORDER) {
831 pwd.pw_uid = SWAP(pwd.pw_uid);
832 pwd.pw_gid = SWAP(pwd.pw_gid);
835 #define WRITEPWTIMEVAR(pwvar) \
836 do { \
837 if (db->wversion == 0 && \
838 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \
839 uint32_t tmp = (uint32_t)pwvar; \
840 if (lorder != BYTE_ORDER) \
841 tmp = SWAP(tmp); \
842 (void)memmove(p, &tmp, sizeof(tmp)); \
843 p += sizeof(tmp); \
844 } else if (db->wversion == 1 && \
845 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \
846 uint64_t tmp = pwvar; \
847 if (lorder != BYTE_ORDER) \
848 tmp = SWAP(tmp); \
849 (void)memmove(p, &tmp, sizeof(tmp)); \
850 p += sizeof(tmp); \
851 } else { \
852 if (lorder != BYTE_ORDER) \
853 pwvar = SWAP(pwvar); \
854 (void)memmove(p, &pwvar, sizeof(pwvar)); \
855 p += sizeof(pwvar); \
857 } while (/*CONSTCOND*/0)
859 /* Create insecure data. */
860 p = buf;
861 COMPACT(pwd.pw_name);
862 COMPACT(passwd);
863 (void)memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
864 p += sizeof(pwd.pw_uid);
865 (void)memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
866 p += sizeof(pwd.pw_gid);
867 WRITEPWTIMEVAR(pwd.pw_change);
868 COMPACT(pwd.pw_class);
869 COMPACT(pwd.pw_gecos);
870 COMPACT(pwd.pw_dir);
871 COMPACT(pwd.pw_shell);
872 WRITEPWTIMEVAR(pwd.pw_expire);
873 x = flags;
874 if (lorder != BYTE_ORDER)
875 x = SWAP(x);
876 (void)memmove(p, &x, sizeof(x));
877 p += sizeof(x);
878 data.size = p - buf;
880 /* Store insecure by name. */
881 tbuf[0] = _PW_KEYBYNAME;
882 len = strlen(pwd.pw_name);
883 (void)memmove(tbuf + 1, pwd.pw_name, len);
884 key.size = len + 1;
885 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1)
886 wr_error(db->dbname);
888 /* Store insecure by number. */
889 tbuf[0] = _PW_KEYBYNUM;
890 x = lineno;
891 if (lorder != BYTE_ORDER)
892 x = SWAP(x);
893 (void)memmove(tbuf + 1, &x, sizeof(x));
894 key.size = sizeof(x) + 1;
895 if ((*db->db->put)(db->db, &key, &data, dbflg) == -1)
896 wr_error(db->dbname);
898 /* Store insecure by uid. */
899 tbuf[0] = _PW_KEYBYUID;
900 (void)memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
901 key.size = sizeof(pwd.pw_uid) + 1;
902 if ((*db->db->put)(db->db, &key, &data, uid_dbflg) == -1)
903 wr_error(db->dbname);
906 void
907 deldbent(struct pwddb *db, int type, void *keyp)
909 char tbuf[1024];
910 DBT key;
911 u_int32_t x;
912 size_t len;
914 key.data = (u_char *)tbuf;
916 switch (tbuf[0] = type) {
917 case _PW_KEYBYNAME:
918 len = strlen((char *)keyp);
919 (void)memcpy(tbuf + 1, keyp, len);
920 key.size = len + 1;
921 break;
923 case _PW_KEYBYNUM:
924 case _PW_KEYBYUID:
925 x = *(int *)keyp;
926 if (lorder != BYTE_ORDER)
927 x = SWAP(x);
928 (void)memmove(tbuf + 1, &x, sizeof(x));
929 key.size = sizeof(x) + 1;
930 break;
933 if ((*db->db->del)(db->db, &key, 0) == -1)
934 wr_error(db->dbname);
938 getdbent(struct pwddb *db, int type, void *keyp, struct passwd **tpwd)
940 static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)];
941 static struct passwd pwd;
942 char tbuf[1024], *p;
943 DBT key, data;
944 u_int32_t x;
945 size_t len;
946 int rv;
948 data.data = (u_char *)buf;
949 data.size = sizeof(buf);
950 key.data = (u_char *)tbuf;
952 switch (tbuf[0] = type) {
953 case _PW_KEYBYNAME:
954 len = strlen((char *)keyp);
955 (void)memcpy(tbuf + 1, keyp, len);
956 key.size = len + 1;
957 break;
959 case _PW_KEYBYNUM:
960 case _PW_KEYBYUID:
961 x = *(int *)keyp;
962 if (lorder != BYTE_ORDER)
963 x = SWAP(x);
964 (void)memmove(tbuf + 1, &x, sizeof(x));
965 key.size = sizeof(x) + 1;
966 break;
969 if ((rv = (*db->db->get)(db->db, &key, &data, 0)) == 1)
970 return (rv);
971 if (rv == -1)
972 mkpw_error("Error getting record from `%s'", db->dbname);
974 p = (char *)data.data;
976 pwd.pw_name = p;
977 while (*p++ != '\0')
978 continue;
979 pwd.pw_passwd = p;
980 while (*p++ != '\0')
981 continue;
983 (void)memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid));
984 p += sizeof(pwd.pw_uid);
985 (void)memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid));
986 p += sizeof(pwd.pw_gid);
988 #define READPWTIMEVAR(pwvar) \
989 do { \
990 if (db->rversion == 0 && \
991 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \
992 uint32_t tmp; \
993 (void)memcpy(&tmp, p, sizeof(tmp)); \
994 p += sizeof(tmp); \
995 if (lorder != BYTE_ORDER) \
996 pwvar = SWAP(tmp); \
997 else \
998 pwvar = tmp; \
999 } else if (db->rversion == 1 && \
1000 /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \
1001 uint64_t tmp; \
1002 (void)memcpy(&tmp, p, sizeof(tmp)); \
1003 p += sizeof(tmp); \
1004 if (lorder != BYTE_ORDER) \
1005 pwvar = (uint32_t)SWAP(tmp); \
1006 else \
1007 pwvar = (uint32_t)tmp; \
1008 } else { \
1009 (void)memcpy(&pwvar, p, sizeof(pwvar)); \
1010 p += sizeof(pwvar); \
1011 if (lorder != BYTE_ORDER) \
1012 pwvar = SWAP(pwvar); \
1014 } while (/*CONSTCOND*/0)
1016 READPWTIMEVAR(pwd.pw_change);
1018 pwd.pw_class = p;
1019 while (*p++ != '\0')
1020 continue;
1021 pwd.pw_gecos = p;
1022 while (*p++ != '\0')
1023 continue;
1024 pwd.pw_dir = p;
1025 while (*p++ != '\0')
1026 continue;
1027 pwd.pw_shell = p;
1028 while (*p++ != '\0')
1029 continue;
1031 READPWTIMEVAR(pwd.pw_expire);
1033 if (lorder != BYTE_ORDER) {
1034 pwd.pw_uid = SWAP(pwd.pw_uid);
1035 pwd.pw_gid = SWAP(pwd.pw_gid);
1038 *tpwd = &pwd;
1039 return (0);
1042 void
1043 putyptoken(struct pwddb *db)
1045 DBT data, key;
1047 key.data = __UNCONST(__yp_token);
1048 key.size = strlen(__yp_token);
1049 data.data = (u_char *)NULL;
1050 data.size = 0;
1052 if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) == -1)
1053 wr_error(db->dbname);
1056 void
1057 usage(void)
1060 (void)fprintf(stderr,
1061 "Usage: %s [-BLlpsvw] [-c cachesize] [-d directory] [-u user] "
1062 "[-V version] file\n",
1063 getprogname());
1064 exit(EXIT_FAILURE);