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.4 (Berkeley) 04/16/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. The resulting file should be readable by 120 * everyone, don't trust the user's umask. 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 (void)fchmod(tfd, PERM_INSECURE); 128 if ((oldfp = fdopen(tfd, "w")) == NULL) 129 error(buf); 130 clean = FILE_ORIG; 131 } 132 133 /* 134 * The databases actually contain three copies of the original data. 135 * Each password file entry is converted into a rough approximation 136 * of a ``struct passwd'', with the strings placed inline. This 137 * object is then stored as the data for three separate keys. The 138 * first key * is the pw_name field prepended by the _PW_KEYBYNAME 139 * character. The second key is the pw_uid field prepended by the 140 * _PW_KEYBYUID character. The third key is the line number in the 141 * original file prepended by the _PW_KEYBYNUM character. (The special 142 * characters are prepended to ensure that the keys do not collide.) 143 */ 144 data.data = (u_char *)buf; 145 key.data = (u_char *)tbuf; 146 for (cnt = 1; scan(fp, &pwd); ++cnt) { 147 #define COMPACT(e) t = e; while (*p++ = *t++); 148 /* Create insecure data. */ 149 p = buf; 150 COMPACT(pwd.pw_name); 151 COMPACT("*"); 152 memmove(p, &pwd.pw_uid, sizeof(int)); 153 p += sizeof(int); 154 memmove(p, &pwd.pw_gid, sizeof(int)); 155 p += sizeof(int); 156 memmove(p, &pwd.pw_change, sizeof(time_t)); 157 p += sizeof(time_t); 158 COMPACT(pwd.pw_class); 159 COMPACT(pwd.pw_gecos); 160 COMPACT(pwd.pw_dir); 161 COMPACT(pwd.pw_shell); 162 memmove(p, &pwd.pw_expire, sizeof(time_t)); 163 p += sizeof(time_t); 164 data.size = p - buf; 165 166 /* Store insecure by name. */ 167 tbuf[0] = _PW_KEYBYNAME; 168 len = strlen(pwd.pw_name); 169 memmove(tbuf + 1, pwd.pw_name, len); 170 key.size = len + 1; 171 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 172 error("put"); 173 174 /* Store insecure by number. */ 175 tbuf[0] = _PW_KEYBYNUM; 176 memmove(tbuf + 1, &cnt, sizeof(cnt)); 177 key.size = sizeof(cnt) + 1; 178 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 179 error("put"); 180 181 /* Store insecure by uid. */ 182 tbuf[0] = _PW_KEYBYUID; 183 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 184 key.size = sizeof(pwd.pw_uid) + 1; 185 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 186 error("put"); 187 188 /* Create original format password file entry */ 189 if (makeold) 190 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n", 191 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, 192 pwd.pw_dir, pwd.pw_shell); 193 } 194 (void)(dp->close)(dp); 195 if (makeold) { 196 (void)fflush(oldfp); 197 (void)fclose(oldfp); 198 } 199 200 /* Open the temporary encrypted password database. */ 201 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 202 edp = dbopen(buf, 203 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 204 if (!edp) 205 error(buf); 206 clean = FILE_SECURE; 207 208 rewind(fp); 209 for (cnt = 1; scan(fp, &pwd); ++cnt) { 210 211 /* Create secure data. */ 212 p = buf; 213 COMPACT(pwd.pw_name); 214 COMPACT(pwd.pw_passwd); 215 memmove(p, &pwd.pw_uid, sizeof(int)); 216 p += sizeof(int); 217 memmove(p, &pwd.pw_gid, sizeof(int)); 218 p += sizeof(int); 219 memmove(p, &pwd.pw_change, sizeof(time_t)); 220 p += sizeof(time_t); 221 COMPACT(pwd.pw_class); 222 COMPACT(pwd.pw_gecos); 223 COMPACT(pwd.pw_dir); 224 COMPACT(pwd.pw_shell); 225 memmove(p, &pwd.pw_expire, sizeof(time_t)); 226 p += sizeof(time_t); 227 data.size = p - buf; 228 229 /* Store secure by name. */ 230 tbuf[0] = _PW_KEYBYNAME; 231 len = strlen(pwd.pw_name); 232 memmove(tbuf + 1, pwd.pw_name, len); 233 key.size = len + 1; 234 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 235 error("put"); 236 237 /* Store secure by number. */ 238 tbuf[0] = _PW_KEYBYNUM; 239 memmove(tbuf + 1, &cnt, sizeof(cnt)); 240 key.size = sizeof(cnt) + 1; 241 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 242 error("put"); 243 244 /* Store secure by uid. */ 245 tbuf[0] = _PW_KEYBYUID; 246 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 247 key.size = sizeof(pwd.pw_uid) + 1; 248 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 249 error("put"); 250 } 251 252 (void)(edp->close)(edp); 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)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB); 260 mv(buf, _PATH_MP_DB); 261 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 262 mv(buf, _PATH_SMP_DB); 263 if (makeold) { 264 (void)snprintf(buf, sizeof(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 int 278 scan(fp, pw) 279 FILE *fp; 280 struct passwd *pw; 281 { 282 static int lcnt; 283 static char line[LINE_MAX]; 284 char *p; 285 286 if (!fgets(line, sizeof(line), fp)) 287 return (0); 288 ++lcnt; 289 /* 290 * ``... if I swallow anything evil, put your fingers down my 291 * throat...'' 292 * -- The Who 293 */ 294 if (!(p = strchr(line, '\n'))) { 295 warnx("line too long"); 296 goto fmt; 297 298 } 299 *p = '\0'; 300 if (!pw_scan(line, pw)) { 301 warnx("at line #%d", lcnt); 302 fmt: errno = EFTYPE; /* XXX */ 303 error(pname); 304 } 305 306 return (1); 307 } 308 309 void 310 mv(from, to) 311 char *from, *to; 312 { 313 char buf[MAXPATHLEN]; 314 315 if (rename(from, to)) { 316 int sverrno = errno; 317 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 318 errno = sverrno; 319 error(buf); 320 } 321 } 322 323 void 324 error(name) 325 char *name; 326 { 327 328 warn(name); 329 cleanup(); 330 exit(1); 331 } 332 333 void 334 cleanup() 335 { 336 char buf[MAXPATHLEN]; 337 338 switch(clean) { 339 case FILE_ORIG: 340 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 341 (void)unlink(buf); 342 /* FALLTHROUGH */ 343 case FILE_SECURE: 344 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 345 (void)unlink(buf); 346 /* FALLTHROUGH */ 347 case FILE_INSECURE: 348 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB); 349 (void)unlink(buf); 350 } 351 } 352 353 void 354 usage() 355 { 356 357 (void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n"); 358 exit(1); 359 } 360