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