2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 static const char copyright[] =
37 "@(#) Copyright (c) 1991, 1993, 1994\n\
38 The Regents of the University of California. All rights reserved.\n";
42 static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
48 #include <sys/param.h>
50 #include <arpa/inet.h>
68 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
69 #define PERM_SECURE (S_IRUSR|S_IWUSR)
70 #define LEGACY_VERSION(x) _PW_VERSIONED(x, 3)
71 #define CURRENT_VERSION(x) _PW_VERSIONED(x, 4)
77 2048 * 1024, /* cachesize */
82 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
83 static struct passwd pwd; /* password structure */
84 static char *pname; /* password file name */
85 static char prefix[MAXPATHLEN];
87 static int is_comment; /* flag for comments */
88 static char line[LINE_MAX];
91 void error(const char *);
92 void cp(char *, char *, mode_t mode);
93 void mv(char *, char *);
94 int scan(FILE *, struct passwd *);
95 static void usage(void);
98 main(int argc, char *argv[])
100 static char verskey[] = _PWD_VERSION_KEY;
101 char version = _PWD_CURRENT_VERSION;
102 DB *dp, *sdp, *pw_db;
103 DBT data, sdata, key;
106 int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
108 int32_t pw_change, pw_expire;
112 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
113 char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
114 char buf2[MAXPATHLEN];
115 char sbuf2[MAXPATHLEN];
117 u_int method, methoduid;
118 int Cflag, dflag, iflag;
121 iflag = dflag = Cflag = 0;
122 strcpy(prefix, _PATH_PWD);
125 while ((ch = getopt(argc, argv, "CNd:ps:u:v")) != -1)
127 case 'C': /* verify only */
130 case 'N': /* do not wait for lock */
131 nblock = LOCK_NB; /* will fail if locked */
135 strlcpy(prefix, optarg, sizeof(prefix));
140 case 'p': /* create V7 "file.orig" */
143 case 's': /* change default cachesize */
144 openinfo.cachesize = atoi(optarg) * 1024 * 1024;
146 case 'u': /* only update this record */
149 case 'v': /* backward compatible */
157 if (argc != 1 || (username && (*username == '+' || *username == '-')))
161 * This could be changed to allow the user to interrupt.
162 * Probably not worth the effort.
165 sigaddset(&set, SIGTSTP);
166 sigaddset(&set, SIGHUP);
167 sigaddset(&set, SIGINT);
168 sigaddset(&set, SIGQUIT);
169 sigaddset(&set, SIGTERM);
170 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
172 /* We don't care what the user wants. */
178 * Open and lock the original password file. We have to check
179 * the hardlink count after we get the lock to handle any potential
180 * unlink/rename race.
182 * This lock is necessary when someone runs pwd_mkdb manually, directly
183 * on master.passwd, to handle the case where a user might try to
184 * change his password while pwd_mkdb is running.
189 if (!(fp = fopen(pname, "r")))
191 if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag))
193 if (fstat(fileno(fp), &st) < 0)
195 if (st.st_nlink != 0)
201 /* check only if password database is valid */
203 for (cnt = 1; scan(fp, &pwd); ++cnt);
207 /* Open the temporary insecure password database. */
208 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
209 (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
213 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
214 (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
216 clean = FILE_INSECURE;
217 cp(buf2, buf, PERM_INSECURE);
219 O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
224 cp(sbuf2, sbuf, PERM_SECURE);
226 O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
231 * Do some trouble to check if we should store this users
232 * uid. Don't use getpwnam/getpwuid as that interferes
235 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
240 key.size = sizeof(verskey)-1;
241 if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
242 use_version = *(unsigned char *)data.data;
245 buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
246 len = strlen(username);
248 /* Only check that username fits in buffer */
249 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
250 key.data = (u_char *)buf;
252 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
253 p = (char *)data.data;
255 /* jump over pw_name and pw_passwd, to get to pw_uid */
261 buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
262 memmove(buf + 1, p, sizeof(store));
263 key.data = (u_char *)buf;
264 key.size = sizeof(store) + 1;
266 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
267 /* First field of data.data holds pw_pwname */
268 if (!strcmp(data.data, username))
271 methoduid = R_NOOVERWRITE;
273 methoduid = R_NOOVERWRITE;
276 methoduid = R_NOOVERWRITE;
278 if ((pw_db->close)(pw_db))
279 error("close pw_db");
283 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
286 clean = FILE_INSECURE;
289 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
294 method = R_NOOVERWRITE;
295 methoduid = R_NOOVERWRITE;
299 * Open file for old password file. Minor trickiness -- don't want to
300 * chance the file already existing, since someone (stupidly) might
301 * still be using this for permission checking. So, open it first and
302 * fdopen the resulting fd. The resulting file should be readable by
306 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
308 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
310 if ((oldfp = fdopen(tfd, "w")) == NULL)
316 * The databases actually contain three copies of the original data.
317 * Each password file entry is converted into a rough approximation
318 * of a ``struct passwd'', with the strings placed inline. This
319 * object is then stored as the data for three separate keys. The
320 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
321 * character. The second key is the pw_uid field prepended by the
322 * _PW_KEYBYUID character. The third key is the line number in the
323 * original file prepended by the _PW_KEYBYNUM character. (The special
324 * characters are prepended to ensure that the keys do not collide.)
326 /* In order to transition this file into a machine-independent
327 * form, we have to change the format of entries. However, since
328 * older binaries will still expect the old MD format entries, we
329 * create those as usual and use versioned tags for the new entries.
331 if (username == NULL) {
332 /* Do not add the VERSION tag when updating a single
333 * user. When operating on `old format' databases, this
334 * would result in applications `seeing' only the updated
338 key.size = sizeof(verskey)-1;
339 data.data = &version;
341 if ((dp->put)(dp, &key, &data, 0) == -1)
343 if ((dp->put)(sdp, &key, &data, 0) == -1)
347 data.data = (u_char *)buf;
348 sdata.data = (u_char *)sbuf;
349 key.data = (u_char *)tbuf;
350 for (cnt = 1; scan(fp, &pwd); ++cnt) {
352 (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
356 #define COMPACT(e) t = e; while ((*p++ = *t++));
357 #define SCALAR(e) store = htonl((uint32_t)(e)); \
358 memmove(p, &store, sizeof(store)); \
361 (!username || (strcmp(username, pwd.pw_name) == 0))) {
362 pw_change = pwd.pw_change;
363 pw_expire = pwd.pw_expire;
364 /* Create insecure data. */
366 COMPACT(pwd.pw_name);
370 SCALAR(pwd.pw_change);
371 COMPACT(pwd.pw_class);
372 COMPACT(pwd.pw_gecos);
374 COMPACT(pwd.pw_shell);
375 SCALAR(pwd.pw_expire);
376 SCALAR(pwd.pw_fields);
379 /* Create secure data. */
381 COMPACT(pwd.pw_name);
382 COMPACT(pwd.pw_passwd);
385 SCALAR(pwd.pw_change);
386 COMPACT(pwd.pw_class);
387 COMPACT(pwd.pw_gecos);
389 COMPACT(pwd.pw_shell);
390 SCALAR(pwd.pw_expire);
391 SCALAR(pwd.pw_fields);
392 sdata.size = p - sbuf;
394 /* Store insecure by name. */
395 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
396 len = strlen(pwd.pw_name);
397 memmove(tbuf + 1, pwd.pw_name, len);
399 if ((dp->put)(dp, &key, &data, method) == -1)
402 /* Store insecure by number. */
403 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
405 memmove(tbuf + 1, &store, sizeof(store));
406 key.size = sizeof(store) + 1;
407 if ((dp->put)(dp, &key, &data, method) == -1)
410 /* Store insecure by uid. */
411 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
412 store = htonl(pwd.pw_uid);
413 memmove(tbuf + 1, &store, sizeof(store));
414 key.size = sizeof(store) + 1;
415 if ((dp->put)(dp, &key, &data, methoduid) == -1)
418 /* Store secure by name. */
419 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
420 len = strlen(pwd.pw_name);
421 memmove(tbuf + 1, pwd.pw_name, len);
423 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
426 /* Store secure by number. */
427 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
429 memmove(tbuf + 1, &store, sizeof(store));
430 key.size = sizeof(store) + 1;
431 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
434 /* Store secure by uid. */
435 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
436 store = htonl(pwd.pw_uid);
437 memmove(tbuf + 1, &store, sizeof(store));
438 key.size = sizeof(store) + 1;
439 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
442 /* Store insecure and secure special plus and special minus */
443 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
444 tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
445 store = htonl(ypcnt);
446 memmove(tbuf + 1, &store, sizeof(store));
448 key.size = sizeof(store) + 1;
449 if ((dp->put)(dp, &key, &data, method) == -1)
451 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
455 /* Create insecure data. (legacy version) */
457 COMPACT(pwd.pw_name);
459 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
461 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
463 memmove(p, &pw_change, sizeof(pw_change));
464 p += sizeof(pw_change);
465 COMPACT(pwd.pw_class);
466 COMPACT(pwd.pw_gecos);
468 COMPACT(pwd.pw_shell);
469 memmove(p, &pw_expire, sizeof(pw_expire));
470 p += sizeof(pw_expire);
471 memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
472 p += sizeof pwd.pw_fields;
475 /* Create secure data. (legacy version) */
477 COMPACT(pwd.pw_name);
478 COMPACT(pwd.pw_passwd);
479 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
481 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
483 memmove(p, &pw_change, sizeof(pw_change));
484 p += sizeof(pw_change);
485 COMPACT(pwd.pw_class);
486 COMPACT(pwd.pw_gecos);
488 COMPACT(pwd.pw_shell);
489 memmove(p, &pw_expire, sizeof(pw_expire));
490 p += sizeof(pw_expire);
491 memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
492 p += sizeof pwd.pw_fields;
493 sdata.size = p - sbuf;
495 /* Store insecure by name. */
496 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
497 len = strlen(pwd.pw_name);
498 memmove(tbuf + 1, pwd.pw_name, len);
500 if ((dp->put)(dp, &key, &data, method) == -1)
503 /* Store insecure by number. */
504 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
505 memmove(tbuf + 1, &cnt, sizeof(cnt));
506 key.size = sizeof(cnt) + 1;
507 if ((dp->put)(dp, &key, &data, method) == -1)
510 /* Store insecure by uid. */
511 tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
512 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
513 key.size = sizeof(pwd.pw_uid) + 1;
514 if ((dp->put)(dp, &key, &data, methoduid) == -1)
517 /* Store secure by name. */
518 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
519 len = strlen(pwd.pw_name);
520 memmove(tbuf + 1, pwd.pw_name, len);
522 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
525 /* Store secure by number. */
526 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
527 memmove(tbuf + 1, &cnt, sizeof(cnt));
528 key.size = sizeof(cnt) + 1;
529 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
532 /* Store secure by uid. */
533 tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
534 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
535 key.size = sizeof(pwd.pw_uid) + 1;
536 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
539 /* Store insecure and secure special plus and special minus */
540 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
541 tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
542 memmove(tbuf + 1, &ypcnt, sizeof(cnt));
544 key.size = sizeof(cnt) + 1;
545 if ((dp->put)(dp, &key, &data, method) == -1)
547 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
551 /* Create original format password file entry */
552 if (is_comment && makeold){ /* copy comments */
553 if (fprintf(oldfp, "%s\n", line) < 0)
555 } else if (makeold) {
559 snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
560 snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
562 if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
563 pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
564 pwd.pw_fields & _PWF_GID ? gidstr : "",
565 pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
569 /* If YP enabled, set flag. */
571 buf[0] = yp_enabled + 2;
574 tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
575 if ((dp->put)(dp, &key, &data, method) == -1)
577 if ((sdp->put)(sdp, &key, &data, method) == -1)
579 tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
581 if ((dp->put)(dp, &key, &data, method) == -1)
583 if ((sdp->put)(sdp, &key, &data, method) == -1)
587 if ((dp->close)(dp) == -1)
589 if ((sdp->close)(sdp) == -1)
593 if (fclose(oldfp) == EOF)
597 /* Set master.passwd permissions, in case caller forgot. */
598 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
600 /* Install as the real password files. */
601 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
602 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
604 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
605 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
608 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
609 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
613 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
614 * all use flock(2) on it to block other incarnations of themselves.
615 * The rename means that everything is unlocked, as the original file
616 * can no longer be accessed.
618 (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
622 * Close locked password file after rename()
624 if (fclose(fp) == EOF)
638 if (!fgets(line, sizeof(line), fp))
642 * ``... if I swallow anything evil, put your fingers down my
646 if (!(p = strchr(line, '\n'))) {
648 * XXX: This may also happen if the last line in a
649 * file does not have a trailing newline.
651 warnx("line #%d too long", lcnt);
658 * Ignore comments: ^[ \t]*#
660 for (p = line; *p != '\0'; p++)
661 if (*p != ' ' && *p != '\t')
663 if (*p == '#' || *p == '\0') {
669 if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
670 warnx("at line #%d", lcnt);
671 fmt: errno = EFTYPE; /* XXX */
683 static char buf[MAXBSIZE];
684 int from_fd, rcount, to_fd, wcount;
686 if ((from_fd = open(from, O_RDONLY, 0)) < 0)
688 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
690 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
691 wcount = write(to_fd, buf, rcount);
692 if (rcount != wcount || wcount == -1) {
695 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
703 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
714 char buf[MAXPATHLEN];
716 if (rename(from, to)) {
718 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
737 char buf[MAXPATHLEN];
741 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
745 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
749 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
758 (void)fprintf(stderr,
759 "usage: pwd_mkdb [-C] [-N] [-i] [-p] [-d <dest dir>] [-s <cachesize>] [-u <local username>] file\n");