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