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