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