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