1/* 2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "krb5_locl.h" 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* KRB5_CALLCONV 62fcc_get_name(krb5_context context, 63 krb5_ccache id) 64{ 65 if (FCACHE(id) == NULL) 66 return NULL; 67 68 return FILENAME(id); 69} 70 71int 72_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, 73 const char *filename) 74{ 75 int ret; 76#ifdef HAVE_FCNTL 77 struct flock l; 78 79 l.l_start = 0; 80 l.l_len = 0; 81 l.l_type = exclusive ? F_WRLCK : F_RDLCK; 82 l.l_whence = SEEK_SET; 83 ret = fcntl(fd, F_SETLKW, &l); 84#else 85 ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); 86#endif 87 if(ret < 0) 88 ret = errno; 89 if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ 90 ret = EAGAIN; 91 92 switch (ret) { 93 case 0: 94 break; 95 case EINVAL: /* filesystem doesn't support locking, let the user have it */ 96 ret = 0; 97 break; 98 case EAGAIN: 99 krb5_set_error_message(context, ret, 100 N_("timed out locking cache file %s", "file"), 101 filename); 102 break; 103 default: { 104 char buf[128]; 105 rk_strerror_r(ret, buf, sizeof(buf)); 106 krb5_set_error_message(context, ret, 107 N_("error locking cache file %s: %s", 108 "file, error"), filename, buf); 109 break; 110 } 111 } 112 return ret; 113} 114 115int 116_krb5_xunlock(krb5_context context, int fd) 117{ 118 int ret; 119#ifdef HAVE_FCNTL 120 struct flock l; 121 l.l_start = 0; 122 l.l_len = 0; 123 l.l_type = F_UNLCK; 124 l.l_whence = SEEK_SET; 125 ret = fcntl(fd, F_SETLKW, &l); 126#else 127 ret = flock(fd, LOCK_UN); 128#endif 129 if (ret < 0) 130 ret = errno; 131 switch (ret) { 132 case 0: 133 break; 134 case EINVAL: /* filesystem doesn't support locking, let the user have it */ 135 ret = 0; 136 break; 137 default: { 138 char buf[128]; 139 rk_strerror_r(ret, buf, sizeof(buf)); 140 krb5_set_error_message(context, ret, 141 N_("Failed to unlock file: %s", ""), buf); 142 break; 143 } 144 } 145 return ret; 146} 147 148static krb5_error_code 149write_storage(krb5_context context, krb5_storage *sp, int fd) 150{ 151 krb5_error_code ret; 152 krb5_data data; 153 ssize_t sret; 154 155 ret = krb5_storage_to_data(sp, &data); 156 if (ret) { 157 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 158 return ret; 159 } 160 sret = write(fd, data.data, data.length); 161 ret = (sret != (ssize_t)data.length); 162 krb5_data_free(&data); 163 if (ret) { 164 ret = errno; 165 krb5_set_error_message(context, ret, 166 N_("Failed to write FILE credential data", "")); 167 return ret; 168 } 169 return 0; 170} 171 172 173static krb5_error_code KRB5_CALLCONV 174fcc_lock(krb5_context context, krb5_ccache id, 175 int fd, krb5_boolean exclusive) 176{ 177 return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id)); 178} 179 180static krb5_error_code KRB5_CALLCONV 181fcc_unlock(krb5_context context, int fd) 182{ 183 return _krb5_xunlock(context, fd); 184} 185 186static krb5_error_code KRB5_CALLCONV 187fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 188{ 189 krb5_fcache *f; 190 f = malloc(sizeof(*f)); 191 if(f == NULL) { 192 krb5_set_error_message(context, KRB5_CC_NOMEM, 193 N_("malloc: out of memory", "")); 194 return KRB5_CC_NOMEM; 195 } 196 f->filename = strdup(res); 197 if(f->filename == NULL){ 198 free(f); 199 krb5_set_error_message(context, KRB5_CC_NOMEM, 200 N_("malloc: out of memory", "")); 201 return KRB5_CC_NOMEM; 202 } 203 f->version = 0; 204 (*id)->data.data = f; 205 (*id)->data.length = sizeof(*f); 206 return 0; 207} 208 209/* 210 * Try to scrub the contents of `filename' safely. 211 */ 212 213static int 214scrub_file (int fd) 215{ 216 off_t pos; 217 char buf[128]; 218 219 pos = lseek(fd, 0, SEEK_END); 220 if (pos < 0) 221 return errno; 222 if (lseek(fd, 0, SEEK_SET) < 0) 223 return errno; 224 memset(buf, 0, sizeof(buf)); 225 while(pos > 0) { 226 ssize_t tmp; 227 size_t wr = sizeof(buf); 228 if ((off_t)wr > pos) 229 wr = (size_t)pos; 230 tmp = write(fd, buf, wr); 231 232 if (tmp < 0) 233 return errno; 234 pos -= tmp; 235 } 236#ifdef _MSC_VER 237 _commit (fd); 238#else 239 fsync (fd); 240#endif 241 return 0; 242} 243 244/* 245 * Erase `filename' if it exists, trying to remove the contents if 246 * it's `safe'. We always try to remove the file, it it exists. It's 247 * only overwritten if it's a regular file (not a symlink and not a 248 * hardlink) 249 */ 250 251krb5_error_code 252_krb5_erase_file(krb5_context context, const char *filename) 253{ 254 int fd; 255 struct stat sb1, sb2; 256 int ret; 257 258 ret = lstat (filename, &sb1); 259 if (ret < 0) 260 return errno; 261 262 fd = open(filename, O_RDWR | O_BINARY); 263 if(fd < 0) { 264 if(errno == ENOENT) 265 return 0; 266 else 267 return errno; 268 } 269 rk_cloexec(fd); 270 ret = _krb5_xlock(context, fd, 1, filename); 271 if (ret) { 272 close(fd); 273 return ret; 274 } 275 if (unlink(filename) < 0) { 276 _krb5_xunlock(context, fd); 277 close (fd); 278 return errno; 279 } 280 ret = fstat (fd, &sb2); 281 if (ret < 0) { 282 _krb5_xunlock(context, fd); 283 close (fd); 284 return errno; 285 } 286 287 /* check if someone was playing with symlinks */ 288 289 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 290 _krb5_xunlock(context, fd); 291 close (fd); 292 return EPERM; 293 } 294 295 /* there are still hard links to this file */ 296 297 if (sb2.st_nlink != 0) { 298 _krb5_xunlock(context, fd); 299 close (fd); 300 return 0; 301 } 302 303 ret = scrub_file (fd); 304 if (ret) { 305 _krb5_xunlock(context, fd); 306 close(fd); 307 return ret; 308 } 309 ret = _krb5_xunlock(context, fd); 310 close (fd); 311 return ret; 312} 313 314static krb5_error_code KRB5_CALLCONV 315fcc_gen_new(krb5_context context, krb5_ccache *id) 316{ 317 char *file = NULL, *exp_file = NULL; 318 krb5_error_code ret; 319 krb5_fcache *f; 320 int fd; 321 322 f = malloc(sizeof(*f)); 323 if(f == NULL) { 324 krb5_set_error_message(context, KRB5_CC_NOMEM, 325 N_("malloc: out of memory", "")); 326 return KRB5_CC_NOMEM; 327 } 328 ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 329 if(ret < 0 || file == NULL) { 330 free(f); 331 krb5_set_error_message(context, KRB5_CC_NOMEM, 332 N_("malloc: out of memory", "")); 333 return KRB5_CC_NOMEM; 334 } 335 ret = _krb5_expand_path_tokens(context, file, &exp_file); 336 free(file); 337 if (ret) { 338 free(f); 339 return ret; 340 } 341 342 file = exp_file; 343 344 fd = mkstemp(exp_file); 345 if(fd < 0) { 346 ret = (krb5_error_code)errno; 347 krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), exp_file); 348 free(f); 349 free(exp_file); 350 return ret; 351 } 352 close(fd); 353 f->filename = exp_file; 354 f->version = 0; 355 (*id)->data.data = f; 356 (*id)->data.length = sizeof(*f); 357 return 0; 358} 359 360static void 361storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 362{ 363 int flags = 0; 364 switch(vno) { 365 case KRB5_FCC_FVNO_1: 366 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 367 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 368 flags |= KRB5_STORAGE_HOST_BYTEORDER; 369 break; 370 case KRB5_FCC_FVNO_2: 371 flags |= KRB5_STORAGE_HOST_BYTEORDER; 372 break; 373 case KRB5_FCC_FVNO_3: 374 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 375 break; 376 case KRB5_FCC_FVNO_4: 377 break; 378 default: 379 krb5_abortx(context, 380 "storage_set_flags called with bad vno (%x)", vno); 381 } 382 krb5_storage_set_flags(sp, flags); 383} 384 385static krb5_error_code KRB5_CALLCONV 386fcc_open(krb5_context context, 387 krb5_ccache id, 388 const char *operation, 389 int *fd_ret, 390 int flags, 391 mode_t mode) 392{ 393 krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 394 (flags | O_RDWR) == flags); 395 krb5_error_code ret; 396 const char *filename; 397 int fd; 398 399 if (FCACHE(id) == NULL) 400 return krb5_einval(context, 2); 401 402 filename = FILENAME(id); 403 404 fd = open(filename, flags, mode); 405 if(fd < 0) { 406 char buf[128]; 407 ret = errno; 408 rk_strerror_r(ret, buf, sizeof(buf)); 409 krb5_set_error_message(context, ret, N_("%s open(%s): %s", "file, error"), 410 operation, filename, buf); 411 return ret; 412 } 413 rk_cloexec(fd); 414 415 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 416 close(fd); 417 return ret; 418 } 419 *fd_ret = fd; 420 return 0; 421} 422 423static krb5_error_code KRB5_CALLCONV 424fcc_initialize(krb5_context context, 425 krb5_ccache id, 426 krb5_principal primary_principal) 427{ 428 krb5_fcache *f = FCACHE(id); 429 int ret = 0; 430 int fd; 431 432 if (f == NULL) 433 return krb5_einval(context, 2); 434 435 unlink (f->filename); 436 437 ret = fcc_open(context, id, "initialize", &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 438 if(ret) 439 return ret; 440 { 441 krb5_storage *sp; 442 sp = krb5_storage_emem(); 443 krb5_storage_set_eof_code(sp, KRB5_CC_END); 444 if(context->fcache_vno != 0) 445 f->version = context->fcache_vno; 446 else 447 f->version = KRB5_FCC_FVNO_4; 448 ret |= krb5_store_int8(sp, 5); 449 ret |= krb5_store_int8(sp, f->version); 450 storage_set_flags(context, sp, f->version); 451 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 452 /* V4 stuff */ 453 if (context->kdc_sec_offset) { 454 ret |= krb5_store_int16 (sp, 12); /* length */ 455 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 456 ret |= krb5_store_int16 (sp, 8); /* length of data */ 457 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 458 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 459 } else { 460 ret |= krb5_store_int16 (sp, 0); 461 } 462 } 463 ret |= krb5_store_principal(sp, primary_principal); 464 465 ret |= write_storage(context, sp, fd); 466 467 krb5_storage_free(sp); 468 } 469 fcc_unlock(context, fd); 470 if (close(fd) < 0) 471 if (ret == 0) { 472 char buf[128]; 473 ret = errno; 474 rk_strerror_r(ret, buf, sizeof(buf)); 475 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 476 FILENAME(id), buf); 477 } 478 return ret; 479} 480 481static krb5_error_code KRB5_CALLCONV 482fcc_close(krb5_context context, 483 krb5_ccache id) 484{ 485 if (FCACHE(id) == NULL) 486 return krb5_einval(context, 2); 487 488 free (FILENAME(id)); 489 krb5_data_free(&id->data); 490 return 0; 491} 492 493static krb5_error_code KRB5_CALLCONV 494fcc_destroy(krb5_context context, 495 krb5_ccache id) 496{ 497 if (FCACHE(id) == NULL) 498 return krb5_einval(context, 2); 499 500 _krb5_erase_file(context, FILENAME(id)); 501 return 0; 502} 503 504static krb5_error_code KRB5_CALLCONV 505fcc_store_cred(krb5_context context, 506 krb5_ccache id, 507 krb5_creds *creds) 508{ 509 int ret; 510 int fd; 511 512 ret = fcc_open(context, id, "store", &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0); 513 if(ret) 514 return ret; 515 { 516 krb5_storage *sp; 517 518 sp = krb5_storage_emem(); 519 krb5_storage_set_eof_code(sp, KRB5_CC_END); 520 storage_set_flags(context, sp, FCACHE(id)->version); 521 if (!krb5_config_get_bool_default(context, NULL, TRUE, 522 "libdefaults", 523 "fcc-mit-ticketflags", 524 NULL)) 525 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 526 ret = krb5_store_creds(sp, creds); 527 if (ret == 0) 528 ret = write_storage(context, sp, fd); 529 krb5_storage_free(sp); 530 } 531 fcc_unlock(context, fd); 532 if (close(fd) < 0) { 533 if (ret == 0) { 534 char buf[128]; 535 rk_strerror_r(ret, buf, sizeof(buf)); 536 ret = errno; 537 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 538 FILENAME(id), buf); 539 } 540 } 541 return ret; 542} 543 544static krb5_error_code 545init_fcc(krb5_context context, 546 krb5_ccache id, 547 const char *operation, 548 krb5_storage **ret_sp, 549 int *ret_fd, 550 krb5_deltat *kdc_offset) 551{ 552 int fd; 553 int8_t pvno, tag; 554 krb5_storage *sp; 555 krb5_error_code ret; 556 557 if (kdc_offset) 558 *kdc_offset = 0; 559 560 ret = fcc_open(context, id, operation, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 561 if(ret) 562 return ret; 563 564 sp = krb5_storage_from_fd(fd); 565 if(sp == NULL) { 566 krb5_clear_error_message(context); 567 ret = ENOMEM; 568 goto out; 569 } 570 krb5_storage_set_eof_code(sp, KRB5_CC_END); 571 ret = krb5_ret_int8(sp, &pvno); 572 if(ret != 0) { 573 if(ret == KRB5_CC_END) { 574 ret = ENOENT; 575 krb5_set_error_message(context, ret, 576 N_("Empty credential cache file: %s", ""), 577 FILENAME(id)); 578 } else 579 krb5_set_error_message(context, ret, N_("Error reading pvno " 580 "in cache file: %s", ""), 581 FILENAME(id)); 582 goto out; 583 } 584 if(pvno != 5) { 585 ret = KRB5_CCACHE_BADVNO; 586 krb5_set_error_message(context, ret, N_("Bad version number in credential " 587 "cache file: %s", ""), 588 FILENAME(id)); 589 goto out; 590 } 591 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 592 if(ret != 0) { 593 ret = KRB5_CC_FORMAT; 594 krb5_set_error_message(context, ret, "Error reading tag in " 595 "cache file: %s", FILENAME(id)); 596 goto out; 597 } 598 FCACHE(id)->version = tag; 599 storage_set_flags(context, sp, FCACHE(id)->version); 600 switch (tag) { 601 case KRB5_FCC_FVNO_4: { 602 int16_t length; 603 604 ret = krb5_ret_int16 (sp, &length); 605 if(ret) { 606 ret = KRB5_CC_FORMAT; 607 krb5_set_error_message(context, ret, 608 N_("Error reading tag length in " 609 "cache file: %s", ""), FILENAME(id)); 610 goto out; 611 } 612 while(length > 0) { 613 int16_t dtag, data_len; 614 int i; 615 int8_t dummy; 616 617 ret = krb5_ret_int16 (sp, &dtag); 618 if(ret) { 619 ret = KRB5_CC_FORMAT; 620 krb5_set_error_message(context, ret, N_("Error reading dtag in " 621 "cache file: %s", ""), 622 FILENAME(id)); 623 goto out; 624 } 625 ret = krb5_ret_int16 (sp, &data_len); 626 if(ret) { 627 ret = KRB5_CC_FORMAT; 628 krb5_set_error_message(context, ret, 629 N_("Error reading dlength " 630 "in cache file: %s",""), 631 FILENAME(id)); 632 goto out; 633 } 634 switch (dtag) { 635 case FCC_TAG_DELTATIME : { 636 int32_t offset; 637 638 ret = krb5_ret_int32 (sp, &offset); 639 ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); 640 if(ret) { 641 ret = KRB5_CC_FORMAT; 642 krb5_set_error_message(context, ret, 643 N_("Error reading kdc_sec in " 644 "cache file: %s", ""), 645 FILENAME(id)); 646 goto out; 647 } 648 context->kdc_sec_offset = offset; 649 if (kdc_offset) 650 *kdc_offset = offset; 651 break; 652 } 653 default : 654 for (i = 0; i < data_len; ++i) { 655 ret = krb5_ret_int8 (sp, &dummy); 656 if(ret) { 657 ret = KRB5_CC_FORMAT; 658 krb5_set_error_message(context, ret, 659 N_("Error reading unknown " 660 "tag in cache file: %s", ""), 661 FILENAME(id)); 662 goto out; 663 } 664 } 665 break; 666 } 667 length -= 4 + data_len; 668 } 669 break; 670 } 671 case KRB5_FCC_FVNO_3: 672 case KRB5_FCC_FVNO_2: 673 case KRB5_FCC_FVNO_1: 674 break; 675 default : 676 ret = KRB5_CCACHE_BADVNO; 677 krb5_set_error_message(context, ret, 678 N_("Unknown version number (%d) in " 679 "credential cache file: %s", ""), 680 (int)tag, FILENAME(id)); 681 goto out; 682 } 683 *ret_sp = sp; 684 *ret_fd = fd; 685 686 return 0; 687 out: 688 if(sp != NULL) 689 krb5_storage_free(sp); 690 fcc_unlock(context, fd); 691 close(fd); 692 return ret; 693} 694 695static krb5_error_code KRB5_CALLCONV 696fcc_get_principal(krb5_context context, 697 krb5_ccache id, 698 krb5_principal *principal) 699{ 700 krb5_error_code ret; 701 int fd; 702 krb5_storage *sp; 703 704 ret = init_fcc (context, id, "get-pricipal", &sp, &fd, NULL); 705 if (ret) 706 return ret; 707 ret = krb5_ret_principal(sp, principal); 708 if (ret) 709 krb5_clear_error_message(context); 710 krb5_storage_free(sp); 711 fcc_unlock(context, fd); 712 close(fd); 713 return ret; 714} 715 716static krb5_error_code KRB5_CALLCONV 717fcc_end_get (krb5_context context, 718 krb5_ccache id, 719 krb5_cc_cursor *cursor); 720 721static krb5_error_code KRB5_CALLCONV 722fcc_get_first (krb5_context context, 723 krb5_ccache id, 724 krb5_cc_cursor *cursor) 725{ 726 krb5_error_code ret; 727 krb5_principal principal; 728 729 if (FCACHE(id) == NULL) 730 return krb5_einval(context, 2); 731 732 *cursor = malloc(sizeof(struct fcc_cursor)); 733 if (*cursor == NULL) { 734 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 735 return ENOMEM; 736 } 737 memset(*cursor, 0, sizeof(struct fcc_cursor)); 738 739 ret = init_fcc(context, id, "get-frist", &FCC_CURSOR(*cursor)->sp, 740 &FCC_CURSOR(*cursor)->fd, NULL); 741 if (ret) { 742 free(*cursor); 743 *cursor = NULL; 744 return ret; 745 } 746 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 747 if(ret) { 748 krb5_clear_error_message(context); 749 fcc_end_get(context, id, cursor); 750 return ret; 751 } 752 krb5_free_principal (context, principal); 753 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 754 return 0; 755} 756 757static krb5_error_code KRB5_CALLCONV 758fcc_get_next (krb5_context context, 759 krb5_ccache id, 760 krb5_cc_cursor *cursor, 761 krb5_creds *creds) 762{ 763 krb5_error_code ret; 764 765 if (FCACHE(id) == NULL) 766 return krb5_einval(context, 2); 767 768 if (FCC_CURSOR(*cursor) == NULL) 769 return krb5_einval(context, 3); 770 771 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 772 return ret; 773 774 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 775 if (ret) 776 krb5_clear_error_message(context); 777 778 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 779 return ret; 780} 781 782static krb5_error_code KRB5_CALLCONV 783fcc_end_get (krb5_context context, 784 krb5_ccache id, 785 krb5_cc_cursor *cursor) 786{ 787 788 if (FCACHE(id) == NULL) 789 return krb5_einval(context, 2); 790 791 if (FCC_CURSOR(*cursor) == NULL) 792 return krb5_einval(context, 3); 793 794 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 795 close (FCC_CURSOR(*cursor)->fd); 796 free(*cursor); 797 *cursor = NULL; 798 return 0; 799} 800 801static krb5_error_code KRB5_CALLCONV 802fcc_remove_cred(krb5_context context, 803 krb5_ccache id, 804 krb5_flags which, 805 krb5_creds *cred) 806{ 807 krb5_error_code ret; 808 krb5_ccache copy, newfile; 809 char *newname = NULL; 810 int fd; 811 812 if (FCACHE(id) == NULL) 813 return krb5_einval(context, 2); 814 815 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, ©); 816 if (ret) 817 return ret; 818 819 ret = krb5_cc_copy_cache(context, id, copy); 820 if (ret) { 821 krb5_cc_destroy(context, copy); 822 return ret; 823 } 824 825 ret = krb5_cc_remove_cred(context, copy, which, cred); 826 if (ret) { 827 krb5_cc_destroy(context, copy); 828 return ret; 829 } 830 831 ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id)); 832 if (ret < 0 || newname == NULL) { 833 krb5_cc_destroy(context, copy); 834 return ENOMEM; 835 } 836 837 fd = mkstemp(&newname[5]); 838 if (fd < 0) { 839 ret = errno; 840 krb5_cc_destroy(context, copy); 841 return ret; 842 } 843 close(fd); 844 845 ret = krb5_cc_resolve(context, newname, &newfile); 846 if (ret) { 847 unlink(&newname[5]); 848 free(newname); 849 krb5_cc_destroy(context, copy); 850 return ret; 851 } 852 853 ret = krb5_cc_copy_cache(context, copy, newfile); 854 krb5_cc_destroy(context, copy); 855 if (ret) { 856 free(newname); 857 krb5_cc_destroy(context, newfile); 858 return ret; 859 } 860 861 ret = rk_rename(&newname[5], FILENAME(id)); 862 if (ret) 863 ret = errno; 864 free(newname); 865 krb5_cc_close(context, newfile); 866 867 return ret; 868} 869 870static krb5_error_code KRB5_CALLCONV 871fcc_set_flags(krb5_context context, 872 krb5_ccache id, 873 krb5_flags flags) 874{ 875 if (FCACHE(id) == NULL) 876 return krb5_einval(context, 2); 877 878 return 0; /* XXX */ 879} 880 881static int KRB5_CALLCONV 882fcc_get_version(krb5_context context, 883 krb5_ccache id) 884{ 885 if (FCACHE(id) == NULL) 886 return -1; 887 888 return FCACHE(id)->version; 889} 890 891struct fcache_iter { 892 int first; 893}; 894 895static krb5_error_code KRB5_CALLCONV 896fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 897{ 898 struct fcache_iter *iter; 899 900 iter = calloc(1, sizeof(*iter)); 901 if (iter == NULL) { 902 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 903 return ENOMEM; 904 } 905 iter->first = 1; 906 *cursor = iter; 907 return 0; 908} 909 910static krb5_error_code KRB5_CALLCONV 911fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 912{ 913 struct fcache_iter *iter = cursor; 914 krb5_error_code ret; 915 const char *fn; 916 char *expandedfn = NULL; 917 918 if (iter == NULL) 919 return krb5_einval(context, 2); 920 921 if (!iter->first) { 922 krb5_clear_error_message(context); 923 return KRB5_CC_END; 924 } 925 iter->first = 0; 926 927 /* 928 * Can't call krb5_cc_default_name here since it refers back to 929 * krb5_cc_cache_match() which will call back into this function. 930 * 931 * Just use the default value if its set, otherwise, use the 932 * default hardcoded value. 933 */ 934 fn = context->default_cc_name; 935 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) { 936 ret = _krb5_expand_default_cc_name(context, 937 KRB5_DEFAULT_CCNAME_FILE, 938 &expandedfn); 939 if (ret) 940 return ret; 941 fn = expandedfn; 942 } 943 /* check if file exists, don't return a non existant "next" */ 944 if (strncasecmp(fn, "FILE:", 5) == 0) { 945 struct stat sb; 946 ret = stat(fn + 5, &sb); 947 if (ret) { 948 ret = KRB5_CC_END; 949 goto out; 950 } 951 } 952 ret = krb5_cc_resolve(context, fn, id); 953 out: 954 if (expandedfn) 955 free(expandedfn); 956 957 return ret; 958} 959 960static krb5_error_code KRB5_CALLCONV 961fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 962{ 963 struct fcache_iter *iter = cursor; 964 965 if (iter == NULL) 966 return krb5_einval(context, 2); 967 968 free(iter); 969 return 0; 970} 971 972static krb5_error_code KRB5_CALLCONV 973fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 974{ 975 krb5_error_code ret = 0; 976 977 ret = rk_rename(FILENAME(from), FILENAME(to)); 978 979 if (ret && errno != EXDEV) { 980 char buf[128]; 981 ret = errno; 982 rk_strerror_r(ret, buf, sizeof(buf)); 983 krb5_set_error_message(context, ret, 984 N_("Rename of file from %s " 985 "to %s failed: %s", ""), 986 FILENAME(from), FILENAME(to), buf); 987 return ret; 988 } else if (ret && errno == EXDEV) { 989 /* make a copy and delete the orignal */ 990 krb5_ssize_t sz1, sz2; 991 int fd1, fd2; 992 char buf[BUFSIZ]; 993 994 ret = fcc_open(context, from, "move/from", &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 995 if(ret) 996 return ret; 997 998 unlink(FILENAME(to)); 999 1000 ret = fcc_open(context, to, "move/to", &fd2, 1001 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 1002 if(ret) 1003 goto out1; 1004 1005 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 1006 sz2 = write(fd2, buf, sz1); 1007 if (sz1 != sz2) { 1008 ret = EIO; 1009 krb5_set_error_message(context, ret, 1010 N_("Failed to write data from one file " 1011 "credential cache to the other", "")); 1012 goto out2; 1013 } 1014 } 1015 if (sz1 < 0) { 1016 ret = EIO; 1017 krb5_set_error_message(context, ret, 1018 N_("Failed to read data from one file " 1019 "credential cache to the other", "")); 1020 goto out2; 1021 } 1022 out2: 1023 fcc_unlock(context, fd2); 1024 close(fd2); 1025 1026 out1: 1027 fcc_unlock(context, fd1); 1028 close(fd1); 1029 1030 _krb5_erase_file(context, FILENAME(from)); 1031 1032 if (ret) { 1033 _krb5_erase_file(context, FILENAME(to)); 1034 return ret; 1035 } 1036 } 1037 1038 /* make sure ->version is uptodate */ 1039 { 1040 krb5_storage *sp; 1041 int fd; 1042 if ((ret = init_fcc (context, to, "move", &sp, &fd, NULL)) == 0) { 1043 if (sp) 1044 krb5_storage_free(sp); 1045 fcc_unlock(context, fd); 1046 close(fd); 1047 } 1048 } 1049 1050 fcc_close(context, from); 1051 1052 return ret; 1053} 1054 1055static krb5_error_code KRB5_CALLCONV 1056fcc_get_default_name(krb5_context context, char **str) 1057{ 1058 return _krb5_expand_default_cc_name(context, 1059 KRB5_DEFAULT_CCNAME_FILE, 1060 str); 1061} 1062 1063static krb5_error_code KRB5_CALLCONV 1064fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1065{ 1066 krb5_error_code ret; 1067 struct stat sb; 1068 int fd; 1069 1070 ret = fcc_open(context, id, "lastchange", &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 1071 if(ret) 1072 return ret; 1073 ret = fstat(fd, &sb); 1074 close(fd); 1075 if (ret) { 1076 ret = errno; 1077 krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); 1078 return ret; 1079 } 1080 *mtime = sb.st_mtime; 1081 return 0; 1082} 1083 1084static krb5_error_code KRB5_CALLCONV 1085fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1086{ 1087 return 0; 1088} 1089 1090static krb5_error_code KRB5_CALLCONV 1091fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1092{ 1093 krb5_error_code ret; 1094 krb5_storage *sp = NULL; 1095 int fd; 1096 ret = init_fcc(context, id, "get-kdc-offset", &sp, &fd, kdc_offset); 1097 if (sp) 1098 krb5_storage_free(sp); 1099 fcc_unlock(context, fd); 1100 close(fd); 1101 1102 return ret; 1103} 1104 1105 1106/** 1107 * Variable containing the FILE based credential cache implemention. 1108 * 1109 * @ingroup krb5_ccache 1110 */ 1111 1112KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { 1113 KRB5_CC_OPS_VERSION, 1114 "FILE", 1115 fcc_get_name, 1116 fcc_resolve, 1117 fcc_gen_new, 1118 fcc_initialize, 1119 fcc_destroy, 1120 fcc_close, 1121 fcc_store_cred, 1122 NULL, /* fcc_retrieve */ 1123 fcc_get_principal, 1124 fcc_get_first, 1125 fcc_get_next, 1126 fcc_end_get, 1127 fcc_remove_cred, 1128 fcc_set_flags, 1129 fcc_get_version, 1130 fcc_get_cache_first, 1131 fcc_get_cache_next, 1132 fcc_end_cache_get, 1133 fcc_move, 1134 fcc_get_default_name, 1135 NULL, 1136 fcc_lastchange, 1137 fcc_set_kdc_offset, 1138 fcc_get_kdc_offset 1139}; 1140