xcache.c
1 /* 2 * Copyright (c) 2013 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2013 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 #include "heimcred.h" 38 39 #ifdef HAVE_XCC 40 41 #define CFRELEASE_NULL(x) do { if (x) { CFRelease(x); x = NULL; } } while(0) 42 43 typedef struct krb5_xcc { 44 CFUUIDRef uuid; 45 HeimCredRef cred; 46 CFStringRef clientName; 47 krb5_principal primary_principal; 48 char *cache_name; 49 } krb5_xcc; 50 51 struct xcc_cursor { 52 CFArrayRef array; 53 CFIndex offset; 54 }; 55 56 #define XCACHE(X) ((krb5_xcc *)(X)->data.data) 57 58 static void 59 free_cursor(struct xcc_cursor *c) 60 { 61 if (c->array) 62 CFRelease(c->array); 63 free(c); 64 } 65 66 static CFStringRef 67 CFStringCreateFromPrincipal(krb5_context context, krb5_principal principal) 68 { 69 CFStringRef str; 70 char *p; 71 72 if (krb5_unparse_name(context, principal, &p) != 0) 73 return NULL; 74 75 str = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); 76 krb5_xfree(p); 77 78 return str; 79 } 80 81 static krb5_principal 82 PrincipalFromCFString(krb5_context context, CFStringRef string) 83 { 84 krb5_principal principal = NULL; 85 char *p = rk_cfstring2cstring(string); 86 if (p == NULL) 87 return NULL; 88 89 (void)krb5_parse_name(context, p, &principal); 90 free(p); 91 return principal; 92 } 93 94 95 static const char* KRB5_CALLCONV 96 xcc_get_name(krb5_context context, 97 krb5_ccache id) 98 { 99 krb5_xcc *a = XCACHE(id); 100 return a->cache_name; 101 } 102 103 static krb5_error_code KRB5_CALLCONV 104 xcc_alloc(krb5_context context, krb5_ccache *id) 105 { 106 (*id)->data.data = calloc(1, sizeof(krb5_xcc)); 107 if ((*id)->data.data == NULL) 108 return krb5_enomem(context); 109 (*id)->data.length = sizeof(krb5_xcc); 110 111 return 0; 112 } 113 114 static void 115 genName(krb5_xcc *x) 116 { 117 if (x->cache_name) 118 return; 119 CFUUIDBytes bytes = CFUUIDGetUUIDBytes(x->uuid); 120 x->cache_name = malloc(37); 121 uuid_unparse((void *)&bytes, x->cache_name); 122 } 123 124 static krb5_error_code KRB5_CALLCONV 125 update_cache_info(krb5_context context, 126 krb5_xcc *x) 127 { 128 if (x->cred == NULL) { 129 //only update x->cred if it exists in GSSCred 130 131 HeimCredRef cred = HeimCredCopyFromUUID(x->uuid); 132 CFDictionaryRef attrs = HeimCredCopyAttributes(cred, NULL, NULL); 133 if (attrs) { 134 // if there are attrs, then the cache exists on the server. this means we can save the cred entry for the cache. 135 x->cred = cred; 136 } 137 CFRELEASE_NULL(attrs); 138 if (x->cred == NULL) { 139 //if the cred is null, then return not found so that xcc_get_principal can return the error 140 krb5_set_error_message(context, KRB5_CC_NOTFOUND, "no credential for %s", x->cache_name); 141 return KRB5_CC_NOTFOUND; 142 } 143 } 144 if (x->clientName == NULL && x->cred) { 145 x->clientName = HeimCredCopyAttribute(x->cred, kHEIMAttrClientName); 146 if (x->clientName == NULL) { 147 krb5_set_error_message(context, KRB5_CC_NOTFOUND, "no cache for %s", x->cache_name); 148 return KRB5_CC_NOTFOUND; 149 } 150 } 151 if (x->primary_principal == NULL) { 152 x->primary_principal = PrincipalFromCFString(context, x->clientName); 153 if (x->primary_principal == NULL) { 154 krb5_set_error_message(context, KRB5_CC_NOTFOUND, "no principal for %s", x->cache_name); 155 return KRB5_CC_NOTFOUND; 156 } 157 } 158 return 0; 159 } 160 161 static krb5_error_code KRB5_CALLCONV 162 xcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 163 { 164 krb5_error_code ret; 165 CFUUIDBytes bytes; 166 krb5_xcc *x; 167 168 if (uuid_parse(res, (void *)&bytes) != 0) { 169 krb5_set_error_message(context, KRB5_CC_END, "failed to parse uuid: %s", res); 170 return KRB5_CC_END; 171 } 172 173 CFUUIDRef uuidref = CFUUIDCreateFromUUIDBytes(NULL, bytes); 174 if (uuidref == NULL) { 175 krb5_set_error_message(context, KRB5_CC_END, "failed to create uuid from: %s", res); 176 return KRB5_CC_END; 177 } 178 179 ret = xcc_alloc(context, id); 180 if (ret) { 181 CFRELEASE_NULL(uuidref); 182 return ret; 183 } 184 185 x = XCACHE((*id)); 186 187 x->uuid = uuidref; 188 genName(x); 189 190 //if the cache already exists, then fetch the values from gsscred to stay in sync 191 ret = update_cache_info(context, x); 192 if (ret) { 193 //ignore errors here, it's expected that the resolved cache may not exist. 194 } 195 196 return 0; 197 } 198 199 static krb5_error_code 200 xcc_create(krb5_context context, krb5_xcc *x, CFUUIDRef uuid, bool isTemporaryCache) 201 { 202 const void *keys[] = { 203 (void *)kHEIMObjectType, 204 (void *)kHEIMAttrType, 205 (void *)kHEIMAttrTemporaryCache, 206 (void *)kHEIMAttrUUID 207 }; 208 const void *values[] = { 209 (void *)kHEIMObjectKerberos, 210 (void *)kHEIMTypeKerberos, 211 (isTemporaryCache ? kCFBooleanTrue : kCFBooleanFalse), 212 (void *)uuid 213 }; 214 CFDictionaryRef attrs; 215 krb5_error_code ret; 216 CFErrorRef error = NULL; 217 218 CFIndex num_keys = sizeof(keys)/sizeof(keys[0]); 219 if (uuid == NULL) 220 num_keys -= 1; 221 222 attrs = CFDictionaryCreate(NULL, keys, values, num_keys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 223 heim_assert(attrs != NULL, "Failed to create dictionary"); 224 225 /* 226 * Contract with HeimCredCreate is creates a uuid's when they are 227 * not set on the attributes passed in. 228 */ 229 x->cred = HeimCredCreate(attrs, &error); 230 CFRelease(attrs); 231 if (x->cred) { 232 // xcc_gen_new will not send a uuid, however, xcc_initialize after receiving an empty default cache can send a uuid. This case has a x->uuid but no x-cred. 233 if (!uuid) { 234 heim_assert(x->uuid == NULL, "credential should not already have a UUID"); 235 x->uuid = HeimCredGetUUID(x->cred); 236 heim_assert(x->uuid != NULL, "no uuid for credential?"); 237 CFRetain(x->uuid); 238 } 239 240 ret = 0; 241 genName(x); 242 } else { 243 ret = _krb5_set_cf_error_message(context, ENOMEM, error, N_("no reply from GSSCred", "")); 244 } 245 246 CFRELEASE_NULL(error); 247 248 return ret; 249 } 250 251 static krb5_error_code KRB5_CALLCONV 252 xcc_gen_new(krb5_context context, krb5_ccache *id) 253 { 254 krb5_error_code ret; 255 krb5_xcc *x; 256 257 ret = xcc_alloc(context, id); 258 if (ret) 259 return ret; 260 261 x = XCACHE(*id); 262 263 ret = xcc_create(context, x, NULL, ((*id)->ops == &krb5_xcc_temp_api_ops)); 264 265 return ret; 266 } 267 268 static krb5_error_code KRB5_CALLCONV 269 xcc_initialize(krb5_context context, 270 krb5_ccache id, 271 krb5_principal primary_principal) 272 { 273 krb5_xcc *x = XCACHE(id); 274 krb5_error_code ret; 275 276 if (x->primary_principal) 277 krb5_free_principal(context, x->primary_principal); 278 279 ret = krb5_copy_principal(context, primary_principal, &x->primary_principal); 280 if (ret) 281 return ret; 282 283 CFRELEASE_NULL(x->clientName); 284 285 x->clientName = CFStringCreateFromPrincipal(context, primary_principal); 286 if (x->clientName == NULL) 287 return krb5_enomem(context); 288 289 if (x->cred == NULL) { 290 ret = xcc_create(context, x, x->uuid, (id->ops == &krb5_xcc_temp_api_ops)); 291 if (ret) 292 return ret; 293 } else { 294 const void *remove_keys[] = { 295 kHEIMAttrType, 296 kHEIMAttrParentCredential 297 }; 298 const void *remove_values[] = { 299 (void *)kHEIMTypeKerberos, 300 x->uuid, 301 }; 302 CFDictionaryRef query; 303 304 query = CFDictionaryCreate(NULL, remove_keys, remove_values, sizeof(remove_keys) / sizeof(remove_keys[0]), NULL, NULL); 305 heim_assert(query != NULL, "Failed to create dictionary"); 306 307 HeimCredDeleteQuery(query, NULL); 308 CFRelease(query); 309 310 } 311 if (!HeimCredSetAttribute(x->cred, kHEIMAttrClientName, x->clientName, NULL)) {\ 312 ret = EINVAL; 313 krb5_set_error_message(context, ret, "failed to store credential to %s", x->cache_name); 314 } 315 316 317 return ret; 318 } 319 320 static krb5_error_code KRB5_CALLCONV 321 xcc_close(krb5_context context, 322 krb5_ccache id) 323 { 324 krb5_xcc *x = XCACHE(id); 325 CFRELEASE_NULL(x->uuid); 326 CFRELEASE_NULL(x->cred); 327 CFRELEASE_NULL(x->clientName); 328 krb5_free_principal(context, x->primary_principal); 329 free(x->cache_name); 330 331 krb5_data_free(&id->data); 332 333 return 0; 334 } 335 336 static krb5_error_code KRB5_CALLCONV 337 xcc_destroy(krb5_context context, 338 krb5_ccache id) 339 { 340 krb5_xcc *x = XCACHE(id); 341 if (x->uuid) 342 HeimCredDeleteByUUID(x->uuid); 343 CFRELEASE_NULL(x->cred); 344 345 return 0; 346 } 347 348 static krb5_error_code KRB5_CALLCONV 349 xcc_store_cred(krb5_context context, 350 krb5_ccache id, 351 krb5_creds *creds) 352 { 353 krb5_xcc *x = XCACHE(id); 354 krb5_storage *sp = NULL; 355 CFDataRef dref = NULL; 356 krb5_data data; 357 CFStringRef principal = NULL; 358 CFDictionaryRef query = NULL; 359 krb5_error_code ret; 360 CFBooleanRef is_tgt = kCFBooleanFalse; 361 CFDateRef authtime = NULL; 362 CFDateRef expiretime = NULL; 363 CFDateRef renew_till = NULL; 364 365 krb5_data_zero(&data); 366 367 if (creds->times.starttime) { 368 authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.starttime - kCFAbsoluteTimeIntervalSince1970); 369 } else if (creds->times.authtime) { 370 authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.authtime - kCFAbsoluteTimeIntervalSince1970); 371 } else { 372 authtime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 373 } 374 if (authtime == NULL) { 375 ret = krb5_enomem(context); 376 goto out; 377 } 378 379 if (creds->times.endtime) { 380 expiretime = CFDateCreate(NULL, (CFTimeInterval)creds->times.endtime - kCFAbsoluteTimeIntervalSince1970); 381 } 382 383 if (creds->times.renew_till) { 384 renew_till = CFDateCreate(NULL, (CFTimeInterval)creds->times.renew_till - kCFAbsoluteTimeIntervalSince1970); 385 } 386 387 sp = krb5_storage_emem(); 388 if (sp == NULL) { 389 ret = krb5_enomem(context); 390 goto out; 391 } 392 393 ret = krb5_store_creds(sp, creds); 394 if (ret) 395 goto out; 396 397 krb5_storage_to_data(sp, &data); 398 399 dref = CFDataCreateWithBytesNoCopy(NULL, data.data, data.length, kCFAllocatorNull); 400 if (dref == NULL) { 401 ret = krb5_enomem(context); 402 goto out; 403 } 404 405 if (krb5_principal_is_root_krbtgt(context, creds->server)) 406 is_tgt = kCFBooleanTrue; 407 408 principal = CFStringCreateFromPrincipal(context, creds->server); 409 if (principal == NULL) { 410 ret = krb5_enomem(context); 411 goto out; 412 } 413 414 const void *add_keys[] = { 415 (void *)kHEIMObjectType, 416 kHEIMAttrType, 417 kHEIMAttrClientName, 418 kHEIMAttrServerName, 419 kHEIMAttrData, 420 kHEIMAttrParentCredential, 421 kHEIMAttrLeadCredential, 422 kHEIMAttrAuthTime, 423 kHEIMAttrRenewTill 424 }; 425 const void *add_values[] = { 426 (void *)kHEIMObjectKerberos, 427 kHEIMTypeKerberos, 428 x->clientName, 429 principal, 430 dref, 431 x->uuid, 432 is_tgt, 433 authtime, 434 renew_till 435 }; 436 CFIndex num_keys = sizeof(add_keys)/sizeof(add_keys[0]); 437 if (renew_till == NULL) 438 num_keys -= 1; 439 440 query = CFDictionaryCreate(NULL, add_keys, add_values, num_keys, NULL, NULL); 441 heim_assert(query != NULL, "Failed to create dictionary"); 442 443 if (expiretime) { 444 CFMutableDictionaryRef newQuery = CFDictionaryCreateMutableCopy(NULL, 0, query); 445 CFDictionarySetValue(newQuery, kHEIMAttrExpire, expiretime); 446 CFRELEASE_NULL(query); 447 query = newQuery; 448 } 449 450 HeimCredRef ccred = HeimCredCreate(query, NULL); 451 if (ccred) { 452 CFRelease(ccred); 453 } else { 454 _krb5_debugx(context, 5, "failed to add credential to %s\n", x->cache_name); 455 ret = EINVAL; 456 krb5_set_error_message(context, ret, "failed to store credential to %s", x->cache_name); 457 goto out; 458 } 459 460 out: 461 if (sp) 462 krb5_storage_free(sp); 463 CFRELEASE_NULL(query); 464 CFRELEASE_NULL(dref); 465 CFRELEASE_NULL(principal); 466 CFRELEASE_NULL(authtime); 467 CFRELEASE_NULL(expiretime); 468 CFRELEASE_NULL(renew_till); 469 krb5_data_free(&data); 470 471 return ret; 472 } 473 474 static krb5_error_code KRB5_CALLCONV 475 xcc_get_principal(krb5_context context, 476 krb5_ccache id, 477 krb5_principal *principal) 478 { 479 krb5_xcc *x = XCACHE(id); 480 krb5_error_code ret; 481 ret = update_cache_info(context, x); 482 if (ret) { 483 return ret; 484 } 485 486 return krb5_copy_principal(context, x->primary_principal, principal); 487 } 488 489 static krb5_error_code KRB5_CALLCONV 490 xcc_get_first (krb5_context context, 491 krb5_ccache id, 492 krb5_cc_cursor *cursor) 493 { 494 CFDictionaryRef query; 495 krb5_xcc *x = XCACHE(id); 496 497 CFUUIDRef uuid = x->uuid; 498 if (uuid == NULL) { 499 return KRB5_CC_END; 500 } 501 502 struct xcc_cursor *c; 503 504 c = calloc(1, sizeof(*c)); 505 if (c == NULL) 506 return krb5_enomem(context); 507 508 const void *keys[] = { (void *)kHEIMAttrParentCredential, kHEIMAttrType }; 509 const void *values[] = { (void *)uuid, kHEIMTypeKerberos }; 510 511 query = CFDictionaryCreate(NULL, keys, values, sizeof(keys)/sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 512 heim_assert(query != NULL, "out of memory"); 513 514 c->array = HeimCredCopyQuery(query); 515 CFRELEASE_NULL(query); 516 if (c->array == NULL) { 517 free_cursor(c); 518 return KRB5_CC_END; 519 } 520 521 *cursor = c; 522 523 return 0; 524 } 525 526 527 static krb5_error_code KRB5_CALLCONV 528 xcc_get_next (krb5_context context, 529 krb5_ccache id, 530 krb5_cc_cursor *cursor, 531 krb5_creds *creds) 532 { 533 struct xcc_cursor *c = *cursor; 534 krb5_error_code ret; 535 krb5_storage *sp; 536 HeimCredRef cred; 537 CFDataRef data; 538 539 if (c->array == NULL) 540 return KRB5_CC_END; 541 542 next: 543 if (c->offset >= CFArrayGetCount(c->array)) 544 return KRB5_CC_END; 545 546 cred = (HeimCredRef)CFArrayGetValueAtIndex(c->array, c->offset++); 547 if (cred == NULL) 548 return KRB5_CC_END; 549 550 data = HeimCredCopyAttribute(cred, kHEIMAttrData); 551 if (data == NULL) { 552 goto next; 553 } 554 555 sp = krb5_storage_from_readonly_mem(CFDataGetBytePtr(data), CFDataGetLength(data)); 556 if (sp == NULL) { 557 CFRELEASE_NULL(data); 558 return KRB5_CC_END; 559 } 560 561 ret = krb5_ret_creds(sp, creds); 562 krb5_storage_free(sp); 563 CFRELEASE_NULL(data); 564 565 return ret; 566 } 567 568 static krb5_error_code KRB5_CALLCONV 569 xcc_end_get (krb5_context context, 570 krb5_ccache id, 571 krb5_cc_cursor *cursor) 572 { 573 free_cursor((struct xcc_cursor *)*cursor); 574 *cursor = NULL; 575 576 return 0; 577 } 578 579 static krb5_error_code KRB5_CALLCONV 580 xcc_remove_cred(krb5_context context, 581 krb5_ccache id, 582 krb5_flags which, 583 krb5_creds *cred) 584 { 585 CFDictionaryRef query; 586 krb5_xcc *x = XCACHE(id); 587 588 CFStringRef servername = CFStringCreateFromPrincipal(context, cred->server); 589 if (servername == NULL) 590 return KRB5_CC_END; 591 592 const void *keys[] = { (const void *)kHEIMAttrParentCredential, kHEIMAttrType, kHEIMAttrServerName }; 593 const void *values[] = { (const void *)x->uuid, kHEIMTypeKerberos, servername }; 594 595 /* XXX match enctype */ 596 597 query = CFDictionaryCreate(NULL, keys, values, sizeof(keys)/sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 598 heim_assert(query != NULL, "Failed to create dictionary"); 599 600 CFRELEASE_NULL(servername); 601 602 bool res = HeimCredDeleteQuery(query, NULL); 603 CFRELEASE_NULL(query); 604 605 if (!res) { 606 krb5_set_error_message(context, KRB5_CC_NOTFOUND, N_("Deleted credential not found", "")); 607 return KRB5_CC_NOTFOUND; 608 } 609 return 0; 610 } 611 612 static krb5_error_code KRB5_CALLCONV 613 xcc_set_flags(krb5_context context, 614 krb5_ccache id, 615 krb5_flags flags) 616 { 617 return 0; 618 } 619 620 static int KRB5_CALLCONV 621 xcc_get_version(krb5_context context, 622 krb5_ccache id) 623 { 624 return 0; 625 } 626 627 static krb5_error_code KRB5_CALLCONV 628 xcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 629 { 630 CFDictionaryRef query; 631 struct xcc_cursor *c; 632 633 const void *keys[] = { 634 (const void *)kHEIMAttrType, 635 (const void *)kHEIMAttrServerName, 636 }; 637 const void *values[] = { 638 (const void *)kHEIMTypeKerberos, 639 (const void *)kCFNull, 640 }; 641 642 c = calloc(1, sizeof(*c)); 643 if (c == NULL) 644 return krb5_enomem(context); 645 646 query = CFDictionaryCreate(NULL, keys, values, sizeof(keys)/sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 647 heim_assert(query != NULL, "Failed to create dictionary"); 648 649 c->array = HeimCredCopyQuery(query); 650 CFRELEASE_NULL(query); 651 if (c->array == NULL) { 652 free_cursor(c); 653 return KRB5_CC_END; 654 } 655 *cursor = c; 656 return 0; 657 } 658 659 static krb5_error_code KRB5_CALLCONV 660 xcc_temp_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 661 { 662 return KRB5_CC_END; 663 } 664 665 static krb5_error_code KRB5_CALLCONV 666 get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id) 667 { 668 struct xcc_cursor *c = cursor; 669 krb5_error_code ret; 670 HeimCredRef cred; 671 krb5_xcc *x; 672 673 if (c->array == NULL) 674 return KRB5_CC_END; 675 676 if (c->offset >= CFArrayGetCount(c->array)) 677 return KRB5_CC_END; 678 679 cred = (HeimCredRef)CFArrayGetValueAtIndex(c->array, c->offset++); 680 if (cred == NULL) 681 return KRB5_CC_END; 682 683 ret = _krb5_cc_allocate(context, ops, id); 684 if (ret) 685 return ret; 686 687 xcc_alloc(context, id); 688 x = XCACHE((*id)); 689 690 x->uuid = HeimCredGetUUID(cred); 691 CFRetain(x->uuid); 692 x->cred = cred; 693 CFRetain(cred); 694 genName(x); 695 696 return ret; 697 } 698 699 static krb5_error_code KRB5_CALLCONV 700 xcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 701 { 702 #ifdef XCACHE_IS_API_CACHE 703 return KRB5_CC_END; 704 #else 705 return get_cache_next(context, cursor, &krb5_xcc_ops, id); 706 #endif 707 } 708 709 static krb5_error_code KRB5_CALLCONV 710 xcc_api_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 711 { 712 #ifdef XCACHE_IS_API_CACHE 713 return get_cache_next(context, cursor, &krb5_xcc_api_ops, id); 714 #else 715 return KRB5_CC_END; 716 #endif 717 } 718 719 static krb5_error_code KRB5_CALLCONV 720 xcc_temp_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 721 { 722 //temp caches do not need to be used in cache cursors 723 return KRB5_CC_END; 724 } 725 726 static krb5_error_code KRB5_CALLCONV 727 xcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 728 { 729 free_cursor((struct xcc_cursor *)cursor); 730 return 0; 731 } 732 733 static krb5_error_code KRB5_CALLCONV 734 xcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 735 { 736 krb5_xcc *xfrom = XCACHE(from); 737 krb5_xcc *xto = XCACHE(to); 738 739 /* uuid */ 740 if (!HeimCredMove(xfrom->uuid, xto->uuid)) 741 return KRB5_CC_END; 742 743 if (xfrom->uuid) 744 HeimCredDeleteByUUID(xfrom->uuid); 745 746 CFRELEASE_NULL(xfrom->uuid); 747 748 /* cred */ 749 CFRELEASE_NULL(xto->cred); 750 CFRELEASE_NULL(xfrom->cred); 751 752 /* clientName */ 753 CFRELEASE_NULL(xto->clientName); 754 xto->clientName = xfrom->clientName; 755 xfrom->clientName = NULL; 756 757 /* primary_principal */ 758 if (xto->primary_principal) 759 krb5_free_principal(context, xto->primary_principal); 760 xto->primary_principal = xfrom->primary_principal; 761 xfrom->primary_principal = NULL; 762 763 /* cache_name */ 764 free(xto->cache_name); 765 xto->cache_name = NULL; 766 genName(xto); 767 768 free(xfrom->cache_name); 769 770 /* outer foo */ 771 krb5_data_free(&from->data); 772 773 return 0; 774 } 775 776 static krb5_error_code KRB5_CALLCONV 777 get_default_name(krb5_context context, 778 const krb5_cc_ops *ops, 779 const char *cachename, 780 char **str) 781 { 782 CFUUIDRef uuid = NULL; 783 CFUUIDBytes bytes; 784 785 uuid = HeimCredCopyDefaultCredential(kHEIMTypeKerberos, NULL); 786 if (uuid == NULL) { 787 return _krb5_expand_default_cc_name(context, cachename, str); 788 } 789 bytes = CFUUIDGetUUIDBytes(uuid); 790 791 char uuidstr[37]; 792 uuid_unparse((void *)&bytes, uuidstr); 793 794 CFRELEASE_NULL(uuid); 795 796 asprintf(str, "%s:%s", ops->prefix, uuidstr); 797 798 return 0; 799 } 800 801 static krb5_error_code KRB5_CALLCONV 802 xcc_get_default_name(krb5_context context, char **str) 803 { 804 return get_default_name(context, &krb5_xcc_ops, "XCACHE:11111111-71F2-48EB-94C4-7D7392E900E5", str); 805 } 806 807 static krb5_error_code KRB5_CALLCONV 808 xcc_api_get_default_name(krb5_context context, char **str) 809 { 810 return get_default_name(context, &krb5_xcc_api_ops, "API:11111111-71F2-48EB-94C4-7D7392E900E5", str); 811 } 812 813 static krb5_error_code KRB5_CALLCONV 814 xcc_set_default(krb5_context context, krb5_ccache id) 815 { 816 krb5_xcc *x = XCACHE(id); 817 krb5_error_code ret = 0; 818 819 if (x->cred == NULL) { 820 x->cred = HeimCredCopyFromUUID(x->uuid); 821 if (x->cred == NULL) 822 return KRB5_CC_END; 823 } 824 825 if (!HeimCredSetAttribute(x->cred, kHEIMAttrDefaultCredential, kCFBooleanTrue, NULL)) { 826 ret = EINVAL; 827 krb5_set_error_message(context, ret, "XCACHE couldn't set default credential"); 828 } 829 return ret; 830 } 831 832 static krb5_error_code KRB5_CALLCONV 833 xcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 834 { 835 *mtime = 0; 836 return 0; 837 } 838 839 static krb5_error_code KRB5_CALLCONV 840 xcc_hold(krb5_context context, 841 krb5_ccache id) 842 { 843 krb5_xcc *x = XCACHE(id); 844 if (!x->cred) { 845 x->cred = HeimCredCopyFromUUID(x->uuid); 846 } 847 if (x->cred) { 848 HeimCredRetainTransient(x->cred); 849 } else { 850 return KRB5_CC_NOTFOUND; 851 } 852 853 return 0; 854 } 855 856 static krb5_error_code KRB5_CALLCONV 857 xcc_unhold(krb5_context context, 858 krb5_ccache id) 859 { 860 krb5_xcc *x = XCACHE(id); 861 if (!x->cred) { 862 x->cred = HeimCredCopyFromUUID(x->uuid); 863 } 864 if (x->cred) { 865 HeimCredReleaseTransient(x->cred); 866 } else { 867 return KRB5_CC_NOTFOUND; 868 } 869 870 return 0; 871 } 872 873 874 875 static krb5_error_code 876 xcc_get_uuid(krb5_context context, krb5_ccache id, krb5_uuid uuid) 877 { 878 krb5_xcc *x = XCACHE(id); 879 CFUUIDBytes bytes = CFUUIDGetUUIDBytes(x->uuid); 880 memcpy(uuid, &bytes, sizeof(krb5_uuid)); 881 return 0; 882 } 883 884 static krb5_error_code 885 resolve_by_uuid(krb5_context context, 886 krb5_ccache id, 887 const krb5_cc_ops *ops, 888 krb5_uuid uuid) 889 { 890 krb5_error_code ret; 891 CFUUIDBytes bytes; 892 krb5_xcc *x; 893 894 memcpy(&bytes, uuid, sizeof(bytes)); 895 896 CFUUIDRef uuidref = CFUUIDCreateFromUUIDBytes(NULL, bytes); 897 if (uuidref == NULL) { 898 krb5_set_error_message(context, KRB5_CC_END, "failed to create uuid"); 899 return KRB5_CC_END; 900 } 901 902 ret = xcc_alloc(context, &id); 903 if (ret) { 904 CFRELEASE_NULL(uuidref); 905 return ret; 906 } 907 908 x = XCACHE(id); 909 910 x->uuid = uuidref; 911 genName(x); 912 913 return 0; 914 } 915 916 static krb5_error_code 917 xcc_resolve_by_uuid(krb5_context context, krb5_ccache id, krb5_uuid uuid) 918 { 919 return resolve_by_uuid(context, id, &krb5_xcc_ops, uuid); 920 } 921 922 static krb5_error_code 923 xcc_api_resolve_by_uuid(krb5_context context, krb5_ccache id, krb5_uuid uuid) 924 { 925 return resolve_by_uuid(context, id, &krb5_xcc_api_ops, uuid); 926 } 927 928 static krb5_error_code 929 xcc_set_acl(krb5_context context, krb5_ccache id, const char *type, /* heim_object_t */ void *obj) 930 { 931 krb5_xcc *x = XCACHE(id); 932 bool res; 933 934 if (x->cred == NULL) { 935 x->cred = HeimCredCopyFromUUID(x->uuid); 936 if (x->cred == NULL) 937 return KRB5_CC_END; 938 } 939 940 CFStringRef t = CFStringCreateWithCString(NULL, type, kCFStringEncodingUTF8); 941 if (t == NULL) 942 return krb5_enomem(context); 943 944 res = HeimCredSetAttribute(x->cred, t, obj, NULL); 945 CFRELEASE_NULL(t); 946 947 if (!res) 948 return KRB5_CC_END; 949 950 return 0; 951 } 952 953 static krb5_error_code 954 xcc_copy_data(krb5_context context, krb5_ccache id, /* heim_dict_t */ void *keys, /* heim_dict_t */ void **data) 955 { 956 krb5_xcc *x = XCACHE(id); 957 958 *data = NULL; 959 960 if (x->cred == NULL) { 961 x->cred = HeimCredCopyFromUUID(x->uuid); 962 if (x->cred == NULL) 963 return KRB5_CC_END; 964 } 965 966 *data = (heim_dict_t)HeimCredCopyAttributes(x->cred, NULL, NULL); 967 if (*data == NULL) { 968 krb5_set_error_message(context, KRB5_CC_END, 969 N_("Credential have no attributes", "")); 970 return KRB5_CC_END; 971 } 972 return 0; 973 } 974 975 static int KRB5_CALLCONV 976 xcc_can_move_from(krb5_context context, krb5_ccache fromid) 977 { 978 //moves between other xcaches are allowed 979 if (fromid->ops == &krb5_xcc_api_ops 980 || fromid->ops == &krb5_xcc_ops 981 || fromid->ops == &krb5_xcc_temp_api_ops) { 982 return true; 983 } 984 return false; 985 } 986 987 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 988 _krb5_xcc_get_initial_ticket(krb5_context context, 989 krb5_ccache id, 990 krb5_principal client, 991 krb5_principal server, 992 const char *password) 993 { 994 995 krb5_xcc *x = XCACHE(id); 996 krb5_error_code ret = 0; 997 CFDataRef dref = NULL; 998 CFDictionaryRef query = NULL; 999 CFStringRef serverStr = NULL; 1000 1001 dref = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)password, strlen(password), kCFAllocatorNull); 1002 if (dref == NULL) { 1003 ret = krb5_enomem(context); 1004 goto out; 1005 } 1006 1007 if (server) { 1008 serverStr = CFStringCreateFromPrincipal(context, server); 1009 } 1010 1011 const void *add_keys[] = { 1012 (void *)kHEIMObjectType, 1013 kHEIMAttrType, 1014 kHEIMAttrParentCredential, 1015 kHEIMAttrClientName, 1016 kHEIMAttrData, 1017 kHEIMAttrCredential, 1018 kHEIMAttrServerName 1019 }; 1020 const void *add_values[] = { 1021 (void *)kHEIMObjectKerberosAcquireCred, 1022 kHEIMTypeKerberosAcquireCred, 1023 x->uuid, 1024 x->clientName, 1025 dref, 1026 kCFBooleanTrue, 1027 serverStr 1028 }; 1029 1030 CFIndex num_keys = sizeof(add_keys)/sizeof(add_keys[0]); 1031 if (serverStr == NULL) 1032 num_keys -= 1; 1033 1034 query = CFDictionaryCreate(NULL, add_keys, add_values, num_keys, NULL, NULL); 1035 heim_assert(query != NULL, "Failed to create dictionary"); 1036 1037 HeimCredRef ccred = HeimCredCreate(query, NULL); 1038 if (ccred) { 1039 CFRelease(ccred); 1040 } else { 1041 _krb5_debugx(context, 5, "failed to add initial ticket request to %s\n", x->cache_name); 1042 ret = EINVAL; 1043 krb5_set_error_message(context, ret, "failed to store initial ticket request to %s", x->cache_name); 1044 goto out; 1045 } 1046 1047 out: 1048 CFRELEASE_NULL(serverStr); 1049 CFRELEASE_NULL(query); 1050 CFRELEASE_NULL(dref); 1051 1052 return ret; 1053 } 1054 1055 /** 1056 * Variable containing the XCACHE based credential cache implemention. 1057 * 1058 * @ingroup krb5_ccache 1059 */ 1060 1061 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_xcc_ops = { 1062 KRB5_CC_OPS_VERSION, 1063 "XCACHE", 1064 xcc_get_name, 1065 xcc_resolve, 1066 xcc_gen_new, 1067 xcc_initialize, 1068 xcc_destroy, 1069 xcc_close, 1070 xcc_store_cred, 1071 NULL, /* acc_retrieve */ 1072 xcc_get_principal, 1073 xcc_get_first, 1074 xcc_get_next, 1075 xcc_end_get, 1076 xcc_remove_cred, 1077 xcc_set_flags, 1078 xcc_get_version, 1079 xcc_get_cache_first, 1080 xcc_get_cache_next, 1081 xcc_end_cache_get, 1082 xcc_move, 1083 xcc_get_default_name, 1084 xcc_set_default, 1085 xcc_lastchange, 1086 NULL, /* set_kdc_offset */ 1087 NULL, /* get_kdc_offset */ 1088 xcc_hold, 1089 xcc_unhold, 1090 xcc_get_uuid, 1091 xcc_resolve_by_uuid, 1092 NULL, 1093 NULL, 1094 xcc_set_acl, 1095 xcc_copy_data, 1096 NULL, /* copy_query */ 1097 NULL, /* store_data */ 1098 xcc_can_move_from, 1099 }; 1100 1101 /** 1102 * Variable containing the API (XCACHE version) based credential cache implemention. 1103 * 1104 * @ingroup krb5_ccache 1105 */ 1106 1107 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_xcc_api_ops = { 1108 KRB5_CC_OPS_VERSION, 1109 "API", 1110 xcc_get_name, 1111 xcc_resolve, 1112 xcc_gen_new, 1113 xcc_initialize, 1114 xcc_destroy, 1115 xcc_close, 1116 xcc_store_cred, 1117 NULL, /* acc_retrieve */ 1118 xcc_get_principal, 1119 xcc_get_first, 1120 xcc_get_next, 1121 xcc_end_get, 1122 xcc_remove_cred, 1123 xcc_set_flags, 1124 xcc_get_version, 1125 xcc_get_cache_first, 1126 xcc_api_get_cache_next, 1127 xcc_end_cache_get, 1128 xcc_move, 1129 xcc_api_get_default_name, 1130 xcc_set_default, 1131 xcc_lastchange, 1132 NULL, /* set_kdc_offset */ 1133 NULL, /* get_kdc_offset */ 1134 xcc_hold, 1135 xcc_unhold, 1136 xcc_get_uuid, 1137 xcc_api_resolve_by_uuid, 1138 NULL, 1139 NULL, 1140 xcc_set_acl, 1141 xcc_copy_data, 1142 NULL, /* copy_query */ 1143 NULL, /* store_data */ 1144 xcc_can_move_from, 1145 }; 1146 1147 /** 1148 * Variable containing the TEMP XCACHE based credential cache implemention. 1149 * 1150 * @ingroup krb5_ccache 1151 */ 1152 1153 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_xcc_temp_api_ops = { 1154 KRB5_CC_OPS_VERSION, 1155 "XCTEMP", 1156 xcc_get_name, 1157 xcc_resolve, 1158 xcc_gen_new, 1159 xcc_initialize, 1160 xcc_destroy, 1161 xcc_close, 1162 xcc_store_cred, 1163 NULL, /* acc_retrieve */ 1164 xcc_get_principal, 1165 xcc_get_first, 1166 xcc_get_next, 1167 xcc_end_get, 1168 xcc_remove_cred, 1169 NULL, //xcc_set_flags, 1170 xcc_get_version, 1171 xcc_temp_get_cache_first, 1172 xcc_temp_get_cache_next, 1173 NULL, //xcc_end_cache_get, 1174 xcc_move, 1175 NULL, //xcc_api_get_default_name, 1176 NULL, //xcc_set_default, 1177 xcc_lastchange, 1178 NULL, /* set_kdc_offset */ 1179 NULL, /* get_kdc_offset */ 1180 NULL, //xcc_hold, 1181 NULL, //xcc_unhold, 1182 xcc_get_uuid, 1183 xcc_api_resolve_by_uuid, 1184 NULL, 1185 NULL, 1186 xcc_set_acl, //xcc_set_acl, 1187 xcc_copy_data, 1188 NULL, /* copy_query */ 1189 NULL, /* store_data */ 1190 xcc_can_move_from, 1191 }; 1192 1193 #endif /* HAVE_XCC */