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