pwd_mkdb.c revision 1.10 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.10 1997/10/17 12:18:22 lukem 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
87 void cleanup __P((void));
88 void error __P((char *));
89 int main __P((int, char **));
90 void mv __P((char *, char *));
91 int scan __P((FILE *, struct passwd *, int *));
92 void usage __P((void));
93
94 int
95 main(argc, argv)
96 int argc;
97 char *argv[];
98 {
99 DB *dp, *edp;
100 DBT data, key;
101 FILE *fp, *oldfp;
102 sigset_t set;
103 int ch, cnt, len, makeold, tfd, flags;
104 char *p, *t;
105 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], buf2[MAXPATHLEN], tbuf[1024];
106 int hasyp = 0;
107 DBT ypdata, ypkey;
108
109 oldfp = NULL;
110 strcpy(prefix, "/");
111 makeold = 0;
112 while ((ch = getopt(argc, argv, "d:pv")) != -1)
113 switch(ch) {
114 case 'd':
115 strncpy(prefix, optarg, sizeof(prefix));
116 prefix[sizeof(prefix)-1] = '\0';
117 break;
118 case 'p': /* create V7 "file.orig" */
119 makeold = 1;
120 break;
121 case 'v': /* backward compatible */
122 break;
123 case '?':
124 default:
125 usage();
126 }
127 argc -= optind;
128 argv += optind;
129
130 if (argc != 1)
131 usage();
132
133 /*
134 * This could be changed to allow the user to interrupt.
135 * Probably not worth the effort.
136 */
137 sigemptyset(&set);
138 sigaddset(&set, SIGTSTP);
139 sigaddset(&set, SIGHUP);
140 sigaddset(&set, SIGINT);
141 sigaddset(&set, SIGQUIT);
142 sigaddset(&set, SIGTERM);
143 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
144
145 /* We don't care what the user wants. */
146 (void)umask(0);
147
148 pname = *argv;
149 /* Open the original password file */
150 if (!(fp = fopen(pname, "r")))
151 error(pname);
152
153 /* Open the temporary insecure password database. */
154 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_MP_DB);
155 dp = dbopen(buf,
156 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
157 if (dp == NULL)
158 error(buf);
159 clean = FILE_INSECURE;
160
161 /*
162 * Open file for old password file. Minor trickiness -- don't want to
163 * chance the file already existing, since someone (stupidly) might
164 * still be using this for permission checking. So, open it first and
165 * fdopen the resulting fd. The resulting file should be readable by
166 * everyone.
167 */
168 if (makeold) {
169 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
170 if ((tfd = open(buf,
171 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
172 error(buf);
173 if ((oldfp = fdopen(tfd, "w")) == NULL)
174 error(buf);
175 clean = FILE_ORIG;
176 }
177
178 /*
179 * The databases actually contain three copies of the original data.
180 * Each password file entry is converted into a rough approximation
181 * of a ``struct passwd'', with the strings placed inline. This
182 * object is then stored as the data for three separate keys. The
183 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
184 * character. The second key is the pw_uid field prepended by the
185 * _PW_KEYBYUID character. The third key is the line number in the
186 * original file prepended by the _PW_KEYBYNUM character. (The special
187 * characters are prepended to ensure that the keys do not collide.)
188 *
189 * If we see something go by that looks like YP, we save a special
190 * pointer record, which if YP is enabled in the C lib, will speed
191 * things up.
192 */
193 data.data = (u_char *)buf;
194 key.data = (u_char *)tbuf;
195 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) {
196 #define COMPACT(e) t = e; while ((*p++ = *t++));
197
198 /* look like YP? */
199 if((pwd.pw_name[0] == '+') || (pwd.pw_name[0] == '-'))
200 hasyp++;
201
202 /*
203 * Warn about potentially unsafe uid/gid overrides.
204 */
205 if (pwd.pw_name[0] == '+') {
206 if ((flags & _PASSWORD_NOUID) == 0 && pwd.pw_uid == 0)
207 warnx("line %d: superuser override in YP inclusion", cnt);
208 if ((flags & _PASSWORD_NOGID) == 0 && pwd.pw_gid == 0)
209 warnx("line %d: wheel override in YP inclusion", cnt);
210 }
211
212 /* Create insecure data. */
213 p = buf;
214 COMPACT(pwd.pw_name);
215 COMPACT("*");
216 memmove(p, &pwd.pw_uid, sizeof(int));
217 p += sizeof(int);
218 memmove(p, &pwd.pw_gid, sizeof(int));
219 p += sizeof(int);
220 memmove(p, &pwd.pw_change, sizeof(time_t));
221 p += sizeof(time_t);
222 COMPACT(pwd.pw_class);
223 COMPACT(pwd.pw_gecos);
224 COMPACT(pwd.pw_dir);
225 COMPACT(pwd.pw_shell);
226 memmove(p, &pwd.pw_expire, sizeof(time_t));
227 p += sizeof(time_t);
228 memmove(p, &flags, sizeof(int));
229 p += sizeof(int);
230 data.size = p - buf;
231
232 /* Store insecure by name. */
233 tbuf[0] = _PW_KEYBYNAME;
234 len = strlen(pwd.pw_name);
235 memmove(tbuf + 1, pwd.pw_name, len);
236 key.size = len + 1;
237 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
238 error("put");
239
240 /* Store insecure by number. */
241 tbuf[0] = _PW_KEYBYNUM;
242 memmove(tbuf + 1, &cnt, sizeof(cnt));
243 key.size = sizeof(cnt) + 1;
244 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
245 error("put");
246
247 /* Store insecure by uid. */
248 tbuf[0] = _PW_KEYBYUID;
249 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
250 key.size = sizeof(pwd.pw_uid) + 1;
251 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
252 error("put");
253
254 /* Create original format password file entry */
255 if (makeold)
256 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
257 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
258 pwd.pw_dir, pwd.pw_shell);
259 }
260
261 /* Store YP token, if needed. */
262 if(hasyp) {
263 ypkey.data = (u_char *)__yp_token;
264 ypkey.size = strlen(__yp_token);
265 ypdata.data = (u_char *)NULL;
266 ypdata.size = 0;
267
268 if ((dp->put)(dp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
269 error("put");
270 }
271
272 (void)(dp->close)(dp);
273 if (makeold) {
274 (void)fflush(oldfp);
275 (void)fclose(oldfp);
276 }
277
278 /* Open the temporary encrypted password database. */
279 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_SMP_DB);
280 edp = dbopen(buf,
281 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
282 if (!edp)
283 error(buf);
284 clean = FILE_SECURE;
285
286 rewind(fp);
287 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) {
288
289 /* Create secure data. */
290 p = buf;
291 COMPACT(pwd.pw_name);
292 COMPACT(pwd.pw_passwd);
293 memmove(p, &pwd.pw_uid, sizeof(int));
294 p += sizeof(int);
295 memmove(p, &pwd.pw_gid, sizeof(int));
296 p += sizeof(int);
297 memmove(p, &pwd.pw_change, sizeof(time_t));
298 p += sizeof(time_t);
299 COMPACT(pwd.pw_class);
300 COMPACT(pwd.pw_gecos);
301 COMPACT(pwd.pw_dir);
302 COMPACT(pwd.pw_shell);
303 memmove(p, &pwd.pw_expire, sizeof(time_t));
304 p += sizeof(time_t);
305 memmove(p, &flags, sizeof(int));
306 p += sizeof(int);
307 data.size = p - buf;
308
309 /* Store secure by name. */
310 tbuf[0] = _PW_KEYBYNAME;
311 len = strlen(pwd.pw_name);
312 memmove(tbuf + 1, pwd.pw_name, len);
313 key.size = len + 1;
314 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
315 error("put");
316
317 /* Store secure by number. */
318 tbuf[0] = _PW_KEYBYNUM;
319 memmove(tbuf + 1, &cnt, sizeof(cnt));
320 key.size = sizeof(cnt) + 1;
321 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
322 error("put");
323
324 /* Store secure by uid. */
325 tbuf[0] = _PW_KEYBYUID;
326 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
327 key.size = sizeof(pwd.pw_uid) + 1;
328 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
329 error("put");
330 }
331
332 /* Store YP token, if needed. */
333 if(hasyp) {
334 ypkey.data = (u_char *)__yp_token;
335 ypkey.size = strlen(__yp_token);
336 ypdata.data = (u_char *)NULL;
337 ypdata.size = 0;
338
339 if((dp->put)(edp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
340 error("put");
341 }
342
343 (void)(edp->close)(edp);
344
345 /* Set master.passwd permissions, in case caller forgot. */
346 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
347 (void)fclose(fp);
348
349 /* Install as the real password files. */
350 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_MP_DB);
351 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix, _PATH_MP_DB);
352 mv(buf, buf2);
353 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_SMP_DB);
354 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix, _PATH_SMP_DB);
355 mv(buf, buf2);
356 if (makeold) {
357 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
358 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix,
359 _PATH_PASSWD);
360 mv(buf, buf2);
361 }
362 /*
363 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
364 * all use flock(2) on it to block other incarnations of themselves.
365 * The rename means that everything is unlocked, as the original file
366 * can no longer be accessed.
367 */
368 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, _PATH_MASTERPASSWD);
369 mv(pname, buf);
370 exit(0);
371 }
372
373 int
374 scan(fp, pw, flags)
375 FILE *fp;
376 struct passwd *pw;
377 int *flags;
378 {
379 static int lcnt;
380 static char line[LINE_MAX];
381 char *p;
382
383 if (!fgets(line, sizeof(line), fp))
384 return (0);
385 ++lcnt;
386 /*
387 * ``... if I swallow anything evil, put your fingers down my
388 * throat...''
389 * -- The Who
390 */
391 if (!(p = strchr(line, '\n'))) {
392 warnx("line too long");
393 goto fmt;
394
395 }
396 *p = '\0';
397 if (!pw_scan(line, pw, flags)) {
398 warnx("at line #%d", lcnt);
399 fmt: errno = EFTYPE; /* XXX */
400 error(pname);
401 }
402
403 return (1);
404 }
405
406 void
407 mv(from, to)
408 char *from, *to;
409 {
410 char buf[MAXPATHLEN];
411
412 if (rename(from, to)) {
413 int sverrno = errno;
414 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
415 errno = sverrno;
416 error(buf);
417 }
418 }
419
420 void
421 error(name)
422 char *name;
423 {
424
425 warn(name);
426 cleanup();
427 exit(1);
428 }
429
430 void
431 cleanup()
432 {
433 char buf[MAXPATHLEN];
434
435 switch(clean) {
436 case FILE_ORIG:
437 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
438 (void)unlink(buf);
439 /* FALLTHROUGH */
440 case FILE_SECURE:
441 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix,
442 _PATH_SMP_DB);
443 (void)unlink(buf);
444 /* FALLTHROUGH */
445 case FILE_INSECURE:
446 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix,
447 _PATH_MP_DB);
448 (void)unlink(buf);
449 }
450 }
451
452 void
453 usage()
454 {
455
456 (void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d directory] file\n");
457 exit(1);
458 }
459