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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <fcntl.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <unistd.h> 31 #include <locale.h> 32 #include <libgen.h> 33 #include <sys/types.h> 34 #include <zone.h> 35 #include <sys/crypto/ioctladmin.h> 36 #include "cryptoadm.h" 37 38 #define DEFAULT_DEV_NUM 5 39 #define DEFAULT_SOFT_NUM 10 40 41 static crypto_get_soft_info_t *setup_get_soft_info(char *, int); 42 43 /* 44 * Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the 45 * provider pointed by pent. Return NULL if out of memory. 46 */ 47 crypto_load_soft_config_t * 48 setup_soft_conf(entry_t *pent) 49 { 50 crypto_load_soft_config_t *pload_soft_conf; 51 mechlist_t *plist; 52 uint_t sup_count; 53 size_t extra_mech_size = 0; 54 int i; 55 56 if (pent == NULL) { 57 return (NULL); 58 } 59 60 sup_count = pent->sup_count; 61 if (sup_count > 1) { 62 extra_mech_size = sizeof (crypto_mech_name_t) * 63 (sup_count - 1); 64 } 65 66 pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) + 67 extra_mech_size); 68 if (pload_soft_conf == NULL) { 69 cryptodebug("out of memory."); 70 return (NULL); 71 } 72 73 (void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN); 74 pload_soft_conf->sc_count = sup_count; 75 76 i = 0; 77 plist = pent->suplist; 78 while (i < sup_count) { 79 (void) strlcpy(pload_soft_conf->sc_list[i++], 80 plist->name, CRYPTO_MAX_MECH_NAME); 81 plist = plist->next; 82 } 83 84 return (pload_soft_conf); 85 } 86 87 88 /* 89 * Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the 90 * provider pointed by pent. Return NULL if out of memory. 91 */ 92 crypto_load_soft_disabled_t * 93 setup_soft_dis(entry_t *pent) 94 { 95 crypto_load_soft_disabled_t *pload_soft_dis = NULL; 96 mechlist_t *plist = NULL; 97 size_t extra_mech_size = 0; 98 uint_t dis_count; 99 int i; 100 101 if (pent == NULL) { 102 return (NULL); 103 } 104 105 dis_count = pent->dis_count; 106 if (dis_count > 1) { 107 extra_mech_size = sizeof (crypto_mech_name_t) * 108 (dis_count - 1); 109 } 110 111 pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) + 112 extra_mech_size); 113 if (pload_soft_dis == NULL) { 114 cryptodebug("out of memory."); 115 return (NULL); 116 } 117 118 (void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN); 119 pload_soft_dis->sd_count = dis_count; 120 121 i = 0; 122 plist = pent->dislist; 123 while (i < dis_count) { 124 (void) strlcpy(pload_soft_dis->sd_list[i++], 125 plist->name, CRYPTO_MAX_MECH_NAME); 126 plist = plist->next; 127 } 128 129 return (pload_soft_dis); 130 } 131 132 133 /* 134 * Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the 135 * provider pointed by pent. Return NULL if out of memory. 136 */ 137 crypto_load_dev_disabled_t * 138 setup_dev_dis(entry_t *pent) 139 { 140 crypto_load_dev_disabled_t *pload_dev_dis = NULL; 141 mechlist_t *plist = NULL; 142 size_t extra_mech_size = 0; 143 uint_t dis_count; 144 int i; 145 char pname[MAXNAMELEN]; 146 int inst_num; 147 148 if (pent == NULL) { 149 return (NULL); 150 } 151 152 /* get the device name and the instance number */ 153 if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) { 154 return (NULL); 155 } 156 157 /* allocate space for pload_dev_des */ 158 dis_count = pent->dis_count; 159 if (dis_count > 1) { 160 extra_mech_size = sizeof (crypto_mech_name_t) * 161 (dis_count - 1); 162 } 163 164 pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) + 165 extra_mech_size); 166 if (pload_dev_dis == NULL) { 167 cryptodebug("out of memory."); 168 return (NULL); 169 } 170 171 /* set the values for pload_dev_dis */ 172 (void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN); 173 pload_dev_dis->dd_dev_instance = inst_num; 174 pload_dev_dis->dd_count = dis_count; 175 176 i = 0; 177 plist = pent->dislist; 178 while (i < dis_count) { 179 (void) strlcpy(pload_dev_dis->dd_list[i++], 180 plist->name, CRYPTO_MAX_MECH_NAME); 181 plist = plist->next; 182 } 183 184 return (pload_dev_dis); 185 } 186 187 188 /* 189 * Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the 190 * provider pointed by pent. Return NULL if out of memory. 191 */ 192 crypto_unload_soft_module_t * 193 setup_unload_soft(entry_t *pent) 194 { 195 crypto_unload_soft_module_t *punload_soft; 196 197 if (pent == NULL) { 198 return (NULL); 199 } 200 201 punload_soft = malloc(sizeof (crypto_unload_soft_module_t)); 202 if (punload_soft == NULL) { 203 cryptodebug("out of memory."); 204 return (NULL); 205 } 206 207 (void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN); 208 209 return (punload_soft); 210 } 211 212 213 /* 214 * Prepare the calling argument for the GET_SOFT_INFO call for the provider 215 * with the number of mechanisms specified in the second argument. 216 * 217 * Called by get_soft_info(). 218 */ 219 static crypto_get_soft_info_t * 220 setup_get_soft_info(char *provname, int count) 221 { 222 crypto_get_soft_info_t *psoft_info; 223 size_t extra_mech_size = 0; 224 225 if (provname == NULL) { 226 return (NULL); 227 } 228 229 if (count > 1) { 230 extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1); 231 } 232 233 psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size); 234 if (psoft_info == NULL) { 235 cryptodebug("out of memory."); 236 return (NULL); 237 } 238 239 (void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN); 240 psoft_info->si_count = count; 241 242 return (psoft_info); 243 } 244 245 246 /* 247 * Get the device list from kernel. 248 */ 249 int 250 get_dev_list(crypto_get_dev_list_t **ppdevlist) 251 { 252 crypto_get_dev_list_t *pdevlist; 253 int fd = -1; 254 int count = DEFAULT_DEV_NUM; 255 256 pdevlist = malloc(sizeof (crypto_get_dev_list_t) + 257 sizeof (crypto_dev_list_entry_t) * (count - 1)); 258 if (pdevlist == NULL) { 259 cryptodebug("out of memory."); 260 return (FAILURE); 261 } 262 263 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { 264 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), 265 ADMIN_IOCTL_DEVICE, strerror(errno)); 266 return (FAILURE); 267 } 268 269 pdevlist->dl_dev_count = count; 270 if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) { 271 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s", 272 strerror(errno)); 273 free(pdevlist); 274 (void) close(fd); 275 return (FAILURE); 276 } 277 278 /* BUFFER is too small, get the number of devices and retry it. */ 279 if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) { 280 count = pdevlist->dl_dev_count; 281 free(pdevlist); 282 pdevlist = malloc(sizeof (crypto_get_dev_list_t) + 283 sizeof (crypto_dev_list_entry_t) * (count - 1)); 284 if (pdevlist == NULL) { 285 cryptodebug("out of memory."); 286 (void) close(fd); 287 return (FAILURE); 288 } 289 290 if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) { 291 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s", 292 strerror(errno)); 293 free(pdevlist); 294 (void) close(fd); 295 return (FAILURE); 296 } 297 } 298 299 if (pdevlist->dl_return_value != CRYPTO_SUCCESS) { 300 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, " 301 "return_value = %d", pdevlist->dl_return_value); 302 free(pdevlist); 303 (void) close(fd); 304 return (FAILURE); 305 } 306 307 *ppdevlist = pdevlist; 308 (void) close(fd); 309 return (SUCCESS); 310 } 311 312 313 /* 314 * Get all the mechanisms supported by the hardware provider. 315 * The result will be stored in the second argument. 316 */ 317 int 318 get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist) 319 { 320 crypto_get_dev_info_t *dev_info; 321 mechlist_t *phead; 322 mechlist_t *pcur; 323 mechlist_t *pmech; 324 int fd = -1; 325 int i; 326 int rc; 327 328 if (devname == NULL || count < 1) { 329 cryptodebug("get_dev_info(): devname is NULL or bogus count"); 330 return (FAILURE); 331 } 332 333 /* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */ 334 dev_info = malloc(sizeof (crypto_get_dev_info_t) + 335 sizeof (crypto_mech_name_t) * (count - 1)); 336 if (dev_info == NULL) { 337 cryptodebug("out of memory."); 338 return (FAILURE); 339 } 340 (void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN); 341 dev_info->di_dev_instance = inst_num; 342 dev_info->di_count = count; 343 344 /* Open the ioctl device */ 345 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { 346 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), 347 ADMIN_IOCTL_DEVICE, strerror(errno)); 348 free(dev_info); 349 return (FAILURE); 350 } 351 352 if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) { 353 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s", 354 strerror(errno)); 355 free(dev_info); 356 (void) close(fd); 357 return (FAILURE); 358 } 359 360 if (dev_info->di_return_value != CRYPTO_SUCCESS) { 361 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, " 362 "return_value = %d", dev_info->di_return_value); 363 free(dev_info); 364 (void) close(fd); 365 return (FAILURE); 366 } 367 368 phead = pcur = NULL; 369 rc = SUCCESS; 370 for (i = 0; i < dev_info->di_count; i++) { 371 pmech = create_mech(&dev_info->di_list[i][0]); 372 if (pmech == NULL) { 373 rc = FAILURE; 374 break; 375 } else { 376 if (phead == NULL) { 377 phead = pcur = pmech; 378 } else { 379 pcur->next = pmech; 380 pcur = pmech; 381 } 382 } 383 } 384 385 if (rc == SUCCESS) { 386 *ppmechlist = phead; 387 } else { 388 free_mechlist(phead); 389 } 390 391 free(dev_info); 392 (void) close(fd); 393 return (rc); 394 } 395 396 397 /* 398 * Get the supported mechanism list of the software provider from kernel. 399 * 400 * Parameters phardlist and psoftlist are supplied by get_kcfconf_info(). 401 * If NULL, this function calls get_kcfconf_info() internally. 402 */ 403 int 404 get_soft_info(char *provname, mechlist_t **ppmechlist, 405 entrylist_t *phardlist, entrylist_t *psoftlist) 406 { 407 boolean_t in_kernel = B_FALSE; 408 crypto_get_soft_info_t *psoft_info; 409 mechlist_t *phead; 410 mechlist_t *pmech; 411 mechlist_t *pcur; 412 entry_t *pent = NULL; 413 int count; 414 int fd = -1; 415 int rc; 416 int i; 417 418 if (provname == NULL) { 419 return (FAILURE); 420 } 421 422 if (getzoneid() == GLOBAL_ZONEID) { 423 /* use kcf.conf for kernel software providers in global zone */ 424 if ((pent = getent_kef(provname, phardlist, psoftlist)) == 425 NULL) { 426 427 /* No kcf.conf entry for this provider */ 428 if (check_kernel_for_soft(provname, NULL, &in_kernel) 429 == FAILURE) { 430 return (FAILURE); 431 } else if (in_kernel == B_FALSE) { 432 cryptoerror(LOG_STDERR, 433 gettext("%s does not exist."), provname); 434 return (FAILURE); 435 } 436 437 /* 438 * Set mech count to 1. It will be reset to the 439 * correct value later if the setup buffer is too small. 440 */ 441 count = 1; 442 } else { 443 count = pent->sup_count; 444 free_entry(pent); 445 } 446 } else { 447 /* 448 * kcf.conf not there in non-global zone: set mech count to 1. 449 * It will be reset to the correct value later if the setup 450 * buffer is too small. 451 */ 452 count = 1; 453 } 454 455 if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) { 456 return (FAILURE); 457 } 458 459 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { 460 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), 461 ADMIN_IOCTL_DEVICE, strerror(errno)); 462 free(psoft_info); 463 return (FAILURE); 464 } 465 466 /* make GET_SOFT_INFO ioctl call */ 467 if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) { 468 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s", 469 strerror(errno)); 470 (void) close(fd); 471 free(psoft_info); 472 return (FAILURE); 473 } 474 475 /* BUFFER is too small, get the number of mechanisms and retry it. */ 476 if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) { 477 count = psoft_info->si_count; 478 free(psoft_info); 479 if ((psoft_info = setup_get_soft_info(provname, count)) 480 == NULL) { 481 (void) close(fd); 482 return (FAILURE); 483 } else { 484 rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info); 485 if (rc == -1) { 486 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl " 487 "failed: %s", strerror(errno)); 488 (void) close(fd); 489 free(psoft_info); 490 return (FAILURE); 491 } 492 } 493 } 494 495 (void) close(fd); 496 if (psoft_info->si_return_value != CRYPTO_SUCCESS) { 497 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, " 498 "return_value = %d", psoft_info->si_return_value); 499 free(psoft_info); 500 return (FAILURE); 501 } 502 503 504 /* Build the mechanism linked list and return it */ 505 rc = SUCCESS; 506 phead = pcur = NULL; 507 for (i = 0; i < psoft_info->si_count; i++) { 508 pmech = create_mech(&psoft_info->si_list[i][0]); 509 if (pmech == NULL) { 510 rc = FAILURE; 511 break; 512 } else { 513 if (phead == NULL) { 514 phead = pcur = pmech; 515 } else { 516 pcur->next = pmech; 517 pcur = pmech; 518 } 519 } 520 } 521 522 if (rc == FAILURE) { 523 free_mechlist(phead); 524 } else { 525 *ppmechlist = phead; 526 } 527 528 free(psoft_info); 529 return (rc); 530 } 531 532 533 /* 534 * Get the kernel software provider list from kernel. 535 */ 536 int 537 get_soft_list(crypto_get_soft_list_t **ppsoftlist) 538 { 539 crypto_get_soft_list_t *psoftlist = NULL; 540 int count = DEFAULT_SOFT_NUM; 541 int len; 542 int fd = -1; 543 544 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { 545 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), 546 ADMIN_IOCTL_DEVICE, strerror(errno)); 547 return (FAILURE); 548 } 549 550 len = MAXNAMELEN * count; 551 psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len); 552 if (psoftlist == NULL) { 553 cryptodebug("out of memory."); 554 (void) close(fd); 555 return (FAILURE); 556 } 557 psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1); 558 psoftlist->sl_soft_count = count; 559 psoftlist->sl_soft_len = len; 560 561 if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) { 562 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s", 563 strerror(errno)); 564 free(psoftlist); 565 (void) close(fd); 566 return (FAILURE); 567 } 568 569 /* 570 * if BUFFER is too small, get the number of software providers and 571 * the minimum length needed for names and length and retry it. 572 */ 573 if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) { 574 count = psoftlist->sl_soft_count; 575 len = psoftlist->sl_soft_len; 576 free(psoftlist); 577 psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len); 578 if (psoftlist == NULL) { 579 cryptodebug("out of memory."); 580 (void) close(fd); 581 return (FAILURE); 582 } 583 psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1); 584 psoftlist->sl_soft_count = count; 585 psoftlist->sl_soft_len = len; 586 587 if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) { 588 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:" 589 "%s", strerror(errno)); 590 free(psoftlist); 591 (void) close(fd); 592 return (FAILURE); 593 } 594 } 595 596 if (psoftlist->sl_return_value != CRYPTO_SUCCESS) { 597 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, " 598 "return_value = %d", psoftlist->sl_return_value); 599 free(psoftlist); 600 (void) close(fd); 601 return (FAILURE); 602 } 603 604 *ppsoftlist = psoftlist; 605 (void) close(fd); 606 return (SUCCESS); 607 } 608 609 /* 610 * Perform the FIPS related actions 611 */ 612 int 613 do_fips_actions(int action, int caller) 614 { 615 616 crypto_fips140_t fips_info; 617 int fd; 618 int rc = SUCCESS; 619 int pkcs11_fips_mode = 0; 620 621 /* Get FIPS-140 status from pkcs11.conf */ 622 fips_status_pkcs11conf(&pkcs11_fips_mode); 623 624 if (action == FIPS140_STATUS) { 625 if (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED) 626 (void) printf(gettext( 627 "\tFIPS-140 mode is enabled.\n")); 628 else 629 (void) printf(gettext( 630 "\tFIPS-140 mode is disabled.\n")); 631 return (SUCCESS); 632 } 633 634 if (caller == NOT_REFRESH) { 635 /* Is it a duplicate operation? */ 636 if ((action == FIPS140_ENABLE) && 637 (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED)) { 638 cryptoerror(LOG_STDERR, 639 gettext("FIPS-140 mode has already " 640 "been enabled.\n")); 641 return (FAILURE); 642 } 643 644 if ((action == FIPS140_DISABLE) && 645 (pkcs11_fips_mode == CRYPTO_FIPS_MODE_DISABLED)) { 646 cryptoerror(LOG_STDERR, 647 gettext("FIPS-140 mode has already " 648 "been disabled.\n")); 649 return (FAILURE); 650 } 651 652 if ((action == FIPS140_ENABLE) || (action == FIPS140_DISABLE)) { 653 /* Update pkcs11.conf */ 654 if ((rc = fips_update_pkcs11conf(action)) != SUCCESS) 655 return (rc); 656 } 657 658 /* No need to inform kernel */ 659 if (action == FIPS140_ENABLE) { 660 (void) printf(gettext( 661 "FIPS-140 mode was enabled successfully.\n")); 662 (void) printf(gettext( 663 "Warning: In this release, the Cryptographic " 664 "Framework has not been FIPS 140-2 " 665 "certified.\n\n")); 666 } else { 667 (void) printf(gettext( 668 "FIPS-140 mode was disabled successfully.\n")); 669 } 670 671 (void) printf(gettext( 672 "The FIPS-140 mode has changed.\n")); 673 (void) printf(gettext( 674 "The system will require a reboot.\n\n")); 675 return (SUCCESS); 676 677 } 678 679 /* This is refresh, need to inform kernel */ 680 (void) memset(&fips_info, 0, sizeof (crypto_fips140_t)); 681 682 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { 683 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), 684 ADMIN_IOCTL_DEVICE, strerror(errno)); 685 return (FAILURE); 686 } 687 688 switch (action) { 689 case FIPS140_ENABLE: 690 /* make CRYPTO_FIPS_SET ioctl call */ 691 fips_info.fips140_op = FIPS140_ENABLE; 692 if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) { 693 cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed: %s", 694 strerror(errno)); 695 rc = FAILURE; 696 goto out; 697 } 698 699 if (fips_info.fips140_return_value != CRYPTO_SUCCESS) { 700 cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed, " 701 "return_value = %d", 702 fips_info.fips140_return_value); 703 rc = FAILURE; 704 } 705 706 break; 707 708 case FIPS140_DISABLE: 709 /* make CRYPTO_FIPS140_SET ioctl call */ 710 fips_info.fips140_op = FIPS140_DISABLE; 711 if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) { 712 cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed: %s", 713 strerror(errno)); 714 rc = FAILURE; 715 goto out; 716 } 717 718 if (fips_info.fips140_return_value != CRYPTO_SUCCESS) { 719 cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed, " 720 "return_value = %d", 721 fips_info.fips140_return_value); 722 rc = FAILURE; 723 } 724 725 break; 726 727 default: 728 rc = FAILURE; 729 break; 730 }; 731 732 out: 733 (void) close(fd); 734 return (rc); 735 } 736