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 36RCSID("$Id: fcache.c 22522 2008-01-24 11:56:25Z lha $"); 37 38typedef struct krb5_fcache{ 39 char *filename; 40 int version; 41}krb5_fcache; 42 43struct 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 61static const char* 62fcc_get_name(krb5_context context, 63 krb5_ccache id) 64{ 65 return FILENAME(id); 66} 67 68int 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 107int 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 137static krb5_error_code 138fcc_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 144static krb5_error_code 145fcc_unlock(krb5_context context, int fd) 146{ 147 return _krb5_xunlock(context, fd); 148} 149 150static krb5_error_code 151fcc_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 175static int 176scrub_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 205static krb5_error_code 206erase_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 252static krb5_error_code 253fcc_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 286static void 287storage_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 311static krb5_error_code 312fcc_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 339static krb5_error_code 340fcc_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 391static krb5_error_code 392fcc_close(krb5_context context, 393 krb5_ccache id) 394{ 395 free (FILENAME(id)); 396 krb5_data_free(&id->data); 397 return 0; 398} 399 400static krb5_error_code 401fcc_destroy(krb5_context context, 402 krb5_ccache id) 403{ 404 erase_file(FILENAME(id)); 405 return 0; 406} 407 408static krb5_error_code 409fcc_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 442static krb5_error_code 443init_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 577static krb5_error_code 578fcc_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 598static krb5_error_code 599fcc_end_get (krb5_context context, 600 krb5_ccache id, 601 krb5_cc_cursor *cursor); 602 603static krb5_error_code 604fcc_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 636static krb5_error_code 637fcc_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 654static krb5_error_code 655fcc_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 666static krb5_error_code 667fcc_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 699static krb5_error_code 700fcc_set_flags(krb5_context context, 701 krb5_ccache id, 702 krb5_flags flags) 703{ 704 return 0; /* XXX */ 705} 706 707static krb5_error_code 708fcc_get_version(krb5_context context, 709 krb5_ccache id) 710{ 711 return FCACHE(id)->version; 712} 713 714struct fcache_iter { 715 int first; 716}; 717 718static krb5_error_code 719fcc_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 733static krb5_error_code 734fcc_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 762static krb5_error_code 763fcc_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 770static krb5_error_code 771fcc_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 845static krb5_error_code 846fcc_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 859const 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