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