1 /* 2 * Copyright (c) 1997 - 2008 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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 #include "krb5_locl.h" 35 36 RCSID("$Id: fcache.c 22522 2008-01-24 11:56:25Z lha $"); 37 38 typedef struct krb5_fcache{ 39 char *filename; 40 int version; 41 }krb5_fcache; 42 43 struct fcc_cursor { 44 int fd; 45 krb5_storage *sp; 46 }; 47 48 #define KRB5_FCC_FVNO_1 1 49 #define KRB5_FCC_FVNO_2 2 50 #define KRB5_FCC_FVNO_3 3 51 #define KRB5_FCC_FVNO_4 4 52 53 #define FCC_TAG_DELTATIME 1 54 55 #define FCACHE(X) ((krb5_fcache*)(X)->data.data) 56 57 #define FILENAME(X) (FCACHE(X)->filename) 58 59 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) 60 61 static const char* 62 fcc_get_name(krb5_context context, 63 krb5_ccache id) 64 { 65 return FILENAME(id); 66 } 67 68 int 69 _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, 70 const char *filename) 71 { 72 int ret; 73 #ifdef HAVE_FCNTL 74 struct flock l; 75 76 l.l_start = 0; 77 l.l_len = 0; 78 l.l_type = exclusive ? F_WRLCK : F_RDLCK; 79 l.l_whence = SEEK_SET; 80 ret = fcntl(fd, F_SETLKW, &l); 81 #else 82 ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); 83 #endif 84 if(ret < 0) 85 ret = errno; 86 if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ 87 ret = EAGAIN; 88 89 switch (ret) { 90 case 0: 91 break; 92 case EINVAL: /* filesystem doesn't support locking, let the user have it */ 93 ret = 0; 94 break; 95 case EAGAIN: 96 krb5_set_error_string(context, "timed out locking cache file %s", 97 filename); 98 break; 99 default: 100 krb5_set_error_string(context, "error locking cache file %s: %s", 101 filename, strerror(ret)); 102 break; 103 } 104 return ret; 105 } 106 107 int 108 _krb5_xunlock(krb5_context context, int fd) 109 { 110 int ret; 111 #ifdef HAVE_FCNTL 112 struct flock l; 113 l.l_start = 0; 114 l.l_len = 0; 115 l.l_type = F_UNLCK; 116 l.l_whence = SEEK_SET; 117 ret = fcntl(fd, F_SETLKW, &l); 118 #else 119 ret = flock(fd, LOCK_UN); 120 #endif 121 if (ret < 0) 122 ret = errno; 123 switch (ret) { 124 case 0: 125 break; 126 case EINVAL: /* filesystem doesn't support locking, let the user have it */ 127 ret = 0; 128 break; 129 default: 130 krb5_set_error_string(context, 131 "Failed to unlock file: %s", strerror(ret)); 132 break; 133 } 134 return ret; 135 } 136 137 static krb5_error_code 138 fcc_lock(krb5_context context, krb5_ccache id, 139 int fd, krb5_boolean exclusive) 140 { 141 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id)); 142 } 143 144 static krb5_error_code 145 fcc_unlock(krb5_context context, int fd) 146 { 147 return _krb5_xunlock(context, fd); 148 } 149 150 static krb5_error_code 151 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 152 { 153 krb5_fcache *f; 154 f = malloc(sizeof(*f)); 155 if(f == NULL) { 156 krb5_set_error_string(context, "malloc: out of memory"); 157 return KRB5_CC_NOMEM; 158 } 159 f->filename = strdup(res); 160 if(f->filename == NULL){ 161 free(f); 162 krb5_set_error_string(context, "malloc: out of memory"); 163 return KRB5_CC_NOMEM; 164 } 165 f->version = 0; 166 (*id)->data.data = f; 167 (*id)->data.length = sizeof(*f); 168 return 0; 169 } 170 171 /* 172 * Try to scrub the contents of `filename' safely. 173 */ 174 175 static int 176 scrub_file (int fd) 177 { 178 off_t pos; 179 char buf[128]; 180 181 pos = lseek(fd, 0, SEEK_END); 182 if (pos < 0) 183 return errno; 184 if (lseek(fd, 0, SEEK_SET) < 0) 185 return errno; 186 memset(buf, 0, sizeof(buf)); 187 while(pos > 0) { 188 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos)); 189 190 if (tmp < 0) 191 return errno; 192 pos -= tmp; 193 } 194 fsync (fd); 195 return 0; 196 } 197 198 /* 199 * Erase `filename' if it exists, trying to remove the contents if 200 * it's `safe'. We always try to remove the file, it it exists. It's 201 * only overwritten if it's a regular file (not a symlink and not a 202 * hardlink) 203 */ 204 205 static krb5_error_code 206 erase_file(const char *filename) 207 { 208 int fd; 209 struct stat sb1, sb2; 210 int ret; 211 212 ret = lstat (filename, &sb1); 213 if (ret < 0) 214 return errno; 215 216 fd = open(filename, O_RDWR | O_BINARY); 217 if(fd < 0) { 218 if(errno == ENOENT) 219 return 0; 220 else 221 return errno; 222 } 223 if (unlink(filename) < 0) { 224 close (fd); 225 return errno; 226 } 227 ret = fstat (fd, &sb2); 228 if (ret < 0) { 229 close (fd); 230 return errno; 231 } 232 233 /* check if someone was playing with symlinks */ 234 235 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 236 close (fd); 237 return EPERM; 238 } 239 240 /* there are still hard links to this file */ 241 242 if (sb2.st_nlink != 0) { 243 close (fd); 244 return 0; 245 } 246 247 ret = scrub_file (fd); 248 close (fd); 249 return ret; 250 } 251 252 static krb5_error_code 253 fcc_gen_new(krb5_context context, krb5_ccache *id) 254 { 255 krb5_fcache *f; 256 int fd; 257 char *file; 258 259 f = malloc(sizeof(*f)); 260 if(f == NULL) { 261 krb5_set_error_string(context, "malloc: out of memory"); 262 return KRB5_CC_NOMEM; 263 } 264 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 265 if(file == NULL) { 266 free(f); 267 krb5_set_error_string(context, "malloc: out of memory"); 268 return KRB5_CC_NOMEM; 269 } 270 fd = mkstemp(file); 271 if(fd < 0) { 272 int ret = errno; 273 krb5_set_error_string(context, "mkstemp %s", file); 274 free(f); 275 free(file); 276 return ret; 277 } 278 close(fd); 279 f->filename = file; 280 f->version = 0; 281 (*id)->data.data = f; 282 (*id)->data.length = sizeof(*f); 283 return 0; 284 } 285 286 static void 287 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 288 { 289 int flags = 0; 290 switch(vno) { 291 case KRB5_FCC_FVNO_1: 292 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 293 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 294 flags |= KRB5_STORAGE_HOST_BYTEORDER; 295 break; 296 case KRB5_FCC_FVNO_2: 297 flags |= KRB5_STORAGE_HOST_BYTEORDER; 298 break; 299 case KRB5_FCC_FVNO_3: 300 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 301 break; 302 case KRB5_FCC_FVNO_4: 303 break; 304 default: 305 krb5_abortx(context, 306 "storage_set_flags called with bad vno (%x)", vno); 307 } 308 krb5_storage_set_flags(sp, flags); 309 } 310 311 static krb5_error_code 312 fcc_open(krb5_context context, 313 krb5_ccache id, 314 int *fd_ret, 315 int flags, 316 mode_t mode) 317 { 318 krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 319 (flags | O_RDWR) == flags); 320 krb5_error_code ret; 321 const char *filename = FILENAME(id); 322 int fd; 323 fd = open(filename, flags, mode); 324 if(fd < 0) { 325 ret = errno; 326 krb5_set_error_string(context, "open(%s): %s", filename, 327 strerror(ret)); 328 return ret; 329 } 330 331 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 332 close(fd); 333 return ret; 334 } 335 *fd_ret = fd; 336 return 0; 337 } 338 339 static krb5_error_code 340 fcc_initialize(krb5_context context, 341 krb5_ccache id, 342 krb5_principal primary_principal) 343 { 344 krb5_fcache *f = FCACHE(id); 345 int ret = 0; 346 int fd; 347 char *filename = f->filename; 348 349 unlink (filename); 350 351 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); 352 if(ret) 353 return ret; 354 { 355 krb5_storage *sp; 356 sp = krb5_storage_from_fd(fd); 357 krb5_storage_set_eof_code(sp, KRB5_CC_END); 358 if(context->fcache_vno != 0) 359 f->version = context->fcache_vno; 360 else 361 f->version = KRB5_FCC_FVNO_4; 362 ret |= krb5_store_int8(sp, 5); 363 ret |= krb5_store_int8(sp, f->version); 364 storage_set_flags(context, sp, f->version); 365 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 366 /* V4 stuff */ 367 if (context->kdc_sec_offset) { 368 ret |= krb5_store_int16 (sp, 12); /* length */ 369 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 370 ret |= krb5_store_int16 (sp, 8); /* length of data */ 371 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 372 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 373 } else { 374 ret |= krb5_store_int16 (sp, 0); 375 } 376 } 377 ret |= krb5_store_principal(sp, primary_principal); 378 379 krb5_storage_free(sp); 380 } 381 fcc_unlock(context, fd); 382 if (close(fd) < 0) 383 if (ret == 0) { 384 ret = errno; 385 krb5_set_error_string (context, "close %s: %s", 386 FILENAME(id), strerror(ret)); 387 } 388 return ret; 389 } 390 391 static krb5_error_code 392 fcc_close(krb5_context context, 393 krb5_ccache id) 394 { 395 free (FILENAME(id)); 396 krb5_data_free(&id->data); 397 return 0; 398 } 399 400 static krb5_error_code 401 fcc_destroy(krb5_context context, 402 krb5_ccache id) 403 { 404 erase_file(FILENAME(id)); 405 return 0; 406 } 407 408 static krb5_error_code 409 fcc_store_cred(krb5_context context, 410 krb5_ccache id, 411 krb5_creds *creds) 412 { 413 int ret; 414 int fd; 415 416 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY, 0); 417 if(ret) 418 return ret; 419 { 420 krb5_storage *sp; 421 sp = krb5_storage_from_fd(fd); 422 krb5_storage_set_eof_code(sp, KRB5_CC_END); 423 storage_set_flags(context, sp, FCACHE(id)->version); 424 if (!krb5_config_get_bool_default(context, NULL, TRUE, 425 "libdefaults", 426 "fcc-mit-ticketflags", 427 NULL)) 428 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 429 ret = krb5_store_creds(sp, creds); 430 krb5_storage_free(sp); 431 } 432 fcc_unlock(context, fd); 433 if (close(fd) < 0) 434 if (ret == 0) { 435 ret = errno; 436 krb5_set_error_string (context, "close %s: %s", 437 FILENAME(id), strerror(ret)); 438 } 439 return ret; 440 } 441 442 static krb5_error_code 443 init_fcc (krb5_context context, 444 krb5_ccache id, 445 krb5_storage **ret_sp, 446 int *ret_fd) 447 { 448 int fd; 449 int8_t pvno, tag; 450 krb5_storage *sp; 451 krb5_error_code ret; 452 453 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY, 0); 454 if(ret) 455 return ret; 456 457 sp = krb5_storage_from_fd(fd); 458 if(sp == NULL) { 459 krb5_clear_error_string(context); 460 ret = ENOMEM; 461 goto out; 462 } 463 krb5_storage_set_eof_code(sp, KRB5_CC_END); 464 ret = krb5_ret_int8(sp, &pvno); 465 if(ret != 0) { 466 if(ret == KRB5_CC_END) { 467 krb5_set_error_string(context, "Empty credential cache file: %s", 468 FILENAME(id)); 469 ret = ENOENT; 470 } else 471 krb5_set_error_string(context, "Error reading pvno in " 472 "cache file: %s", FILENAME(id)); 473 goto out; 474 } 475 if(pvno != 5) { 476 krb5_set_error_string(context, "Bad version number in credential " 477 "cache file: %s", FILENAME(id)); 478 ret = KRB5_CCACHE_BADVNO; 479 goto out; 480 } 481 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 482 if(ret != 0) { 483 krb5_set_error_string(context, "Error reading tag in " 484 "cache file: %s", FILENAME(id)); 485 ret = KRB5_CC_FORMAT; 486 goto out; 487 } 488 FCACHE(id)->version = tag; 489 storage_set_flags(context, sp, FCACHE(id)->version); 490 switch (tag) { 491 case KRB5_FCC_FVNO_4: { 492 int16_t length; 493 494 ret = krb5_ret_int16 (sp, &length); 495 if(ret) { 496 ret = KRB5_CC_FORMAT; 497 krb5_set_error_string(context, "Error reading tag length in " 498 "cache file: %s", FILENAME(id)); 499 goto out; 500 } 501 while(length > 0) { 502 int16_t dtag, data_len; 503 int i; 504 int8_t dummy; 505 506 ret = krb5_ret_int16 (sp, &dtag); 507 if(ret) { 508 krb5_set_error_string(context, "Error reading dtag in " 509 "cache file: %s", FILENAME(id)); 510 ret = KRB5_CC_FORMAT; 511 goto out; 512 } 513 ret = krb5_ret_int16 (sp, &data_len); 514 if(ret) { 515 krb5_set_error_string(context, "Error reading dlength in " 516 "cache file: %s", FILENAME(id)); 517 ret = KRB5_CC_FORMAT; 518 goto out; 519 } 520 switch (dtag) { 521 case FCC_TAG_DELTATIME : 522 ret = krb5_ret_int32 (sp, &context->kdc_sec_offset); 523 if(ret) { 524 krb5_set_error_string(context, "Error reading kdc_sec in " 525 "cache file: %s", FILENAME(id)); 526 ret = KRB5_CC_FORMAT; 527 goto out; 528 } 529 ret = krb5_ret_int32 (sp, &context->kdc_usec_offset); 530 if(ret) { 531 krb5_set_error_string(context, "Error reading kdc_usec in " 532 "cache file: %s", FILENAME(id)); 533 ret = KRB5_CC_FORMAT; 534 goto out; 535 } 536 break; 537 default : 538 for (i = 0; i < data_len; ++i) { 539 ret = krb5_ret_int8 (sp, &dummy); 540 if(ret) { 541 krb5_set_error_string(context, "Error reading unknown " 542 "tag in cache file: %s", 543 FILENAME(id)); 544 ret = KRB5_CC_FORMAT; 545 goto out; 546 } 547 } 548 break; 549 } 550 length -= 4 + data_len; 551 } 552 break; 553 } 554 case KRB5_FCC_FVNO_3: 555 case KRB5_FCC_FVNO_2: 556 case KRB5_FCC_FVNO_1: 557 break; 558 default : 559 ret = KRB5_CCACHE_BADVNO; 560 krb5_set_error_string(context, "Unknown version number (%d) in " 561 "credential cache file: %s", 562 (int)tag, FILENAME(id)); 563 goto out; 564 } 565 *ret_sp = sp; 566 *ret_fd = fd; 567 568 return 0; 569 out: 570 if(sp != NULL) 571 krb5_storage_free(sp); 572 fcc_unlock(context, fd); 573 close(fd); 574 return ret; 575 } 576 577 static krb5_error_code 578 fcc_get_principal(krb5_context context, 579 krb5_ccache id, 580 krb5_principal *principal) 581 { 582 krb5_error_code ret; 583 int fd; 584 krb5_storage *sp; 585 586 ret = init_fcc (context, id, &sp, &fd); 587 if (ret) 588 return ret; 589 ret = krb5_ret_principal(sp, principal); 590 if (ret) 591 krb5_clear_error_string(context); 592 krb5_storage_free(sp); 593 fcc_unlock(context, fd); 594 close(fd); 595 return ret; 596 } 597 598 static krb5_error_code 599 fcc_end_get (krb5_context context, 600 krb5_ccache id, 601 krb5_cc_cursor *cursor); 602 603 static krb5_error_code 604 fcc_get_first (krb5_context context, 605 krb5_ccache id, 606 krb5_cc_cursor *cursor) 607 { 608 krb5_error_code ret; 609 krb5_principal principal; 610 611 *cursor = malloc(sizeof(struct fcc_cursor)); 612 if (*cursor == NULL) { 613 krb5_set_error_string (context, "malloc: out of memory"); 614 return ENOMEM; 615 } 616 memset(*cursor, 0, sizeof(struct fcc_cursor)); 617 618 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 619 &FCC_CURSOR(*cursor)->fd); 620 if (ret) { 621 free(*cursor); 622 *cursor = NULL; 623 return ret; 624 } 625 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 626 if(ret) { 627 krb5_clear_error_string(context); 628 fcc_end_get(context, id, cursor); 629 return ret; 630 } 631 krb5_free_principal (context, principal); 632 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 633 return 0; 634 } 635 636 static krb5_error_code 637 fcc_get_next (krb5_context context, 638 krb5_ccache id, 639 krb5_cc_cursor *cursor, 640 krb5_creds *creds) 641 { 642 krb5_error_code ret; 643 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 644 return ret; 645 646 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 647 if (ret) 648 krb5_clear_error_string(context); 649 650 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 651 return ret; 652 } 653 654 static krb5_error_code 655 fcc_end_get (krb5_context context, 656 krb5_ccache id, 657 krb5_cc_cursor *cursor) 658 { 659 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 660 close (FCC_CURSOR(*cursor)->fd); 661 free(*cursor); 662 *cursor = NULL; 663 return 0; 664 } 665 666 static krb5_error_code 667 fcc_remove_cred(krb5_context context, 668 krb5_ccache id, 669 krb5_flags which, 670 krb5_creds *cred) 671 { 672 krb5_error_code ret; 673 krb5_ccache copy; 674 675 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, ©); 676 if (ret) 677 return ret; 678 679 ret = krb5_cc_copy_cache(context, id, copy); 680 if (ret) { 681 krb5_cc_destroy(context, copy); 682 return ret; 683 } 684 685 ret = krb5_cc_remove_cred(context, copy, which, cred); 686 if (ret) { 687 krb5_cc_destroy(context, copy); 688 return ret; 689 } 690 691 fcc_destroy(context, id); 692 693 ret = krb5_cc_copy_cache(context, copy, id); 694 krb5_cc_destroy(context, copy); 695 696 return ret; 697 } 698 699 static krb5_error_code 700 fcc_set_flags(krb5_context context, 701 krb5_ccache id, 702 krb5_flags flags) 703 { 704 return 0; /* XXX */ 705 } 706 707 static krb5_error_code 708 fcc_get_version(krb5_context context, 709 krb5_ccache id) 710 { 711 return FCACHE(id)->version; 712 } 713 714 struct fcache_iter { 715 int first; 716 }; 717 718 static krb5_error_code 719 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 720 { 721 struct fcache_iter *iter; 722 723 iter = calloc(1, sizeof(*iter)); 724 if (iter == NULL) { 725 krb5_set_error_string(context, "malloc - out of memory"); 726 return ENOMEM; 727 } 728 iter->first = 1; 729 *cursor = iter; 730 return 0; 731 } 732 733 static krb5_error_code 734 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 735 { 736 struct fcache_iter *iter = cursor; 737 krb5_error_code ret; 738 const char *fn; 739 char *expandedfn = NULL; 740 741 if (!iter->first) { 742 krb5_clear_error_string(context); 743 return KRB5_CC_END; 744 } 745 iter->first = 0; 746 747 fn = krb5_cc_default_name(context); 748 if (strncasecmp(fn, "FILE:", 5) != 0) { 749 ret = _krb5_expand_default_cc_name(context, 750 KRB5_DEFAULT_CCNAME_FILE, 751 &expandedfn); 752 if (ret) 753 return ret; 754 } 755 ret = krb5_cc_resolve(context, fn, id); 756 if (expandedfn) 757 free(expandedfn); 758 759 return ret; 760 } 761 762 static krb5_error_code 763 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 764 { 765 struct fcache_iter *iter = cursor; 766 free(iter); 767 return 0; 768 } 769 770 static krb5_error_code 771 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 772 { 773 krb5_error_code ret = 0; 774 775 ret = rename(FILENAME(from), FILENAME(to)); 776 if (ret && errno != EXDEV) { 777 ret = errno; 778 krb5_set_error_string(context, 779 "Rename of file from %s to %s failed: %s", 780 FILENAME(from), FILENAME(to), 781 strerror(ret)); 782 return ret; 783 } else if (ret && errno == EXDEV) { 784 /* make a copy and delete the orignal */ 785 krb5_ssize_t sz1, sz2; 786 int fd1, fd2; 787 char buf[BUFSIZ]; 788 789 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY, 0); 790 if(ret) 791 return ret; 792 793 unlink(FILENAME(to)); 794 795 ret = fcc_open(context, to, &fd2, 796 O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600); 797 if(ret) 798 goto out1; 799 800 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 801 sz2 = write(fd2, buf, sz1); 802 if (sz1 != sz2) { 803 ret = EIO; 804 krb5_set_error_string(context, 805 "Failed to write data from one file " 806 "credential cache to the other"); 807 goto out2; 808 } 809 } 810 if (sz1 < 0) { 811 ret = EIO; 812 krb5_set_error_string(context, 813 "Failed to read data from one file " 814 "credential cache to the other"); 815 goto out2; 816 } 817 erase_file(FILENAME(from)); 818 819 out2: 820 fcc_unlock(context, fd2); 821 close(fd2); 822 823 out1: 824 fcc_unlock(context, fd1); 825 close(fd1); 826 827 if (ret) { 828 erase_file(FILENAME(to)); 829 return ret; 830 } 831 } 832 833 /* make sure ->version is uptodate */ 834 { 835 krb5_storage *sp; 836 int fd; 837 ret = init_fcc (context, to, &sp, &fd); 838 krb5_storage_free(sp); 839 fcc_unlock(context, fd); 840 close(fd); 841 } 842 return ret; 843 } 844 845 static krb5_error_code 846 fcc_default_name(krb5_context context, char **str) 847 { 848 return _krb5_expand_default_cc_name(context, 849 KRB5_DEFAULT_CCNAME_FILE, 850 str); 851 } 852 853 /** 854 * Variable containing the FILE based credential cache implemention. 855 * 856 * @ingroup krb5_ccache 857 */ 858 859 const krb5_cc_ops krb5_fcc_ops = { 860 "FILE", 861 fcc_get_name, 862 fcc_resolve, 863 fcc_gen_new, 864 fcc_initialize, 865 fcc_destroy, 866 fcc_close, 867 fcc_store_cred, 868 NULL, /* fcc_retrieve */ 869 fcc_get_principal, 870 fcc_get_first, 871 fcc_get_next, 872 fcc_end_get, 873 fcc_remove_cred, 874 fcc_set_flags, 875 fcc_get_version, 876 fcc_get_cache_first, 877 fcc_get_cache_next, 878 fcc_end_cache_get, 879 fcc_move, 880 fcc_default_name 881 }; 882