pwd_mkdb.c revision 1.17 1 /*
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Portions Copyright(C) 1994, Jason Downs. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 2000\n\
38 The NetBSD Foundation, Inc. All rights reserved.\n\
39 Copyright (c) 1991, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n");
41 __SCCSID("from: @(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94");
42 __RCSID("$NetBSD: pwd_mkdb.c,v 1.17 2000/01/23 19:59:33 mycroft Exp $");
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47
48 #include <db.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <pwd.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <util.h>
60
61 #define INSECURE 1
62 #define SECURE 2
63 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
64 #define PERM_SECURE (S_IRUSR|S_IWUSR)
65
66 /* pull this out of the C library. */
67 extern const char __yp_token[];
68
69 HASHINFO openinfo = {
70 4096, /* bsize */
71 32, /* ffactor */
72 256, /* nelem */
73 2048 * 1024, /* cachesize */
74 NULL, /* hash() */
75 0 /* lorder */
76 };
77
78 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
79 static struct passwd pwd; /* password structure */
80 static char *pname; /* password file name */
81 static char prefix[MAXPATHLEN];
82 static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)];
83 static char pwd_db_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
84 static char pwd_Sdb_tmp[MAX(MAXPATHLEN, LINE_MAX * 2)];
85
86 void cleanup __P((void));
87 void error __P((char *));
88 void wr_error __P((char *));
89 int main __P((int, char **));
90 void mv __P((char *, char *));
91 void rm __P((char *));
92 int scan __P((FILE *, struct passwd *, u_int32_t *));
93 void usage __P((void));
94
95 int
96 main(argc, argv)
97 int argc;
98 char *argv[];
99 {
100 DB *dp, *edp;
101 DBT data, key;
102 FILE *fp, *oldfp;
103 sigset_t set;
104 int ch, len, makeold, tfd;
105 u_int32_t x, cnt, flags;
106 char *p;
107 const char *t;
108 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
109 int hasyp = 0;
110 DBT ypdata, ypkey;
111 int lorder = BYTE_ORDER;
112
113 oldfp = NULL;
114 strcpy(prefix, "/");
115 makeold = 0;
116 while ((ch = getopt(argc, argv, "d:pvBL")) != -1)
117 switch(ch) {
118 case 'd':
119 strncpy(prefix, optarg, sizeof(prefix));
120 prefix[sizeof(prefix)-1] = '\0';
121 break;
122 case 'p': /* create V7 "file.orig" */
123 makeold = 1;
124 break;
125 case 'v': /* backward compatible */
126 break;
127 case 'B':
128 lorder = BIG_ENDIAN;
129 break;
130 case 'L':
131 lorder = LITTLE_ENDIAN;
132 break;
133 case '?':
134 default:
135 usage();
136 }
137 argc -= optind;
138 argv += optind;
139
140 if (argc != 1)
141 usage();
142
143 /*
144 * This could be changed to allow the user to interrupt.
145 * Probably not worth the effort.
146 */
147 sigemptyset(&set);
148 sigaddset(&set, SIGTSTP);
149 sigaddset(&set, SIGHUP);
150 sigaddset(&set, SIGINT);
151 sigaddset(&set, SIGQUIT);
152 sigaddset(&set, SIGTERM);
153 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
154
155 /* We don't care what the user wants. */
156 (void)umask(0);
157
158 pname = *argv;
159 /* Open the original password file */
160 if (!(fp = fopen(pname, "r")))
161 error(pname);
162
163 openinfo.lorder = lorder;
164
165 /* Open the temporary insecure password database. */
166 (void)snprintf(pwd_db_tmp, sizeof(pwd_db_tmp), "%s%s.tmp", prefix,
167 _PATH_MP_DB);
168 dp = dbopen(pwd_db_tmp,
169 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
170 if (dp == NULL)
171 error(pwd_db_tmp);
172 clean = FILE_INSECURE;
173
174 /*
175 * Open file for old password file. Minor trickiness -- don't want to
176 * chance the file already existing, since someone (stupidly) might
177 * still be using this for permission checking. So, open it first and
178 * fdopen the resulting fd. The resulting file should be readable by
179 * everyone.
180 */
181 if (makeold) {
182 (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig",
183 pname);
184 if ((tfd = open(oldpwdfile,
185 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
186 error(oldpwdfile);
187 if ((oldfp = fdopen(tfd, "w")) == NULL)
188 error(oldpwdfile);
189 clean = FILE_ORIG;
190 }
191
192 /*
193 * The databases actually contain three copies of the original data.
194 * Each password file entry is converted into a rough approximation
195 * of a ``struct passwd'', with the strings placed inline. This
196 * object is then stored as the data for three separate keys. The
197 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
198 * character. The second key is the pw_uid field prepended by the
199 * _PW_KEYBYUID character. The third key is the line number in the
200 * original file prepended by the _PW_KEYBYNUM character. (The special
201 * characters are prepended to ensure that the keys do not collide.)
202 *
203 * If we see something go by that looks like YP, we save a special
204 * pointer record, which if YP is enabled in the C lib, will speed
205 * things up.
206 */
207 data.data = (u_char *)buf;
208 key.data = (u_char *)tbuf;
209 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) {
210 #define COMPACT(e) t = e; while ((*p++ = *t++));
211
212 /* look like YP? */
213 if((pwd.pw_name[0] == '+') || (pwd.pw_name[0] == '-'))
214 hasyp++;
215
216 /*
217 * Warn about potentially unsafe uid/gid overrides.
218 */
219 if (pwd.pw_name[0] == '+') {
220 if ((flags & _PASSWORD_NOUID) == 0 && pwd.pw_uid == 0)
221 warnx(
222 "line %d: superuser override in YP inclusion",
223 cnt);
224 if ((flags & _PASSWORD_NOGID) == 0 && pwd.pw_gid == 0)
225 warnx("line %d: wheel override in YP inclusion",
226 cnt);
227 }
228
229 if (lorder != BYTE_ORDER) {
230 M_32_SWAP(pwd.pw_uid);
231 M_32_SWAP(pwd.pw_gid);
232 M_32_SWAP(pwd.pw_change);
233 M_32_SWAP(pwd.pw_expire);
234 M_32_SWAP(flags);
235 }
236
237 /* Create insecure data. */
238 p = buf;
239 COMPACT(pwd.pw_name);
240 COMPACT("*");
241 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
242 p += sizeof(pwd.pw_uid);
243 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
244 p += sizeof(pwd.pw_gid);
245 memmove(p, &pwd.pw_change, sizeof(pwd.pw_change));
246 p += sizeof(pwd.pw_change);
247 COMPACT(pwd.pw_class);
248 COMPACT(pwd.pw_gecos);
249 COMPACT(pwd.pw_dir);
250 COMPACT(pwd.pw_shell);
251 memmove(p, &pwd.pw_expire, sizeof(pwd.pw_expire));
252 p += sizeof(pwd.pw_expire);
253 memmove(p, &flags, sizeof(flags));
254 p += sizeof(flags);
255 data.size = p - buf;
256
257 /* Store insecure by name. */
258 tbuf[0] = _PW_KEYBYNAME;
259 len = strlen(pwd.pw_name);
260 memmove(tbuf + 1, pwd.pw_name, len);
261 key.size = len + 1;
262 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
263 wr_error(pwd_db_tmp);
264
265 /* Store insecure by number. */
266 tbuf[0] = _PW_KEYBYNUM;
267 x = cnt;
268 if (lorder != BYTE_ORDER)
269 M_32_SWAP(x);
270 memmove(tbuf + 1, &x, sizeof(x));
271 key.size = sizeof(x) + 1;
272 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
273 wr_error(pwd_db_tmp);
274
275 /* Store insecure by uid. */
276 tbuf[0] = _PW_KEYBYUID;
277 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
278 key.size = sizeof(pwd.pw_uid) + 1;
279 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
280 wr_error(pwd_db_tmp);
281
282 /* Create original format password file entry */
283 if (makeold) {
284 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
285 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
286 pwd.pw_dir, pwd.pw_shell);
287 if (ferror(oldfp)) {
288 wr_error(oldpwdfile);
289 }
290 }
291 }
292
293 /* Store YP token, if needed. */
294 if(hasyp) {
295 ypkey.data = (u_char *)__yp_token;
296 ypkey.size = strlen(__yp_token);
297 ypdata.data = (u_char *)NULL;
298 ypdata.size = 0;
299
300 if ((dp->put)(dp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
301 wr_error(pwd_db_tmp);
302 }
303
304 if ((dp->close)(dp) < 0) {
305 wr_error(pwd_db_tmp);
306 }
307 if (makeold) {
308 if (fflush(oldfp) == EOF) {
309 wr_error(oldpwdfile);
310 }
311 if (fclose(oldfp) == EOF) {
312 wr_error(oldpwdfile);
313 }
314 }
315
316 /* Open the temporary encrypted password database. */
317 (void)snprintf(pwd_Sdb_tmp, sizeof(pwd_Sdb_tmp), "%s%s.tmp", prefix,
318 _PATH_SMP_DB);
319 edp = dbopen(pwd_Sdb_tmp,
320 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
321 if (!edp)
322 error(pwd_Sdb_tmp);
323 clean = FILE_SECURE;
324
325 rewind(fp);
326 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) {
327
328 if (lorder != BYTE_ORDER) {
329 M_32_SWAP(pwd.pw_uid);
330 M_32_SWAP(pwd.pw_gid);
331 M_32_SWAP(pwd.pw_change);
332 M_32_SWAP(pwd.pw_expire);
333 M_32_SWAP(flags);
334 }
335
336 /* Create secure data. */
337 p = buf;
338 COMPACT(pwd.pw_name);
339 COMPACT(pwd.pw_passwd);
340 memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
341 p += sizeof(pwd.pw_uid);
342 memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
343 p += sizeof(pwd.pw_gid);
344 memmove(p, &pwd.pw_change, sizeof(pwd.pw_change));
345 p += sizeof(pwd.pw_change);
346 COMPACT(pwd.pw_class);
347 COMPACT(pwd.pw_gecos);
348 COMPACT(pwd.pw_dir);
349 COMPACT(pwd.pw_shell);
350 memmove(p, &pwd.pw_expire, sizeof(pwd.pw_expire));
351 p += sizeof(pwd.pw_expire);
352 memmove(p, &flags, sizeof(flags));
353 p += sizeof(flags);
354 data.size = p - buf;
355
356 /* Store secure by name. */
357 tbuf[0] = _PW_KEYBYNAME;
358 len = strlen(pwd.pw_name);
359 memmove(tbuf + 1, pwd.pw_name, len);
360 key.size = len + 1;
361 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
362 wr_error(pwd_Sdb_tmp);
363
364 /* Store secure by number. */
365 tbuf[0] = _PW_KEYBYNUM;
366 x = cnt;
367 if (lorder != BYTE_ORDER)
368 M_32_SWAP(x);
369 memmove(tbuf + 1, &x, sizeof(x));
370 key.size = sizeof(x) + 1;
371 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
372 wr_error(pwd_Sdb_tmp);
373
374 /* Store secure by uid. */
375 tbuf[0] = _PW_KEYBYUID;
376 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
377 key.size = sizeof(pwd.pw_uid) + 1;
378 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
379 wr_error(pwd_Sdb_tmp);
380 }
381
382 /* Store YP token, if needed. */
383 if(hasyp) {
384 ypkey.data = (u_char *)__yp_token;
385 ypkey.size = strlen(__yp_token);
386 ypdata.data = (u_char *)NULL;
387 ypdata.size = 0;
388
389 if((edp->put)(edp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
390 wr_error(pwd_Sdb_tmp);
391 }
392
393 if ((edp->close)(edp) < 0) {
394 wr_error(_PATH_SMP_DB);
395 }
396
397 /* Set master.passwd permissions, in case caller forgot. */
398 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
399 if (fclose(fp) == EOF) {
400 wr_error(pname);
401 }
402
403 /* Install as the real password files. */
404 {
405 char destination[MAXPATHLEN];
406
407 (void)snprintf(destination, sizeof(destination),
408 "%s%s", prefix, _PATH_MP_DB);
409 mv(pwd_db_tmp, destination);
410
411 (void)snprintf(destination, sizeof(destination),
412 "%s%s", prefix, _PATH_SMP_DB);
413 mv(pwd_Sdb_tmp, destination);
414
415 if (makeold) {
416 (void)snprintf(destination, sizeof(destination),
417 "%s%s", prefix, _PATH_PASSWD);
418 mv(oldpwdfile, destination);
419 }
420
421 /*
422 * Move the master password LAST -- chpass(1),
423 * passwd(1) and vipw(8) all use flock(2) on it to
424 * block other incarnations of themselves. The rename
425 * means that everything is unlocked, as the original
426 * file can no longer be accessed.
427 */
428 (void)snprintf(destination, sizeof(destination),
429 "%s%s", prefix, _PATH_MASTERPASSWD);
430 mv(pname, destination);
431 }
432 exit(0);
433 }
434
435 int
436 scan(fp, pw, flags)
437 FILE *fp;
438 struct passwd *pw;
439 u_int32_t *flags;
440 {
441 static int lcnt;
442 static char line[LINE_MAX];
443 char *p;
444 int oflags;
445
446 if (!fgets(line, sizeof(line), fp))
447 return (0);
448 ++lcnt;
449 /*
450 * ``... if I swallow anything evil, put your fingers down my
451 * throat...''
452 * -- The Who
453 */
454 if (!(p = strchr(line, '\n'))) {
455 warnx("line too long");
456 goto fmt;
457
458 }
459 *p = '\0';
460 if (strcmp(line, "+") == 0)
461 strcpy(line, "+:::::::::"); /* pw_scan() can't handle "+" */
462 oflags = 0;
463 if (!pw_scan(line, pw, &oflags)) {
464 warnx("at line #%d", lcnt);
465 fmt: errno = EFTYPE; /* XXX */
466 error(pname);
467 }
468 *flags = oflags;
469
470 return (1);
471 }
472
473 void
474 mv(from, to)
475 char *from, *to;
476 {
477 char buf[MAXPATHLEN];
478
479 if (rename(from, to)) {
480 int sverrno = errno;
481 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
482 errno = sverrno;
483 error(buf);
484 }
485 }
486
487 void
488 wr_error(name)
489 char *name;
490 {
491 char errbuf[BUFSIZ];
492 int sverrno = errno;
493
494 (void)snprintf(errbuf, sizeof(errbuf),
495 "attempt to write %s failed", name);
496
497 errno = sverrno;
498 error(errbuf);
499 }
500
501 void
502 error(name)
503 char *name;
504 {
505
506 warn(name);
507 cleanup();
508 #ifdef think_about_this_a_while_longer
509 fputs("NOTE: possible inconsistencies between text files and databases\n", stderr);
510 fputs("re-run pwd_mkdb when you have fixed the problem.\n", stderr);
511 #endif
512 exit(1);
513 }
514
515 void
516 rm(victim)
517 char *victim;
518 {
519 if (unlink(victim) < 0) {
520 warn("unlink(%s)", victim);
521 }
522 }
523
524 void
525 cleanup()
526 {
527 switch(clean) {
528 case FILE_ORIG:
529 rm(oldpwdfile);
530 /* FALLTHROUGH */
531 case FILE_SECURE:
532 rm(pwd_Sdb_tmp);
533 /* FALLTHROUGH */
534 case FILE_INSECURE:
535 rm(pwd_db_tmp);
536 }
537 }
538
539 void
540 usage()
541 {
542
543 (void)fprintf(stderr, "usage: pwd_mkdb [-pBL] [-d directory] file\n");
544 exit(1);
545 }
546