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.5 (Berkeley) 04/20/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 /* We don't care what the user wants. */ 103 (void)umask(0); 104 105 pname = *argv; 106 /* Open the original password file */ 107 if (!(fp = fopen(pname, "r"))) 108 error(pname); 109 110 /* Open the temporary insecure password database. */ 111 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB); 112 dp = dbopen(buf, 113 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 114 if (dp == NULL) 115 error(buf); 116 clean = FILE_INSECURE; 117 118 /* 119 * Open file for old password file. Minor trickiness -- don't want to 120 * chance the file already existing, since someone (stupidly) might 121 * still be using this for permission checking. So, open it first and 122 * fdopen the resulting fd. The resulting file should be readable by 123 * everyone. 124 */ 125 if (makeold) { 126 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 127 if ((tfd = open(buf, 128 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) 129 error(buf); 130 if ((oldfp = fdopen(tfd, "w")) == NULL) 131 error(buf); 132 clean = FILE_ORIG; 133 } 134 135 /* 136 * The databases actually contain three copies of the original data. 137 * Each password file entry is converted into a rough approximation 138 * of a ``struct passwd'', with the strings placed inline. This 139 * object is then stored as the data for three separate keys. The 140 * first key * is the pw_name field prepended by the _PW_KEYBYNAME 141 * character. The second key is the pw_uid field prepended by the 142 * _PW_KEYBYUID character. The third key is the line number in the 143 * original file prepended by the _PW_KEYBYNUM character. (The special 144 * characters are prepended to ensure that the keys do not collide.) 145 */ 146 data.data = (u_char *)buf; 147 key.data = (u_char *)tbuf; 148 for (cnt = 1; scan(fp, &pwd); ++cnt) { 149 #define COMPACT(e) t = e; while (*p++ = *t++); 150 /* Create insecure data. */ 151 p = buf; 152 COMPACT(pwd.pw_name); 153 COMPACT("*"); 154 memmove(p, &pwd.pw_uid, sizeof(int)); 155 p += sizeof(int); 156 memmove(p, &pwd.pw_gid, sizeof(int)); 157 p += sizeof(int); 158 memmove(p, &pwd.pw_change, sizeof(time_t)); 159 p += sizeof(time_t); 160 COMPACT(pwd.pw_class); 161 COMPACT(pwd.pw_gecos); 162 COMPACT(pwd.pw_dir); 163 COMPACT(pwd.pw_shell); 164 memmove(p, &pwd.pw_expire, sizeof(time_t)); 165 p += sizeof(time_t); 166 data.size = p - buf; 167 168 /* Store insecure by name. */ 169 tbuf[0] = _PW_KEYBYNAME; 170 len = strlen(pwd.pw_name); 171 memmove(tbuf + 1, pwd.pw_name, len); 172 key.size = len + 1; 173 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 174 error("put"); 175 176 /* Store insecure by number. */ 177 tbuf[0] = _PW_KEYBYNUM; 178 memmove(tbuf + 1, &cnt, sizeof(cnt)); 179 key.size = sizeof(cnt) + 1; 180 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 181 error("put"); 182 183 /* Store insecure by uid. */ 184 tbuf[0] = _PW_KEYBYUID; 185 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 186 key.size = sizeof(pwd.pw_uid) + 1; 187 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 188 error("put"); 189 190 /* Create original format password file entry */ 191 if (makeold) 192 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n", 193 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, 194 pwd.pw_dir, pwd.pw_shell); 195 } 196 (void)(dp->close)(dp); 197 if (makeold) { 198 (void)fflush(oldfp); 199 (void)fclose(oldfp); 200 } 201 202 /* Open the temporary encrypted password database. */ 203 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 204 edp = dbopen(buf, 205 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 206 if (!edp) 207 error(buf); 208 clean = FILE_SECURE; 209 210 rewind(fp); 211 for (cnt = 1; scan(fp, &pwd); ++cnt) { 212 213 /* Create secure data. */ 214 p = buf; 215 COMPACT(pwd.pw_name); 216 COMPACT(pwd.pw_passwd); 217 memmove(p, &pwd.pw_uid, sizeof(int)); 218 p += sizeof(int); 219 memmove(p, &pwd.pw_gid, sizeof(int)); 220 p += sizeof(int); 221 memmove(p, &pwd.pw_change, sizeof(time_t)); 222 p += sizeof(time_t); 223 COMPACT(pwd.pw_class); 224 COMPACT(pwd.pw_gecos); 225 COMPACT(pwd.pw_dir); 226 COMPACT(pwd.pw_shell); 227 memmove(p, &pwd.pw_expire, sizeof(time_t)); 228 p += sizeof(time_t); 229 data.size = p - buf; 230 231 /* Store secure by name. */ 232 tbuf[0] = _PW_KEYBYNAME; 233 len = strlen(pwd.pw_name); 234 memmove(tbuf + 1, pwd.pw_name, len); 235 key.size = len + 1; 236 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 237 error("put"); 238 239 /* Store secure by number. */ 240 tbuf[0] = _PW_KEYBYNUM; 241 memmove(tbuf + 1, &cnt, sizeof(cnt)); 242 key.size = sizeof(cnt) + 1; 243 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 244 error("put"); 245 246 /* Store secure by uid. */ 247 tbuf[0] = _PW_KEYBYUID; 248 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 249 key.size = sizeof(pwd.pw_uid) + 1; 250 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 251 error("put"); 252 } 253 254 (void)(edp->close)(edp); 255 256 /* Set master.passwd permissions, in case caller forgot. */ 257 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); 258 (void)fclose(fp); 259 260 /* Install as the real password files. */ 261 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB); 262 mv(buf, _PATH_MP_DB); 263 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 264 mv(buf, _PATH_SMP_DB); 265 if (makeold) { 266 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 267 mv(buf, _PATH_PASSWD); 268 } 269 /* 270 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) 271 * all use flock(2) on it to block other incarnations of themselves. 272 * The rename means that everything is unlocked, as the original file 273 * can no longer be accessed. 274 */ 275 mv(pname, _PATH_MASTERPASSWD); 276 exit(0); 277 } 278 279 int 280 scan(fp, pw) 281 FILE *fp; 282 struct passwd *pw; 283 { 284 static int lcnt; 285 static char line[LINE_MAX]; 286 char *p; 287 288 if (!fgets(line, sizeof(line), fp)) 289 return (0); 290 ++lcnt; 291 /* 292 * ``... if I swallow anything evil, put your fingers down my 293 * throat...'' 294 * -- The Who 295 */ 296 if (!(p = strchr(line, '\n'))) { 297 warnx("line too long"); 298 goto fmt; 299 300 } 301 *p = '\0'; 302 if (!pw_scan(line, pw)) { 303 warnx("at line #%d", lcnt); 304 fmt: errno = EFTYPE; /* XXX */ 305 error(pname); 306 } 307 308 return (1); 309 } 310 311 void 312 mv(from, to) 313 char *from, *to; 314 { 315 char buf[MAXPATHLEN]; 316 317 if (rename(from, to)) { 318 int sverrno = errno; 319 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 320 errno = sverrno; 321 error(buf); 322 } 323 } 324 325 void 326 error(name) 327 char *name; 328 { 329 330 warn(name); 331 cleanup(); 332 exit(1); 333 } 334 335 void 336 cleanup() 337 { 338 char buf[MAXPATHLEN]; 339 340 switch(clean) { 341 case FILE_ORIG: 342 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 343 (void)unlink(buf); 344 /* FALLTHROUGH */ 345 case FILE_SECURE: 346 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_SMP_DB); 347 (void)unlink(buf); 348 /* FALLTHROUGH */ 349 case FILE_INSECURE: 350 (void)snprintf(buf, sizeof(buf), "%s.tmp", _PATH_MP_DB); 351 (void)unlink(buf); 352 } 353 } 354 355 void 356 usage() 357 { 358 359 (void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n"); 360 exit(1); 361 } 362