1/* 2 * Copyright (c) 2011 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2011 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 KTH nor the names of its contributors may be 20 * used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 37#define HEIMDAL_PRINTF_ATTRIBUTE(x) 38 39#include <GSSItem.h> 40#include <gssapi.h> 41#include <gssapi_spi.h> 42#include <gssapi_mech.h> 43#include <heimbase.h> 44#include <krb5.h> 45#include <roken.h> 46#include <kcm.h> 47 48 49 50#include <notify.h> 51 52#include <Security/Security.h> 53 54#define GSS_CONST_TYPE(t,k) const t k = (t)(CFSTR(#k)); 55 56GSS_CONST_TYPE(CFTypeRef, kGSSAttrClass); 57GSS_CONST_TYPE(CFStringRef, kGSSAttrClassKerberos); 58GSS_CONST_TYPE(CFStringRef, kGSSAttrClassNTLM); 59GSS_CONST_TYPE(CFStringRef, kGSSAttrClassIAKerb); 60 61GSS_CONST_TYPE(CFTypeRef, kGSSAttrSupportGSSCredential); 62GSS_CONST_TYPE(CFTypeRef, kGSSAttrNameType); 63GSS_CONST_TYPE(CFTypeRef, kGSSAttrNameTypeGSSExportedName); 64GSS_CONST_TYPE(CFTypeRef, kGSSAttrNameTypeGSSUsername); 65GSS_CONST_TYPE(CFTypeRef, kGSSAttrNameTypeGSSHostBasedService); 66GSS_CONST_TYPE(CFTypeRef, kGSSAttrName); 67GSS_CONST_TYPE(CFTypeRef, kGSSAttrNameDisplay); 68GSS_CONST_TYPE(CFTypeRef, kGSSAttrUUID); 69GSS_CONST_TYPE(CFTypeRef, kGSSAttrTransientExpire); 70GSS_CONST_TYPE(CFTypeRef, kGSSAttrTransientDefaultInClass); 71GSS_CONST_TYPE(CFTypeRef, kGSSAttrCredentialPassword); 72GSS_CONST_TYPE(CFTypeRef, kGSSAttrCredentialStore); 73GSS_CONST_TYPE(CFTypeRef, kGSSAttrCredentialSecIdentity); 74GSS_CONST_TYPE(CFTypeRef, kGSSAttrCredentialExists); 75GSS_CONST_TYPE(CFTypeRef, kGSSAttrStatusPersistant); 76GSS_CONST_TYPE(CFTypeRef, kGSSAttrStatusAutoAcquire); 77GSS_CONST_TYPE(CFTypeRef, kGSSAttrStatusTransient); 78 79GSS_CONST_TYPE(CFTypeRef, kGSSAttrStatusAutoAcquireStatus); 80 81 82GSS_CONST_TYPE(CFTypeRef, kGSSOperationChangePasswordOldPassword); 83GSS_CONST_TYPE(CFTypeRef, kGSSOperationChangePasswordNewPassword); 84 85#undef GSS_CONST_TYPE 86 87static CFNumberRef kGSSSecPasswordType = NULL; 88 89static gss_cred_id_t itemToGSSCred(GSSItemRef, OM_uint32 *, CFErrorRef *); 90static void updateTransientValues(GSSItemRef); 91 92 93static CFStringRef kGSSConfKeys = CFSTR("kGSSConfKeys"); 94 95 96struct GSSItem { 97 CFRuntimeBase base; 98 CFMutableDictionaryRef keys; 99 CFUUIDRef gssCredential; 100}; 101 102static void 103_gssitem_release(struct GSSItem *item) 104{ 105 if (item->keys) { 106 CFRelease(item->keys); 107 item->keys = NULL; 108 } 109 if (item->gssCredential) { 110 CFRelease(item->gssCredential); 111 item->gssCredential = NULL; 112 } 113} 114 115static CFDictionaryRef valid_set_types; 116static CFDictionaryRef transient_types; 117static int notify_token; 118static dispatch_queue_t bgq; 119static CFTypeID gssitemid = _kCFRuntimeNotATypeID; 120 121 122static void 123create_tables(void * ctx __attribute__((__unused__))) 124{ 125 CFMutableDictionaryRef top, cl; 126 127 bgq = dispatch_queue_create("org.h5l.gss.item", DISPATCH_QUEUE_CONCURRENT); 128 heim_assert(bgq != NULL, "no breakground queue"); 129 130 top = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 131 heim_assert(top != NULL, "out of memory"); 132 cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 133 heim_assert(top != NULL, "out of memory"); 134 135 CFDictionarySetValue(cl, kGSSAttrClass, kCFBooleanTrue); 136 CFDictionarySetValue(cl, kGSSAttrNameType, kCFBooleanTrue); 137 CFDictionarySetValue(cl, kGSSAttrName, kCFBooleanTrue); 138 CFDictionarySetValue(cl, kGSSAttrUUID, kCFBooleanTrue); 139 CFDictionarySetValue(cl, kGSSAttrCredentialPassword, kCFBooleanFalse); 140 CFDictionarySetValue(cl, kGSSAttrCredentialSecIdentity, kCFBooleanFalse); 141 CFDictionarySetValue(cl, kGSSAttrStatusPersistant, kCFBooleanFalse); 142 CFDictionarySetValue(cl, kGSSAttrStatusAutoAcquire, kCFBooleanFalse); 143 CFDictionarySetValue(cl, kGSSAttrStatusTransient, kCFBooleanFalse); 144 145 CFDictionarySetValue(top, kGSSAttrClassKerberos, cl); 146 CFRelease(cl); 147 148 cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 149 150 CFDictionarySetValue(cl, kGSSAttrClass, kCFBooleanTrue); 151 CFDictionarySetValue(cl, kGSSAttrNameType, kCFBooleanTrue); 152 CFDictionarySetValue(cl, kGSSAttrName, kCFBooleanTrue); 153 CFDictionarySetValue(cl, kGSSAttrUUID, kCFBooleanTrue); 154 CFDictionarySetValue(cl, kGSSAttrCredentialPassword, kCFBooleanFalse); 155 CFDictionarySetValue(cl, kGSSAttrStatusPersistant, kCFBooleanFalse); 156 CFDictionarySetValue(cl, kGSSAttrStatusAutoAcquire, kCFBooleanFalse); 157 CFDictionarySetValue(cl, kGSSAttrStatusTransient, kCFBooleanFalse); 158 159 CFDictionarySetValue(top, kGSSAttrClassNTLM, cl); 160 CFRelease(cl); 161 162 valid_set_types = top; 163 164 cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 165 166 CFDictionarySetValue(cl, kGSSAttrTransientExpire, kCFBooleanTrue); 167 transient_types = cl; 168 169 const int32_t gssnum = 'GSSP'; 170 kGSSSecPasswordType = CFNumberCreate(NULL, kCFNumberSInt32Type, &gssnum); 171 172 (void)notify_register_check(KRB5_KCM_NOTIFY_CACHE_CHANGED, ¬ify_token); 173 174 static const CFRuntimeClass gssItemClass = { 175 0, 176 "GSSItem", 177 NULL, 178 NULL, 179 (void(*)(CFTypeRef))_gssitem_release, 180 NULL, 181 NULL, 182 NULL, 183 NULL 184 }; 185 gssitemid = _CFRuntimeRegisterClass(&gssItemClass); 186} 187 188static void 189gss_init(void) 190{ 191 static dispatch_once_t once; 192 dispatch_once_f(&once, NULL, create_tables); 193} 194 195#pragma mark Item 196 197CFTypeID 198GSSItemGetTypeID(void) 199{ 200 gss_init(); 201 return gssitemid; 202} 203 204static GSSItemRef 205GSSCreateItem(CFAllocatorRef alloc, CFDictionaryRef keys) 206{ 207 GSSItemRef item; 208 209 item = (GSSItemRef)_CFRuntimeCreateInstance(alloc, gssitemid, sizeof(struct GSSItem) - sizeof(CFRuntimeBase), NULL); 210 if (item == NULL) 211 return NULL; 212 213 if (keys) 214 item->keys = CFDictionaryCreateMutableCopy(alloc, 0, keys); 215 else 216 item->keys = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 217 item->gssCredential = NULL; 218 219 return item; 220} 221 222#pragma mark Configuration 223 224 225static CFStringRef 226CreateNewUUID(GSSItemRef item) 227{ 228 CFUUIDRef uuid = CFUUIDCreate(NULL); 229 if (uuid == NULL) 230 return NULL; 231 232 /* uuid is not a property list type, make it a string */ 233 CFStringRef uuidstr = CFUUIDCreateString(NULL, uuid); 234 CFRelease(uuid); 235 if (uuidstr == NULL) 236 return NULL; 237 238 CFDictionarySetValue(item->keys, kGSSAttrUUID, uuidstr); 239 return uuidstr; 240} 241 242static CFURLRef 243copyConfigurationURL(void) 244{ 245 CFURLRef file, home = CFCopyHomeDirectoryURLForUser(NULL); 246 if (home == NULL) 247 return NULL; 248 249 file = CFURLCreateCopyAppendingPathComponent(NULL, home, CFSTR("Library/Preferences/com.apple.GSS.items.plist"), false); 250 CFRelease(home); 251 252 return file; 253} 254 255static CFStringRef 256CopyTransientUUID(gss_cred_id_t cred) 257{ 258 CFUUIDRef uuid = GSSCredentialCopyUUID(cred); 259 CFStringRef uuidstr = NULL; 260 261 if (uuid) { 262 uuidstr = CFUUIDCreateString(NULL, uuid); 263 CFRelease(uuid); 264 } 265 266 return uuidstr; 267} 268 269struct CreateContext { 270 CFMutableDictionaryRef c; 271 CFMutableDictionaryRef transitentUUIDs; 272}; 273 274static void 275createItem(const void *key, const void *value, void *contextValue) 276{ 277 struct CreateContext *context = contextValue; 278 279 GSSItemRef item = GSSCreateItem(NULL, value); 280 if (item) { 281 gss_cred_id_t cred = itemToGSSCred(item, NULL, NULL); 282 283 if (cred) { 284 CFStringRef uuid = CopyTransientUUID(cred); 285 if (uuid) { 286 CFDictionarySetValue(context->transitentUUIDs, uuid, kCFBooleanTrue); 287 CFRelease(uuid); 288 } 289 } 290 291 CFDictionarySetValue(context->c, key, item); 292 CFRelease(item); 293 } 294} 295 296static void 297addTransientKeys(struct CreateContext *createContext) 298{ 299 OM_uint32 min_stat; 300 301 gss_iter_creds (&min_stat, 0, GSS_KRB5_MECHANISM, ^(gss_iter_OID oid, gss_cred_id_t cred) { 302 OM_uint32 maj_stat, junk; 303 gss_name_t name; 304 305 CFStringRef uuid = CopyTransientUUID(cred); 306 if (uuid == NULL) 307 return; 308 309 if (CFDictionaryGetValue(createContext->transitentUUIDs, uuid)) { 310 CFRelease(uuid); 311 return; 312 } 313 314 GSSItemRef item = GSSCreateItem(NULL, NULL); 315 if (item == NULL) { 316 CFRelease(uuid); 317 return; 318 } 319 320 CFDictionarySetValue(item->keys, kGSSAttrUUID, uuid); 321 322 CFDictionarySetValue(item->keys, kGSSAttrClass, kGSSAttrClassKerberos); 323 CFDictionarySetValue(item->keys, kGSSAttrNameType, kGSSAttrNameTypeGSSExportedName); 324 325 name = _gss_cred_copy_name(&junk, cred, NULL); 326 if (name == NULL) { 327 CFRelease(uuid); 328 CFRelease(item); 329 return; 330 } 331 332 gss_buffer_desc buffer = { 0, NULL }; 333 maj_stat = gss_export_name(&junk, name, &buffer); 334 gss_release_name(&junk, &name); 335 if (maj_stat) { 336 CFRelease(uuid); 337 CFRelease(item); 338 return; 339 } 340 341 CFDataRef data = CFDataCreate(NULL, buffer.value, buffer.length); 342 CFDictionarySetValue(item->keys, kGSSAttrName, data); 343 CFRelease(data); 344 345 updateTransientValues(item); 346 CFDictionarySetValue(item->keys, kGSSAttrStatusTransient, kCFBooleanTrue); 347 348 CFDictionarySetValue(createContext->c, uuid, item); 349 350 item->gssCredential = GSSCredentialCopyUUID(cred); 351 352 CFRelease(item); 353 CFRelease(uuid); 354 }); 355} 356 357static void 358initCreateContext(struct CreateContext *createContext) 359{ 360 heim_assert(createContext->c == NULL, "init more then once"); 361 362 createContext->c = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 363 heim_assert(createContext->c != NULL, "out of memory"); 364 365 createContext->transitentUUIDs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 366 heim_assert(createContext->transitentUUIDs != NULL, "out of memory"); 367} 368 369static CFMutableDictionaryRef 370copyConfiguration(bool create, CFErrorRef *error) 371{ 372 struct CreateContext createContext = { NULL, NULL }; 373 CFReadStreamRef s; 374 CFDictionaryRef d = NULL, keys; 375 CFURLRef url; 376 377 url = copyConfigurationURL(); 378 if (url == NULL) 379 return NULL; 380 381 s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); 382 CFRelease(url); 383 if (s == NULL) 384 goto out; 385 386 if (!CFReadStreamOpen(s)) { 387 CFRelease(s); 388 goto out; 389 } 390 391 d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, error); 392 CFRelease(s); 393 if (d == NULL) 394 goto out; 395 396 if (CFGetTypeID(d) != CFDictionaryGetTypeID()) 397 goto out; 398 399 initCreateContext(&createContext); 400 401 keys = CFDictionaryGetValue(d, kGSSConfKeys); 402 if (keys == NULL) { 403 CFRelease(createContext.c); 404 createContext.c = NULL; 405 goto out; 406 } 407 408 CFDictionaryApplyFunction(keys, createItem, &createContext); 409 410 out: 411 if (create && createContext.c == NULL) 412 initCreateContext(&createContext); 413 414 if (createContext.c) 415 addTransientKeys(&createContext); 416 417 if (createContext.transitentUUIDs) 418 CFRelease(createContext.transitentUUIDs); 419 if (d) 420 CFRelease(d); 421 422 return createContext.c; 423} 424 425static void 426storeItem(const void *key, const void *value, void *context) 427{ 428 GSSItemRef item = (GSSItemRef)value; 429 430 heim_assert(CFGetTypeID(item) == GSSItemGetTypeID(), "trying to store a non GSSItem"); 431 432 /* if the credential is a transit credential, dont store in store */ 433 if (CFDictionaryGetValue(item->keys, kGSSAttrStatusTransient)) 434 return; 435 CFDictionarySetValue(context, key, item->keys); 436} 437 438static void 439storeConfiguration(CFDictionaryRef conf) 440{ 441 CFMutableDictionaryRef c = NULL, keys = NULL; 442 CFWriteStreamRef s; 443 CFURLRef url; 444 445 url = copyConfigurationURL(); 446 if (url == NULL) 447 return; 448 449 s = CFWriteStreamCreateWithFile(NULL, url); 450 CFRelease(url); 451 if (s == NULL) 452 return; 453 454 keys = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 455 if (keys == NULL) { 456 CFRelease(s); 457 return; 458 } 459 CFDictionaryApplyFunction(conf, storeItem, keys); 460 461 c = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 462 if (c == NULL) { 463 CFRelease(keys); 464 CFRelease(s); 465 return; 466 } 467 468 CFDictionarySetValue(c, kGSSConfKeys, keys); 469 CFRelease(keys); 470 471 if (CFWriteStreamOpen(s)) { 472 CFPropertyListWrite(c, s, kCFPropertyListBinaryFormat_v1_0, 0, NULL); 473 CFWriteStreamClose(s); 474 } 475 476 CFRelease(s); 477 CFRelease(c); 478} 479 480#pragma mark ToGSS conversion 481 482static gss_name_t 483itemCopyGSSName(GSSItemRef item, CFErrorRef *error) 484{ 485 gss_name_t gssname = GSS_C_NO_NAME; 486 CFTypeRef name, type; 487 CFTypeID nametype; 488 gss_buffer_desc buffer; 489 gss_const_OID nameoid; 490 OM_uint32 junk; 491 492 type = CFDictionaryGetValue(item->keys, kGSSAttrNameType); 493 if (type == NULL) 494 return GSS_C_NO_NAME; 495 496 if (CFEqual(type, kGSSAttrNameTypeGSSUsername)) 497 nameoid = GSS_C_NT_USER_NAME; 498 else if (CFEqual(type, kGSSAttrNameTypeGSSHostBasedService)) 499 nameoid = GSS_C_NT_HOSTBASED_SERVICE; 500 else if (CFEqual(type, kGSSAttrNameTypeGSSExportedName)) 501 nameoid = GSS_C_NT_EXPORT_NAME; 502 else 503 return GSS_C_NO_NAME; 504 505 name = CFDictionaryGetValue(item->keys, kGSSAttrName); 506 if (name == NULL) 507 return GSS_C_NO_NAME; 508 509 nametype = CFGetTypeID(name); 510 if (nametype == CFStringGetTypeID()) { 511 buffer.value = rk_cfstring2cstring(name); 512 if (buffer.value == NULL) 513 return GSS_C_NO_NAME; 514 buffer.length = strlen((char *)buffer.value); 515 } else if (nametype == CFDataGetTypeID()) { 516 buffer.value = malloc(CFDataGetLength(name)); 517 if (buffer.value == NULL) 518 return GSS_C_NO_NAME; 519 memcpy(buffer.value, CFDataGetBytePtr(name), CFDataGetLength(name)); 520 buffer.length = CFDataGetLength(name); 521 } else 522 return GSS_C_NO_NAME; 523 524 (void)gss_import_name(&junk, &buffer, nameoid, &gssname); 525 526 return gssname; 527} 528 529static gss_const_OID 530itemToMechOID(GSSItemRef item, CFErrorRef *error) 531{ 532 CFTypeRef type; 533 534 type = CFDictionaryGetValue(item->keys, kGSSAttrClass); 535 if (type == NULL) 536 return GSS_C_NO_OID; 537 538 if (CFEqual(type, kGSSAttrClassKerberos)) 539 return GSS_KRB5_MECHANISM; 540 else if (CFEqual(type, kGSSAttrClassNTLM)) 541 return GSS_NTLM_MECHANISM; 542 else if (CFEqual(type, kGSSAttrClassIAKerb)) 543 return GSS_IAKERB_MECHANISM; 544 545 return GSS_C_NO_OID; 546} 547 548static gss_cred_id_t 549itemToGSSCred(GSSItemRef item, OM_uint32 *lifetime, CFErrorRef *error) 550{ 551 gss_OID_set mechs = GSS_C_NO_OID_SET; 552 OM_uint32 maj_stat, min_stat; 553 gss_cred_id_t gsscred; 554 gss_const_OID mechoid; 555 gss_name_t name; 556 557 if (item->gssCredential) { 558 gsscred = GSSCreateCredentialFromUUID(item->gssCredential); 559 if (gsscred && lifetime) 560 (void)gss_inquire_cred(&min_stat, gsscred, NULL, lifetime, NULL, NULL); 561 return gsscred; 562 } 563 564 mechoid = itemToMechOID(item, error); 565 if (mechoid == NULL) 566 return GSS_C_NO_CREDENTIAL; 567 568 name = itemCopyGSSName(item, error); 569 if (name == NULL) 570 return GSS_C_NO_CREDENTIAL; 571 572 maj_stat = gss_create_empty_oid_set(&min_stat, &mechs); 573 if (maj_stat != GSS_S_COMPLETE) { 574 if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, NULL); 575 gss_release_name(&min_stat, &name); 576 return GSS_C_NO_CREDENTIAL; 577 } 578 579 maj_stat = gss_add_oid_set_member(&min_stat, mechoid, &mechs); 580 if (maj_stat != GSS_S_COMPLETE) { 581 if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, NULL); 582 gss_release_oid_set(&min_stat, &mechs); 583 gss_release_name(&min_stat, &name); 584 return GSS_C_NO_CREDENTIAL; 585 } 586 587 maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE, mechs, 588 GSS_C_INITIATE, &gsscred, NULL, lifetime); 589 gss_release_oid_set(&min_stat, &mechs); 590 gss_release_name(&min_stat, &name); 591 if (maj_stat) { 592 if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, mechoid); 593 return GSS_C_NO_CREDENTIAL; 594 } 595 596 item->gssCredential = GSSCredentialCopyUUID(gsscred); 597 598 return gsscred; 599} 600 601#pragma mark Keychain 602 603static CFTypeRef 604extractCopyPassword(GSSItemRef item, bool getAttributes) 605{ 606#ifndef __APPLE_TARGET_EMBEDDED__ 607 OM_uint32 maj_stat, junk; 608 gss_buffer_desc buffer; 609 CFDataRef password = NULL; 610 char *name, *realm; 611 gss_name_t gssname; 612 CFStringRef val; 613 OSStatus osret; 614 UInt32 length; 615 void *buf; 616 void *ptr; 617 618 /* First look for UUID version of the credential */ 619 620 val = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID); 621 if (val) { 622 CFTypeRef result = NULL; 623 624 name = rk_cfstring2cstring(val); 625 if (name == NULL) 626 return NULL; 627 628 CFDataRef dataoid = CFDataCreate(NULL, (void *)name, strlen(name)); 629 free(name); 630 631 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 632 if (query == NULL) { 633 CFRelease(dataoid); 634 return NULL; 635 } 636 637 CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword); 638 CFDictionaryAddValue(query, kSecAttrType, kGSSSecPasswordType); 639 CFDictionaryAddValue(query, kSecAttrGeneric, dataoid); 640 CFDictionaryAddValue(query, kSecAttrService, CFSTR("GSS")); 641 CFRelease(dataoid); 642 if (getAttributes) 643 CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue); 644 else 645 CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue); 646 647 osret = SecItemCopyMatching(query, &result); 648 CFRelease(query); 649 if (osret == noErr) { 650 CFTypeID expectedType = getAttributes ? CFDictionaryGetTypeID() : CFDataGetTypeID(); 651 if (CFGetTypeID(result) != expectedType) { 652 CFRelease(result); 653 return NULL; 654 } 655 return result; 656 } 657 } 658 659 if (getAttributes) 660 return NULL; 661 662 /* check for legacy kerberos version */ 663 664 gssname = itemCopyGSSName(item, NULL); 665 if (gssname == NULL) 666 return NULL; 667 668 maj_stat = gss_display_name(&junk, gssname, &buffer, NULL); 669 gss_release_name(&junk, &gssname); 670 if (maj_stat) 671 return NULL; 672 673 ptr = memchr(buffer.value, '@', buffer.length); 674 if (ptr == NULL) { 675 asprintf(&name, "%.*s", (int)buffer.length, buffer.value); 676 realm = strdup(""); 677 } else { 678 size_t len = (int)((char *)ptr - (char *)buffer.value); 679 asprintf(&name, "%.*s", (int)len, (char *)buffer.value); 680 asprintf(&realm, "%.*s", (int)(buffer.length - len - 1), (char *)buffer.value + len + 1); 681 } 682 gss_release_buffer(&junk, &buffer); 683 684 osret = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(realm), realm, 685 (UInt32)strlen(name), name, 686 &length, &buf, NULL); 687 free(name); 688 free(realm); 689 if (osret != noErr) 690 return NULL; 691 692 password = CFDataCreate(NULL, buf, length); 693 SecKeychainItemFreeContent(NULL, buf); 694 695 return password; 696#else /* __APPLE_TARGET_EMBEDDED__ */ 697 return NULL; 698#endif /* !__APPLE_TARGET_EMBEDDED__ */ 699} 700 701static Boolean 702storePassword(GSSItemRef item, CFDictionaryRef attributes, CFStringRef password, CFErrorRef *error) 703{ 704#ifndef __APPLE_TARGET_EMBEDDED__ 705 CFTypeRef itemRef = NULL; 706 OSStatus osret; 707 CFStringRef uuidname; 708 char *name, *pw; 709 710 uuidname = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID); 711 if (uuidname == NULL && CFGetTypeID(uuidname) != CFStringGetTypeID()) 712 return false; 713 714 name = rk_cfstring2cstring(uuidname); 715 if (name == NULL) 716 return false; 717 718 CFDataRef dataoid = CFDataCreate(NULL, (void *)name, strlen(name)); 719 free(name); 720 if (dataoid == NULL) 721 return false; 722 723 CFMutableDictionaryRef itemAttr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 724 if (itemAttr == NULL) { 725 CFRelease(dataoid); 726 return false; 727 } 728 729 CFDictionaryAddValue(itemAttr, kSecAttrGeneric, dataoid); 730 CFRelease(dataoid); 731 732 CFStringRef gssclass = CFDictionaryGetValue(item->keys, kGSSAttrClass); 733 if (gssclass == NULL) 734 gssclass = CFSTR("unknown mech"); 735 CFStringRef displayname = CFDictionaryGetValue(item->keys, kGSSAttrNameDisplay); 736 if (displayname == NULL) 737 displayname = uuidname; 738 739 CFStringRef description = CFStringCreateWithFormat(NULL, NULL, CFSTR("GSS %@ password for %@"), gssclass, displayname); 740 if (description == NULL) { 741 CFRelease(itemAttr); 742 return false; 743 } 744 CFDictionaryAddValue(itemAttr, kSecAttrDescription, description); 745 CFDictionaryAddValue(itemAttr, kSecAttrLabel, description); 746 CFRelease(description); 747 748 CFDictionaryAddValue(itemAttr, kSecClass, kSecClassGenericPassword); 749 CFDictionaryAddValue(itemAttr, kSecAttrType, kGSSSecPasswordType); 750 CFDictionaryAddValue(itemAttr, kSecAttrAccount, uuidname); 751 CFDictionaryAddValue(itemAttr, kSecAttrService, CFSTR("GSS")); 752 753 pw = rk_cfstring2cstring(password); 754 if (pw == NULL) { 755 CFRelease(itemAttr); 756 return false; 757 } 758 CFDataRef datapw = CFDataCreate(NULL, (void *)pw, strlen(pw)); 759 memset(pw, 0, strlen(pw)); 760 free(pw); 761 if (datapw == NULL) { 762 CFRelease(itemAttr); 763 return false; 764 } 765 CFDictionaryAddValue(itemAttr, kSecValueData, datapw); 766 CFRelease(datapw); 767 768 CFTypeRef access = (void *)CFDictionaryGetValue(attributes, kSecAttrAccessGroup); 769 if (access) 770 CFDictionaryAddValue(itemAttr, kSecAttrAccessGroup, access); 771 772 osret = SecItemAdd(itemAttr, &itemRef); 773 CFRelease(itemAttr); 774 if (osret) 775 return false; 776 777 CFRelease(itemRef); 778 779 return true; 780#else 781 return false; 782#endif /* !__APPLE_TARGET_EMBEDDED__ */ 783} 784 785#pragma mark Matching 786 787struct iterateAttr { 788 GSSItemRef item; 789 CFDictionaryRef attrs; 790 CFErrorRef error; 791 bool match; 792}; 793 794static bool 795applyClassItems(GSSItemRef item, CFDictionaryRef attributes, 796 CFDictionaryApplierFunction applier, CFErrorRef *error) 797{ 798 struct iterateAttr mattrs; 799 CFDictionaryRef classattrs; 800 CFTypeRef itemclass; 801 802 if (error) 803 *error = NULL; 804 805 itemclass = CFDictionaryGetValue(attributes, kGSSAttrClass); 806 if (itemclass == NULL) { 807 itemclass = CFDictionaryGetValue(item->keys, kGSSAttrClass); 808 if (itemclass == NULL) 809 return false; 810 } 811 812 classattrs = CFDictionaryGetValue(valid_set_types, itemclass); 813 if (classattrs == NULL) 814 return false; 815 816 mattrs.attrs = attributes; 817 mattrs.item = item; 818 mattrs.error = NULL; 819 mattrs.match = true; 820 821 CFDictionaryApplyFunction(classattrs, applier, &mattrs); 822 823 if (mattrs.error) { 824 if (error) 825 *error = mattrs.error; 826 else 827 CFRelease(mattrs.error); 828 return false; 829 } 830 return mattrs.match; 831} 832 833static void 834matchAttr(const void *key, const void *value, void *context) 835{ 836 struct iterateAttr *mattrs = context; 837 838 if (!mattrs->match) 839 return; 840 841 CFTypeRef attrvalue = CFDictionaryGetValue(mattrs->attrs, key); 842 CFTypeRef itemvalue = CFDictionaryGetValue(mattrs->item->keys, key); 843 844 if (attrvalue && itemvalue && !CFEqual(attrvalue, itemvalue)) 845 mattrs->match = false; 846} 847 848static bool 849matchAttrs(GSSItemRef item, CFDictionaryRef attrs) 850{ 851 return applyClassItems(item, attrs, matchAttr, NULL); 852} 853 854struct search { 855 CFDictionaryRef attrs; 856 CFMutableArrayRef result; 857}; 858 859static void 860searchFunction(const void *key, const void *value, void *context) 861{ 862 struct search *search = context; 863 GSSItemRef item = (GSSItemRef)value; 864 865 if (matchAttrs(item, search->attrs)) 866 CFArrayAppendValue(search->result, item); 867} 868 869 870static CFMutableArrayRef 871searchCopyResult(CFMutableDictionaryRef conf, CFDictionaryRef attrs) 872{ 873 CFMutableArrayRef result; 874 struct search search; 875 876 result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 877 if (result == NULL) 878 return NULL; 879 880 search.attrs = attrs; 881 search.result = result; 882 883 CFDictionaryApplyFunction(conf, searchFunction, &search); 884 885 if (CFArrayGetCount(result) == 0) { 886 CFRelease(result); 887 return NULL; 888 } 889 890 return result; 891} 892 893static void 894modifyAttributes(const void *key, const void *value, void *context) 895{ 896 struct iterateAttr *mattrs = context; 897 CFTypeRef newValue = CFDictionaryGetValue(mattrs->attrs, key); 898 899 /* check if this valid should be stored in the permanent store */ 900 if (!CFBooleanGetValue(value)) 901 return; 902 903 if (newValue) 904 CFDictionarySetValue(mattrs->item->keys, key, newValue); 905 906} 907 908static bool 909modifyItem(GSSItemRef item, CFDictionaryRef attributes, CFErrorRef *error) 910{ 911 bool res = applyClassItems(item, attributes, modifyAttributes, error); 912 if (!res) 913 return res; 914 915 CFTypeRef cred = CFDictionaryGetValue(attributes, kGSSAttrCredentialPassword); 916 917 if (cred && CFGetTypeID(cred) == CFStringGetTypeID()) { 918 bool shouldStore = true, mustStore = false; 919 CFBooleanRef store; 920 921 store = CFDictionaryGetValue(attributes, kGSSAttrCredentialStore); 922 if (store) 923 shouldStore = mustStore = (CFGetTypeID(store) == CFBooleanGetTypeID() && CFBooleanGetValue(store)); 924 925 if (shouldStore) { 926 res = storePassword(item, attributes, cred, error); 927 if (res && mustStore) 928 return res; 929 } 930 } 931 return true; 932} 933 934 935static void 936validateAttributes(const void *key, const void *value, void *context) 937{ 938 struct iterateAttr *mattrs = context; 939 940 if (mattrs->error != NULL) 941 return; 942 943 Boolean b = CFBooleanGetValue(value); 944 if (b) { 945 CFTypeRef status = CFDictionaryGetValue(mattrs->attrs, key); 946 if (status == NULL) 947 mattrs->error = CFErrorCreate(NULL, CFSTR("com.apple.GSS"), EINVAL, NULL); 948 } 949} 950 951 952static bool 953validateItem(GSSItemRef item, CFErrorRef *error) 954{ 955 return applyClassItems(item, item->keys, validateAttributes, error); 956} 957 958/* 959 * 960 */ 961 962static void 963updateTransientValues(GSSItemRef item) 964{ 965 OM_uint32 maj_stat, junk, lifetime = 0; 966 gss_cred_id_t gsscred = itemToGSSCred(item, &lifetime, NULL); 967 gss_buffer_desc buffer; 968 969 CFDictionaryRef attributes = extractCopyPassword(item, 1); 970 if (attributes) { 971 CFDictionarySetValue(item->keys, kGSSAttrCredentialPassword, kCFBooleanTrue); 972 CFRelease(attributes); 973 } 974 975 if (gsscred) { 976 977 CFDictionarySetValue(item->keys, kGSSAttrCredentialExists, kCFBooleanTrue); 978 979 maj_stat = gss_cred_label_get(&junk, gsscred, "kcm-status", &buffer); 980 if (maj_stat == GSS_S_COMPLETE) { 981 CFDataRef data = CFDataCreate(NULL, buffer.value, buffer.length); 982 983 if (data) { 984 CFDictionarySetValue(item->keys, kGSSAttrStatusAutoAcquireStatus, data); 985 CFRelease(data); 986 } 987 gss_release_buffer(&junk, &buffer); 988 } 989 990#define TimeIntervalSince1970 978307200.0 991 CFDateRef date; 992 if (lifetime) 993 date = CFDateCreate(NULL, time(NULL) - TimeIntervalSince1970 + lifetime); 994 else 995 date = CFDateCreate(NULL, 0.0); /* We don't know when expired, so make it expired */ 996 997 CFDictionarySetValue(item->keys, kGSSAttrTransientExpire, date); 998 CFRelease(date); 999 1000 { 1001 gss_buffer_set_t buffers = NULL; 1002 1003 maj_stat = gss_inquire_cred_by_oid(&junk, gsscred, GSS_C_CRED_GET_DEFAULT, &buffers); 1004 gss_release_buffer_set(&junk, &buffers); 1005 if (maj_stat == GSS_S_COMPLETE) 1006 CFDictionarySetValue(item->keys, kGSSAttrTransientDefaultInClass, kCFBooleanTrue); 1007 } 1008 1009 CFRelease(gsscred); 1010 1011 } else { 1012 CFDictionaryRemoveValue(item->keys, kGSSAttrTransientExpire); 1013 } 1014 1015 gss_name_t gssname = itemCopyGSSName(item, NULL); 1016 if (gssname) { 1017 1018 maj_stat = gss_display_name(&junk, gssname, &buffer, NULL); 1019 if (maj_stat == GSS_S_COMPLETE) { 1020 CFStringRef name; 1021 name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%.*s"), (int)buffer.length, buffer.value); 1022 gss_release_buffer(&junk, &buffer); 1023 if (name) { 1024 CFDictionarySetValue(item->keys, kGSSAttrNameDisplay, name); 1025 CFRelease(name); 1026 } 1027 } 1028 gss_release_name(&junk, &gssname); 1029 } 1030} 1031 1032#pragma mark API 1033 1034GSSItemRef 1035GSSItemAdd(CFDictionaryRef attributes, CFErrorRef *error) 1036{ 1037 CFMutableDictionaryRef conf; 1038 GSSItemRef item; 1039 CFStringRef uuidstr = NULL; 1040 CFMutableArrayRef items; 1041 1042 gss_init(); 1043 1044 heim_assert(attributes != NULL, "no attributes passed to GSSItemAdd"); 1045 1046 if (error) 1047 *error = NULL; 1048 1049 conf = copyConfiguration(true, error); 1050 if (conf == NULL) 1051 return NULL; 1052 1053 /* refuse to add dups */ 1054 items = searchCopyResult(conf, attributes); 1055 if (items) { 1056 CFRelease(items); 1057 CFRelease(conf); 1058 return NULL; 1059 } 1060 1061 item = GSSCreateItem(NULL, NULL); 1062 if (item == NULL) 1063 goto out; 1064 1065 uuidstr = CreateNewUUID(item); 1066 if (uuidstr == NULL) { 1067 CFRelease(item); 1068 item = NULL; 1069 goto out; 1070 } 1071 1072 if (!modifyItem(item, attributes, error)) { 1073 CFRelease(item); 1074 item = NULL; 1075 goto out; 1076 } 1077 1078 if (!validateItem(item, error)) { 1079 CFRelease(item); 1080 item = NULL; 1081 goto out; 1082 } 1083 1084 updateTransientValues(item); 1085 1086 CFDictionarySetValue(conf, uuidstr, item); 1087 1088 storeConfiguration(conf); 1089 1090out: 1091 if (uuidstr) 1092 CFRelease(uuidstr); 1093 if (conf) 1094 CFRelease(conf); 1095 1096 return item; 1097} 1098 1099Boolean 1100GSSItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) 1101{ 1102 CFMutableDictionaryRef conf; 1103 CFMutableArrayRef items; 1104 1105 gss_init(); 1106 1107 heim_assert(query != NULL, "no attributes passed to GSSItemAdd"); 1108 1109 if (error) 1110 *error = NULL; 1111 1112 conf = copyConfiguration(true, error); 1113 if (conf == NULL) 1114 return false; 1115 1116 items = searchCopyResult(conf, query); 1117 if (items == NULL) { 1118 CFRelease(conf); 1119 return false; 1120 } 1121 1122 CFIndex n, count = CFArrayGetCount(items); 1123 Boolean res = true; 1124 for (n = 0; n < count; n++) { 1125 GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n); 1126 1127 res = modifyItem(item, attributesToUpdate, error); 1128 if (res) 1129 break; 1130 } 1131 CFRelease(items); 1132 1133 if (res) 1134 storeConfiguration(conf); 1135 1136 CFRelease(conf); 1137 1138 return res; 1139} 1140 1141static Boolean 1142ItemDeleteItem(CFMutableDictionaryRef conf, GSSItemRef item, CFErrorRef *error) 1143{ 1144 CFTypeRef uuidstr = CFDictionaryGetValue(item->keys, kGSSAttrUUID); 1145 1146 if (uuidstr == NULL) 1147 return false; 1148 1149 gss_cred_id_t cred = itemToGSSCred(item, NULL, NULL); 1150 if (cred) { 1151 OM_uint32 junk; 1152 gss_destroy_cred(&junk, &cred); 1153 } 1154 CFDictionaryRemoveValue(conf, uuidstr); 1155 1156 return true; 1157} 1158 1159Boolean 1160GSSItemDeleteItem(GSSItemRef item, CFErrorRef *error) 1161{ 1162 CFMutableDictionaryRef conf; 1163 Boolean res = false; 1164 1165 conf = copyConfiguration(false, error); 1166 if (conf == NULL) 1167 return false; 1168 1169 res = ItemDeleteItem(conf, item, error); 1170 if (res) 1171 storeConfiguration(conf); 1172 1173 CFRelease(conf); 1174 1175 return res; 1176} 1177 1178Boolean 1179GSSItemDelete(CFDictionaryRef query, CFErrorRef *error) 1180{ 1181 CFMutableDictionaryRef conf; 1182 CFMutableArrayRef items; 1183 1184 gss_init(); 1185 1186 heim_assert(query != NULL, "no attributes passed to GSSItemDelete"); 1187 1188 if (error) 1189 *error = NULL; 1190 1191 conf = copyConfiguration(false, error); 1192 if (conf == NULL) 1193 return false; 1194 1195 items = searchCopyResult(conf, query); 1196 if (items == NULL) { 1197 CFRelease(conf); 1198 return false; 1199 } 1200 1201 CFIndex n, count = CFArrayGetCount(items); 1202 Boolean res = false; 1203 1204 for (n = 0; n < count; n++) { 1205 GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n); 1206 1207 if (ItemDeleteItem(conf, item, error)) 1208 res = true; 1209 } 1210 CFRelease(items); 1211 1212 if (res) 1213 storeConfiguration(conf); 1214 1215 CFRelease(conf); 1216 1217 return res; 1218} 1219 1220CFArrayRef 1221GSSItemCopyMatching(CFDictionaryRef query, CFErrorRef *error) 1222{ 1223 CFMutableDictionaryRef conf; 1224 CFMutableArrayRef items; 1225 1226 gss_init(); 1227 1228 if (error) 1229 *error = NULL; 1230 1231 conf = copyConfiguration(true, error); 1232 if (conf == NULL) 1233 return NULL; 1234 1235 items = searchCopyResult(conf, query); 1236 CFRelease(conf); 1237 if (items == NULL) 1238 return NULL; 1239 1240 /* 1241 * Update the transient info 1242 */ 1243 1244 CFIndex n, count = CFArrayGetCount(items); 1245 for (n = 0; n < count; n++) { 1246 GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n); 1247 1248 updateTransientValues(item); 1249 } 1250 1251 return items; 1252} 1253 1254/* 1255 * 1256 */ 1257 1258struct __GSSOperationType { 1259 void (*func)(GSSItemRef, CFDictionaryRef, dispatch_queue_t, GSSItemOperationCallbackBlock); 1260}; 1261 1262static void 1263itemAcquire(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1264{ 1265 gss_cred_id_t gsscred = GSS_C_NO_CREDENTIAL; 1266 CFMutableDictionaryRef gssattrs = NULL; 1267 gss_name_t name = GSS_C_NO_NAME; 1268 CFErrorRef error = NULL; 1269 gss_const_OID mech; 1270 CFTypeRef cred = NULL; 1271 OM_uint32 junk; 1272 1273 name = itemCopyGSSName(item, &error); 1274 if (name == NULL) 1275 goto out; 1276 1277 mech = itemToMechOID(item, &error); 1278 if (mech == GSS_C_NO_OID) 1279 goto out; 1280 1281 gssattrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1282 if (gssattrs == NULL) 1283 goto out; 1284 1285 if (options) { 1286 cred = CFDictionaryGetValue(options, kGSSAttrCredentialPassword); 1287 if (cred) 1288 CFDictionarySetValue(gssattrs, kGSSICPassword, cred); 1289 else { 1290 cred = CFDictionaryGetValue(options, kGSSAttrCredentialSecIdentity); 1291 if (cred) 1292 CFDictionarySetValue(gssattrs, kGSSICCertificate, cred); 1293 } 1294 } 1295 1296 /* 1297 * Force Kerberos/kcm/gsscred layer to use the the same UUID 1298 */ 1299 CFTypeRef uuidstr = CFDictionaryGetValue(item->keys, kGSSAttrUUID); 1300 if (uuidstr) { 1301 CFStringRef cacheName = CFStringCreateWithFormat(NULL, NULL, CFSTR("API:%@"), uuidstr); 1302 if (cacheName == NULL) 1303 goto out; 1304 1305 CFDictionarySetValue(gssattrs, kGSSICKerberosCacheName, cacheName); 1306 CFRelease(cacheName); 1307 } 1308 1309 if (cred == NULL) { 1310 1311 if ((cred = extractCopyPassword(item, 0)) != NULL) { 1312 CFDictionarySetValue(gssattrs, kGSSICPassword, cred); 1313 CFRelease(cred); 1314 cred = NULL; 1315 } else if ((cred = NULL /* XXX extract certfigcate */) != NULL) { 1316 /* CFDictionarySetValue(gssattrs, kGSSAttrCredentialSecIdentity, cred); */ 1317 } 1318 } 1319 1320 (void)gss_aapl_initial_cred(name, mech, gssattrs, &gsscred, &error); 1321 1322 if (item->gssCredential) { 1323 CFRelease(item->gssCredential); 1324 item->gssCredential = NULL; 1325 } 1326 1327 if (gsscred) 1328 item->gssCredential = GSSCredentialCopyUUID(gsscred); 1329 1330 updateTransientValues(item); 1331 1332 out: 1333 if (gssattrs) 1334 CFRelease(gssattrs); 1335 if (name) 1336 gss_release_name(&junk, &name); 1337 1338 dispatch_async(q, ^{ 1339 callback(gsscred, error); 1340 if (error) 1341 CFRelease(error); 1342 if (gsscred) 1343 CFRelease(gsscred); 1344 }); 1345} 1346 1347const struct __GSSOperationType __kGSSOperationAcquire = { 1348 itemAcquire 1349}; 1350 1351static void 1352itemDestroyTransient(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1353{ 1354 CFErrorRef error = NULL; 1355 gss_cred_id_t gsscred; 1356 OM_uint32 junk; 1357 CFBooleanRef result = kCFBooleanFalse; 1358 1359 gsscred = itemToGSSCred(item, NULL, &error); 1360 if (gsscred) { 1361 (void)gss_destroy_cred(&junk, &gsscred); 1362 result = kCFBooleanTrue; 1363 } 1364 1365 dispatch_async(q, ^{ 1366 callback(result, error); 1367 if (error) 1368 CFRelease(error); 1369 }); 1370} 1371 1372const struct __GSSOperationType __kGSSOperationDestoryTransient = { 1373 itemDestroyTransient 1374}; 1375const struct __GSSOperationType __kGSSOperationDestroyTransient = { 1376 itemDestroyTransient 1377}; 1378 1379 1380static void 1381itemGetGSSCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1382{ 1383 gss_cred_id_t gsscred; 1384 CFErrorRef error = NULL; 1385 1386 gsscred = itemToGSSCred(item, NULL, &error); 1387 1388 dispatch_async(q, ^{ 1389 callback(gsscred, error); 1390 if (error) 1391 CFRelease(error); 1392 }); 1393} 1394 1395const struct __GSSOperationType __kGSSOperationGetGSSCredential = { 1396 itemGetGSSCredential 1397}; 1398 1399static void 1400itemCredentialDiagnostics(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1401{ 1402 gss_cred_id_t gsscred; 1403 CFErrorRef error = NULL; 1404 CFMutableArrayRef array = NULL; 1405 OM_uint32 junk; 1406 1407 gsscred = itemToGSSCred(item, NULL, &error); 1408 if (gsscred) { 1409 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; 1410 1411 if (gss_inquire_cred_by_oid(&junk, gsscred, GSS_C_CRED_DIAG, &data_set) != GSS_S_COMPLETE || data_set->count < 1) 1412 goto out; 1413 1414 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1415 if (array == NULL) 1416 goto out; 1417 1418 OM_uint32 n; 1419 1420 for (n = 0; n < data_set->count; n++) { 1421 CFDataRef data = NULL; 1422 1423 data = CFDataCreate(NULL, data_set->elements[n].value, data_set->elements[n].length); 1424 if (data) { 1425 CFArrayAppendValue(array, data); 1426 CFRelease(data); 1427 } 1428 } 1429 gss_release_buffer_set(&junk, &data_set); 1430 CFRelease(gsscred); 1431 } 1432 1433 out: 1434 1435 dispatch_async(q, ^{ 1436 callback(array, error); 1437 if (array) 1438 CFRelease(array); 1439 if (error) 1440 CFRelease(error); 1441 }); 1442} 1443 1444const struct __GSSOperationType __kGSSOperationCredentialDiagnostics = { 1445 itemCredentialDiagnostics 1446}; 1447 1448static void 1449itemChangePassword(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1450{ 1451 CFMutableDictionaryRef chpwdopts = NULL; 1452 gss_name_t gssname = NULL; 1453 CFErrorRef error = NULL; 1454 gss_const_OID mech; 1455 1456 CFStringRef oldpw = CFDictionaryGetValue(options, kGSSOperationChangePasswordOldPassword); 1457 CFStringRef newpw = CFDictionaryGetValue(options, kGSSOperationChangePasswordNewPassword); 1458 1459 if (oldpw == NULL || newpw == NULL) 1460 goto out; 1461 1462 mech = itemToMechOID(item, &error); 1463 if (mech == GSS_C_NO_OID) 1464 goto out; 1465 1466 chpwdopts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1467 if (chpwdopts == NULL) 1468 goto out; 1469 1470 gssname = itemCopyGSSName(item, &error); 1471 if (gssname) { 1472 1473 CFDictionaryAddValue(chpwdopts, kGSSChangePasswordOldPassword, oldpw); 1474 CFDictionaryAddValue(chpwdopts, kGSSChangePasswordNewPassword, newpw); 1475 1476 (void)gss_aapl_change_password(gssname, mech, chpwdopts, &error); 1477 } 1478 out: 1479 if (gssname) 1480 CFRelease(gssname); 1481 1482 dispatch_async(q, ^{ 1483 callback(NULL, error); 1484 if (error) 1485 CFRelease(error); 1486 }); 1487} 1488 1489const struct __GSSOperationType __kGSSOperationChangePassword = { 1490 itemChangePassword 1491}; 1492 1493/* 1494 * 1495 */ 1496 1497static void 1498itemSetDefault(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1499{ 1500 CFErrorRef error = NULL; 1501 gss_cred_id_t gsscred; 1502 1503 gsscred = itemToGSSCred(item, NULL, &error); 1504 if (gsscred) { 1505 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; 1506 OM_uint32 maj_stat, min_stat, junk; 1507 1508 maj_stat = gss_inquire_cred_by_oid(&min_stat, gsscred, GSS_C_CRED_SET_DEFAULT, &data_set); 1509 gss_release_buffer_set(&junk, &data_set); 1510 CFRelease(gsscred); 1511 if (maj_stat != GSS_S_COMPLETE) 1512 error = _gss_mg_create_cferror(maj_stat, min_stat, NULL); 1513 } 1514 1515 dispatch_async(q, ^{ 1516 callback(NULL, error); 1517 if (error) 1518 CFRelease(error); 1519 }); 1520} 1521 1522const struct __GSSOperationType __kGSSOperationSetDefault = { 1523 itemSetDefault 1524}; 1525 1526 1527/* 1528 * 1529 */ 1530 1531static void 1532itemRenewCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1533{ 1534 CFErrorRef error = NULL; 1535 gss_cred_id_t gsscred; 1536 OM_uint32 maj_stat, min_stat; 1537 1538 gsscred = itemToGSSCred(item, NULL, &error); 1539 if (gsscred) { 1540 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; 1541 1542 maj_stat = gss_inquire_cred_by_oid(&min_stat, gsscred, GSS_C_CRED_RENEW, &data_set); 1543 gss_release_buffer_set(&min_stat, &data_set); 1544 CFRelease(gsscred); 1545 if (maj_stat != GSS_S_COMPLETE) 1546 error = _gss_mg_create_cferror(maj_stat, min_stat, NULL); 1547 } 1548 1549 dispatch_async(q, ^{ 1550 callback(NULL, error); 1551 if (error) 1552 CFRelease(error); 1553 }); 1554} 1555 1556const struct __GSSOperationType __kGSSOperationRenewCredential = { 1557 itemRenewCredential 1558}; 1559 1560/* 1561 * 1562 */ 1563 1564static void 1565itemRemoveBackingCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback) 1566{ 1567 CFErrorRef error = NULL; 1568 CFStringRef uuidname; 1569 1570 uuidname = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID); 1571 if (uuidname == NULL || CFGetTypeID(uuidname) != CFStringGetTypeID()) 1572 goto out; 1573 1574 CFMutableDictionaryRef itemAttr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1575 if (itemAttr == NULL) { 1576 goto out; 1577 } 1578 1579 CFDictionaryAddValue(itemAttr, kSecClass, kSecClassGenericPassword); 1580 CFDictionaryAddValue(itemAttr, kSecAttrType, kGSSSecPasswordType); 1581 CFDictionaryAddValue(itemAttr, kSecAttrAccount, uuidname); 1582 CFDictionaryAddValue(itemAttr, kSecAttrService, CFSTR("GSS")); 1583 1584 (void)SecItemDelete(itemAttr); 1585 CFRelease(itemAttr); 1586 1587 out: 1588 dispatch_async(q, ^{ 1589 callback(NULL, error); 1590 if (error) 1591 CFRelease(error); 1592 }); 1593} 1594 1595 1596const struct __GSSOperationType __kGSSOperationRemoveBackingCredential = { 1597 itemRemoveBackingCredential 1598}; 1599 1600 1601/* 1602 * 1603 */ 1604 1605Boolean 1606GSSItemOperation(GSSItemRef item, GSSOperation op, CFDictionaryRef options, 1607 dispatch_queue_t q, GSSItemOperationCallbackBlock func) 1608{ 1609 GSSItemOperationCallbackBlock callback; 1610 1611 gss_init(); 1612 1613 callback = (GSSItemOperationCallbackBlock)Block_copy(func); 1614 1615 CFRetain(item); 1616 if (options) 1617 CFRetain(options); 1618 1619 dispatch_async(bgq, ^{ 1620 op->func(item, options, q, callback); 1621 Block_release(callback); 1622 CFRelease(item); 1623 if (options) 1624 CFRelease(options); 1625 }); 1626 1627 return true; 1628} 1629 1630/* 1631 * 1632 */ 1633 1634CFTypeRef 1635GSSItemGetValue(GSSItemRef item, CFStringRef key) 1636{ 1637 int checkNeeded = 0; 1638 1639 if (CFDictionaryGetValue(transient_types, key) 1640 && notify_check(notify_token, &checkNeeded) == NOTIFY_STATUS_OK 1641 && checkNeeded) 1642 updateTransientValues(item); 1643 1644 return CFDictionaryGetValue(item->keys, key); 1645} 1646