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