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