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 (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 int *fd_ret, 389 int flags, 390 mode_t mode) 391{ 392 krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 393 (flags | O_RDWR) == flags); 394 krb5_error_code ret; 395 const char *filename; 396 int fd; 397 398 if (FCACHE(id) == NULL) 399 return krb5_einval(context, 2); 400 401 filename = FILENAME(id); 402 403 fd = open(filename, flags, mode); 404 if(fd < 0) { 405 char buf[128]; 406 ret = errno; 407 rk_strerror_r(ret, buf, sizeof(buf)); 408 krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"), 409 filename, buf); 410 return ret; 411 } 412 rk_cloexec(fd); 413 414 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 415 close(fd); 416 return ret; 417 } 418 *fd_ret = fd; 419 return 0; 420} 421 422static krb5_error_code KRB5_CALLCONV 423fcc_initialize(krb5_context context, 424 krb5_ccache id, 425 krb5_principal primary_principal) 426{ 427 krb5_fcache *f = FCACHE(id); 428 int ret = 0; 429 int fd; 430 431 if (f == NULL) 432 return krb5_einval(context, 2); 433 434 unlink (f->filename); 435 436 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 437 if(ret) 438 return ret; 439 { 440 krb5_storage *sp; 441 sp = krb5_storage_emem(); 442 krb5_storage_set_eof_code(sp, KRB5_CC_END); 443 if(context->fcache_vno != 0) 444 f->version = context->fcache_vno; 445 else 446 f->version = KRB5_FCC_FVNO_4; 447 ret |= krb5_store_int8(sp, 5); 448 ret |= krb5_store_int8(sp, f->version); 449 storage_set_flags(context, sp, f->version); 450 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 451 /* V4 stuff */ 452 if (context->kdc_sec_offset) { 453 ret |= krb5_store_int16 (sp, 12); /* length */ 454 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 455 ret |= krb5_store_int16 (sp, 8); /* length of data */ 456 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 457 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 458 } else { 459 ret |= krb5_store_int16 (sp, 0); 460 } 461 } 462 ret |= krb5_store_principal(sp, primary_principal); 463 464 ret |= write_storage(context, sp, fd); 465 466 krb5_storage_free(sp); 467 } 468 fcc_unlock(context, fd); 469 if (close(fd) < 0) 470 if (ret == 0) { 471 char buf[128]; 472 ret = errno; 473 rk_strerror_r(ret, buf, sizeof(buf)); 474 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 475 FILENAME(id), buf); 476 } 477 return ret; 478} 479 480static krb5_error_code KRB5_CALLCONV 481fcc_close(krb5_context context, 482 krb5_ccache id) 483{ 484 if (FCACHE(id) == NULL) 485 return krb5_einval(context, 2); 486 487 free (FILENAME(id)); 488 krb5_data_free(&id->data); 489 return 0; 490} 491 492static krb5_error_code KRB5_CALLCONV 493fcc_destroy(krb5_context context, 494 krb5_ccache id) 495{ 496 if (FCACHE(id) == NULL) 497 return krb5_einval(context, 2); 498 499 _krb5_erase_file(context, FILENAME(id)); 500 return 0; 501} 502 503static krb5_error_code KRB5_CALLCONV 504fcc_store_cred(krb5_context context, 505 krb5_ccache id, 506 krb5_creds *creds) 507{ 508 int ret; 509 int fd; 510 511 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0); 512 if(ret) 513 return ret; 514 { 515 krb5_storage *sp; 516 517 sp = krb5_storage_emem(); 518 krb5_storage_set_eof_code(sp, KRB5_CC_END); 519 storage_set_flags(context, sp, FCACHE(id)->version); 520 if (!krb5_config_get_bool_default(context, NULL, TRUE, 521 "libdefaults", 522 "fcc-mit-ticketflags", 523 NULL)) 524 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 525 ret = krb5_store_creds(sp, creds); 526 if (ret == 0) 527 ret = write_storage(context, sp, fd); 528 krb5_storage_free(sp); 529 } 530 fcc_unlock(context, fd); 531 if (close(fd) < 0) { 532 if (ret == 0) { 533 char buf[128]; 534 rk_strerror_r(ret, buf, sizeof(buf)); 535 ret = errno; 536 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 537 FILENAME(id), buf); 538 } 539 } 540 return ret; 541} 542 543static krb5_error_code 544init_fcc (krb5_context context, 545 krb5_ccache id, 546 krb5_storage **ret_sp, 547 int *ret_fd, 548 krb5_deltat *kdc_offset) 549{ 550 int fd; 551 int8_t pvno, tag; 552 krb5_storage *sp; 553 krb5_error_code ret; 554 555 if (kdc_offset) 556 *kdc_offset = 0; 557 558 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 559 if(ret) 560 return ret; 561 562 sp = krb5_storage_from_fd(fd); 563 if(sp == NULL) { 564 krb5_clear_error_message(context); 565 ret = ENOMEM; 566 goto out; 567 } 568 krb5_storage_set_eof_code(sp, KRB5_CC_END); 569 ret = krb5_ret_int8(sp, &pvno); 570 if(ret != 0) { 571 if(ret == KRB5_CC_END) { 572 ret = ENOENT; 573 krb5_set_error_message(context, ret, 574 N_("Empty credential cache file: %s", ""), 575 FILENAME(id)); 576 } else 577 krb5_set_error_message(context, ret, N_("Error reading pvno " 578 "in cache file: %s", ""), 579 FILENAME(id)); 580 goto out; 581 } 582 if(pvno != 5) { 583 ret = KRB5_CCACHE_BADVNO; 584 krb5_set_error_message(context, ret, N_("Bad version number in credential " 585 "cache file: %s", ""), 586 FILENAME(id)); 587 goto out; 588 } 589 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 590 if(ret != 0) { 591 ret = KRB5_CC_FORMAT; 592 krb5_set_error_message(context, ret, "Error reading tag in " 593 "cache file: %s", FILENAME(id)); 594 goto out; 595 } 596 FCACHE(id)->version = tag; 597 storage_set_flags(context, sp, FCACHE(id)->version); 598 switch (tag) { 599 case KRB5_FCC_FVNO_4: { 600 int16_t length; 601 602 ret = krb5_ret_int16 (sp, &length); 603 if(ret) { 604 ret = KRB5_CC_FORMAT; 605 krb5_set_error_message(context, ret, 606 N_("Error reading tag length in " 607 "cache file: %s", ""), FILENAME(id)); 608 goto out; 609 } 610 while(length > 0) { 611 int16_t dtag, data_len; 612 int i; 613 int8_t dummy; 614 615 ret = krb5_ret_int16 (sp, &dtag); 616 if(ret) { 617 ret = KRB5_CC_FORMAT; 618 krb5_set_error_message(context, ret, N_("Error reading dtag in " 619 "cache file: %s", ""), 620 FILENAME(id)); 621 goto out; 622 } 623 ret = krb5_ret_int16 (sp, &data_len); 624 if(ret) { 625 ret = KRB5_CC_FORMAT; 626 krb5_set_error_message(context, ret, 627 N_("Error reading dlength " 628 "in cache file: %s",""), 629 FILENAME(id)); 630 goto out; 631 } 632 switch (dtag) { 633 case FCC_TAG_DELTATIME : { 634 int32_t offset; 635 636 ret = krb5_ret_int32 (sp, &offset); 637 ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); 638 if(ret) { 639 ret = KRB5_CC_FORMAT; 640 krb5_set_error_message(context, ret, 641 N_("Error reading kdc_sec in " 642 "cache file: %s", ""), 643 FILENAME(id)); 644 goto out; 645 } 646 context->kdc_sec_offset = offset; 647 if (kdc_offset) 648 *kdc_offset = offset; 649 break; 650 } 651 default : 652 for (i = 0; i < data_len; ++i) { 653 ret = krb5_ret_int8 (sp, &dummy); 654 if(ret) { 655 ret = KRB5_CC_FORMAT; 656 krb5_set_error_message(context, ret, 657 N_("Error reading unknown " 658 "tag in cache file: %s", ""), 659 FILENAME(id)); 660 goto out; 661 } 662 } 663 break; 664 } 665 length -= 4 + data_len; 666 } 667 break; 668 } 669 case KRB5_FCC_FVNO_3: 670 case KRB5_FCC_FVNO_2: 671 case KRB5_FCC_FVNO_1: 672 break; 673 default : 674 ret = KRB5_CCACHE_BADVNO; 675 krb5_set_error_message(context, ret, 676 N_("Unknown version number (%d) in " 677 "credential cache file: %s", ""), 678 (int)tag, FILENAME(id)); 679 goto out; 680 } 681 *ret_sp = sp; 682 *ret_fd = fd; 683 684 return 0; 685 out: 686 if(sp != NULL) 687 krb5_storage_free(sp); 688 fcc_unlock(context, fd); 689 close(fd); 690 return ret; 691} 692 693static krb5_error_code KRB5_CALLCONV 694fcc_get_principal(krb5_context context, 695 krb5_ccache id, 696 krb5_principal *principal) 697{ 698 krb5_error_code ret; 699 int fd; 700 krb5_storage *sp; 701 702 ret = init_fcc (context, id, &sp, &fd, NULL); 703 if (ret) 704 return ret; 705 ret = krb5_ret_principal(sp, principal); 706 if (ret) 707 krb5_clear_error_message(context); 708 krb5_storage_free(sp); 709 fcc_unlock(context, fd); 710 close(fd); 711 return ret; 712} 713 714static krb5_error_code KRB5_CALLCONV 715fcc_end_get (krb5_context context, 716 krb5_ccache id, 717 krb5_cc_cursor *cursor); 718 719static krb5_error_code KRB5_CALLCONV 720fcc_get_first (krb5_context context, 721 krb5_ccache id, 722 krb5_cc_cursor *cursor) 723{ 724 krb5_error_code ret; 725 krb5_principal principal; 726 727 if (FCACHE(id) == NULL) 728 return krb5_einval(context, 2); 729 730 *cursor = malloc(sizeof(struct fcc_cursor)); 731 if (*cursor == NULL) { 732 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 733 return ENOMEM; 734 } 735 memset(*cursor, 0, sizeof(struct fcc_cursor)); 736 737 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 738 &FCC_CURSOR(*cursor)->fd, NULL); 739 if (ret) { 740 free(*cursor); 741 *cursor = NULL; 742 return ret; 743 } 744 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 745 if(ret) { 746 krb5_clear_error_message(context); 747 fcc_end_get(context, id, cursor); 748 return ret; 749 } 750 krb5_free_principal (context, principal); 751 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 752 return 0; 753} 754 755static krb5_error_code KRB5_CALLCONV 756fcc_get_next (krb5_context context, 757 krb5_ccache id, 758 krb5_cc_cursor *cursor, 759 krb5_creds *creds) 760{ 761 krb5_error_code ret; 762 763 if (FCACHE(id) == NULL) 764 return krb5_einval(context, 2); 765 766 if (FCC_CURSOR(*cursor) == NULL) 767 return krb5_einval(context, 3); 768 769 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 770 return ret; 771 772 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 773 if (ret) 774 krb5_clear_error_message(context); 775 776 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 777 return ret; 778} 779 780static krb5_error_code KRB5_CALLCONV 781fcc_end_get (krb5_context context, 782 krb5_ccache id, 783 krb5_cc_cursor *cursor) 784{ 785 786 if (FCACHE(id) == NULL) 787 return krb5_einval(context, 2); 788 789 if (FCC_CURSOR(*cursor) == NULL) 790 return krb5_einval(context, 3); 791 792 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 793 close (FCC_CURSOR(*cursor)->fd); 794 free(*cursor); 795 *cursor = NULL; 796 return 0; 797} 798 799static krb5_error_code KRB5_CALLCONV 800fcc_remove_cred(krb5_context context, 801 krb5_ccache id, 802 krb5_flags which, 803 krb5_creds *cred) 804{ 805 krb5_error_code ret; 806 krb5_ccache copy, newfile; 807 char *newname = NULL; 808 int fd; 809 810 if (FCACHE(id) == NULL) 811 return krb5_einval(context, 2); 812 813 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, ©); 814 if (ret) 815 return ret; 816 817 ret = krb5_cc_copy_cache(context, id, copy); 818 if (ret) { 819 krb5_cc_destroy(context, copy); 820 return ret; 821 } 822 823 ret = krb5_cc_remove_cred(context, copy, which, cred); 824 if (ret) { 825 krb5_cc_destroy(context, copy); 826 return ret; 827 } 828 829 ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id)); 830 if (ret < 0 || newname == NULL) { 831 krb5_cc_destroy(context, copy); 832 return ENOMEM; 833 } 834 835 fd = mkstemp(&newname[5]); 836 if (fd < 0) { 837 ret = errno; 838 krb5_cc_destroy(context, copy); 839 return ret; 840 } 841 close(fd); 842 843 ret = krb5_cc_resolve(context, newname, &newfile); 844 if (ret) { 845 unlink(&newname[5]); 846 free(newname); 847 krb5_cc_destroy(context, copy); 848 return ret; 849 } 850 851 ret = krb5_cc_copy_cache(context, copy, newfile); 852 krb5_cc_destroy(context, copy); 853 if (ret) { 854 free(newname); 855 krb5_cc_destroy(context, newfile); 856 return ret; 857 } 858 859 ret = rk_rename(&newname[5], FILENAME(id)); 860 if (ret) 861 ret = errno; 862 free(newname); 863 krb5_cc_close(context, newfile); 864 865 return ret; 866} 867 868static krb5_error_code KRB5_CALLCONV 869fcc_set_flags(krb5_context context, 870 krb5_ccache id, 871 krb5_flags flags) 872{ 873 if (FCACHE(id) == NULL) 874 return krb5_einval(context, 2); 875 876 return 0; /* XXX */ 877} 878 879static int KRB5_CALLCONV 880fcc_get_version(krb5_context context, 881 krb5_ccache id) 882{ 883 if (FCACHE(id) == NULL) 884 return -1; 885 886 return FCACHE(id)->version; 887} 888 889struct fcache_iter { 890 int first; 891}; 892 893static krb5_error_code KRB5_CALLCONV 894fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 895{ 896 struct fcache_iter *iter; 897 898 iter = calloc(1, sizeof(*iter)); 899 if (iter == NULL) { 900 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 901 return ENOMEM; 902 } 903 iter->first = 1; 904 *cursor = iter; 905 return 0; 906} 907 908static krb5_error_code KRB5_CALLCONV 909fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 910{ 911 struct fcache_iter *iter = cursor; 912 krb5_error_code ret; 913 const char *fn; 914 char *expandedfn = NULL; 915 916 if (iter == NULL) 917 return krb5_einval(context, 2); 918 919 if (!iter->first) { 920 krb5_clear_error_message(context); 921 return KRB5_CC_END; 922 } 923 iter->first = 0; 924 925 /* 926 * Can't call krb5_cc_default_name here since it refers back to 927 * krb5_cc_cache_match() which will call back into this function. 928 * 929 * Just use the default value if its set, otherwise, use the 930 * default hardcoded value. 931 */ 932 fn = context->default_cc_name; 933 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) { 934 ret = _krb5_expand_default_cc_name(context, 935 KRB5_DEFAULT_CCNAME_FILE, 936 &expandedfn); 937 if (ret) 938 return ret; 939 fn = expandedfn; 940 } 941 /* check if file exists, don't return a non existant "next" */ 942 if (strncasecmp(fn, "FILE:", 5) == 0) { 943 struct stat sb; 944 ret = stat(fn + 5, &sb); 945 if (ret) { 946 ret = KRB5_CC_END; 947 goto out; 948 } 949 } 950 ret = krb5_cc_resolve(context, fn, id); 951 out: 952 if (expandedfn) 953 free(expandedfn); 954 955 return ret; 956} 957 958static krb5_error_code KRB5_CALLCONV 959fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 960{ 961 struct fcache_iter *iter = cursor; 962 963 if (iter == NULL) 964 return krb5_einval(context, 2); 965 966 free(iter); 967 return 0; 968} 969 970static krb5_error_code KRB5_CALLCONV 971fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 972{ 973 krb5_error_code ret = 0; 974 975 ret = rk_rename(FILENAME(from), FILENAME(to)); 976 977 if (ret && errno != EXDEV) { 978 char buf[128]; 979 ret = errno; 980 rk_strerror_r(ret, buf, sizeof(buf)); 981 krb5_set_error_message(context, ret, 982 N_("Rename of file from %s " 983 "to %s failed: %s", ""), 984 FILENAME(from), FILENAME(to), buf); 985 return ret; 986 } else if (ret && errno == EXDEV) { 987 /* make a copy and delete the orignal */ 988 krb5_ssize_t sz1, sz2; 989 int fd1, fd2; 990 char buf[BUFSIZ]; 991 992 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 993 if(ret) 994 return ret; 995 996 unlink(FILENAME(to)); 997 998 ret = fcc_open(context, to, &fd2, 999 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 1000 if(ret) 1001 goto out1; 1002 1003 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 1004 sz2 = write(fd2, buf, sz1); 1005 if (sz1 != sz2) { 1006 ret = EIO; 1007 krb5_set_error_message(context, ret, 1008 N_("Failed to write data from one file " 1009 "credential cache to the other", "")); 1010 goto out2; 1011 } 1012 } 1013 if (sz1 < 0) { 1014 ret = EIO; 1015 krb5_set_error_message(context, ret, 1016 N_("Failed to read data from one file " 1017 "credential cache to the other", "")); 1018 goto out2; 1019 } 1020 out2: 1021 fcc_unlock(context, fd2); 1022 close(fd2); 1023 1024 out1: 1025 fcc_unlock(context, fd1); 1026 close(fd1); 1027 1028 _krb5_erase_file(context, FILENAME(from)); 1029 1030 if (ret) { 1031 _krb5_erase_file(context, FILENAME(to)); 1032 return ret; 1033 } 1034 } 1035 1036 /* make sure ->version is uptodate */ 1037 { 1038 krb5_storage *sp; 1039 int fd; 1040 if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) { 1041 if (sp) 1042 krb5_storage_free(sp); 1043 fcc_unlock(context, fd); 1044 close(fd); 1045 } 1046 } 1047 1048 fcc_close(context, from); 1049 1050 return ret; 1051} 1052 1053static krb5_error_code KRB5_CALLCONV 1054fcc_get_default_name(krb5_context context, char **str) 1055{ 1056 return _krb5_expand_default_cc_name(context, 1057 KRB5_DEFAULT_CCNAME_FILE, 1058 str); 1059} 1060 1061static krb5_error_code KRB5_CALLCONV 1062fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1063{ 1064 krb5_error_code ret; 1065 struct stat sb; 1066 int fd; 1067 1068 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 1069 if(ret) 1070 return ret; 1071 ret = fstat(fd, &sb); 1072 close(fd); 1073 if (ret) { 1074 ret = errno; 1075 krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); 1076 return ret; 1077 } 1078 *mtime = sb.st_mtime; 1079 return 0; 1080} 1081 1082static krb5_error_code KRB5_CALLCONV 1083fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1084{ 1085 return 0; 1086} 1087 1088static krb5_error_code KRB5_CALLCONV 1089fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1090{ 1091 krb5_error_code ret; 1092 krb5_storage *sp = NULL; 1093 int fd; 1094 ret = init_fcc(context, id, &sp, &fd, kdc_offset); 1095 if (sp) 1096 krb5_storage_free(sp); 1097 fcc_unlock(context, fd); 1098 close(fd); 1099 1100 return ret; 1101} 1102 1103 1104/** 1105 * Variable containing the FILE based credential cache implemention. 1106 * 1107 * @ingroup krb5_ccache 1108 */ 1109 1110KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { 1111 KRB5_CC_OPS_VERSION, 1112 "FILE", 1113 fcc_get_name, 1114 fcc_resolve, 1115 fcc_gen_new, 1116 fcc_initialize, 1117 fcc_destroy, 1118 fcc_close, 1119 fcc_store_cred, 1120 NULL, /* fcc_retrieve */ 1121 fcc_get_principal, 1122 fcc_get_first, 1123 fcc_get_next, 1124 fcc_end_get, 1125 fcc_remove_cred, 1126 fcc_set_flags, 1127 fcc_get_version, 1128 fcc_get_cache_first, 1129 fcc_get_cache_next, 1130 fcc_end_cache_get, 1131 fcc_move, 1132 fcc_get_default_name, 1133 NULL, 1134 fcc_lastchange, 1135 fcc_set_kdc_offset, 1136 fcc_get_kdc_offset 1137}; 1138