]> git.saurik.com Git - apple/system_cmds.git/blob - pwd_mkdb.tproj/pwd_mkdb.c
5d3d10cf3b7918fc81d8152228277b8e020f1302
[apple/system_cmds.git] / pwd_mkdb.tproj / pwd_mkdb.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*-
26 * Copyright (c) 1991, 1993, 1994
27 * The Regents of the University of California. All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58 #ifndef lint
59 static char copyright[] =
60 "@(#) Copyright (c) 1991, 1993, 1994\n\
61 The Regents of the University of California. All rights reserved.\n";
62 #endif /* not lint */
63
64 #ifndef lint
65 static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
66 #endif /* not lint */
67
68 #include <sys/param.h>
69 #include <sys/stat.h>
70
71 #include <db.h>
72 #include <err.h>
73 #include <errno.h>
74 #include <fcntl.h>
75 #include <limits.h>
76 #include <pwd.h>
77 #include <signal.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <unistd.h>
82
83 #include "pw_scan.h"
84
85 #define INSECURE 1
86 #define SECURE 2
87 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
88 #define PERM_SECURE (S_IRUSR|S_IWUSR)
89
90 HASHINFO openinfo = {
91 4096, /* bsize */
92 32, /* ffactor */
93 256, /* nelem */
94 2048 * 1024, /* cachesize */
95 NULL, /* hash() */
96 0 /* lorder */
97 };
98
99 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
100 static struct passwd pwd; /* password structure */
101 static char *pname; /* password file name */
102
103 void cleanup __P((void));
104 void error __P((char *));
105 void mv __P((char *, char *));
106 int scan __P((FILE *, struct passwd *));
107 void usage __P((void));
108
109 int
110 main(argc, argv)
111 int argc;
112 char *argv[];
113 {
114 DB *dp, *edp;
115 DBT data, key;
116 FILE *fp, *oldfp;
117 sigset_t set;
118 int ch, cnt, len, makeold, tfd;
119 char *p, *t;
120 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
121
122 makeold = 0;
123 while ((ch = getopt(argc, argv, "pv")) != EOF)
124 switch(ch) {
125 case 'p': /* create V7 "file.orig" */
126 makeold = 1;
127 break;
128 case 'v': /* backward compatible */
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")))
158 error(pname);
159
160 /* Open the temporary insecure password database. */
161 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
162 dp = dbopen(buf,
163 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
164 if (dp == NULL)
165 error(buf);
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(buf, sizeof(buf), "%s.orig", pname);
177 if ((tfd = open(buf,
178 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
179 error(buf);
180 if ((oldfp = fdopen(tfd, "w")) == NULL)
181 error(buf);
182 clean = FILE_ORIG;
183 }
184
185 /*
186 * The databases actually contain three copies of the original data.
187 * Each password file entry is converted into a rough approximation
188 * of a ``struct passwd'', with the strings placed inline. This
189 * object is then stored as the data for three separate keys. The
190 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
191 * character. The second key is the pw_uid field prepended by the
192 * _PW_KEYBYUID character. The third key is the line number in the
193 * original file prepended by the _PW_KEYBYNUM character. (The special
194 * characters are prepended to ensure that the keys do not collide.)
195 */
196 data.data = (u_char *)buf;
197 key.data = (u_char *)tbuf;
198 for (cnt = 1; scan(fp, &pwd); ++cnt) {
199 #define COMPACT(e) t = e; while (*p++ = *t++);
200 /* Create insecure data. */
201 p = buf;
202 COMPACT(pwd.pw_name);
203 COMPACT("*");
204 memmove(p, &pwd.pw_uid, sizeof(int));
205 p += sizeof(int);
206 memmove(p, &pwd.pw_gid, sizeof(int));
207 p += sizeof(int);
208 memmove(p, &pwd.pw_change, sizeof(time_t));
209 p += sizeof(time_t);
210 COMPACT(pwd.pw_class);
211 COMPACT(pwd.pw_gecos);
212 COMPACT(pwd.pw_dir);
213 COMPACT(pwd.pw_shell);
214 memmove(p, &pwd.pw_expire, sizeof(time_t));
215 p += sizeof(time_t);
216 data.size = p - buf;
217
218 /* Store insecure by name. */
219 tbuf[0] = _PW_KEYBYNAME;
220 len = strlen(pwd.pw_name);
221 memmove(tbuf + 1, pwd.pw_name, len);
222 key.size = len + 1;
223 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
224 error("put");
225
226 /* Store insecure by number. */
227 tbuf[0] = _PW_KEYBYNUM;
228 memmove(tbuf + 1, &cnt, sizeof(cnt));
229 key.size = sizeof(cnt) + 1;
230 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
231 error("put");
232
233 /* Store insecure by uid. */
234 tbuf[0] = _PW_KEYBYUID;
235 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
236 key.size = sizeof(pwd.pw_uid) + 1;
237 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
238 error("put");
239
240 /* Create original format password file entry */
241 if (makeold)
242 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n",
243 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos,
244 pwd.pw_dir, pwd.pw_shell);
245 }
246 (void)(dp->close)(dp);
247 if (makeold) {
248 (void)fflush(oldfp);
249 (void)fclose(oldfp);
250 }
251
252 /* Open the temporary encrypted password database. */
253 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
254 edp = dbopen(buf,
255 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
256 if (!edp)
257 error(buf);
258 clean = FILE_SECURE;
259
260 rewind(fp);
261 for (cnt = 1; scan(fp, &pwd); ++cnt) {
262
263 /* Create secure data. */
264 p = buf;
265 COMPACT(pwd.pw_name);
266 COMPACT(pwd.pw_passwd);
267 memmove(p, &pwd.pw_uid, sizeof(int));
268 p += sizeof(int);
269 memmove(p, &pwd.pw_gid, sizeof(int));
270 p += sizeof(int);
271 memmove(p, &pwd.pw_change, sizeof(time_t));
272 p += sizeof(time_t);
273 COMPACT(pwd.pw_class);
274 COMPACT(pwd.pw_gecos);
275 COMPACT(pwd.pw_dir);
276 COMPACT(pwd.pw_shell);
277 memmove(p, &pwd.pw_expire, sizeof(time_t));
278 p += sizeof(time_t);
279 data.size = p - buf;
280
281 /* Store secure by name. */
282 tbuf[0] = _PW_KEYBYNAME;
283 len = strlen(pwd.pw_name);
284 memmove(tbuf + 1, pwd.pw_name, len);
285 key.size = len + 1;
286 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
287 error("put");
288
289 /* Store secure by number. */
290 tbuf[0] = _PW_KEYBYNUM;
291 memmove(tbuf + 1, &cnt, sizeof(cnt));
292 key.size = sizeof(cnt) + 1;
293 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
294 error("put");
295
296 /* Store secure by uid. */
297 tbuf[0] = _PW_KEYBYUID;
298 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
299 key.size = sizeof(pwd.pw_uid) + 1;
300 if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
301 error("put");
302 }
303
304 (void)(edp->close)(edp);
305
306 /* Set master.passwd permissions, in case caller forgot. */
307 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
308 (void)fclose(fp);
309
310 /* Install as the real password files. */
311 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
312 mv(buf, _PATH_MP_DB);
313 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
314 mv(buf, _PATH_SMP_DB);
315 if (makeold) {
316 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
317 mv(buf, _PATH_PASSWD);
318 }
319 /*
320 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
321 * all use flock(2) on it to block other incarnations of themselves.
322 * The rename means that everything is unlocked, as the original file
323 * can no longer be accessed.
324 */
325 mv(pname, _PATH_MASTERPASSWD);
326 exit(0);
327 }
328
329 int
330 scan(fp, pw)
331 FILE *fp;
332 struct passwd *pw;
333 {
334 static int lcnt;
335 static char line[LINE_MAX];
336 char *p;
337
338 #if defined(__APPLE__)
339 do {
340 if (!fgets(line, sizeof(line), fp))
341 return (0);
342 } while (line[0] == '#');
343 #else
344 if (!fgets(line, sizeof(line), fp))
345 return (0);
346 #endif
347 ++lcnt;
348 /*
349 * ``... if I swallow anything evil, put your fingers down my
350 * throat...''
351 * -- The Who
352 */
353 if (!(p = strchr(line, '\n'))) {
354 warnx("line too long");
355 goto fmt;
356
357 }
358 *p = '\0';
359 if (!pw_scan(line, pw)) {
360 warnx("at line #%d", lcnt);
361 fmt: errno = EFTYPE; /* XXX */
362 error(pname);
363 }
364
365 return (1);
366 }
367
368 void
369 mv(from, to)
370 char *from, *to;
371 {
372 char buf[MAXPATHLEN];
373
374 if (rename(from, to)) {
375 int sverrno = errno;
376 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
377 errno = sverrno;
378 error(buf);
379 }
380 }
381
382 void
383 error(name)
384 char *name;
385 {
386
387 warn(name);
388 cleanup();
389 exit(1);
390 }
391
392 void
393 cleanup()
394 {
395 char buf[MAXPATHLEN];
396
397 switch(clean) {
398 case FILE_ORIG:
399 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
400 (void)unlink(buf);
401 /* FALLTHROUGH */
402 case FILE_SECURE:
403 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB);
404 (void)unlink(buf);
405 /* FALLTHROUGH */
406 case FILE_INSECURE:
407 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB);
408 (void)unlink(buf);
409 }
410 }
411
412 void
413 usage()
414 {
415
416 (void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n");
417 exit(1);
418 }