1 /*- 2 * Copyright (c) 1991, 1993, 1994 3 * The Regents of the University of California. 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 static char copyright[] = 36 "@(#) Copyright (c) 1991, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/stat.h> 46 47 #include <db.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <limits.h> 52 #include <pwd.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "pw_scan.h" 60 61 #define INSECURE 1 62 #define SECURE 2 63 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 64 #define PERM_SECURE (S_IRUSR|S_IWUSR) 65 66 HASHINFO openinfo = { 67 4096, /* bsize */ 68 32, /* ffactor */ 69 256, /* nelem */ 70 2048 * 1024, /* cachesize */ 71 NULL, /* hash() */ 72 0 /* lorder */ 73 }; 74 75 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; 76 static struct passwd pwd; /* password structure */ 77 static char *pname; /* password file name */ 78 static char prefix[MAXPATHLEN]; 79 80 static int Cflag; /* flag for comments */ 81 static char line[LINE_MAX]; 82 83 void cleanup __P((void)); 84 void error __P((char *)); 85 void cp __P((char *, char *, mode_t mode)); 86 void mv __P((char *, char *)); 87 int scan __P((FILE *, struct passwd *)); 88 void usage __P((void)); 89 90 int 91 main(argc, argv) 92 int argc; 93 char *argv[]; 94 { 95 DB *dp, *sdp, *pw_db; 96 DBT data, sdata, key; 97 FILE *fp, *oldfp; 98 sigset_t set; 99 int ch, cnt, ypcnt, len, makeold, tfd, yp_enabled = 0; 100 char *p, *t; 101 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024]; 102 char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)]; 103 char buf2[MAXPATHLEN]; 104 char sbuf2[MAXPATHLEN]; 105 char *username; 106 u_int method, methoduid; 107 int cflag; 108 109 cflag = 0; 110 strcpy(prefix, _PATH_PWD); 111 makeold = 0; 112 username = NULL; 113 while ((ch = getopt(argc, argv, "cd:pu:v")) != EOF) 114 switch(ch) { 115 case 'c': /* verify only */ 116 cflag = 1; 117 break; 118 case 'd': 119 strncpy(prefix, optarg, sizeof prefix - 1); 120 break; 121 case 'p': /* create V7 "file.orig" */ 122 makeold = 1; 123 break; 124 case 'u': /* only update this record */ 125 username = optarg; 126 break; 127 case 'v': /* backward compatible */ 128 break; 129 default: 130 usage(); 131 } 132 argc -= optind; 133 argv += optind; 134 135 if (argc != 1 || (username && (*username == '+' || *username == '-'))) 136 usage(); 137 138 /* 139 * This could be changed to allow the user to interrupt. 140 * Probably not worth the effort. 141 */ 142 sigemptyset(&set); 143 sigaddset(&set, SIGTSTP); 144 sigaddset(&set, SIGHUP); 145 sigaddset(&set, SIGINT); 146 sigaddset(&set, SIGQUIT); 147 sigaddset(&set, SIGTERM); 148 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); 149 150 /* We don't care what the user wants. */ 151 (void)umask(0); 152 153 pname = *argv; 154 /* Open the original password file */ 155 if (!(fp = fopen(pname, "r"))) 156 error(pname); 157 158 /* check only if password database is valid */ 159 if (cflag) { 160 for (cnt = 1; scan(fp, &pwd); ++cnt); 161 exit(0); 162 } 163 164 /* Open the temporary insecure password database. */ 165 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 166 (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB); 167 if (username) { 168 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); 169 (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB); 170 171 clean = FILE_INSECURE; 172 cp(buf2, buf, PERM_INSECURE); 173 dp = dbopen(buf, 174 O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 175 if (dp == NULL) 176 error(buf); 177 178 clean = FILE_SECURE; 179 cp(sbuf2, sbuf, PERM_SECURE); 180 sdp = dbopen(sbuf, 181 O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 182 if (sdp == NULL) 183 error(sbuf); 184 185 /* 186 * Do some trouble to check if we should store this users 187 * uid. Don't use getpwnam/getpwuid as that interferes 188 * with NIS. 189 */ 190 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); 191 if (!pw_db) 192 error(_MP_DB); 193 buf[0] = _PW_KEYBYNAME; 194 len = strlen(username); 195 196 /* Only check that username fits in buffer */ 197 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1)); 198 key.data = (u_char *)buf; 199 key.size = len + 1; 200 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { 201 p = (char *)data.data; 202 203 /* jump over pw_name and pw_passwd, to get to pw_uid */ 204 while (*p++) 205 ; 206 while (*p++) 207 ; 208 209 buf[0] = _PW_KEYBYUID; 210 memmove(buf + 1, p, sizeof(int)); 211 key.data = (u_char *)buf; 212 key.size = sizeof(int) + 1; 213 214 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { 215 /* First field of data.data holds pw_pwname */ 216 if (!strcmp(data.data, username)) 217 methoduid = 0; 218 else 219 methoduid = R_NOOVERWRITE; 220 } else { 221 methoduid = R_NOOVERWRITE; 222 } 223 } else { 224 methoduid = R_NOOVERWRITE; 225 } 226 (void)(pw_db->close)(pw_db); 227 method = 0; 228 } else { 229 dp = dbopen(buf, 230 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 231 if (dp == NULL) 232 error(buf); 233 clean = FILE_INSECURE; 234 235 sdp = dbopen(sbuf, 236 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 237 if (sdp == NULL) 238 error(sbuf); 239 clean = FILE_SECURE; 240 241 method = R_NOOVERWRITE; 242 methoduid = R_NOOVERWRITE; 243 } 244 245 /* 246 * Open file for old password file. Minor trickiness -- don't want to 247 * chance the file already existing, since someone (stupidly) might 248 * still be using this for permission checking. So, open it first and 249 * fdopen the resulting fd. The resulting file should be readable by 250 * everyone. 251 */ 252 if (makeold) { 253 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 254 if ((tfd = open(buf, 255 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) 256 error(buf); 257 if ((oldfp = fdopen(tfd, "w")) == NULL) 258 error(buf); 259 clean = FILE_ORIG; 260 } 261 262 /* 263 * The databases actually contain three copies of the original data. 264 * Each password file entry is converted into a rough approximation 265 * of a ``struct passwd'', with the strings placed inline. This 266 * object is then stored as the data for three separate keys. The 267 * first key * is the pw_name field prepended by the _PW_KEYBYNAME 268 * character. The second key is the pw_uid field prepended by the 269 * _PW_KEYBYUID character. The third key is the line number in the 270 * original file prepended by the _PW_KEYBYNUM character. (The special 271 * characters are prepended to ensure that the keys do not collide.) 272 */ 273 ypcnt = 1; 274 data.data = (u_char *)buf; 275 sdata.data = (u_char *)sbuf; 276 key.data = (u_char *)tbuf; 277 for (cnt = 1; scan(fp, &pwd); ++cnt) { 278 if (!Cflag && 279 (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) 280 yp_enabled = 1; 281 #define COMPACT(e) t = e; while (*p++ = *t++); 282 if (!Cflag && 283 (!username || (strcmp(username, pwd.pw_name) == 0))) { 284 /* Create insecure data. */ 285 p = buf; 286 COMPACT(pwd.pw_name); 287 COMPACT("*"); 288 memmove(p, &pwd.pw_uid, sizeof(int)); 289 p += sizeof(int); 290 memmove(p, &pwd.pw_gid, sizeof(int)); 291 p += sizeof(int); 292 memmove(p, &pwd.pw_change, sizeof(time_t)); 293 p += sizeof(time_t); 294 COMPACT(pwd.pw_class); 295 COMPACT(pwd.pw_gecos); 296 COMPACT(pwd.pw_dir); 297 COMPACT(pwd.pw_shell); 298 memmove(p, &pwd.pw_expire, sizeof(time_t)); 299 p += sizeof(time_t); 300 memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields); 301 p += sizeof pwd.pw_fields; 302 data.size = p - buf; 303 304 /* Create secure data. */ 305 p = sbuf; 306 COMPACT(pwd.pw_name); 307 COMPACT(pwd.pw_passwd); 308 memmove(p, &pwd.pw_uid, sizeof(int)); 309 p += sizeof(int); 310 memmove(p, &pwd.pw_gid, sizeof(int)); 311 p += sizeof(int); 312 memmove(p, &pwd.pw_change, sizeof(time_t)); 313 p += sizeof(time_t); 314 COMPACT(pwd.pw_class); 315 COMPACT(pwd.pw_gecos); 316 COMPACT(pwd.pw_dir); 317 COMPACT(pwd.pw_shell); 318 memmove(p, &pwd.pw_expire, sizeof(time_t)); 319 p += sizeof(time_t); 320 memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields); 321 p += sizeof pwd.pw_fields; 322 sdata.size = p - sbuf; 323 324 /* Store insecure by name. */ 325 tbuf[0] = _PW_KEYBYNAME; 326 len = strlen(pwd.pw_name); 327 memmove(tbuf + 1, pwd.pw_name, len); 328 key.size = len + 1; 329 if ((dp->put)(dp, &key, &data, method) == -1) 330 error("put"); 331 332 /* Store insecure by number. */ 333 tbuf[0] = _PW_KEYBYNUM; 334 memmove(tbuf + 1, &cnt, sizeof(cnt)); 335 key.size = sizeof(cnt) + 1; 336 if ((dp->put)(dp, &key, &data, method) == -1) 337 error("put"); 338 339 /* Store insecure by uid. */ 340 tbuf[0] = _PW_KEYBYUID; 341 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 342 key.size = sizeof(pwd.pw_uid) + 1; 343 if ((dp->put)(dp, &key, &data, methoduid) == -1) 344 error("put"); 345 346 /* Store secure by name. */ 347 tbuf[0] = _PW_KEYBYNAME; 348 len = strlen(pwd.pw_name); 349 memmove(tbuf + 1, pwd.pw_name, len); 350 key.size = len + 1; 351 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 352 error("put"); 353 354 /* Store secure by number. */ 355 tbuf[0] = _PW_KEYBYNUM; 356 memmove(tbuf + 1, &cnt, sizeof(cnt)); 357 key.size = sizeof(cnt) + 1; 358 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 359 error("put"); 360 361 /* Store secure by uid. */ 362 tbuf[0] = _PW_KEYBYUID; 363 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 364 key.size = sizeof(pwd.pw_uid) + 1; 365 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1) 366 error("put"); 367 368 /* Store insecure and secure special plus and special minus */ 369 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') { 370 tbuf[0] = _PW_KEYYPBYNUM; 371 memmove(tbuf + 1, &ypcnt, sizeof(cnt)); 372 ypcnt++; 373 key.size = sizeof(cnt) + 1; 374 if ((dp->put)(dp, &key, &data, method) == -1) 375 error("put"); 376 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 377 error("put"); 378 } 379 } 380 /* Create original format password file entry */ 381 if (Cflag && makeold) /* copy comments */ 382 (void)fprintf(oldfp, "%s\n", line); 383 else if (makeold) { 384 char uidstr[20]; 385 char gidstr[20]; 386 387 snprintf(uidstr, sizeof(uidstr), "%d", pwd.pw_uid); 388 snprintf(gidstr, sizeof(gidstr), "%d", pwd.pw_gid); 389 390 (void)fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n", 391 pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "", 392 pwd.pw_fields & _PWF_GID ? gidstr : "", 393 pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell); 394 } 395 } 396 /* If YP enabled, set flag. */ 397 if (yp_enabled) { 398 buf[0] = yp_enabled + 2; 399 data.size = 1; 400 tbuf[0] = _PW_KEYYPENABLED; 401 key.size = 1; 402 if ((dp->put)(dp, &key, &data, method) == -1) 403 error("put"); 404 if ((sdp->put)(sdp, &key, &data, method) == -1) 405 error("put"); 406 } 407 408 (void)(dp->close)(dp); 409 (void)(sdp->close)(sdp); 410 if (makeold) { 411 (void)fflush(oldfp); 412 (void)fclose(oldfp); 413 } 414 415 /* Set master.passwd permissions, in case caller forgot. */ 416 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); 417 (void)fclose(fp); 418 419 /* Install as the real password files. */ 420 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 421 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); 422 mv(buf, buf2); 423 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); 424 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB); 425 mv(buf, buf2); 426 if (makeold) { 427 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD); 428 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 429 mv(buf, buf2); 430 } 431 /* 432 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) 433 * all use flock(2) on it to block other incarnations of themselves. 434 * The rename means that everything is unlocked, as the original file 435 * can no longer be accessed. 436 */ 437 (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD); 438 mv(pname, buf); 439 exit(0); 440 } 441 442 int 443 scan(fp, pw) 444 FILE *fp; 445 struct passwd *pw; 446 { 447 static int lcnt; 448 char *p; 449 450 if (!fgets(line, sizeof(line), fp)) 451 return (0); 452 ++lcnt; 453 /* 454 * ``... if I swallow anything evil, put your fingers down my 455 * throat...'' 456 * -- The Who 457 */ 458 if (!(p = strchr(line, '\n'))) { 459 warnx("line too long"); 460 goto fmt; 461 462 } 463 *p = '\0'; 464 465 #ifdef PASSWD_IGNORE_COMMENTS 466 /* 467 * Ignore comments: ^[ \t]*# 468 */ 469 for (p = line; *p != '\0'; p++) 470 if (*p != ' ' && *p != '\t') 471 break; 472 if (*p == '#' || *p == '\0') { 473 Cflag = 1; 474 return(1); 475 } else 476 Cflag = 0; 477 #endif 478 479 if (!pw_scan(line, pw)) { 480 warnx("at line #%d", lcnt); 481 fmt: errno = EFTYPE; /* XXX */ 482 error(pname); 483 } 484 485 return (1); 486 } 487 488 void 489 cp(from, to, mode) 490 char *from, *to; 491 mode_t mode; 492 { 493 static char buf[MAXBSIZE]; 494 int from_fd, rcount, to_fd, wcount; 495 496 if ((from_fd = open(from, O_RDONLY, 0)) < 0) 497 error(from); 498 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) 499 error(to); 500 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 501 wcount = write(to_fd, buf, rcount); 502 if (rcount != wcount || wcount == -1) { 503 int sverrno = errno; 504 505 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 506 errno = sverrno; 507 error(buf); 508 } 509 } 510 if (rcount < 0) { 511 int sverrno = errno; 512 513 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 514 errno = sverrno; 515 error(buf); 516 } 517 } 518 519 520 void 521 mv(from, to) 522 char *from, *to; 523 { 524 char buf[MAXPATHLEN]; 525 526 if (rename(from, to)) { 527 int sverrno = errno; 528 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 529 errno = sverrno; 530 error(buf); 531 } 532 } 533 534 void 535 error(name) 536 char *name; 537 { 538 539 warn(name); 540 cleanup(); 541 exit(1); 542 } 543 544 void 545 cleanup() 546 { 547 char buf[MAXPATHLEN]; 548 549 switch(clean) { 550 case FILE_ORIG: 551 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 552 (void)unlink(buf); 553 /* FALLTHROUGH */ 554 case FILE_SECURE: 555 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); 556 (void)unlink(buf); 557 /* FALLTHROUGH */ 558 case FILE_INSECURE: 559 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 560 (void)unlink(buf); 561 } 562 } 563 564 void 565 usage() 566 { 567 568 (void)fprintf(stderr, "usage: pwd_mkdb [-c] [-p] [-d <dest dir>] [-u <local username>] file\n"); 569 exit(1); 570 } 571