Clean (void) casts from usr.sbin
[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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1991, 1993, 1994 The Regents of the University of California.  All rights reserved.
34  * @(#)pwd_mkdb.c       8.5 (Berkeley) 4/20/94
35  * $FreeBSD: src/usr.sbin/pwd_mkdb/pwd_mkdb.c,v 1.35 2000/03/09 18:11:16 paul Exp $
36  * $DragonFly: src/usr.sbin/pwd_mkdb/pwd_mkdb.c,v 1.4 2004/12/18 22:48:04 swildner Exp $
37  */
38
39 #include <sys/param.h>
40 #include <sys/stat.h>
41
42 #include <db.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <limits.h>
47 #include <pwd.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "pw_scan.h"
55
56 #define INSECURE        1
57 #define SECURE          2
58 #define PERM_INSECURE   (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
59 #define PERM_SECURE     (S_IRUSR|S_IWUSR)
60
61 HASHINFO openinfo = {
62         4096,           /* bsize */
63         32,             /* ffactor */
64         256,            /* nelem */
65         2048 * 1024,    /* cachesize */
66         NULL,           /* hash() */
67         0               /* lorder */
68 };
69
70 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
71 static struct passwd pwd;                       /* password structure */
72 static char *pname;                             /* password file name */
73 static char prefix[MAXPATHLEN];
74
75 static int is_comment;  /* flag for comments */
76 static char line[LINE_MAX];
77
78 void    cleanup(void);
79 void    error(char *);
80 void    cp(char *, char *, mode_t mode);
81 void    mv(char *, char *);
82 int     scan(FILE *, struct passwd *);
83 static void     usage(void);
84
85 int
86 main(argc, argv)
87         int argc;
88         char *argv[];
89 {
90         DB *dp, *sdp, *pw_db;
91         DBT data, sdata, key;
92         FILE *fp, *oldfp;
93         sigset_t set;
94         int ch, cnt, ypcnt, len, makeold, tfd, yp_enabled = 0;
95         char *p, *t;
96         char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
97         char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
98         char buf2[MAXPATHLEN];
99         char sbuf2[MAXPATHLEN];
100         char *username;
101         u_int method, methoduid;
102         int Cflag;
103         int nblock = 0;
104
105         Cflag = 0;
106         strcpy(prefix, _PATH_PWD);
107         makeold = 0;
108         username = NULL;
109         while ((ch = getopt(argc, argv, "Cd:ps:u:vN")) != -1)
110                 switch(ch) {
111                 case 'C':                       /* verify only */
112                         Cflag = 1;
113                         break;
114                 case 'd':
115                         strncpy(prefix, optarg, sizeof prefix - 1);
116                         break;
117                 case 'p':                       /* create V7 "file.orig" */
118                         makeold = 1;
119                         break;
120                 case 's':                       /* change default cachesize */
121                         openinfo.cachesize = atoi(optarg) * 1024 * 1024;
122                         break;
123                 case 'u':                       /* only update this record */
124                         username = optarg;
125                         break;
126                 case 'v':                       /* backward compatible */
127                         break;
128                 case 'N':                       /* do not wait for lock */
129                         nblock = LOCK_NB;
130                         break;
131                 default:
132                         usage();
133                 }
134         argc -= optind;
135         argv += optind;
136
137         if (argc != 1 || (username && (*username == '+' || *username == '-')))
138                 usage();
139
140         /*
141          * This could be changed to allow the user to interrupt.
142          * Probably not worth the effort.
143          */
144         sigemptyset(&set);
145         sigaddset(&set, SIGTSTP);
146         sigaddset(&set, SIGHUP);
147         sigaddset(&set, SIGINT);
148         sigaddset(&set, SIGQUIT);
149         sigaddset(&set, SIGTERM);
150         sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
151
152         /* We don't care what the user wants. */
153         umask(0);
154
155         pname = *argv;
156
157         /*
158          * Open and lock the original password file.  We have to check
159          * the hardlink count after we get the lock to handle any potential
160          * unlink/rename race.
161          *
162          * This lock is necessary when someone runs pwd_mkdb manually, directly
163          * on master.passwd, to handle the case where a user might try to
164          * change his password while pwd_mkdb is running. 
165          */
166         for (;;) {
167                 struct stat st;
168
169                 if (!(fp = fopen(pname, "r")))
170                         error(pname);
171                 if (flock(fileno(fp), LOCK_EX|nblock) < 0)
172                         error("flock");
173                 if (fstat(fileno(fp), &st) < 0)
174                         error(pname);
175                 if (st.st_nlink != 0)
176                         break;
177                 fclose(fp);
178                 fp = NULL;
179         }
180
181         /* check only if password database is valid */
182         if (Cflag) {
183                 for (cnt = 1; scan(fp, &pwd); ++cnt);
184                 exit(0);
185         }
186
187         /* Open the temporary insecure password database. */
188         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
189         snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
190         if (username) {
191                 snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
192                 snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
193
194                 clean = FILE_INSECURE;
195                 cp(buf2, buf, PERM_INSECURE);
196                 dp = dbopen(buf,
197                     O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
198                 if (dp == NULL)
199                         error(buf);
200
201                 clean = FILE_SECURE;
202                 cp(sbuf2, sbuf, PERM_SECURE);
203                 sdp = dbopen(sbuf,
204                     O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
205                 if (sdp == NULL)
206                         error(sbuf);
207
208                 /*
209                  * Do some trouble to check if we should store this users 
210                  * uid. Don't use getpwnam/getpwuid as that interferes 
211                  * with NIS.
212                  */
213                 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
214                 if (!pw_db)
215                         error(_MP_DB);
216                 buf[0] = _PW_KEYBYNAME;
217                 len = strlen(username);
218
219                 /* Only check that username fits in buffer */
220                 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
221                 key.data = (u_char *)buf;
222                 key.size = len + 1;
223                 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
224                         p = (char *)data.data;
225
226                         /* jump over pw_name and pw_passwd, to get to pw_uid */
227                         while (*p++)
228                                 ;
229                         while (*p++)
230                                 ;
231
232                         buf[0] = _PW_KEYBYUID;
233                         memmove(buf + 1, p, sizeof(int));
234                         key.data = (u_char *)buf;
235                         key.size = sizeof(int) + 1;
236
237                         if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
238                                 /* First field of data.data holds pw_pwname */
239                                 if (!strcmp(data.data, username))
240                                         methoduid = 0;
241                                 else
242                                         methoduid = R_NOOVERWRITE;
243                         } else {
244                                 methoduid = R_NOOVERWRITE;
245                         }
246                 } else {
247                         methoduid = R_NOOVERWRITE;
248                 }
249                 if ((pw_db->close)(pw_db))
250                         error("close pw_db");
251                 method = 0;
252         } else {
253                 dp = dbopen(buf,
254                     O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
255                 if (dp == NULL)
256                         error(buf);
257                 clean = FILE_INSECURE;
258
259                 sdp = dbopen(sbuf,
260                     O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
261                 if (sdp == NULL)
262                         error(sbuf);
263                 clean = FILE_SECURE;
264
265                 method = R_NOOVERWRITE;
266                 methoduid = R_NOOVERWRITE;
267         }
268
269         /*
270          * Open file for old password file.  Minor trickiness -- don't want to
271          * chance the file already existing, since someone (stupidly) might
272          * still be using this for permission checking.  So, open it first and
273          * fdopen the resulting fd.  The resulting file should be readable by
274          * everyone.
275          */
276         if (makeold) {
277                 snprintf(buf, sizeof(buf), "%s.orig", pname);
278                 if ((tfd = open(buf,
279                     O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
280                         error(buf);
281                 if ((oldfp = fdopen(tfd, "w")) == NULL)
282                         error(buf);
283                 clean = FILE_ORIG;
284         }
285
286         /*
287          * The databases actually contain three copies of the original data.
288          * Each password file entry is converted into a rough approximation
289          * of a ``struct passwd'', with the strings placed inline.  This
290          * object is then stored as the data for three separate keys.  The
291          * first key * is the pw_name field prepended by the _PW_KEYBYNAME
292          * character.  The second key is the pw_uid field prepended by the
293          * _PW_KEYBYUID character.  The third key is the line number in the
294          * original file prepended by the _PW_KEYBYNUM character.  (The special
295          * characters are prepended to ensure that the keys do not collide.)
296          */
297         ypcnt = 1;
298         data.data = (u_char *)buf;
299         sdata.data = (u_char *)sbuf;
300         key.data = (u_char *)tbuf;
301         for (cnt = 1; scan(fp, &pwd); ++cnt) {
302                 if (!is_comment && 
303                     (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
304                         yp_enabled = 1;
305                 if (is_comment)
306                         --cnt;
307 #define COMPACT(e)      t = e; while ((*p++ = *t++));
308                 if (!is_comment && 
309                     (!username || (strcmp(username, pwd.pw_name) == 0))) {
310                         /* Create insecure data. */
311                         p = buf;
312                         COMPACT(pwd.pw_name);
313                         COMPACT("*");
314                         memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
315                         p += sizeof(int);
316                         memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
317                         p += sizeof(int);
318                         memmove(p, &pwd.pw_change, sizeof(time_t));
319                         p += sizeof(time_t);
320                         COMPACT(pwd.pw_class);
321                         COMPACT(pwd.pw_gecos);
322                         COMPACT(pwd.pw_dir);
323                         COMPACT(pwd.pw_shell);
324                         memmove(p, &pwd.pw_expire, sizeof(time_t));
325                         p += sizeof(time_t);
326                         memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
327                         p += sizeof pwd.pw_fields;
328                         data.size = p - buf;
329
330                         /* Create secure data. */
331                         p = sbuf;
332                         COMPACT(pwd.pw_name);
333                         COMPACT(pwd.pw_passwd);
334                         memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
335                         p += sizeof(int);
336                         memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
337                         p += sizeof(int);
338                         memmove(p, &pwd.pw_change, sizeof(time_t));
339                         p += sizeof(time_t);
340                         COMPACT(pwd.pw_class);
341                         COMPACT(pwd.pw_gecos);
342                         COMPACT(pwd.pw_dir);
343                         COMPACT(pwd.pw_shell);
344                         memmove(p, &pwd.pw_expire, sizeof(time_t));
345                         p += sizeof(time_t);
346                         memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
347                         p += sizeof pwd.pw_fields;
348                         sdata.size = p - sbuf;
349
350                         /* Store insecure by name. */
351                         tbuf[0] = _PW_KEYBYNAME;
352                         len = strlen(pwd.pw_name);
353                         memmove(tbuf + 1, pwd.pw_name, len);
354                         key.size = len + 1;
355                         if ((dp->put)(dp, &key, &data, method) == -1)
356                                 error("put");
357
358                         /* Store insecure by number. */
359                         tbuf[0] = _PW_KEYBYNUM;
360                         memmove(tbuf + 1, &cnt, sizeof(cnt));
361                         key.size = sizeof(cnt) + 1;
362                         if ((dp->put)(dp, &key, &data, method) == -1)
363                                 error("put");
364
365                         /* Store insecure by uid. */
366                         tbuf[0] = _PW_KEYBYUID;
367                         memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
368                         key.size = sizeof(pwd.pw_uid) + 1;
369                         if ((dp->put)(dp, &key, &data, methoduid) == -1)
370                                 error("put");
371
372                         /* Store secure by name. */
373                         tbuf[0] = _PW_KEYBYNAME;
374                         len = strlen(pwd.pw_name);
375                         memmove(tbuf + 1, pwd.pw_name, len);
376                         key.size = len + 1;
377                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
378                                 error("put");
379
380                         /* Store secure by number. */
381                         tbuf[0] = _PW_KEYBYNUM;
382                         memmove(tbuf + 1, &cnt, sizeof(cnt));
383                         key.size = sizeof(cnt) + 1;
384                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
385                                 error("put");
386
387                         /* Store secure by uid. */
388                         tbuf[0] = _PW_KEYBYUID;
389                         memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
390                         key.size = sizeof(pwd.pw_uid) + 1;
391                         if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
392                                 error("put");
393
394                         /* Store insecure and secure special plus and special minus */
395                         if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
396                                 tbuf[0] = _PW_KEYYPBYNUM;
397                                 memmove(tbuf + 1, &ypcnt, sizeof(cnt));
398                                 ypcnt++;
399                                 key.size = sizeof(cnt) + 1;
400                                 if ((dp->put)(dp, &key, &data, method) == -1)
401                                         error("put");
402                                 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
403                                         error("put");
404                         }
405                 }
406                 /* Create original format password file entry */
407                 if (is_comment && makeold){     /* copy comments */
408                         if (fprintf(oldfp, "%s\n", line) < 0)
409                                 error("write old");
410                 } else if (makeold) {
411                         char uidstr[20];
412                         char gidstr[20];
413
414                         snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
415                         snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
416
417                         if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
418                             pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
419                             pwd.pw_fields & _PWF_GID ? gidstr : "",
420                             pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
421                                 error("write old");
422                 }
423         }
424         /* If YP enabled, set flag. */
425         if (yp_enabled) {
426                 buf[0] = yp_enabled + 2;
427                 data.size = 1;
428                 tbuf[0] = _PW_KEYYPENABLED;
429                 key.size = 1;
430                 if ((dp->put)(dp, &key, &data, method) == -1)
431                         error("put");
432                 if ((sdp->put)(sdp, &key, &data, method) == -1)
433                         error("put");
434         }
435
436         if ((dp->close)(dp) == -1)
437                 error("close");
438         if ((sdp->close)(sdp) == -1)
439                 error("close");
440         if (makeold) {
441                 fflush(oldfp);
442                 if (fclose(oldfp) == EOF)
443                         error("close old");
444         }
445
446         /* Set master.passwd permissions, in case caller forgot. */
447         fchmod(fileno(fp), S_IRUSR|S_IWUSR);
448
449         /* Install as the real password files. */
450         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
451         snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
452         mv(buf, buf2);
453         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
454         snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
455         mv(buf, buf2);
456         if (makeold) {
457                 snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
458                 snprintf(buf, sizeof(buf), "%s.orig", pname);
459                 mv(buf, buf2);
460         }
461         /*
462          * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
463          * all use flock(2) on it to block other incarnations of themselves.
464          * The rename means that everything is unlocked, as the original file
465          * can no longer be accessed.
466          */
467         snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
468         mv(pname, buf);
469
470         /*
471          * Close locked password file after rename()
472          */
473         if (fclose(fp) == EOF)
474                 error("close fp");
475
476         exit(0);
477 }
478
479 int
480 scan(fp, pw)
481         FILE *fp;
482         struct passwd *pw;
483 {
484         static int lcnt;
485         char *p;
486
487         if (!fgets(line, sizeof(line), fp))
488                 return (0);
489         ++lcnt;
490         /*
491          * ``... if I swallow anything evil, put your fingers down my
492          * throat...''
493          *      -- The Who
494          */
495         if (!(p = strchr(line, '\n'))) {
496                 warnx("line too long");
497                 goto fmt;
498
499         }
500         *p = '\0';
501
502         /* 
503          * Ignore comments: ^[ \t]*#
504          */
505         for (p = line; *p != '\0'; p++)
506                 if (*p != ' ' && *p != '\t')
507                         break;
508         if (*p == '#' || *p == '\0') {
509                 is_comment = 1;
510                 return(1);
511         } else
512                 is_comment = 0;
513
514         if (!pw_scan(line, pw)) {
515                 warnx("at line #%d", lcnt);
516 fmt:            errno = EFTYPE; /* XXX */
517                 error(pname);
518         }
519
520         return (1);
521 }
522
523 void                    
524 cp(from, to, mode)              
525         char *from, *to;
526         mode_t mode;    
527 {               
528         static char buf[MAXBSIZE];
529         int from_fd, rcount, to_fd, wcount;
530
531         if ((from_fd = open(from, O_RDONLY, 0)) < 0)
532                 error(from);
533         if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
534                 error(to);
535         while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
536                 wcount = write(to_fd, buf, rcount);
537                 if (rcount != wcount || wcount == -1) {
538                         int sverrno = errno;
539
540                         snprintf(buf, sizeof(buf), "%s to %s", from, to);
541                         errno = sverrno;
542                         error(buf);
543                 }
544         }
545         if (rcount < 0) {
546                 int sverrno = errno;
547
548                 snprintf(buf, sizeof(buf), "%s to %s", from, to);
549                 errno = sverrno;
550                 error(buf);
551         }
552 }
553
554
555 void
556 mv(from, to)
557         char *from, *to;
558 {
559         char buf[MAXPATHLEN];
560
561         if (rename(from, to)) {
562                 int sverrno = errno;
563                 snprintf(buf, sizeof(buf), "%s to %s", from, to);
564                 errno = sverrno;
565                 error(buf);
566         }
567 }
568
569 void
570 error(name)
571         char *name;
572 {
573
574         warn("%s", name);
575         cleanup();
576         exit(1);
577 }
578
579 void
580 cleanup()
581 {
582         char buf[MAXPATHLEN];
583
584         switch(clean) {
585         case FILE_ORIG:
586                 snprintf(buf, sizeof(buf), "%s.orig", pname);
587                 unlink(buf);
588                 /* FALLTHROUGH */
589         case FILE_SECURE:
590                 snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
591                 unlink(buf);
592                 /* FALLTHROUGH */
593         case FILE_INSECURE:
594                 snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
595                 unlink(buf);
596         }
597 }
598
599 static void
600 usage()
601 {
602
603         fprintf(stderr,
604 "usage: pwd_mkdb [-C] [-N] [-p] [-d <dest dir>] [-s <cachesize>] [-u <local username>] file\n");
605         exit(1);
606 }