pwd_mkdb.c revision 1.5 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
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1991, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";*/
42 static char *rcsid = "$Id: pwd_mkdb.c,v 1.5 1994/08/28 23:32:54 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
60 #include "pw_scan.h"
61
62 #define INSECURE 1
63 #define SECURE 2
64 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
65 #define PERM_SECURE (S_IRUSR|S_IWUSR)
66
67 HASHINFO openinfo = {
68 4096, /* bsize */
69 32, /* ffactor */
70 256, /* nelem */
71 2048 * 1024, /* cachesize */
72 NULL, /* hash() */
73 0 /* lorder */
74 };
75
76 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
77 static struct passwd pwd; /* password structure */
78 static char *pname; /* password file name */
79
80 void cleanup __P((void));
81 void error __P((char *));
82 void mv __P((char *, char *));
83 int scan __P((FILE *, struct passwd *));
84 void usage __P((void));
85
86 int
87 main(argc, argv)
88 int argc;
89 char *argv[];
90 {
91 DB *dp, *edp;
92 DBT data, key;
93 FILE *fp, *oldfp;
94 sigset_t set;
95 int ch, cnt, len, makeold, tfd;
96 char *p, *t;
97 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
98
99 makeold = 0;
100 while ((ch = getopt(argc, argv, "pv")) != EOF)
101 switch(ch) {
102 case 'p': /* create V7 "file.orig" */
103 makeold = 1;
104 break;
105 case 'v': /* backward compatible */
106 break;
107 case '?':
108 default:
109 usage();
110 }
111 argc -= optind;
112 argv += optind;
113
114 if (argc != 1)
115 usage();
116
117 /*
118 * This could be changed to allow the user to interrupt.
119 * Probably not worth the effort.
120 */
121 sigemptyset(&set);
122 sigaddset(&set, SIGTSTP);
123 sigaddset(&set, SIGHUP);
124 sigaddset(&set, SIGINT);
125 sigaddset(&set, SIGQUIT);
126 sigaddset(&set, SIGTERM);
127 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
128
129 /* We don't care what the user wants. */
130 (void)umask(0);
131
132 pname = *argv;
133 /* Open the original password file */
134 if (!(fp = fopen(pname, "r")))
135 error(pname);
136
137 /* Open the temporary insecure password database. */
138 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
139 dp = dbopen(buf,
140 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
141 if (dp == NULL)
142 error(buf);
143 clean = FILE_INSECURE;
144
145 /*
146 * Open file for old password file. Minor trickiness -- don't want to
147 * chance the file already existing, since someone (stupidly) might
148 * still be using this for permission checking. So, open it first and
149 * fdopen the resulting fd. The resulting file should be readable by
150 * everyone.
151 */
152 if (makeold) {
153 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
154 if ((tfd = open(buf,
155 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
156 error(buf);
157 if ((oldfp = fdopen(tfd, "w")) == NULL)
158 error(buf);
159 clean = FILE_ORIG;
160 }
161
162 /*
163 * The databases actually contain three copies of the original data.
164 * Each password file entry is converted into a rough approximation
165 * of a ``struct passwd'', with the strings placed inline. This
166 * object is then stored as the data for three separate keys. The
167 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
168 * character. The second key is the pw_uid field prepended by the
169 * _PW_KEYBYUID character. The third key is the line number in the
170 * original file prepended by the _PW_KEYBYNUM character. (The special
171 * characters are prepended to ensure that the keys do not collide.)
172 */
173 data.data = (u_char *)buf;
174 key.data = (u_char *)tbuf;
175 for (cnt = 1; scan(fp, &pwd); ++cnt) {
176 #define COMPACT(e) t = e; while (*p++ = *t++);
177 /* Create insecure data. */
178 p = buf;
179 COMPACT(pwd.pw_name);
180 COMPACT("*");
181 memmove(p, &pwd.pw_uid, sizeof(int));
182 p += sizeof(int);
183 memmove(p, &pwd.pw_gid, sizeof(int));
184 p += sizeof(int);
185 memmove(p, &pwd.pw_change, sizeof(time_t));
186 p += sizeof(time_t);
187 COMPACT(pwd.pw_class);
188 COMPACT(pwd.pw_gecos);
189 COMPACT(pwd.pw_dir);
190 COMPACT(pwd.pw_shell);
191 memmove(p, &pwd.pw_expire, sizeof(time_t));
192 p += sizeof(time_t);
193 data.size = p - buf;
194
195 /* Store insecure by name. */
196 tbuf[0] = _PW_KEYBYNAME;
197 len = strlen(pwd.pw_name);
198 memmove(tbuf + 1, pwd.pw_name, len);
199 key.size = len + 1;
200 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
201 error("put");
202
203 /* Store insecure by number. */
204 tbuf[0] = _PW_KEYBYNUM;
205 memmove(tbuf + 1, &cnt, sizeof(cnt));
206 key.size = sizeof(cnt) + 1;
207 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
208 error("put");
209
210 /* Store insecure by uid. */
211 tbuf[0] = _PW_KEYBYUID;
212 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
213 key.size = sizeof(pwd.pw_uid) + 1;
214 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
215 error("put");
216
217 /* Create original format password file entry */
218 if (makeold)
219 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
220 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
221 pwd.pw_dir, pwd.pw_shell);
222 }
223 (void)(dp->close)(dp);
224 if (makeold) {
225 (void)fflush(oldfp);
226 (void)fclose(oldfp);
227 }
228
229 /* Open the temporary encrypted password database. */
230 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
231 edp = dbopen(buf,
232 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
233 if (!edp)
234 error(buf);
235 clean = FILE_SECURE;
236
237 rewind(fp);
238 for (cnt = 1; scan(fp, &pwd); ++cnt) {
239
240 /* Create secure data. */
241 p = buf;
242 COMPACT(pwd.pw_name);
243 COMPACT(pwd.pw_passwd);
244 memmove(p, &pwd.pw_uid, sizeof(int));
245 p += sizeof(int);
246 memmove(p, &pwd.pw_gid, sizeof(int));
247 p += sizeof(int);
248 memmove(p, &pwd.pw_change, sizeof(time_t));
249 p += sizeof(time_t);
250 COMPACT(pwd.pw_class);
251 COMPACT(pwd.pw_gecos);
252 COMPACT(pwd.pw_dir);
253 COMPACT(pwd.pw_shell);
254 memmove(p, &pwd.pw_expire, sizeof(time_t));
255 p += sizeof(time_t);
256 data.size = p - buf;
257
258 /* Store secure by name. */
259 tbuf[0] = _PW_KEYBYNAME;
260 len = strlen(pwd.pw_name);
261 memmove(tbuf + 1, pwd.pw_name, len);
262 key.size = len + 1;
263 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
264 error("put");
265
266 /* Store secure by number. */
267 tbuf[0] = _PW_KEYBYNUM;
268 memmove(tbuf + 1, &cnt, sizeof(cnt));
269 key.size = sizeof(cnt) + 1;
270 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
271 error("put");
272
273 /* Store secure by uid. */
274 tbuf[0] = _PW_KEYBYUID;
275 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
276 key.size = sizeof(pwd.pw_uid) + 1;
277 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
278 error("put");
279 }
280
281 (void)(edp->close)(edp);
282
283 /* Set master.passwd permissions, in case caller forgot. */
284 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
285 (void)fclose(fp);
286
287 /* Install as the real password files. */
288 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
289 mv(buf, _PATH_MP_DB);
290 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
291 mv(buf, _PATH_SMP_DB);
292 if (makeold) {
293 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
294 mv(buf, _PATH_PASSWD);
295 }
296 /*
297 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
298 * all use flock(2) on it to block other incarnations of themselves.
299 * The rename means that everything is unlocked, as the original file
300 * can no longer be accessed.
301 */
302 mv(pname, _PATH_MASTERPASSWD);
303 exit(0);
304 }
305
306 int
307 scan(fp, pw)
308 FILE *fp;
309 struct passwd *pw;
310 {
311 static int lcnt;
312 static char line[LINE_MAX];
313 char *p;
314
315 if (!fgets(line, sizeof(line), fp))
316 return (0);
317 ++lcnt;
318 /*
319 * ``... if I swallow anything evil, put your fingers down my
320 * throat...''
321 * -- The Who
322 */
323 if (!(p = strchr(line, '\n'))) {
324 warnx("line too long");
325 goto fmt;
326
327 }
328 *p = '\0';
329 if (!pw_scan(line, pw)) {
330 warnx("at line #%d", lcnt);
331 fmt: errno = EFTYPE; /* XXX */
332 error(pname);
333 }
334
335 return (1);
336 }
337
338 void
339 mv(from, to)
340 char *from, *to;
341 {
342 char buf[MAXPATHLEN];
343
344 if (rename(from, to)) {
345 int sverrno = errno;
346 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
347 errno = sverrno;
348 error(buf);
349 }
350 }
351
352 void
353 error(name)
354 char *name;
355 {
356
357 warn(name);
358 cleanup();
359 exit(1);
360 }
361
362 void
363 cleanup()
364 {
365 char buf[MAXPATHLEN];
366
367 switch(clean) {
368 case FILE_ORIG:
369 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
370 (void)unlink(buf);
371 /* FALLTHROUGH */
372 case FILE_SECURE:
373 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
374 (void)unlink(buf);
375 /* FALLTHROUGH */
376 case FILE_INSECURE:
377 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
378 (void)unlink(buf);
379 }
380 }
381
382 void
383 usage()
384 {
385
386 (void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
387 exit(1);
388 }
389