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