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 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* KRB5_CALLCONV 62 fcc_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 71 int 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 115 int 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 148 static krb5_error_code 149 write_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 173 static krb5_error_code KRB5_CALLCONV 174 fcc_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 180 static krb5_error_code KRB5_CALLCONV 181 fcc_unlock(krb5_context context, int fd) 182 { 183 return _krb5_xunlock(context, fd); 184 } 185 186 static krb5_error_code KRB5_CALLCONV 187 fcc_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 213 static int 214 scrub_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 = write(fd, buf, min((off_t)sizeof(buf), pos)); 227 228 if (tmp < 0) 229 return errno; 230 pos -= tmp; 231 } 232 #ifdef _MSC_VER 233 _commit (fd); 234 #else 235 fsync (fd); 236 #endif 237 return 0; 238 } 239 240 /* 241 * Erase `filename' if it exists, trying to remove the contents if 242 * it's `safe'. We always try to remove the file, it it exists. It's 243 * only overwritten if it's a regular file (not a symlink and not a 244 * hardlink) 245 */ 246 247 krb5_error_code 248 _krb5_erase_file(krb5_context context, const char *filename) 249 { 250 int fd; 251 struct stat sb1, sb2; 252 int ret; 253 254 ret = lstat (filename, &sb1); 255 if (ret < 0) 256 return errno; 257 258 fd = open(filename, O_RDWR | O_BINARY); 259 if(fd < 0) { 260 if(errno == ENOENT) 261 return 0; 262 else 263 return errno; 264 } 265 rk_cloexec(fd); 266 ret = _krb5_xlock(context, fd, 1, filename); 267 if (ret) { 268 close(fd); 269 return ret; 270 } 271 if (unlink(filename) < 0) { 272 _krb5_xunlock(context, fd); 273 close (fd); 274 return errno; 275 } 276 ret = fstat (fd, &sb2); 277 if (ret < 0) { 278 _krb5_xunlock(context, fd); 279 close (fd); 280 return errno; 281 } 282 283 /* check if someone was playing with symlinks */ 284 285 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 286 _krb5_xunlock(context, fd); 287 close (fd); 288 return EPERM; 289 } 290 291 /* there are still hard links to this file */ 292 293 if (sb2.st_nlink != 0) { 294 _krb5_xunlock(context, fd); 295 close (fd); 296 return 0; 297 } 298 299 ret = scrub_file (fd); 300 if (ret) { 301 _krb5_xunlock(context, fd); 302 close(fd); 303 return ret; 304 } 305 ret = _krb5_xunlock(context, fd); 306 close (fd); 307 return ret; 308 } 309 310 static krb5_error_code KRB5_CALLCONV 311 fcc_gen_new(krb5_context context, krb5_ccache *id) 312 { 313 char *file = NULL, *exp_file = NULL; 314 krb5_error_code ret; 315 krb5_fcache *f; 316 int fd; 317 318 f = malloc(sizeof(*f)); 319 if(f == NULL) { 320 krb5_set_error_message(context, KRB5_CC_NOMEM, 321 N_("malloc: out of memory", "")); 322 return KRB5_CC_NOMEM; 323 } 324 ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 325 if(ret < 0 || file == NULL) { 326 free(f); 327 krb5_set_error_message(context, KRB5_CC_NOMEM, 328 N_("malloc: out of memory", "")); 329 return KRB5_CC_NOMEM; 330 } 331 ret = _krb5_expand_path_tokens(context, file, &exp_file); 332 free(file); 333 if (ret) 334 return ret; 335 336 file = exp_file; 337 338 fd = mkstemp(exp_file); 339 if(fd < 0) { 340 int xret = errno; 341 krb5_set_error_message(context, xret, N_("mkstemp %s failed", ""), exp_file); 342 free(f); 343 free(exp_file); 344 return xret; 345 } 346 close(fd); 347 f->filename = exp_file; 348 f->version = 0; 349 (*id)->data.data = f; 350 (*id)->data.length = sizeof(*f); 351 return 0; 352 } 353 354 static void 355 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 356 { 357 int flags = 0; 358 switch(vno) { 359 case KRB5_FCC_FVNO_1: 360 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 361 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 362 flags |= KRB5_STORAGE_HOST_BYTEORDER; 363 break; 364 case KRB5_FCC_FVNO_2: 365 flags |= KRB5_STORAGE_HOST_BYTEORDER; 366 break; 367 case KRB5_FCC_FVNO_3: 368 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 369 break; 370 case KRB5_FCC_FVNO_4: 371 break; 372 default: 373 krb5_abortx(context, 374 "storage_set_flags called with bad vno (%x)", vno); 375 } 376 krb5_storage_set_flags(sp, flags); 377 } 378 379 static krb5_error_code KRB5_CALLCONV 380 fcc_open(krb5_context context, 381 krb5_ccache id, 382 int *fd_ret, 383 int flags, 384 mode_t mode) 385 { 386 krb5_boolean exclusive = ((flags | O_WRONLY) == flags || 387 (flags | O_RDWR) == flags); 388 krb5_error_code ret; 389 const char *filename; 390 int fd; 391 392 if (FCACHE(id) == NULL) 393 return krb5_einval(context, 2); 394 395 filename = FILENAME(id); 396 397 fd = open(filename, flags, mode); 398 if(fd < 0) { 399 char buf[128]; 400 ret = errno; 401 rk_strerror_r(ret, buf, sizeof(buf)); 402 krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"), 403 filename, buf); 404 return ret; 405 } 406 rk_cloexec(fd); 407 408 if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { 409 close(fd); 410 return ret; 411 } 412 *fd_ret = fd; 413 return 0; 414 } 415 416 static krb5_error_code KRB5_CALLCONV 417 fcc_initialize(krb5_context context, 418 krb5_ccache id, 419 krb5_principal primary_principal) 420 { 421 krb5_fcache *f = FCACHE(id); 422 int ret = 0; 423 int fd; 424 425 if (f == NULL) 426 return krb5_einval(context, 2); 427 428 unlink (f->filename); 429 430 ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 431 if(ret) 432 return ret; 433 { 434 krb5_storage *sp; 435 sp = krb5_storage_emem(); 436 krb5_storage_set_eof_code(sp, KRB5_CC_END); 437 if(context->fcache_vno != 0) 438 f->version = context->fcache_vno; 439 else 440 f->version = KRB5_FCC_FVNO_4; 441 ret |= krb5_store_int8(sp, 5); 442 ret |= krb5_store_int8(sp, f->version); 443 storage_set_flags(context, sp, f->version); 444 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 445 /* V4 stuff */ 446 if (context->kdc_sec_offset) { 447 ret |= krb5_store_int16 (sp, 12); /* length */ 448 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 449 ret |= krb5_store_int16 (sp, 8); /* length of data */ 450 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 451 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 452 } else { 453 ret |= krb5_store_int16 (sp, 0); 454 } 455 } 456 ret |= krb5_store_principal(sp, primary_principal); 457 458 ret |= write_storage(context, sp, fd); 459 460 krb5_storage_free(sp); 461 } 462 fcc_unlock(context, fd); 463 if (close(fd) < 0) 464 if (ret == 0) { 465 char buf[128]; 466 ret = errno; 467 rk_strerror_r(ret, buf, sizeof(buf)); 468 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 469 FILENAME(id), buf); 470 } 471 return ret; 472 } 473 474 static krb5_error_code KRB5_CALLCONV 475 fcc_close(krb5_context context, 476 krb5_ccache id) 477 { 478 if (FCACHE(id) == NULL) 479 return krb5_einval(context, 2); 480 481 free (FILENAME(id)); 482 krb5_data_free(&id->data); 483 return 0; 484 } 485 486 static krb5_error_code KRB5_CALLCONV 487 fcc_destroy(krb5_context context, 488 krb5_ccache id) 489 { 490 if (FCACHE(id) == NULL) 491 return krb5_einval(context, 2); 492 493 _krb5_erase_file(context, FILENAME(id)); 494 return 0; 495 } 496 497 static krb5_error_code KRB5_CALLCONV 498 fcc_store_cred(krb5_context context, 499 krb5_ccache id, 500 krb5_creds *creds) 501 { 502 int ret; 503 int fd; 504 505 ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0); 506 if(ret) 507 return ret; 508 { 509 krb5_storage *sp; 510 511 sp = krb5_storage_emem(); 512 krb5_storage_set_eof_code(sp, KRB5_CC_END); 513 storage_set_flags(context, sp, FCACHE(id)->version); 514 if (!krb5_config_get_bool_default(context, NULL, TRUE, 515 "libdefaults", 516 "fcc-mit-ticketflags", 517 NULL)) 518 krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER); 519 ret = krb5_store_creds(sp, creds); 520 if (ret == 0) 521 ret = write_storage(context, sp, fd); 522 krb5_storage_free(sp); 523 } 524 fcc_unlock(context, fd); 525 if (close(fd) < 0) { 526 if (ret == 0) { 527 char buf[128]; 528 rk_strerror_r(ret, buf, sizeof(buf)); 529 ret = errno; 530 krb5_set_error_message (context, ret, N_("close %s: %s", ""), 531 FILENAME(id), buf); 532 } 533 } 534 return ret; 535 } 536 537 static krb5_error_code 538 init_fcc (krb5_context context, 539 krb5_ccache id, 540 krb5_storage **ret_sp, 541 int *ret_fd, 542 krb5_deltat *kdc_offset) 543 { 544 int fd; 545 int8_t pvno, tag; 546 krb5_storage *sp; 547 krb5_error_code ret; 548 549 if (kdc_offset) 550 *kdc_offset = 0; 551 552 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 553 if(ret) 554 return ret; 555 556 sp = krb5_storage_from_fd(fd); 557 if(sp == NULL) { 558 krb5_clear_error_message(context); 559 ret = ENOMEM; 560 goto out; 561 } 562 krb5_storage_set_eof_code(sp, KRB5_CC_END); 563 ret = krb5_ret_int8(sp, &pvno); 564 if(ret != 0) { 565 if(ret == KRB5_CC_END) { 566 ret = ENOENT; 567 krb5_set_error_message(context, ret, 568 N_("Empty credential cache file: %s", ""), 569 FILENAME(id)); 570 } else 571 krb5_set_error_message(context, ret, N_("Error reading pvno " 572 "in cache file: %s", ""), 573 FILENAME(id)); 574 goto out; 575 } 576 if(pvno != 5) { 577 ret = KRB5_CCACHE_BADVNO; 578 krb5_set_error_message(context, ret, N_("Bad version number in credential " 579 "cache file: %s", ""), 580 FILENAME(id)); 581 goto out; 582 } 583 ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ 584 if(ret != 0) { 585 ret = KRB5_CC_FORMAT; 586 krb5_set_error_message(context, ret, "Error reading tag in " 587 "cache file: %s", FILENAME(id)); 588 goto out; 589 } 590 FCACHE(id)->version = tag; 591 storage_set_flags(context, sp, FCACHE(id)->version); 592 switch (tag) { 593 case KRB5_FCC_FVNO_4: { 594 int16_t length; 595 596 ret = krb5_ret_int16 (sp, &length); 597 if(ret) { 598 ret = KRB5_CC_FORMAT; 599 krb5_set_error_message(context, ret, 600 N_("Error reading tag length in " 601 "cache file: %s", ""), FILENAME(id)); 602 goto out; 603 } 604 while(length > 0) { 605 int16_t dtag, data_len; 606 int i; 607 int8_t dummy; 608 609 ret = krb5_ret_int16 (sp, &dtag); 610 if(ret) { 611 ret = KRB5_CC_FORMAT; 612 krb5_set_error_message(context, ret, N_("Error reading dtag in " 613 "cache file: %s", ""), 614 FILENAME(id)); 615 goto out; 616 } 617 ret = krb5_ret_int16 (sp, &data_len); 618 if(ret) { 619 ret = KRB5_CC_FORMAT; 620 krb5_set_error_message(context, ret, 621 N_("Error reading dlength " 622 "in cache file: %s",""), 623 FILENAME(id)); 624 goto out; 625 } 626 switch (dtag) { 627 case FCC_TAG_DELTATIME : { 628 int32_t offset; 629 630 ret = krb5_ret_int32 (sp, &offset); 631 ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); 632 if(ret) { 633 ret = KRB5_CC_FORMAT; 634 krb5_set_error_message(context, ret, 635 N_("Error reading kdc_sec in " 636 "cache file: %s", ""), 637 FILENAME(id)); 638 goto out; 639 } 640 context->kdc_sec_offset = offset; 641 if (kdc_offset) 642 *kdc_offset = offset; 643 break; 644 } 645 default : 646 for (i = 0; i < data_len; ++i) { 647 ret = krb5_ret_int8 (sp, &dummy); 648 if(ret) { 649 ret = KRB5_CC_FORMAT; 650 krb5_set_error_message(context, ret, 651 N_("Error reading unknown " 652 "tag in cache file: %s", ""), 653 FILENAME(id)); 654 goto out; 655 } 656 } 657 break; 658 } 659 length -= 4 + data_len; 660 } 661 break; 662 } 663 case KRB5_FCC_FVNO_3: 664 case KRB5_FCC_FVNO_2: 665 case KRB5_FCC_FVNO_1: 666 break; 667 default : 668 ret = KRB5_CCACHE_BADVNO; 669 krb5_set_error_message(context, ret, 670 N_("Unknown version number (%d) in " 671 "credential cache file: %s", ""), 672 (int)tag, FILENAME(id)); 673 goto out; 674 } 675 *ret_sp = sp; 676 *ret_fd = fd; 677 678 return 0; 679 out: 680 if(sp != NULL) 681 krb5_storage_free(sp); 682 fcc_unlock(context, fd); 683 close(fd); 684 return ret; 685 } 686 687 static krb5_error_code KRB5_CALLCONV 688 fcc_get_principal(krb5_context context, 689 krb5_ccache id, 690 krb5_principal *principal) 691 { 692 krb5_error_code ret; 693 int fd; 694 krb5_storage *sp; 695 696 ret = init_fcc (context, id, &sp, &fd, NULL); 697 if (ret) 698 return ret; 699 ret = krb5_ret_principal(sp, principal); 700 if (ret) 701 krb5_clear_error_message(context); 702 krb5_storage_free(sp); 703 fcc_unlock(context, fd); 704 close(fd); 705 return ret; 706 } 707 708 static krb5_error_code KRB5_CALLCONV 709 fcc_end_get (krb5_context context, 710 krb5_ccache id, 711 krb5_cc_cursor *cursor); 712 713 static krb5_error_code KRB5_CALLCONV 714 fcc_get_first (krb5_context context, 715 krb5_ccache id, 716 krb5_cc_cursor *cursor) 717 { 718 krb5_error_code ret; 719 krb5_principal principal; 720 721 if (FCACHE(id) == NULL) 722 return krb5_einval(context, 2); 723 724 *cursor = malloc(sizeof(struct fcc_cursor)); 725 if (*cursor == NULL) { 726 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 727 return ENOMEM; 728 } 729 memset(*cursor, 0, sizeof(struct fcc_cursor)); 730 731 ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp, 732 &FCC_CURSOR(*cursor)->fd, NULL); 733 if (ret) { 734 free(*cursor); 735 *cursor = NULL; 736 return ret; 737 } 738 ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 739 if(ret) { 740 krb5_clear_error_message(context); 741 fcc_end_get(context, id, cursor); 742 return ret; 743 } 744 krb5_free_principal (context, principal); 745 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 746 return 0; 747 } 748 749 static krb5_error_code KRB5_CALLCONV 750 fcc_get_next (krb5_context context, 751 krb5_ccache id, 752 krb5_cc_cursor *cursor, 753 krb5_creds *creds) 754 { 755 krb5_error_code ret; 756 757 if (FCACHE(id) == NULL) 758 return krb5_einval(context, 2); 759 760 if (FCC_CURSOR(*cursor) == NULL) 761 return krb5_einval(context, 3); 762 763 if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0) 764 return ret; 765 766 ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); 767 if (ret) 768 krb5_clear_error_message(context); 769 770 fcc_unlock(context, FCC_CURSOR(*cursor)->fd); 771 return ret; 772 } 773 774 static krb5_error_code KRB5_CALLCONV 775 fcc_end_get (krb5_context context, 776 krb5_ccache id, 777 krb5_cc_cursor *cursor) 778 { 779 780 if (FCACHE(id) == NULL) 781 return krb5_einval(context, 2); 782 783 if (FCC_CURSOR(*cursor) == NULL) 784 return krb5_einval(context, 3); 785 786 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 787 close (FCC_CURSOR(*cursor)->fd); 788 free(*cursor); 789 *cursor = NULL; 790 return 0; 791 } 792 793 static krb5_error_code KRB5_CALLCONV 794 fcc_remove_cred(krb5_context context, 795 krb5_ccache id, 796 krb5_flags which, 797 krb5_creds *cred) 798 { 799 krb5_error_code ret; 800 krb5_ccache copy, newfile; 801 char *newname = NULL; 802 int fd; 803 804 if (FCACHE(id) == NULL) 805 return krb5_einval(context, 2); 806 807 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, ©); 808 if (ret) 809 return ret; 810 811 ret = krb5_cc_copy_cache(context, id, copy); 812 if (ret) { 813 krb5_cc_destroy(context, copy); 814 return ret; 815 } 816 817 ret = krb5_cc_remove_cred(context, copy, which, cred); 818 if (ret) { 819 krb5_cc_destroy(context, copy); 820 return ret; 821 } 822 823 ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id)); 824 if (ret < 0 || newname == NULL) { 825 krb5_cc_destroy(context, copy); 826 return ENOMEM; 827 } 828 829 fd = mkstemp(&newname[5]); 830 if (fd < 0) { 831 ret = errno; 832 krb5_cc_destroy(context, copy); 833 return ret; 834 } 835 close(fd); 836 837 ret = krb5_cc_resolve(context, newname, &newfile); 838 if (ret) { 839 unlink(&newname[5]); 840 free(newname); 841 krb5_cc_destroy(context, copy); 842 return ret; 843 } 844 845 ret = krb5_cc_copy_cache(context, copy, newfile); 846 krb5_cc_destroy(context, copy); 847 if (ret) { 848 free(newname); 849 krb5_cc_destroy(context, newfile); 850 return ret; 851 } 852 853 ret = rk_rename(&newname[5], FILENAME(id)); 854 if (ret) 855 ret = errno; 856 free(newname); 857 krb5_cc_close(context, newfile); 858 859 return ret; 860 } 861 862 static krb5_error_code KRB5_CALLCONV 863 fcc_set_flags(krb5_context context, 864 krb5_ccache id, 865 krb5_flags flags) 866 { 867 if (FCACHE(id) == NULL) 868 return krb5_einval(context, 2); 869 870 return 0; /* XXX */ 871 } 872 873 static int KRB5_CALLCONV 874 fcc_get_version(krb5_context context, 875 krb5_ccache id) 876 { 877 if (FCACHE(id) == NULL) 878 return -1; 879 880 return FCACHE(id)->version; 881 } 882 883 struct fcache_iter { 884 int first; 885 }; 886 887 static krb5_error_code KRB5_CALLCONV 888 fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 889 { 890 struct fcache_iter *iter; 891 892 iter = calloc(1, sizeof(*iter)); 893 if (iter == NULL) { 894 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 895 return ENOMEM; 896 } 897 iter->first = 1; 898 *cursor = iter; 899 return 0; 900 } 901 902 static krb5_error_code KRB5_CALLCONV 903 fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 904 { 905 struct fcache_iter *iter = cursor; 906 krb5_error_code ret; 907 const char *fn; 908 char *expandedfn = NULL; 909 910 if (iter == NULL) 911 return krb5_einval(context, 2); 912 913 if (!iter->first) { 914 krb5_clear_error_message(context); 915 return KRB5_CC_END; 916 } 917 iter->first = 0; 918 919 fn = krb5_cc_default_name(context); 920 if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) { 921 ret = _krb5_expand_default_cc_name(context, 922 KRB5_DEFAULT_CCNAME_FILE, 923 &expandedfn); 924 if (ret) 925 return ret; 926 fn = expandedfn; 927 } 928 /* check if file exists, don't return a non existant "next" */ 929 if (strncasecmp(fn, "FILE:", 5) == 0) { 930 struct stat sb; 931 ret = stat(fn + 5, &sb); 932 if (ret) { 933 ret = KRB5_CC_END; 934 goto out; 935 } 936 } 937 ret = krb5_cc_resolve(context, fn, id); 938 out: 939 if (expandedfn) 940 free(expandedfn); 941 942 return ret; 943 } 944 945 static krb5_error_code KRB5_CALLCONV 946 fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 947 { 948 struct fcache_iter *iter = cursor; 949 950 if (iter == NULL) 951 return krb5_einval(context, 2); 952 953 free(iter); 954 return 0; 955 } 956 957 static krb5_error_code KRB5_CALLCONV 958 fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 959 { 960 krb5_error_code ret = 0; 961 962 ret = rk_rename(FILENAME(from), FILENAME(to)); 963 964 if (ret && errno != EXDEV) { 965 char buf[128]; 966 ret = errno; 967 rk_strerror_r(ret, buf, sizeof(buf)); 968 krb5_set_error_message(context, ret, 969 N_("Rename of file from %s " 970 "to %s failed: %s", ""), 971 FILENAME(from), FILENAME(to), buf); 972 return ret; 973 } else if (ret && errno == EXDEV) { 974 /* make a copy and delete the orignal */ 975 krb5_ssize_t sz1, sz2; 976 int fd1, fd2; 977 char buf[BUFSIZ]; 978 979 ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 980 if(ret) 981 return ret; 982 983 unlink(FILENAME(to)); 984 985 ret = fcc_open(context, to, &fd2, 986 O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); 987 if(ret) 988 goto out1; 989 990 while((sz1 = read(fd1, buf, sizeof(buf))) > 0) { 991 sz2 = write(fd2, buf, sz1); 992 if (sz1 != sz2) { 993 ret = EIO; 994 krb5_set_error_message(context, ret, 995 N_("Failed to write data from one file " 996 "credential cache to the other", "")); 997 goto out2; 998 } 999 } 1000 if (sz1 < 0) { 1001 ret = EIO; 1002 krb5_set_error_message(context, ret, 1003 N_("Failed to read data from one file " 1004 "credential cache to the other", "")); 1005 goto out2; 1006 } 1007 out2: 1008 fcc_unlock(context, fd2); 1009 close(fd2); 1010 1011 out1: 1012 fcc_unlock(context, fd1); 1013 close(fd1); 1014 1015 _krb5_erase_file(context, FILENAME(from)); 1016 1017 if (ret) { 1018 _krb5_erase_file(context, FILENAME(to)); 1019 return ret; 1020 } 1021 } 1022 1023 /* make sure ->version is uptodate */ 1024 { 1025 krb5_storage *sp; 1026 int fd; 1027 if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) { 1028 if (sp) 1029 krb5_storage_free(sp); 1030 fcc_unlock(context, fd); 1031 close(fd); 1032 } 1033 } 1034 1035 fcc_close(context, from); 1036 1037 return ret; 1038 } 1039 1040 static krb5_error_code KRB5_CALLCONV 1041 fcc_get_default_name(krb5_context context, char **str) 1042 { 1043 return _krb5_expand_default_cc_name(context, 1044 KRB5_DEFAULT_CCNAME_FILE, 1045 str); 1046 } 1047 1048 static krb5_error_code KRB5_CALLCONV 1049 fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1050 { 1051 krb5_error_code ret; 1052 struct stat sb; 1053 int fd; 1054 1055 ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0); 1056 if(ret) 1057 return ret; 1058 ret = fstat(fd, &sb); 1059 close(fd); 1060 if (ret) { 1061 ret = errno; 1062 krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); 1063 return ret; 1064 } 1065 *mtime = sb.st_mtime; 1066 return 0; 1067 } 1068 1069 static krb5_error_code KRB5_CALLCONV 1070 fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1071 { 1072 return 0; 1073 } 1074 1075 static krb5_error_code KRB5_CALLCONV 1076 fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1077 { 1078 krb5_error_code ret; 1079 krb5_storage *sp = NULL; 1080 int fd; 1081 ret = init_fcc(context, id, &sp, &fd, kdc_offset); 1082 if (sp) 1083 krb5_storage_free(sp); 1084 fcc_unlock(context, fd); 1085 close(fd); 1086 1087 return ret; 1088 } 1089 1090 1091 /** 1092 * Variable containing the FILE based credential cache implemention. 1093 * 1094 * @ingroup krb5_ccache 1095 */ 1096 1097 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { 1098 KRB5_CC_OPS_VERSION, 1099 "FILE", 1100 fcc_get_name, 1101 fcc_resolve, 1102 fcc_gen_new, 1103 fcc_initialize, 1104 fcc_destroy, 1105 fcc_close, 1106 fcc_store_cred, 1107 NULL, /* fcc_retrieve */ 1108 fcc_get_principal, 1109 fcc_get_first, 1110 fcc_get_next, 1111 fcc_end_get, 1112 fcc_remove_cred, 1113 fcc_set_flags, 1114 fcc_get_version, 1115 fcc_get_cache_first, 1116 fcc_get_cache_next, 1117 fcc_end_cache_get, 1118 fcc_move, 1119 fcc_get_default_name, 1120 NULL, 1121 fcc_lastchange, 1122 fcc_set_kdc_offset, 1123 fcc_get_kdc_offset 1124 }; 1125