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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#if 0 31#ifndef lint 32static const char copyright[] = 33"@(#) Copyright (c) 1991, 1993, 1994\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35#endif /* not lint */ 36 37#ifndef lint 38static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94"; 39#endif /* not lint */ 40#endif 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD$"); 44 45#include <sys/param.h> 46#include <sys/endian.h> 47#include <sys/stat.h> 48#include <arpa/inet.h> 49 50#include <db.h> 51#include <err.h> 52#include <errno.h> 53#include <fcntl.h> 54#include <libgen.h> 55#include <limits.h> 56#include <pwd.h> 57#include <signal.h> 58#include <stdio.h> 59#include <stdlib.h> 60#include <string.h> 61#include <unistd.h> 62 63#include "pw_scan.h" 64 65#define INSECURE 1 66#define SECURE 2 67#define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 68#define PERM_SECURE (S_IRUSR|S_IWUSR) 69#define LEGACY_VERSION(x) _PW_VERSIONED(x, 3) 70#define CURRENT_VERSION(x) _PW_VERSIONED(x, 4) 71 72static HASHINFO openinfo = { 73 4096, /* bsize */ 74 32, /* ffactor */ 75 256, /* nelem */ 76 2048 * 1024, /* cachesize */ 77 NULL, /* hash() */ 78 BYTE_ORDER /* lorder */ 79}; 80 81static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; 82static struct passwd pwd; /* password structure */ 83static char *pname; /* password file name */ 84static char prefix[MAXPATHLEN]; 85 86static int is_comment; /* flag for comments */ 87static char line[LINE_MAX]; 88 89void cleanup(void); 90void error(const char *); 91void cp(char *, char *, mode_t mode); 92void mv(char *, char *); 93int scan(FILE *, struct passwd *); 94static void usage(void); 95 96int 97main(int argc, char *argv[]) 98{ 99 static char verskey[] = _PWD_VERSION_KEY; 100 char version = _PWD_CURRENT_VERSION; 101 DB *dp, *sdp, *pw_db; 102 DBT data, sdata, key; 103 FILE *fp, *oldfp; 104 sigset_t set; 105 int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0; 106 unsigned int len; 107 uint32_t store; 108 const char *t; 109 char *p; 110 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024]; 111 char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)]; 112 char buf2[MAXPATHLEN]; 113 char sbuf2[MAXPATHLEN]; 114 char *username; 115 u_int method, methoduid; 116 int Cflag, dflag, iflag; 117 int nblock = 0; 118 119 iflag = dflag = Cflag = 0; 120 strcpy(prefix, _PATH_PWD); 121 makeold = 0; 122 username = NULL; 123 oldfp = NULL; 124 while ((ch = getopt(argc, argv, "BCLNd:ips:u:v")) != -1) 125 switch(ch) { 126 case 'B': /* big-endian output */ 127 openinfo.lorder = BIG_ENDIAN; 128 break; 129 case 'C': /* verify only */ 130 Cflag = 1; 131 break; 132 case 'L': /* little-endian output */ 133 openinfo.lorder = LITTLE_ENDIAN; 134 break; 135 case 'N': /* do not wait for lock */ 136 nblock = LOCK_NB; /* will fail if locked */ 137 break; 138 case 'd': 139 dflag++; 140 strlcpy(prefix, optarg, sizeof(prefix)); 141 break; 142 case 'i': 143 iflag++; 144 break; 145 case 'p': /* create V7 "file.orig" */ 146 makeold = 1; 147 break; 148 case 's': /* change default cachesize */ 149 openinfo.cachesize = atoi(optarg) * 1024 * 1024; 150 break; 151 case 'u': /* only update this record */ 152 username = optarg; 153 break; 154 case 'v': /* backward compatible */ 155 break; 156 default: 157 usage(); 158 } 159 argc -= optind; 160 argv += optind; 161 162 if (argc != 1 || (username && (*username == '+' || *username == '-'))) 163 usage(); 164 165 /* 166 * This could be changed to allow the user to interrupt. 167 * Probably not worth the effort. 168 */ 169 sigemptyset(&set); 170 sigaddset(&set, SIGTSTP); 171 sigaddset(&set, SIGHUP); 172 sigaddset(&set, SIGINT); 173 sigaddset(&set, SIGQUIT); 174 sigaddset(&set, SIGTERM); 175 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); 176 177 /* We don't care what the user wants. */ 178 (void)umask(0); 179 180 pname = *argv; 181 182 /* 183 * Open and lock the original password file. We have to check 184 * the hardlink count after we get the lock to handle any potential 185 * unlink/rename race. 186 * 187 * This lock is necessary when someone runs pwd_mkdb manually, directly 188 * on master.passwd, to handle the case where a user might try to 189 * change his password while pwd_mkdb is running. 190 */ 191 for (;;) { 192 struct stat st; 193 194 if (!(fp = fopen(pname, "r"))) 195 error(pname); 196 if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag)) 197 error("flock"); 198 if (fstat(fileno(fp), &st) < 0) 199 error(pname); 200 if (st.st_nlink != 0) 201 break; 202 fclose(fp); 203 fp = NULL; 204 } 205 206 /* check only if password database is valid */ 207 if (Cflag) { 208 while (scan(fp, &pwd)) 209 if (!is_comment && strlen(pwd.pw_name) >= MAXLOGNAME) { 210 warnx("%s: username too long", pwd.pw_name); 211 exit(1); 212 } 213 exit(0); 214 } 215 216 /* Open the temporary insecure password database. */ 217 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 218 (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB); 219 if (username) { 220 int use_version; 221 222 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); 223 (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB); 224 225 clean = FILE_INSECURE; 226 cp(buf2, buf, PERM_INSECURE); 227 dp = dbopen(buf, 228 O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 229 if (dp == NULL) 230 error(buf); 231 232 clean = FILE_SECURE; 233 cp(sbuf2, sbuf, PERM_SECURE); 234 sdp = dbopen(sbuf, 235 O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 236 if (sdp == NULL) 237 error(sbuf); 238 239 /* 240 * Do some trouble to check if we should store this users 241 * uid. Don't use getpwnam/getpwuid as that interferes 242 * with NIS. 243 */ 244 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); 245 if (!pw_db) 246 error(_MP_DB); 247 248 key.data = verskey; 249 key.size = sizeof(verskey)-1; 250 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) 251 use_version = *(unsigned char *)data.data; 252 else 253 use_version = 3; 254 buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version); 255 len = strlen(username); 256 257 /* Only check that username fits in buffer */ 258 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1)); 259 key.data = (u_char *)buf; 260 key.size = len + 1; 261 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { 262 p = (char *)data.data; 263 264 /* jump over pw_name and pw_passwd, to get to pw_uid */ 265 while (*p++) 266 ; 267 while (*p++) 268 ; 269 270 buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version); 271 memmove(buf + 1, p, sizeof(store)); 272 key.data = (u_char *)buf; 273 key.size = sizeof(store) + 1; 274 275 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { 276 /* First field of data.data holds pw_pwname */ 277 if (!strcmp(data.data, username)) 278 methoduid = 0; 279 else 280 methoduid = R_NOOVERWRITE; 281 } else { 282 methoduid = R_NOOVERWRITE; 283 } 284 } else { 285 methoduid = R_NOOVERWRITE; 286 } 287 if ((pw_db->close)(pw_db)) 288 error("close pw_db"); 289 method = 0; 290 } else { 291 dp = dbopen(buf, 292 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 293 if (dp == NULL) 294 error(buf); 295 clean = FILE_INSECURE; 296 297 sdp = dbopen(sbuf, 298 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 299 if (sdp == NULL) 300 error(sbuf); 301 clean = FILE_SECURE; 302 303 method = R_NOOVERWRITE; 304 methoduid = R_NOOVERWRITE; 305 } 306 307 /* 308 * Open file for old password file. Minor trickiness -- don't want to 309 * chance the file already existing, since someone (stupidly) might 310 * still be using this for permission checking. So, open it first and 311 * fdopen the resulting fd. The resulting file should be readable by 312 * everyone. 313 */ 314 if (makeold) { 315 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 316 if ((tfd = open(buf, 317 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) 318 error(buf); 319 if ((oldfp = fdopen(tfd, "w")) == NULL) 320 error(buf); 321 clean = FILE_ORIG; 322 } 323 324 /* 325 * The databases actually contain three copies of the original data. 326 * Each password file entry is converted into a rough approximation 327 * of a ``struct passwd'', with the strings placed inline. This 328 * object is then stored as the data for three separate keys. The 329 * first key * is the pw_name field prepended by the _PW_KEYBYNAME 330 * character. The second key is the pw_uid field prepended by the 331 * _PW_KEYBYUID character. The third key is the line number in the 332 * original file prepended by the _PW_KEYBYNUM character. (The special 333 * characters are prepended to ensure that the keys do not collide.) 334 */ 335 /* In order to transition this file into a machine-independent 336 * form, we have to change the format of entries. However, since 337 * older binaries will still expect the old MD format entries, we 338 * create those as usual and use versioned tags for the new entries. 339 */ 340 if (username == NULL) { 341 /* Do not add the VERSION tag when updating a single 342 * user. When operating on `old format' databases, this 343 * would result in applications `seeing' only the updated 344 * entries. 345 */ 346 key.data = verskey; 347 key.size = sizeof(verskey)-1; 348 data.data = &version; 349 data.size = 1; 350 if ((dp->put)(dp, &key, &data, 0) == -1) 351 error("put"); 352 if ((dp->put)(sdp, &key, &data, 0) == -1) 353 error("put"); 354 } 355 ypcnt = 0; 356 data.data = (u_char *)buf; 357 sdata.data = (u_char *)sbuf; 358 key.data = (u_char *)tbuf; 359 for (cnt = 1; scan(fp, &pwd); ++cnt) { 360 if (!is_comment && 361 (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) { 362 yp_enabled = 1; 363 ypcnt++; 364 } 365 if (is_comment) 366 --cnt; 367#define COMPACT(e) t = e; while ((*p++ = *t++)); 368#define SCALAR(e) store = htonl((uint32_t)(e)); \ 369 memmove(p, &store, sizeof(store)); \ 370 p += sizeof(store); 371#define LSCALAR(e) store = HTOL((uint32_t)(e)); \ 372 memmove(p, &store, sizeof(store)); \ 373 p += sizeof(store); 374#define HTOL(e) (openinfo.lorder == BYTE_ORDER ? \ 375 (uint32_t)(e) : \ 376 bswap32((uint32_t)(e))) 377 if (!is_comment && 378 (!username || (strcmp(username, pwd.pw_name) == 0))) { 379 /* Create insecure data. */ 380 p = buf; 381 COMPACT(pwd.pw_name); 382 COMPACT("*"); 383 SCALAR(pwd.pw_uid); 384 SCALAR(pwd.pw_gid); 385 SCALAR(pwd.pw_change); 386 COMPACT(pwd.pw_class); 387 COMPACT(pwd.pw_gecos); 388 COMPACT(pwd.pw_dir); 389 COMPACT(pwd.pw_shell); 390 SCALAR(pwd.pw_expire); 391 SCALAR(pwd.pw_fields); 392 data.size = p - buf; 393 394 /* Create secure data. */ 395 p = sbuf; 396 COMPACT(pwd.pw_name); 397 COMPACT(pwd.pw_passwd); 398 SCALAR(pwd.pw_uid); 399 SCALAR(pwd.pw_gid); 400 SCALAR(pwd.pw_change); 401 COMPACT(pwd.pw_class); 402 COMPACT(pwd.pw_gecos); 403 COMPACT(pwd.pw_dir); 404 COMPACT(pwd.pw_shell); 405 SCALAR(pwd.pw_expire); 406 SCALAR(pwd.pw_fields); 407 sdata.size = p - sbuf; 408 409 /* Store insecure by name. */ 410 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME); 411 len = strlen(pwd.pw_name); 412 memmove(tbuf + 1, pwd.pw_name, len); 413 key.size = len + 1; 414 if ((dp->put)(dp, &key, &data, method) == -1) 415 error("put"); 416 417 /* Store insecure by number. */ 418 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM); 419 store = htonl(cnt); 420 memmove(tbuf + 1, &store, sizeof(store)); 421 key.size = sizeof(store) + 1; 422 if ((dp->put)(dp, &key, &data, method) == -1) 423 error("put"); 424 425 /* Store insecure by uid. */ 426 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID); 427 store = htonl(pwd.pw_uid); 428 memmove(tbuf + 1, &store, sizeof(store)); 429 key.size = sizeof(store) + 1; 430 if ((dp->put)(dp, &key, &data, methoduid) == -1) 431 error("put"); 432 433 /* Store secure by name. */ 434 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME); 435 len = strlen(pwd.pw_name); 436 memmove(tbuf + 1, pwd.pw_name, len); 437 key.size = len + 1; 438 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 439 error("put"); 440 441 /* Store secure by number. */ 442 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM); 443 store = htonl(cnt); 444 memmove(tbuf + 1, &store, sizeof(store)); 445 key.size = sizeof(store) + 1; 446 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 447 error("put"); 448 449 /* Store secure by uid. */ 450 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID); 451 store = htonl(pwd.pw_uid); 452 memmove(tbuf + 1, &store, sizeof(store)); 453 key.size = sizeof(store) + 1; 454 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1) 455 error("put"); 456 457 /* Store insecure and secure special plus and special minus */ 458 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') { 459 tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM); 460 store = htonl(ypcnt); 461 memmove(tbuf + 1, &store, sizeof(store)); 462 key.size = sizeof(store) + 1; 463 if ((dp->put)(dp, &key, &data, method) == -1) 464 error("put"); 465 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 466 error("put"); 467 } 468 469 /* Create insecure data. (legacy version) */ 470 p = buf; 471 COMPACT(pwd.pw_name); 472 COMPACT("*"); 473 LSCALAR(pwd.pw_uid); 474 LSCALAR(pwd.pw_gid); 475 LSCALAR(pwd.pw_change); 476 COMPACT(pwd.pw_class); 477 COMPACT(pwd.pw_gecos); 478 COMPACT(pwd.pw_dir); 479 COMPACT(pwd.pw_shell); 480 LSCALAR(pwd.pw_expire); 481 LSCALAR(pwd.pw_fields); 482 data.size = p - buf; 483 484 /* Create secure data. (legacy version) */ 485 p = sbuf; 486 COMPACT(pwd.pw_name); 487 COMPACT(pwd.pw_passwd); 488 LSCALAR(pwd.pw_uid); 489 LSCALAR(pwd.pw_gid); 490 LSCALAR(pwd.pw_change); 491 COMPACT(pwd.pw_class); 492 COMPACT(pwd.pw_gecos); 493 COMPACT(pwd.pw_dir); 494 COMPACT(pwd.pw_shell); 495 LSCALAR(pwd.pw_expire); 496 LSCALAR(pwd.pw_fields); 497 sdata.size = p - sbuf; 498 499 /* Store insecure by name. */ 500 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME); 501 len = strlen(pwd.pw_name); 502 memmove(tbuf + 1, pwd.pw_name, len); 503 key.size = len + 1; 504 if ((dp->put)(dp, &key, &data, method) == -1) 505 error("put"); 506 507 /* Store insecure by number. */ 508 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM); 509 store = HTOL(cnt); 510 memmove(tbuf + 1, &store, sizeof(store)); 511 key.size = sizeof(store) + 1; 512 if ((dp->put)(dp, &key, &data, method) == -1) 513 error("put"); 514 515 /* Store insecure by uid. */ 516 tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID); 517 store = HTOL(pwd.pw_uid); 518 memmove(tbuf + 1, &store, sizeof(store)); 519 key.size = sizeof(store) + 1; 520 if ((dp->put)(dp, &key, &data, methoduid) == -1) 521 error("put"); 522 523 /* Store secure by name. */ 524 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME); 525 len = strlen(pwd.pw_name); 526 memmove(tbuf + 1, pwd.pw_name, len); 527 key.size = len + 1; 528 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 529 error("put"); 530 531 /* Store secure by number. */ 532 tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM); 533 store = HTOL(cnt); 534 memmove(tbuf + 1, &store, sizeof(store)); 535 key.size = sizeof(store) + 1; 536 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 537 error("put"); 538 539 /* Store secure by uid. */ 540 tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID); 541 store = HTOL(pwd.pw_uid); 542 memmove(tbuf + 1, &store, sizeof(store)); 543 key.size = sizeof(store) + 1; 544 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1) 545 error("put"); 546 547 /* Store insecure and secure special plus and special minus */ 548 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') { 549 tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM); 550 store = HTOL(ypcnt); 551 memmove(tbuf + 1, &store, sizeof(store)); 552 key.size = sizeof(store) + 1; 553 if ((dp->put)(dp, &key, &data, method) == -1) 554 error("put"); 555 if ((sdp->put)(sdp, &key, &sdata, method) == -1) 556 error("put"); 557 } 558 } 559 /* Create original format password file entry */ 560 if (is_comment && makeold){ /* copy comments */ 561 if (fprintf(oldfp, "%s\n", line) < 0) 562 error("write old"); 563 } else if (makeold) { 564 char uidstr[20]; 565 char gidstr[20]; 566 567 snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid); 568 snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid); 569 570 if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n", 571 pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "", 572 pwd.pw_fields & _PWF_GID ? gidstr : "", 573 pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0) 574 error("write old"); 575 } 576 } 577 /* If YP enabled, set flag. */ 578 if (yp_enabled) { 579 buf[0] = yp_enabled + 2; 580 data.size = 1; 581 key.size = 1; 582 tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED); 583 if ((dp->put)(dp, &key, &data, method) == -1) 584 error("put"); 585 if ((sdp->put)(sdp, &key, &data, method) == -1) 586 error("put"); 587 tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED); 588 key.size = 1; 589 if ((dp->put)(dp, &key, &data, method) == -1) 590 error("put"); 591 if ((sdp->put)(sdp, &key, &data, method) == -1) 592 error("put"); 593 } 594 595 if ((dp->close)(dp) == -1) 596 error("close"); 597 if ((sdp->close)(sdp) == -1) 598 error("close"); 599 if (makeold) { 600 (void)fflush(oldfp); 601 if (fclose(oldfp) == EOF) 602 error("close old"); 603 } 604 605 /* Set master.passwd permissions, in case caller forgot. */ 606 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); 607 608 /* Install as the real password files. */ 609 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 610 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); 611 mv(buf, buf2); 612 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); 613 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB); 614 mv(buf, buf2); 615 if (makeold) { 616 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD); 617 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 618 mv(buf, buf2); 619 } 620 /* 621 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) 622 * all use flock(2) on it to block other incarnations of themselves. 623 * The rename means that everything is unlocked, as the original file 624 * can no longer be accessed. 625 */ 626 (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD); 627 mv(pname, buf); 628 629 /* 630 * Close locked password file after rename() 631 */ 632 if (fclose(fp) == EOF) 633 error("close fp"); 634 635 exit(0); 636} 637 638int 639scan(FILE *fp, struct passwd *pw) 640{ 641 static int lcnt; 642 size_t len; 643 char *p; 644 645 p = fgetln(fp, &len); 646 if (p == NULL) 647 return (0); 648 ++lcnt; 649 /* 650 * ``... if I swallow anything evil, put your fingers down my 651 * throat...'' 652 * -- The Who 653 */ 654 if (len > 0 && p[len - 1] == '\n') 655 len--; 656 if (len >= sizeof(line) - 1) { 657 warnx("line #%d too long", lcnt); 658 goto fmt; 659 } 660 memcpy(line, p, len); 661 line[len] = '\0'; 662 663 /* 664 * Ignore comments: ^[ \t]*# 665 */ 666 for (p = line; *p != '\0'; p++) 667 if (*p != ' ' && *p != '\t') 668 break; 669 if (*p == '#' || *p == '\0') { 670 is_comment = 1; 671 return(1); 672 } else 673 is_comment = 0; 674 675 if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) { 676 warnx("at line #%d", lcnt); 677fmt: errno = EFTYPE; /* XXX */ 678 error(pname); 679 } 680 681 return (1); 682} 683 684void 685cp(char *from, char *to, mode_t mode) 686{ 687 static char buf[MAXBSIZE]; 688 int from_fd, rcount, to_fd, wcount; 689 690 if ((from_fd = open(from, O_RDONLY, 0)) < 0) 691 error(from); 692 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) 693 error(to); 694 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 695 wcount = write(to_fd, buf, rcount); 696 if (rcount != wcount || wcount == -1) { 697 int sverrno = errno; 698 699 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 700 errno = sverrno; 701 error(buf); 702 } 703 } 704 if (rcount < 0) { 705 int sverrno = errno; 706 707 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 708 errno = sverrno; 709 error(buf); 710 } 711} 712 713 714void 715mv(char *from, char *to) 716{ 717 char buf[MAXPATHLEN]; 718 char *to_dir; 719 int to_dir_fd = -1; 720 721 /* 722 * Make sure file is safe on disk. To improve performance we will call 723 * fsync() to the directory where file lies 724 */ 725 if (rename(from, to) != 0 || 726 (to_dir = dirname(to)) == NULL || 727 (to_dir_fd = open(to_dir, O_RDONLY|O_DIRECTORY)) == -1 || 728 fsync(to_dir_fd) != 0) { 729 int sverrno = errno; 730 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 731 errno = sverrno; 732 if (to_dir_fd != -1) 733 close(to_dir_fd); 734 error(buf); 735 } 736 737 if (to_dir_fd != -1) 738 close(to_dir_fd); 739} 740 741void 742error(const char *name) 743{ 744 745 warn("%s", name); 746 cleanup(); 747 exit(1); 748} 749 750void 751cleanup(void) 752{ 753 char buf[MAXPATHLEN]; 754 755 switch(clean) { 756 case FILE_ORIG: 757 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 758 (void)unlink(buf); 759 /* FALLTHROUGH */ 760 case FILE_SECURE: 761 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); 762 (void)unlink(buf); 763 /* FALLTHROUGH */ 764 case FILE_INSECURE: 765 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); 766 (void)unlink(buf); 767 } 768} 769 770static void 771usage(void) 772{ 773 774 (void)fprintf(stderr, 775"usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n"); 776 exit(1); 777} 778