1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * This module fetches group and passwd structs for the caller. It 30 * uses a hash table to speed up retrieval of repeated entries. If 31 * the attempts to initialize the hash tables fail, this just 32 * continues the slow way. 33 */ 34 35 #include <pwd.h> 36 #include <grp.h> 37 #include <string.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include "pkglib.h" 42 #include "pkglocale.h" 43 #include "nhash.h" 44 45 #define HASHSIZE 151 46 #define BSZ 4 47 48 #define ERR_DUPFAIL "%s: strdup(%s) failed.\n" 49 #define ERR_ADDFAIL "%s: add_cache() failed.\n" 50 #define ERR_BADMEMB "%s: %s in \"%s\" %s structure is invalid.\n" 51 #define ERR_NOGRP "dup_gr_ent(): no group entry provided.\n" 52 #define ERR_NOPWD "dup_pw_ent(): no passwd entry provided.\n" 53 #define ERR_NOINIT "%s: init_cache() failed.\n" 54 #define ERR_MALLOC "%s: malloc(%d) failed for %s.\n" 55 56 static Cache *pwnam_cache = (Cache *) NULL; 57 static Cache *grnam_cache = (Cache *) NULL; 58 static Cache *pwuid_cache = (Cache *) NULL; 59 static Cache *grgid_cache = (Cache *) NULL; 60 61 static int dup_gr_ent(struct group *grp); 62 static int dup_pw_ent(struct passwd *pwp); 63 64 /* 65 * These indicate whether the hash table has been initialized for the four 66 * categories. 67 */ 68 static int is_a_pwnam_cache; 69 static int is_a_grnam_cache; 70 static int is_a_pwuid_cache; 71 static int is_a_grgid_cache; 72 73 extern char *get_install_root(void); 74 75 /* 76 * If there's a grnam cache, then update it with this new 77 * group, otherwise, skip it. 78 */ 79 static Item * 80 cache_alloc(char *fname, int len, size_t struct_size) 81 { 82 Item *itemp; 83 84 /* 85 * Allocate space for the Item pointer, key and data. 86 */ 87 if ((itemp = (Item *) malloc(sizeof (*itemp))) == 88 Null_Item) { 89 (void) fprintf(stderr, 90 pkg_gt(ERR_MALLOC), fname, 91 sizeof (*itemp), "itemp"); 92 } else if ((itemp->key = (char *)malloc(len)) == NULL) { 93 (void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname, len, 94 "itemp->key"); 95 free(itemp); 96 } else if ((itemp->data = malloc(struct_size)) == NULL) { 97 (void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname, 98 struct_size, "itemp->data"); 99 free(itemp->key); 100 free(itemp); 101 } else { 102 /* Set length parameters. */ 103 itemp->keyl = len; 104 itemp->datal = struct_size; 105 106 return (itemp); 107 } 108 109 return ((Item *) NULL); 110 } 111 112 /* Get the required group structure based upon the group name. */ 113 struct group * 114 cgrnam(char *nam) 115 { 116 struct group *grp; 117 Item *itemp; 118 int len; 119 static int cache_failed; 120 121 /* Attempt to initialize the grname cache. */ 122 if (!is_a_grnam_cache && !cache_failed) { 123 if (init_cache(&grnam_cache, HASHSIZE, BSZ, 124 (int (*)())NULL, (int (*)())NULL) == -1) { 125 (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrnam()"); 126 grnam_cache = (Cache *) NULL; 127 cache_failed = 1; 128 } else 129 is_a_grnam_cache = 1; 130 } 131 132 len = strlen(nam) + 1; 133 134 /* First look in the cache. Failing that, do it the hard way. */ 135 if ((itemp = lookup_cache(grnam_cache, nam, len)) == Null_Item) { 136 137 /* Get the group by name. */ 138 if ((grp = clgrnam(nam)) != NULL || 139 (grp = getgrnam(nam)) != NULL) { 140 /* A group by that name exists on this machine. */ 141 if (dup_gr_ent(grp)) 142 /* 143 * Effectively no such group since struct 144 * couldn't be duplicated. 145 */ 146 grp = (struct group *)NULL; 147 /* 148 * If there's a grnam cache, then update it with this 149 * new group, otherwise, skip it. 150 */ 151 else if (is_a_grnam_cache) { 152 if ((itemp = cache_alloc("cgrnam()", len, 153 sizeof (struct group))) != Null_Item) { 154 /* 155 * With that allocated, insert the 156 * group name as key and set the key 157 * length. 158 */ 159 (void) memmove(itemp->key, nam, len); 160 161 /* 162 * Insert the data associated with 163 * the key and the data length. 164 */ 165 (void) memmove(itemp->data, grp, 166 sizeof (struct group)); 167 168 /* Insert the Item into the cache. */ 169 if (add_cache(grnam_cache, itemp) == -1) 170 (void) fprintf(stderr, 171 pkg_gt(ERR_ADDFAIL), 172 "cgrnam()"); 173 } 174 } 175 } 176 return (grp); 177 } else /* Found it in the cache. */ 178 return ((struct group *)itemp->data); 179 } 180 181 struct passwd * 182 cpwnam(char *nam) 183 { 184 struct passwd *pwd; 185 Item *itemp; 186 int len; 187 static int cache_failed; 188 189 if (!is_a_pwnam_cache && !cache_failed) { 190 if (init_cache(&pwnam_cache, HASHSIZE, BSZ, 191 (int (*)())NULL, (int (*)())NULL) == -1) { 192 (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cpwnam()"); 193 pwnam_cache = (Cache *) NULL; 194 cache_failed = 1; 195 } else 196 is_a_pwnam_cache = 1; 197 } 198 199 len = strlen(nam) + 1; 200 201 /* First look in the cache. Failing that, do it the hard way. */ 202 if ((itemp = lookup_cache(pwnam_cache, nam, len)) == Null_Item) { 203 204 /* Get the passwd by name. */ 205 if ((pwd = clpwnam(nam)) != NULL || 206 (pwd = getpwnam(nam)) != NULL) { 207 /* A passwd by that name exists on this machine. */ 208 if (dup_pw_ent(pwd)) 209 /* 210 * Effectively no such passwd since struct 211 * couldn't be duplicated. 212 */ 213 pwd = (struct passwd *)NULL; 214 /* 215 * If there's a pwnam cache, then update it with this 216 * new passwd, otherwise, skip it. 217 */ 218 else if (is_a_pwnam_cache) { 219 /* 220 * Allocate space for the Item pointer, key 221 * and data. 222 */ 223 if ((itemp = cache_alloc("cpwnam()", len, 224 sizeof (struct passwd))) != Null_Item) { 225 /* 226 * With that allocated, insert the 227 * group name as key and set the key 228 * length. 229 */ 230 (void) memmove(itemp->key, nam, len); 231 232 /* 233 * Insert the data associated with 234 * the key and the data length. 235 */ 236 (void) memmove(itemp->data, pwd, 237 sizeof (struct passwd)); 238 239 if (add_cache(pwnam_cache, itemp) == -1) 240 (void) fprintf(stderr, 241 pkg_gt(ERR_ADDFAIL), 242 "cpwnam()"); 243 } 244 } 245 } 246 return (pwd); 247 } else /* Found it in the cache. */ 248 return ((struct passwd *)itemp->data); 249 } 250 251 static int 252 uid_hash(void *datap, int datalen, int hsz) 253 { 254 #ifdef lint 255 int i = datalen; 256 datalen = i; 257 #endif /* lint */ 258 259 return (*((uid_t *)datap) % hsz); 260 } 261 262 static int 263 uid_comp(void *datap1, void *datap2, int datalen) 264 { 265 #ifdef lint 266 int i = datalen; 267 datalen = i; 268 #endif /* lint */ 269 270 return (*((uid_t *)datap1) - *((uid_t *)datap2)); 271 } 272 273 struct group * 274 cgrgid(gid_t gid) 275 { 276 struct group *grp; 277 Item *itemp; 278 int len; 279 static int cache_failed; 280 281 if (!is_a_grgid_cache && !cache_failed) { 282 if (init_cache(&grgid_cache, HASHSIZE, BSZ, 283 uid_hash, uid_comp) == -1) { 284 (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrgid()"); 285 grgid_cache = (Cache *) NULL; 286 cache_failed = 1; 287 } else 288 is_a_grgid_cache = 1; 289 } 290 291 len = sizeof (uid_t); 292 293 /* First look in the cache. Failing that, do it the hard way. */ 294 if ((itemp = lookup_cache(grgid_cache, &gid, len)) == Null_Item) { 295 if ((grp = clgrgid(gid)) != NULL || 296 (grp = getgrgid(gid)) != NULL) { 297 /* A group by that number exists on this machine. */ 298 if (dup_gr_ent(grp)) 299 /* 300 * Effectively no such group since struct 301 * couldn't be duplicated. 302 */ 303 grp = (struct group *)NULL; 304 /* 305 * If there's a grnam cache, then update it with this 306 * new group, otherwise, skip it. 307 */ 308 else if (is_a_grgid_cache) { 309 if ((itemp = cache_alloc("cgrgid()", len, 310 sizeof (struct group))) != Null_Item) { 311 /* 312 * With that allocated, insert the 313 * group name as key and set the key 314 * length. 315 */ 316 (void) memmove(itemp->key, &gid, len); 317 318 /* 319 * Insert the data associated with 320 * the key and the data length. 321 */ 322 (void) memmove(itemp->data, grp, 323 sizeof (struct group)); 324 325 if (add_cache(grgid_cache, itemp) == -1) 326 (void) fprintf(stderr, 327 pkg_gt(ERR_ADDFAIL), 328 "cgrgid()"); 329 } 330 } 331 } 332 return (grp); 333 } else /* Found it in the cache. */ 334 return ((struct group *)itemp->data); 335 } 336 337 struct passwd * 338 cpwuid(uid_t uid) 339 { 340 struct passwd *pwd; 341 Item *itemp; 342 int len; 343 static int cache_failed; 344 345 if (!is_a_pwuid_cache && !cache_failed) { 346 if (init_cache(&pwuid_cache, HASHSIZE, BSZ, 347 uid_hash, uid_comp) == -1) { 348 (void) fprintf(stderr, 349 pkg_gt(ERR_NOINIT), "cpwuid()"); 350 pwuid_cache = (Cache *) NULL; 351 cache_failed = 1; 352 } else 353 is_a_pwuid_cache = 1; 354 } 355 356 len = sizeof (uid_t); 357 358 /* First look in the cache. Failing that, do it the hard way. */ 359 if ((itemp = lookup_cache(pwuid_cache, &uid, len)) == Null_Item) { 360 361 /* Get the passwd by number. */ 362 if ((pwd = clpwuid(uid)) != NULL || 363 (pwd = getpwuid(uid)) != NULL) { 364 /* A passwd by that user ID exists on this machine. */ 365 if (dup_pw_ent(pwd)) 366 /* 367 * Effectively no such passwd since struct 368 * couldn't be duplicated. 369 */ 370 pwd = (struct passwd *)NULL; 371 /* 372 * If there's a pwuid cache, then update it with this 373 * new passwd, otherwise, skip it. 374 */ 375 else if (is_a_pwuid_cache) { 376 if ((itemp = cache_alloc("cpwuid()", len, 377 sizeof (struct passwd))) != Null_Item) { 378 /* 379 * With that allocated, insert the 380 * group name as key and set the key 381 * length. 382 */ 383 (void) memmove(itemp->key, &uid, len); 384 385 /* 386 * Insert the data associated with 387 * the key and the data length. 388 */ 389 (void) memmove(itemp->data, pwd, 390 sizeof (struct passwd)); 391 392 if (add_cache(pwuid_cache, itemp) == -1) 393 (void) fprintf(stderr, 394 pkg_gt(ERR_ADDFAIL), 395 "cpwuid()"); 396 } 397 } 398 } 399 return (pwd); 400 } else /* Found it in the cache. */ 401 return ((struct passwd *)itemp->data); 402 } 403 404 /* 405 * This function duplicates the group structure provided from kernel static 406 * memory. There is a lot of defensive coding here because there have been 407 * problems with the getgr*() functions. They will sometimes provide NULL 408 * values instead of pointers to NULL values. There has been no explanation 409 * for the reason behind this; but, this function takes a NULL to be an 410 * invalid (char *) and returns an error. 411 */ 412 static int 413 dup_gr_ent(struct group *grp) 414 { 415 char **tp = NULL; 416 char **memp = NULL; 417 int nent = 0; /* Number of entries in the member list. */ 418 419 if (grp) { 420 if (grp->gr_name == NULL) { 421 (void) fprintf(stderr, 422 pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_name", 423 "unknown", "group"); 424 return (-1); 425 } else if ((grp->gr_name = strdup(grp->gr_name)) == NULL) { 426 (void) fprintf(stderr, 427 pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_name"); 428 return (-1); 429 } 430 if (grp->gr_passwd == NULL) { 431 (void) fprintf(stderr, 432 pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_passwd", 433 grp->gr_name, "group"); 434 return (-1); 435 } else if ((grp->gr_passwd = strdup(grp->gr_passwd)) == NULL) { 436 (void) fprintf(stderr, 437 pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_passwd"); 438 return (-1); 439 } 440 /* 441 * Allocate space for the member list and move the members 442 * into it. 443 */ 444 if (grp->gr_mem) { 445 /* 446 * First count the members. The nent variable will be 447 * the number of members + 1 for the terminator. 448 */ 449 for (tp = grp->gr_mem; *tp; nent++, tp++); 450 451 /* Now allocate room for the pointers. */ 452 memp = malloc(sizeof (char **)* (nent+1)); 453 454 if (memp == NULL) { 455 (void) fprintf(stderr, 456 pkg_gt(ERR_MALLOC), "dup_gr_ent()", 457 (sizeof (char **)* (nent+1)), 458 "memp"); 459 return (-1); 460 } 461 462 /* 463 * Now copy over the pointers and entries. It should 464 * be noted that if the structure is messed up here, 465 * the resulting member list will be truncated at the 466 * NULL entry. 467 */ 468 for (nent = 0, tp = grp->gr_mem; *tp; tp++) { 469 if ((memp[nent++] = strdup(*tp)) == NULL) { 470 (void) fprintf(stderr, 471 pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", 472 "gr_mem"); 473 return (-1); 474 } 475 } 476 } else { 477 (void) fprintf(stderr, 478 pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_mem", 479 grp->gr_name, "group"); 480 return (-1); 481 } 482 } else { 483 (void) fprintf(stderr, pkg_gt(ERR_NOGRP)); 484 return (-1); 485 } 486 memp[nent++] = '\0'; 487 return (0); 488 } 489 490 /* 491 * This function duplicates the passwd structure provided from kernel static 492 * memory. As in the above function, since there have been problems with the 493 * getpw*() functions, the structure provided is rigorously scrubbed. This 494 * function takes a NULL to be an invalid (char *) and returns an error if 495 * one is detected. 496 */ 497 static int 498 dup_pw_ent(struct passwd *pwd) 499 { 500 if (pwd) { 501 if (pwd->pw_name == NULL) { 502 (void) fprintf(stderr, 503 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_name", 504 "unknown", "passwd"); 505 return (-1); 506 } else if ((pwd->pw_name = strdup(pwd->pw_name)) == NULL) { 507 (void) fprintf(stderr, 508 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_name"); 509 return (-1); 510 } 511 512 if (pwd->pw_passwd == NULL) { 513 (void) fprintf(stderr, 514 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_passwd", 515 pwd->pw_name, "passwd"); 516 return (-1); 517 } else if ((pwd->pw_passwd = strdup(pwd->pw_passwd)) == NULL) { 518 (void) fprintf(stderr, 519 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_passwd"); 520 return (-1); 521 } 522 523 if (pwd->pw_age == NULL) { 524 (void) fprintf(stderr, 525 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_age", 526 pwd->pw_name, "passwd"); 527 return (-1); 528 } else if ((pwd->pw_age = strdup(pwd->pw_age)) == NULL) { 529 (void) fprintf(stderr, 530 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_age"); 531 return (-1); 532 } 533 534 if (pwd->pw_comment == NULL) { 535 (void) fprintf(stderr, 536 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_comment", 537 pwd->pw_name, "passwd"); 538 return (-1); 539 } else if ((pwd->pw_comment = strdup(pwd->pw_comment)) == 540 NULL) { 541 (void) fprintf(stderr, 542 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_comment"); 543 return (-1); 544 } 545 546 if (pwd->pw_gecos == NULL) { 547 (void) fprintf(stderr, 548 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_gecos", 549 pwd->pw_name, "passwd"); 550 return (-1); 551 } else if ((pwd->pw_gecos = strdup(pwd->pw_gecos)) == NULL) { 552 (void) fprintf(stderr, 553 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_gecos"); 554 return (-1); 555 } 556 557 if (pwd->pw_dir == NULL) { 558 (void) fprintf(stderr, 559 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_dir", 560 pwd->pw_name, "passwd"); 561 return (-1); 562 } else if ((pwd->pw_dir = strdup(pwd->pw_dir)) == NULL) { 563 (void) fprintf(stderr, 564 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_dir"); 565 return (-1); 566 } 567 568 if (pwd->pw_shell == NULL) { 569 (void) fprintf(stderr, 570 pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_shell", 571 pwd->pw_name, "passwd"); 572 return (-1); 573 } else if ((pwd->pw_shell = strdup(pwd->pw_shell)) == NULL) { 574 (void) fprintf(stderr, 575 pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_shell"); 576 return (-1); 577 } 578 } else { 579 (void) fprintf(stderr, pkg_gt(ERR_NOPWD)); 580 return (-1); 581 } 582 583 return (0); 584 } 585 586 /* 587 * Check the client's etc/group file for the group name 588 * 589 * returns a pointer to the group structure if the group is found 590 * returns NULL if not found 591 */ 592 struct group * 593 clgrnam(char *nam) 594 { 595 struct group *gr; 596 char *instroot, *buf; 597 FILE *gr_ptr; 598 size_t bufsz; 599 600 if ((instroot = get_install_root()) != NULL) { 601 bufsz = strlen(instroot) + strlen(GROUP) + 1; 602 if ((buf = (char *)malloc(bufsz)) == NULL) { 603 (void) fprintf(stderr, 604 pkg_gt(ERR_MALLOC), "clgrnam()", 605 strlen(instroot) + strlen(GROUP), "buf"); 606 } 607 (void) snprintf(buf, bufsz, "%s%s", instroot, GROUP); 608 if ((gr_ptr = fopen(buf, "r")) == NULL) { 609 free(buf); 610 return (NULL); 611 } else { 612 while ((gr = fgetgrent(gr_ptr)) != NULL) { 613 if (strcmp(gr->gr_name, nam) == 0) { 614 break; 615 } 616 } 617 } 618 free(buf); 619 (void) fclose(gr_ptr); 620 return (gr); 621 } else { 622 return (NULL); 623 } 624 } 625 626 /* 627 * Check the client's etc/passwd file for the user name 628 * 629 * returns a pointer to the passwd structure if the passwd is found 630 * returns NULL if not found 631 */ 632 struct passwd * 633 clpwnam(char *nam) 634 { 635 struct passwd *pw; 636 char *instroot, *buf; 637 FILE *pw_ptr; 638 639 if ((instroot = get_install_root()) != NULL) { 640 if (asprintf(&buf, "%s%s", instroot, PASSWD) < 0) { 641 (void) fprintf(stderr, 642 pkg_gt(ERR_MALLOC), "clpwnam()", 643 strlen(instroot) + strlen(PASSWD), "buf"); 644 return (NULL); 645 } 646 if ((pw_ptr = fopen(buf, "r")) == NULL) { 647 free(buf); 648 return (NULL); 649 } else { 650 while ((pw = fgetpwent(pw_ptr)) != NULL) { 651 if (strcmp(pw->pw_name, nam) == 0) { 652 break; 653 } 654 } 655 } 656 free(buf); 657 (void) fclose(pw_ptr); 658 return (pw); 659 } else { 660 return (NULL); 661 } 662 } 663 664 /* 665 * Check the client's etc/group file for the group id 666 * 667 * returns a pointer to the group structure if the group id is found 668 * returns NULL if not found 669 */ 670 struct group * 671 clgrgid(gid_t gid) 672 { 673 struct group *gr; 674 char *instroot, *buf; 675 FILE *gr_ptr; 676 677 if ((instroot = get_install_root()) != NULL) { 678 if (asprintf(&buf, "%s%s", instroot, GROUP) < 0) { 679 (void) fprintf(stderr, 680 pkg_gt(ERR_MALLOC), "clgrgid()", 681 strlen(instroot) + strlen(GROUP), "buf"); 682 return (NULL); 683 } 684 685 if ((gr_ptr = fopen(buf, "r")) == NULL) { 686 free(buf); 687 return (NULL); 688 } else { 689 while ((gr = fgetgrent(gr_ptr)) != NULL) { 690 if (gr->gr_gid == gid) { 691 break; 692 } 693 } 694 } 695 free(buf); 696 (void) fclose(gr_ptr); 697 return (gr); 698 } else { 699 return (NULL); 700 } 701 } 702 703 /* 704 * Check the client's etc/passwd file for the user id 705 * 706 * returns a pointer to the passwd structure if the user id is found 707 * returns NULL if not found 708 */ 709 struct passwd * 710 clpwuid(uid_t uid) 711 { 712 struct passwd *pw; 713 char *instroot, *buf; 714 FILE *pw_ptr; 715 716 if ((instroot = get_install_root()) != NULL) { 717 if (asprintf(&buf, "%s%s", instroot, PASSWD) < 0) { 718 (void) fprintf(stderr, pkg_gt(ERR_MALLOC), "clpwuid()", 719 strlen(instroot) + strlen(PASSWD), "buf"); 720 return (NULL); 721 } 722 if ((pw_ptr = fopen(buf, "r")) == NULL) { 723 free(buf); 724 return (NULL); 725 } else { 726 while ((pw = fgetpwent(pw_ptr)) != NULL) { 727 if (pw->pw_uid == uid) { 728 break; 729 } 730 } 731 } 732 free(buf); 733 (void) fclose(pw_ptr); 734 return (pw); 735 } else { 736 return (NULL); 737 } 738 } 739