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