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