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