1/* 2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <Security/SecBase.h> 25#include <Security/SecKeychainItem.h> 26#include <Security/SecKeychainItemPriv.h> 27 28#include <security_keychain/Keychains.h> 29#include <security_keychain/KeyItem.h> 30#include <security_keychain/Item.h> 31#include <security_keychain/Certificate.h> 32#include <security_keychain/Identity.h> 33#include <security_keychain/KCCursor.h> // @@@ Remove this when SecKeychainItemFindFirst moves to SecKeychainSearch 34 35#include <securityd_client/dictionary.h> 36#include <security_cdsa_utilities/Schema.h> 37#include <Security/cssmapplePriv.h> 38 39#include "SecBridge.h" 40#include "KCExceptions.h" 41#include "Access.h" 42#include "SecKeychainItemExtendedAttributes.h" 43 44// 45// Given a polymorphic Sec type object, return 46// its AclBearer component. 47// Note: Login ACLs are not hooked into this layer; 48// modules or attachments have no Sec* layer representation. 49// 50static 51RefPointer<AclBearer> aclBearer(CFTypeRef itemRef) 52{ 53 // well, exactly what kind of something are you? 54 CFTypeID id = CFGetTypeID(itemRef); 55 if (id == gTypes().ItemImpl.typeID) { 56 // keychain item. If it's in a protected group, return the group key 57 if (SSGroup group = ItemImpl::required(SecKeychainItemRef(itemRef))->group()) 58 return &*group; 59 } else if (id == gTypes().KeyItem.typeID) { 60 // key item, return the key itself. 61 if (CssmClient::Key key = KeyItem::required(SecKeyRef(itemRef))->key()) 62 return &*key; 63 } else if (id == gTypes().KeychainImpl.typeID) { 64 // keychain (this yields the database ACL) 65 //@@@ not hooked up yet 66 } 67 // Guess not. Bummer 68 MacOSError::throwMe(errSecNoAccessForItem); 69} 70 71 72CFTypeID 73SecKeychainItemGetTypeID(void) 74{ 75 BEGIN_SECAPI 76 77 return gTypes().ItemImpl.typeID; 78 79 END_SECAPI1(_kCFRuntimeNotATypeID) 80} 81 82 83OSStatus 84SecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList *attrList, 85 UInt32 length, const void *data, SecKeychainRef keychainRef, 86 SecAccessRef initialAccess, SecKeychainItemRef *itemRef) 87{ 88 BEGIN_SECAPI 89 KCThrowParamErrIf_(length!=0 && data==NULL); 90 Item item(itemClass, attrList, length, data); 91 if (initialAccess) 92 item->setAccess(Access::required(initialAccess)); 93 94 Keychain keychain = nil; 95 try 96 { 97 keychain = Keychain::optional(keychainRef); 98 if ( !keychain->exists() ) 99 { 100 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. 101 } 102 } 103 catch(...) 104 { 105 keychain = globals().storageManager.defaultKeychainUI(item); 106 } 107 108 keychain->add(item); 109 if (itemRef) 110 *itemRef = item->handle(); 111 END_SECAPI 112} 113 114 115OSStatus 116SecKeychainItemModifyContent(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data) 117{ 118 BEGIN_SECAPI 119 Item item = ItemImpl::required(itemRef); 120 item->modifyContent(attrList, length, data); 121 END_SECAPI 122} 123 124 125OSStatus 126SecKeychainItemCopyContent(SecKeychainItemRef itemRef, SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData) 127{ 128 BEGIN_SECAPI 129 Item item = ItemImpl::required(itemRef); 130 item->getContent(itemClass, attrList, length, outData); 131 END_SECAPI 132} 133 134 135OSStatus 136SecKeychainItemFreeContent(SecKeychainAttributeList *attrList, void *data) 137{ 138 BEGIN_SECAPI 139 ItemImpl::freeContent(attrList, data); 140 END_SECAPI 141} 142 143 144OSStatus 145SecKeychainItemModifyAttributesAndData(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data) 146{ 147 BEGIN_SECAPI 148 Item item = ItemImpl::required(itemRef); 149 item->modifyAttributesAndData(attrList, length, data); 150 END_SECAPI 151} 152 153 154OSStatus 155SecKeychainItemCopyAttributesAndData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData) 156{ 157 BEGIN_SECAPI 158 Item item = ItemImpl::required(itemRef); 159 item->getAttributesAndData(info, itemClass, attrList, length, outData); 160 END_SECAPI 161} 162 163 164OSStatus 165SecKeychainItemFreeAttributesAndData(SecKeychainAttributeList *attrList, void *data) 166{ 167 BEGIN_SECAPI 168 ItemImpl::freeAttributesAndData(attrList, data); 169 END_SECAPI 170} 171 172 173OSStatus 174SecKeychainItemDelete(SecKeychainItemRef itemRef) 175{ 176 BEGIN_SECAPI 177 Item item = ItemImpl::required( itemRef ); 178 Keychain keychain = item->keychain(); 179 // item must be persistent. 180 KCThrowIf_( !keychain, errSecInvalidItemRef ); 181 182 /* 183 * Before deleting the item, delete any existing Extended Attributes. 184 */ 185 OSStatus ortn; 186 CFArrayRef attrNames = NULL; 187 ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, NULL); 188 if(ortn == errSecSuccess) { 189 CFIndex numAttrs = CFArrayGetCount(attrNames); 190 for(CFIndex dex=0; dex<numAttrs; dex++) { 191 CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex); 192 /* setting value to NULL ==> delete */ 193 SecKeychainItemSetExtendedAttribute(itemRef, attrName, NULL); 194 } 195 } 196 197 /* now delete the item */ 198 keychain->deleteItem( item ); 199 END_SECAPI 200} 201 202 203OSStatus 204SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef, SecKeychainRef* keychainRef) 205{ 206 BEGIN_SECAPI 207 // make sure this item has a keychain 208 Keychain kc = ItemImpl::required(itemRef)->keychain (); 209 if (kc == NULL) 210 { 211 MacOSError::throwMe(errSecNoSuchKeychain); 212 } 213 214 Required(keychainRef) = kc->handle(); 215 END_SECAPI 216} 217 218 219OSStatus 220SecKeychainItemCreateCopy(SecKeychainItemRef itemRef, SecKeychainRef destKeychainRef, 221 SecAccessRef initialAccess, SecKeychainItemRef *itemCopy) 222{ 223 BEGIN_SECAPI 224 Item copy = ItemImpl::required(itemRef)->copyTo(Keychain::optional(destKeychainRef), Access::optional(initialAccess)); 225 if (itemCopy) 226 *itemCopy = copy->handle(); 227 END_SECAPI 228} 229 230 231OSStatus 232SecKeychainItemGetUniqueRecordID(SecKeychainItemRef itemRef, const CSSM_DB_UNIQUE_RECORD **uniqueRecordID) 233{ 234 BEGIN_SECAPI 235 Required(uniqueRecordID) = ItemImpl::required(itemRef)->dbUniqueRecord(); 236 END_SECAPI 237} 238 239 240OSStatus 241SecKeychainItemGetDLDBHandle(SecKeychainItemRef itemRef, CSSM_DL_DB_HANDLE* dldbHandle) 242{ 243 BEGIN_SECAPI 244 *dldbHandle = ItemImpl::required(itemRef)->keychain()->database()->handle(); 245 END_SECAPI 246} 247 248#if 0 249static 250OSStatus SecAccessCreateFromObject(CFTypeRef sourceRef, 251 SecAccessRef *accessRef) 252{ 253 BEGIN_SECAPI 254 Required(accessRef); // preflight 255 SecPointer<Access> access = new Access(*aclBearer(sourceRef)); 256 *accessRef = access->handle(); 257 END_SECAPI 258} 259 260 261/*! 262 */ 263static 264OSStatus SecAccessModifyObject(SecAccessRef accessRef, CFTypeRef sourceRef) 265{ 266 BEGIN_SECAPI 267 Access::required(accessRef)->setAccess(*aclBearer(sourceRef), true); 268 END_SECAPI 269} 270#endif 271 272OSStatus 273SecKeychainItemCopyAccess(SecKeychainItemRef itemRef, SecAccessRef* accessRef) 274{ 275 BEGIN_SECAPI 276 277 Required(accessRef); // preflight 278 SecPointer<Access> access = new Access(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef))); 279 *accessRef = access->handle(); 280 281 END_SECAPI 282} 283 284 285OSStatus 286SecKeychainItemSetAccess(SecKeychainItemRef itemRef, SecAccessRef accessRef) 287{ 288 BEGIN_SECAPI 289 290 Access::required(accessRef)->setAccess(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef)), true); 291 292 ItemImpl::required(itemRef)->postItemEvent (kSecUpdateEvent); 293 294 END_SECAPI 295} 296 297/* Sets an item's data for legacy "KC" CoreServices APIs. 298 Note this version sets the data, but doesn't update the item 299 as the KC behavior dictates. 300*/ 301OSStatus SecKeychainItemSetData(SecKeychainItemRef itemRef, UInt32 length, const void* data) 302{ 303 BEGIN_SECAPI 304 ItemImpl::required(itemRef)->setData(length, data); 305 END_SECAPI 306} 307 308/* Gets an item's data for legacy "KC" CoreServices APIs. 309 Note this version doesn't take a SecItemClass parameter. 310*/ 311OSStatus SecKeychainItemGetData(SecKeychainItemRef itemRef, UInt32 maxLength, void* data, UInt32* actualLength) 312{ 313 BEGIN_SECAPI 314 /* The caller either needs to specify data and maxLength or an actualLength, so we return either the data itself or the actual length of the data or both. */ 315 if (!((data && maxLength) || actualLength)) 316 MacOSError::throwMe(errSecParam); 317 318 CssmDataContainer aData; 319 ItemImpl::required(itemRef)->getData(aData); 320 if (actualLength) 321 *actualLength = (UInt32)aData.length(); 322 323 if (data) 324 { 325 // Make sure the buffer is big enough 326 if (aData.length() > maxLength) 327 MacOSError::throwMe(errSecBufferTooSmall); 328 memcpy(data, aData.data(), aData.length()); 329 } 330 END_SECAPI 331} 332 333/* Update a keychain item for legacy "KC" CoreServices APIs. 334 The "KC" API's do a 'set attribute', then an 'update'. 335*/ 336OSStatus SecKeychainItemUpdate(SecKeychainItemRef itemRef) 337{ 338 BEGIN_SECAPI 339 ItemImpl::required(itemRef)->update(); 340 END_SECAPI 341} 342 343/* Add a 'floating' keychain item without UI for legacy "KC" CoreServices APIs. 344*/ 345OSStatus SecKeychainItemAddNoUI(SecKeychainRef keychainRef, SecKeychainItemRef itemRef) 346{ 347 BEGIN_SECAPI 348 Item item = ItemImpl::required(itemRef); 349 Keychain::optional(keychainRef)->add(item); 350 END_SECAPI 351} 352 353/* Add a 'floating' keychain item to the default keychain with possible UI for legacy "KC" Carbon APIs. 354*/ 355OSStatus SecKeychainItemAdd(SecKeychainItemRef itemRef) 356{ 357 BEGIN_SECAPI 358 Item item = ItemImpl::required(itemRef); 359 Keychain defaultKeychain = globals().storageManager.defaultKeychainUI(item); 360 defaultKeychain->add(item); 361 END_SECAPI 362} 363 364/* Creates a floating keychain item for legacy "KC" CoreServices APIs 365*/ 366OSStatus SecKeychainItemCreateNew(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, SecKeychainItemRef* itemRef) 367{ 368 BEGIN_SECAPI 369 RequiredParam(itemRef) = Item(itemClass, itemCreator, length, data, false)->handle(); 370 END_SECAPI 371} 372 373/* Gets an individual attribute for legacy "KC" CoreServices APIs 374*/ 375OSStatus SecKeychainItemGetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute, UInt32* actualLength) 376{ 377 BEGIN_SECAPI 378 ItemImpl::required(itemRef)->getAttribute(RequiredParam(attribute), actualLength); 379 END_SECAPI 380} 381 382/* Sets an individual attribute for legacy "KC" CoreServices APIs 383*/ 384OSStatus SecKeychainItemSetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute) 385{ 386 BEGIN_SECAPI 387 ItemImpl::required(itemRef)->setAttribute(RequiredParam(attribute)); 388 END_SECAPI 389} 390 391/* Finds a keychain item for legacy "KC" CoreServices APIs. 392 Note: This version doesn't take a SecItemClass because 393 SecKeychainSearchCreateFromAttributes() requires it. 394 @@@ This should move to SecKeychainSearch.cpp 395*/ 396OSStatus SecKeychainItemFindFirst(SecKeychainRef keychainRef, const SecKeychainAttributeList *attrList, SecKeychainSearchRef *searchRef, SecKeychainItemRef *itemRef) 397{ 398 BEGIN_SECAPI 399 KCCursor cursor; 400 if (keychainRef) 401 cursor = KeychainImpl::required(keychainRef)->createCursor(attrList); 402 else 403 cursor = globals().storageManager.createCursor(attrList); 404 405 Item item; 406 if (!cursor->next(item)) 407 return errSecItemNotFound; 408 409 *itemRef=item->handle(); 410 if (searchRef) 411 *searchRef=cursor->handle(); 412 END_SECAPI 413} 414 415OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CFDataRef *persistentItemRef) 416{ 417 BEGIN_SECAPI 418 KCThrowParamErrIf_(!itemRef || !persistentItemRef); 419 Item item; 420 bool isIdentityRef = (CFGetTypeID(itemRef) == SecIdentityGetTypeID()) ? true : false; 421 if (isIdentityRef) { 422 SecPointer<Certificate> certificatePtr(Identity::required((SecIdentityRef)itemRef)->certificate()); 423 SecCertificateRef certificateRef = certificatePtr->handle(false); 424 item = ItemImpl::required((SecKeychainItemRef)certificateRef); 425 } 426 else { 427 item = ItemImpl::required(itemRef); 428 } 429 item->copyPersistentReference(*persistentItemRef, isIdentityRef); 430 END_SECAPI 431} 432 433OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef) 434{ 435 BEGIN_SECAPI 436 KCThrowParamErrIf_(!persistentItemRef || !itemRef); 437 CFTypeRef result = NULL; 438 bool isIdentityRef = false; 439 Item item = ItemImpl::makeFromPersistentReference(persistentItemRef, &isIdentityRef); 440 if (isIdentityRef) { 441 // item was stored as an identity, attempt to reconstitute it 442 SecPointer<Certificate> certificatePtr(static_cast<Certificate *>(item.get())); 443 StorageManager::KeychainList keychains; 444 globals().storageManager.optionalSearchList(NULL, keychains); 445 SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr)); 446 result = identityPtr->handle(); 447 KCThrowIf_( !result, errSecItemNotFound ); 448 } 449 if (!result) { 450 result = item->handle(); 451 } 452 *itemRef = (SecKeychainItemRef) result; 453 END_SECAPI 454} 455 456OSStatus SecKeychainItemCopyRecordIdentifier(SecKeychainItemRef itemRef, CFDataRef *recordIdentifier) 457{ 458 BEGIN_SECAPI 459 CSSM_DATA data; 460 RequiredParam (recordIdentifier); 461 Item item = ItemImpl::required(itemRef); 462 item->copyRecordIdentifier (data); 463 *recordIdentifier = ::CFDataCreate(kCFAllocatorDefault, (UInt8*) data.Data, data.Length); 464 free (data.Data); 465 END_SECAPI 466} 467 468OSStatus 469SecKeychainItemCopyFromRecordIdentifier(SecKeychainRef keychainRef, 470 SecKeychainItemRef *itemRef, 471 CFDataRef recordIdentifier) 472{ 473 BEGIN_SECAPI 474 // make a local Keychain reference 475 RequiredParam (keychainRef); 476 Keychain keychain = KeychainImpl::optional (keychainRef); 477 RequiredParam (itemRef); 478 RequiredParam (recordIdentifier); 479 480 Db db(keychain->database()); 481 482 // make a raw database call to get the data 483 CSSM_DL_DB_HANDLE dbHandle = db.handle (); 484 CSSM_DB_UNIQUE_RECORD uniqueRecord; 485 486 // according to source, we should be able to reconsitute the uniqueRecord 487 // from the data we earlier retained 488 489 // prepare the record id 490 memset (&uniqueRecord, 0, sizeof (uniqueRecord)); 491 uniqueRecord.RecordIdentifier.Data = (uint8*) CFDataGetBytePtr (recordIdentifier); 492 uniqueRecord.RecordIdentifier.Length = CFDataGetLength (recordIdentifier); 493 494 // convert this unique id to a CSSM_DB_UNIQUE_RECORD that works for the CSP/DL 495 CSSM_DB_UNIQUE_RECORD_PTR outputUniqueRecordPtr; 496 CSSM_RETURN result; 497 result = CSSM_DL_PassThrough (dbHandle, CSSM_APPLECSPDL_DB_CONVERT_RECORD_IDENTIFIER, &uniqueRecord, (void**) &outputUniqueRecordPtr); 498 KCThrowIf_(result != 0, errSecItemNotFound); 499 500 // from this, get the record type 501 CSSM_DB_RECORD_ATTRIBUTE_DATA attributeData; 502 memset (&attributeData, 0, sizeof (attributeData)); 503 504 result = CSSM_DL_DataGetFromUniqueRecordId (dbHandle, outputUniqueRecordPtr, &attributeData, NULL); 505 KCThrowIf_(result != 0, errSecItemNotFound); 506 CSSM_DB_RECORDTYPE recordType = attributeData.DataRecordType; 507 508 // make the unique record item -- precursor to creation of a SecKeychainItemRef 509 DbUniqueRecord unique(db); 510 CSSM_DB_UNIQUE_RECORD_PTR *uniquePtr = unique; 511 *uniquePtr = outputUniqueRecordPtr; 512 513 unique->activate (); 514 Item item = keychain->item (recordType, unique); 515 if (itemRef) 516 { 517 *itemRef = item->handle(); 518 } 519 END_SECAPI 520} 521 522OSStatus SecKeychainItemCreateFromEncryptedContent(SecItemClass itemClass, 523 UInt32 length, const void *data, SecKeychainRef keychainRef, 524 SecAccessRef initialAccess, SecKeychainItemRef *itemRef, CFDataRef *localID) 525{ 526 BEGIN_SECAPI 527 KCThrowParamErrIf_(length!=0 && data==NULL); 528 529 RequiredParam (localID); 530 RequiredParam (keychainRef); 531 532 Item item(itemClass, (uint32) 0, length, data, true); 533 if (initialAccess) 534 item->setAccess(Access::required(initialAccess)); 535 536 Keychain keychain = Keychain::optional(keychainRef); 537 if (!keychain->exists()) 538 { 539 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. 540 } 541 542 item->doNotEncrypt (); 543 try 544 { 545 keychain->add(item); 546 } 547 catch (const CommonError &err) 548 { 549 if (err.osStatus () == errSecNoSuchClass) 550 { 551 // the only time this should happen is if the item is a certificate (for keychain syncing) 552 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE) 553 { 554 // create the certificate relation 555 Db db(keychain->database()); 556 557 db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE, 558 "CSSM_DL_DB_RECORD_X509_CERTIFICATE", 559 Schema::X509CertificateSchemaAttributeCount, 560 Schema::X509CertificateSchemaAttributeList, 561 Schema::X509CertificateSchemaIndexCount, 562 Schema::X509CertificateSchemaIndexList); 563 keychain->keychainSchema()->didCreateRelation( 564 CSSM_DL_DB_RECORD_X509_CERTIFICATE, 565 "CSSM_DL_DB_RECORD_X509_CERTIFICATE", 566 Schema::X509CertificateSchemaAttributeCount, 567 Schema::X509CertificateSchemaAttributeList, 568 Schema::X509CertificateSchemaIndexCount, 569 Schema::X509CertificateSchemaIndexList); 570 571 // add the item again 572 keychain->add(item); 573 } 574 } 575 else 576 { 577 throw; 578 } 579 } 580 581 if (itemRef) 582 *itemRef = item->handle(); 583 584 CSSM_DATA recordID; 585 item->copyRecordIdentifier (recordID); 586 587 *localID = CFDataCreate(kCFAllocatorDefault, (UInt8*) recordID.Data, recordID.Length); 588 free (recordID.Data); 589 END_SECAPI 590} 591 592OSStatus SecKeychainItemCopyAttributesAndEncryptedData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, 593 SecItemClass *itemClass, SecKeychainAttributeList **attrList, 594 UInt32 *length, void **outData) 595{ 596 BEGIN_SECAPI 597 Item item = ItemImpl::required(itemRef); 598 item->doNotEncrypt (); 599 item->getAttributesAndData(info, itemClass, attrList, length, outData); 600 END_SECAPI 601} 602 603OSStatus SecKeychainItemModifyEncryptedData(SecKeychainItemRef itemRef, UInt32 length, const void *data) 604{ 605 BEGIN_SECAPI 606 Item item = ItemImpl::required(itemRef); 607 item->doNotEncrypt (); 608 item->modifyAttributesAndData(NULL, length, data); 609 END_SECAPI 610} 611