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