]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/pwd_mkdb/pwd_mkdb.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.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                 for (cnt = 1; scan(fp, &pwd); ++cnt);
208                 exit(0);
209         }
210
211         /* Open the temporary insecure password database. */
212         (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
213         (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
214         if (username) {
215                 int use_version;
216
217                 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
218                 (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
219
220                 clean = FILE_INSECURE;
221                 cp(buf2, buf, PERM_INSECURE);
222                 dp = dbopen(buf,
223                     O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
224                 if (dp == NULL)
225                         error(buf);
226
227                 clean = FILE_SECURE;
228                 cp(sbuf2, sbuf, PERM_SECURE);
229                 sdp = dbopen(sbuf,
230                     O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
231                 if (sdp == NULL)
232                         error(sbuf);
233
234                 /*
235                  * Do some trouble to check if we should store this users 
236                  * uid. Don't use getpwnam/getpwuid as that interferes 
237                  * with NIS.
238                  */
239                 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
240                 if (!pw_db)
241                         error(_MP_DB);
242
243                 key.data = verskey;
244                 key.size = sizeof(verskey)-1;
245                 if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
246                         use_version = *(unsigned char *)data.data;
247                 else
248                         use_version = 3;
249                 buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
250                 len = strlen(username);
251
252                 /* Only check that username fits in buffer */
253                 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
254                 key.data = (u_char *)buf;
255                 key.size = len + 1;
256                 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
257                         p = (char *)data.data;
258
259                         /* jump over pw_name and pw_passwd, to get to pw_uid */
260                         while (*p++)
261                                 ;
262                         while (*p++)
263                                 ;
264
265                         buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
266                         memmove(buf + 1, p, sizeof(store));
267                         key.data = (u_char *)buf;
268                         key.size = sizeof(store) + 1;
269
270                         if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
271                                 /* First field of data.data holds pw_pwname */
272                                 if (!strcmp(data.data, username))
273                                         methoduid = 0;
274                                 else
275                                         methoduid = R_NOOVERWRITE;
276                         } else {
277                                 methoduid = R_NOOVERWRITE;
278                         }
279                 } else {
280                         methoduid = R_NOOVERWRITE;
281                 }
282                 if ((pw_db->close)(pw_db))
283                         error("close pw_db");
284                 method = 0;
285         } else {
286                 dp = dbopen(buf,
287                     O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
288                 if (dp == NULL)
289                         error(buf);
290                 clean = FILE_INSECURE;
291
292                 sdp = dbopen(sbuf,
293                     O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
294                 if (sdp == NULL)
295                         error(sbuf);
296                 clean = FILE_SECURE;
297
298                 method = R_NOOVERWRITE;
299                 methoduid = R_NOOVERWRITE;
300         }
301
302         /*
303          * Open file for old password file.  Minor trickiness -- don't want to
304          * chance the file already existing, since someone (stupidly) might
305          * still be using this for permission checking.  So, open it first and
306          * fdopen the resulting fd.  The resulting file should be readable by
307          * everyone.
308          */
309         if (makeold) {
310                 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
311                 if ((tfd = open(buf,
312                     O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
313                         error(buf);
314                 if ((oldfp = fdopen(tfd, "w")) == NULL)
315                         error(buf);
316                 clean = FILE_ORIG;
317         }
318
319         /*
320          * The databases actually contain three copies of the original data.
321          * Each password file entry is converted into a rough approximation
322          * of a ``struct passwd'', with the strings placed inline.  This
323          * object is then stored as the data for three separate keys.  The
324          * first key * is the pw_name field prepended by the _PW_KEYBYNAME
325          * character.  The second key is the pw_uid field prepended by the
326          * _PW_KEYBYUID character.  The third key is the line number in the
327          * original file prepended by the _PW_KEYBYNUM character.  (The special
328          * characters are prepended to ensure that the keys do not collide.)
329          */
330         /* In order to transition this file into a machine-independent
331          * form, we have to change the format of entries.  However, since
332          * older binaries will still expect the old MD format entries, we 
333          * create those as usual and use versioned tags for the new entries.
334          */
335         if (username == NULL) {
336                 /* Do not add the VERSION tag when updating a single
337                  * user.  When operating on `old format' databases, this
338                  * would result in applications `seeing' only the updated
339                  * entries.
340                  */
341                 key.data = verskey;
342                 key.size = sizeof(verskey)-1;
343                 data.data = &version;
344                 data.size = 1;
345                 if ((dp->put)(dp, &key, &data, 0) == -1)
346                         error("put");
347                 if ((dp->put)(sdp, &key, &data, 0) == -1)
348                         error("put");
349         }
350         ypcnt = 1;
351         data.data = (u_char *)buf;
352         sdata.data = (u_char *)sbuf;
353         key.data = (u_char *)tbuf;
354         for (cnt = 1; scan(fp, &pwd); ++cnt) {
355                 if (!is_comment && 
356                     (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
357                         yp_enabled = 1;
358                 if (is_comment)
359                         --cnt;
360 #define COMPACT(e)      t = e; while ((*p++ = *t++));
361 #define SCALAR(e)       store = htonl((uint32_t)(e));      \
362                         memmove(p, &store, sizeof(store)); \
363                         p += sizeof(store);
364 #define LSCALAR(e)      store = HTOL((uint32_t)(e));       \
365                         memmove(p, &store, sizeof(store)); \
366                         p += sizeof(store);
367 #define HTOL(e)         (openinfo.lorder == BYTE_ORDER ? \
368                         (uint32_t)(e) : \
369                         bswap32((uint32_t)(e)))
370                 if (!is_comment && 
371                     (!username || (strcmp(username, pwd.pw_name) == 0))) {
372                         /* Create insecure data. */
373                         p = buf;
374                         COMPACT(pwd.pw_name);
375                         COMPACT("*");
376                         SCALAR(pwd.pw_uid);
377                         SCALAR(pwd.pw_gid);
378                         SCALAR(pwd.pw_change);
379                         COMPACT(pwd.pw_class);
380                         COMPACT(pwd.pw_gecos);
381                         COMPACT(pwd.pw_dir);
382                         COMPACT(pwd.pw_shell);
383                         SCALAR(pwd.pw_expire);
384                         SCALAR(pwd.pw_fields);
385                         data.size = p - buf;
386
387                         /* Create secure data. */
388                         p = sbuf;
389                         COMPACT(pwd.pw_name);
390                         COMPACT(pwd.pw_passwd);
391                         SCALAR(pwd.pw_uid);
392                         SCALAR(pwd.pw_gid);
393                         SCALAR(pwd.pw_change);
394                         COMPACT(pwd.pw_class);
395                         COMPACT(pwd.pw_gecos);
396                         COMPACT(pwd.pw_dir);
397                         COMPACT(pwd.pw_shell);
398                         SCALAR(pwd.pw_expire);
399                         SCALAR(pwd.pw_fields);
400                         sdata.size = p - sbuf;
401
402                         /* Store insecure by name. */
403                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
404                         len = strlen(pwd.pw_name);
405                         memmove(tbuf + 1, pwd.pw_name, len);
406                         key.size = len + 1;
407                         if ((dp->put)(dp, &key, &data, method) == -1)
408                                 error("put");
409
410                         /* Store insecure by number. */
411                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
412                         store = htonl(cnt);
413                         memmove(tbuf + 1, &store, sizeof(store));
414                         key.size = sizeof(store) + 1;
415                         if ((dp->put)(dp, &key, &data, method) == -1)
416                                 error("put");
417
418                         /* Store insecure by uid. */
419                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
420                         store = htonl(pwd.pw_uid);
421                         memmove(tbuf + 1, &store, sizeof(store));
422                         key.size = sizeof(store) + 1;
423                         if ((dp->put)(dp, &key, &data, methoduid) == -1)
424                                 error("put");
425
426                         /* Store secure by name. */
427                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
428                         len = strlen(pwd.pw_name);
429                         memmove(tbuf + 1, pwd.pw_name, len);
430                         key.size = len + 1;
431                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
432                                 error("put");
433
434                         /* Store secure by number. */
435                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
436                         store = htonl(cnt);
437                         memmove(tbuf + 1, &store, sizeof(store));
438                         key.size = sizeof(store) + 1;
439                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
440                                 error("put");
441
442                         /* Store secure by uid. */
443                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
444                         store = htonl(pwd.pw_uid);
445                         memmove(tbuf + 1, &store, sizeof(store));
446                         key.size = sizeof(store) + 1;
447                         if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
448                                 error("put");
449
450                         /* Store insecure and secure special plus and special minus */
451                         if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
452                                 tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
453                                 store = htonl(ypcnt);
454                                 memmove(tbuf + 1, &store, sizeof(store));
455                                 ypcnt++;
456                                 key.size = sizeof(store) + 1;
457                                 if ((dp->put)(dp, &key, &data, method) == -1)
458                                         error("put");
459                                 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
460                                         error("put");
461                         }
462
463                         /* Create insecure data. (legacy version) */
464                         p = buf;
465                         COMPACT(pwd.pw_name);
466                         COMPACT("*");
467                         LSCALAR(pwd.pw_uid);
468                         LSCALAR(pwd.pw_gid);
469                         LSCALAR(pwd.pw_change);
470                         COMPACT(pwd.pw_class);
471                         COMPACT(pwd.pw_gecos);
472                         COMPACT(pwd.pw_dir);
473                         COMPACT(pwd.pw_shell);
474                         LSCALAR(pwd.pw_expire);
475                         LSCALAR(pwd.pw_fields);
476                         data.size = p - buf;
477
478                         /* Create secure data. (legacy version) */
479                         p = sbuf;
480                         COMPACT(pwd.pw_name);
481                         COMPACT(pwd.pw_passwd);
482                         LSCALAR(pwd.pw_uid);
483                         LSCALAR(pwd.pw_gid);
484                         LSCALAR(pwd.pw_change);
485                         COMPACT(pwd.pw_class);
486                         COMPACT(pwd.pw_gecos);
487                         COMPACT(pwd.pw_dir);
488                         COMPACT(pwd.pw_shell);
489                         LSCALAR(pwd.pw_expire);
490                         LSCALAR(pwd.pw_fields);
491                         sdata.size = p - sbuf;
492
493                         /* Store insecure by name. */
494                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
495                         len = strlen(pwd.pw_name);
496                         memmove(tbuf + 1, pwd.pw_name, len);
497                         key.size = len + 1;
498                         if ((dp->put)(dp, &key, &data, method) == -1)
499                                 error("put");
500
501                         /* Store insecure by number. */
502                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
503                         store = HTOL(cnt);
504                         memmove(tbuf + 1, &store, sizeof(store));
505                         key.size = sizeof(store) + 1;
506                         if ((dp->put)(dp, &key, &data, method) == -1)
507                                 error("put");
508
509                         /* Store insecure by uid. */
510                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
511                         store = HTOL(pwd.pw_uid);
512                         memmove(tbuf + 1, &store, sizeof(store));
513                         key.size = sizeof(store) + 1;
514                         if ((dp->put)(dp, &key, &data, methoduid) == -1)
515                                 error("put");
516
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);
521                         key.size = len + 1;
522                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
523                                 error("put");
524
525                         /* Store secure by number. */
526                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
527                         store = HTOL(cnt);
528                         memmove(tbuf + 1, &store, sizeof(store));
529                         key.size = sizeof(store) + 1;
530                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
531                                 error("put");
532
533                         /* Store secure by uid. */
534                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
535                         store = HTOL(pwd.pw_uid);
536                         memmove(tbuf + 1, &store, sizeof(store));
537                         key.size = sizeof(store) + 1;
538                         if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
539                                 error("put");
540
541                         /* Store insecure and secure special plus and special minus */
542                         if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
543                                 tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
544                                 store = HTOL(ypcnt);
545                                 memmove(tbuf + 1, &store, sizeof(store));
546                                 ypcnt++;
547                                 key.size = sizeof(store) + 1;
548                                 if ((dp->put)(dp, &key, &data, method) == -1)
549                                         error("put");
550                                 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
551                                         error("put");
552                         }
553                 }
554                 /* Create original format password file entry */
555                 if (is_comment && makeold){     /* copy comments */
556                         if (fprintf(oldfp, "%s\n", line) < 0)
557                                 error("write old");
558                 } else if (makeold) {
559                         char uidstr[20];
560                         char gidstr[20];
561
562                         snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
563                         snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
564
565                         if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
566                             pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
567                             pwd.pw_fields & _PWF_GID ? gidstr : "",
568                             pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
569                                 error("write old");
570                 }
571         }
572         /* If YP enabled, set flag. */
573         if (yp_enabled) {
574                 buf[0] = yp_enabled + 2;
575                 data.size = 1;
576                 key.size = 1;
577                 tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
578                 if ((dp->put)(dp, &key, &data, method) == -1)
579                         error("put");
580                 if ((sdp->put)(sdp, &key, &data, method) == -1)
581                         error("put");
582                 tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
583                 key.size = 1;
584                 if ((dp->put)(dp, &key, &data, method) == -1)
585                         error("put");
586                 if ((sdp->put)(sdp, &key, &data, method) == -1)
587                         error("put");
588         }
589
590         if ((dp->close)(dp) == -1)
591                 error("close");
592         if ((sdp->close)(sdp) == -1)
593                 error("close");
594         if (makeold) {
595                 (void)fflush(oldfp);
596                 if (fclose(oldfp) == EOF)
597                         error("close old");
598         }
599
600         /* Set master.passwd permissions, in case caller forgot. */
601         (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
602
603         /* Install as the real password files. */
604         (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
605         (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
606         mv(buf, buf2);
607         (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
608         (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
609         mv(buf, buf2);
610         if (makeold) {
611                 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
612                 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
613                 mv(buf, buf2);
614         }
615         /*
616          * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
617          * all use flock(2) on it to block other incarnations of themselves.
618          * The rename means that everything is unlocked, as the original file
619          * can no longer be accessed.
620          */
621         (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
622         mv(pname, buf);
623
624         /*
625          * Close locked password file after rename()
626          */
627         if (fclose(fp) == EOF)
628                 error("close fp");
629
630         exit(0);
631 }
632
633 int
634 scan(FILE *fp, struct passwd *pw)
635 {
636         static int lcnt;
637         size_t len;
638         char *p;
639
640         p = fgetln(fp, &len);
641         if (p == NULL)
642                 return (0);
643         ++lcnt;
644         /*
645          * ``... if I swallow anything evil, put your fingers down my
646          * throat...''
647          *      -- The Who
648          */
649         if (len > 0 && p[len - 1] == '\n')
650                 len--;
651         if (len >= sizeof(line) - 1) {
652                 warnx("line #%d too long", lcnt);
653                 goto fmt;
654         }
655         memcpy(line, p, len);
656         line[len] = '\0';
657
658         /* 
659          * Ignore comments: ^[ \t]*#
660          */
661         for (p = line; *p != '\0'; p++)
662                 if (*p != ' ' && *p != '\t')
663                         break;
664         if (*p == '#' || *p == '\0') {
665                 is_comment = 1;
666                 return(1);
667         } else
668                 is_comment = 0;
669
670         if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
671                 warnx("at line #%d", lcnt);
672 fmt:            errno = EFTYPE; /* XXX */
673                 error(pname);
674         }
675
676         return (1);
677 }
678
679 void                    
680 cp(char *from, char *to, mode_t mode)              
681 {               
682         static char buf[MAXBSIZE];
683         int from_fd, rcount, to_fd, wcount;
684
685         if ((from_fd = open(from, O_RDONLY, 0)) < 0)
686                 error(from);
687         if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
688                 error(to);
689         while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
690                 wcount = write(to_fd, buf, rcount);
691                 if (rcount != wcount || wcount == -1) {
692                         int sverrno = errno;
693
694                         (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
695                         errno = sverrno;
696                         error(buf);
697                 }
698         }
699         if (rcount < 0) {
700                 int sverrno = errno;
701
702                 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
703                 errno = sverrno;
704                 error(buf);
705         }
706 }
707
708
709 void
710 mv(char *from, char *to)
711 {
712         char buf[MAXPATHLEN];
713
714         if (rename(from, to)) {
715                 int sverrno = errno;
716                 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
717                 errno = sverrno;
718                 error(buf);
719         }
720 }
721
722 void
723 error(const char *name)
724 {
725
726         warn("%s", name);
727         cleanup();
728         exit(1);
729 }
730
731 void
732 cleanup(void)
733 {
734         char buf[MAXPATHLEN];
735
736         switch(clean) {
737         case FILE_ORIG:
738                 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
739                 (void)unlink(buf);
740                 /* FALLTHROUGH */
741         case FILE_SECURE:
742                 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
743                 (void)unlink(buf);
744                 /* FALLTHROUGH */
745         case FILE_INSECURE:
746                 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
747                 (void)unlink(buf);
748         }
749 }
750
751 static void
752 usage(void)
753 {
754
755         (void)fprintf(stderr,
756 "usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n");
757         exit(1);
758 }