]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/Item.cpp
Security-59306.41.2.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / Item.cpp
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
25 //
26 // Item.cpp
27 //
28
29 #include "Item.h"
30
31 #include "Certificate.h"
32 #include "KeyItem.h"
33 #include "ExtendedAttribute.h"
34
35 #include "Globals.h"
36 #include <security_cdsa_utilities/Schema.h>
37 #include "KCEventNotifier.h"
38 #include "KCExceptions.h"
39 #include "cssmdatetime.h"
40 #include <security_cdsa_client/keychainacl.h>
41 #include <security_utilities/osxcode.h>
42 #include <security_utilities/trackingallocator.h>
43 #include <Security/SecKeychainItemPriv.h>
44 #include <Security/cssmapple.h>
45 #include <CommonCrypto/CommonDigest.h>
46 #include <utilities/der_plist.h>
47
48 #include <security_utilities/CSPDLTransaction.h>
49 #include <Security/SecBasePriv.h>
50
51 //%%% schema indexes should be defined in Schema.h
52 #define _kSecAppleSharePasswordItemClass 'ashp'
53 #define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1 /* schema index for label attribute of keys or certificates */
54 #define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7 /* schema index for label attribute of password items */
55 #define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \
56 (X) == kSecGenericPasswordItemClass || \
57 (X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0
58
59 using namespace KeychainCore;
60 using namespace CSSMDateTimeUtils;
61
62 //
63 // ItemImpl
64 //
65
66 ItemImpl *ItemImpl::required(SecKeychainItemRef ptr)
67 {
68 if (ptr != NULL) {
69 if (ItemImpl *pp = optional(ptr)) {
70 return pp;
71 }
72 }
73 MacOSError::throwMe(errSecInvalidItemRef);
74 }
75
76 ItemImpl *ItemImpl::optional(SecKeychainItemRef ptr)
77 {
78 if (ptr != NULL && CFGetTypeID(ptr) == SecKeyGetTypeID()) {
79 return dynamic_cast<ItemImpl *>(KeyItem::fromSecKeyRef(ptr));
80 } else if (SecCFObject *p = SecCFObject::optional(ptr)) {
81 if (ItemImpl *pp = dynamic_cast<ItemImpl *>(p)) {
82 return pp;
83 } else {
84 MacOSError::throwMe(errSecInvalidItemRef);
85 }
86 } else {
87 return NULL;
88 }
89 }
90
91 // NewItemImpl constructor
92 ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool dontDoAttributes)
93 : mDbAttributes(new DbAttributes()),
94 mKeychain(NULL),
95 secd_PersistentRef(NULL),
96 mDoNotEncrypt(false),
97 mInCache(false),
98 mMutex(Mutex::recursive)
99 {
100 if (length && data)
101 mData = new CssmDataContainer(data, length);
102
103 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
104
105 if (itemCreator)
106 mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator);
107 }
108
109 ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
110 : mDbAttributes(new DbAttributes()),
111 mKeychain(NULL),
112 secd_PersistentRef(NULL),
113 mDoNotEncrypt(false),
114 mInCache(false),
115 mMutex(Mutex::recursive)
116 {
117 if (length && data)
118 mData = new CssmDataContainer(data, length);
119
120
121 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
122
123 if(attrList)
124 {
125 for(UInt32 i=0; i < attrList->count; i++)
126 {
127 mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length));
128 }
129 }
130 }
131
132 // DbItemImpl constructor
133 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId)
134 : mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey),
135 secd_PersistentRef(NULL), mDoNotEncrypt(false), mInCache(false),
136 mMutex(Mutex::recursive)
137 {
138 }
139
140 // PrimaryKey ItemImpl constructor
141 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey)
142 : mKeychain(keychain), mPrimaryKey(primaryKey), secd_PersistentRef(NULL), mDoNotEncrypt(false),
143 mInCache(false),
144 mMutex(Mutex::recursive)
145 {
146 }
147
148 ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
149 {
150 ItemImpl* ii = new ItemImpl(keychain, primaryKey, uniqueId);
151 keychain->addItem(primaryKey, ii);
152 return ii;
153 }
154
155
156
157 ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey)
158 {
159 ItemImpl* ii = new ItemImpl(keychain, primaryKey);
160 keychain->addItem(primaryKey, ii);
161 return ii;
162 }
163
164
165
166 // Constructor used when copying an item to a keychain.
167
168 ItemImpl::ItemImpl(ItemImpl &item) :
169 mData(item.modifiedData() ? NULL : new CssmDataContainer()),
170 mDbAttributes(new DbAttributes()),
171 mKeychain(NULL),
172 secd_PersistentRef(NULL),
173 mDoNotEncrypt(false),
174 mInCache(false),
175 mMutex(Mutex::recursive)
176 {
177 mDbAttributes->recordType(item.recordType());
178
179 if (item.mKeychain) {
180 // get the entire source item from its keychain. This requires figuring
181 // out the schema for the item based on its record type.
182 // Ask the remote item to fill our attributes dictionary, because it probably has an attached keychain to ask
183 item.fillDbAttributesFromSchema(*mDbAttributes, item.recordType());
184
185 item.getContent(mDbAttributes.get(), mData.get());
186 }
187
188 // @@@ We don't deal with modified attributes.
189
190 if (item.modifiedData())
191 // the copied data comes from the source item
192 mData = new CssmDataContainer(item.modifiedData()->Data,
193 item.modifiedData()->Length);
194 }
195
196 ItemImpl::~ItemImpl()
197 try {
198 if (secd_PersistentRef) {
199 CFRelease(secd_PersistentRef);
200 }
201 } catch (...) {
202 #ifndef NDEBUG
203 /* if we get an exception in destructor, presumably the mutex, lets throw if we
204 * are in a debug build (ie reach end of block) */
205 #else
206 return;
207 #endif
208 }
209
210
211
212
213 Mutex*
214 ItemImpl::getMutexForObject() const
215 {
216 if (mKeychain.get())
217 {
218 return mKeychain->getKeychainMutex();
219 }
220
221 return NULL;
222 }
223
224
225 void
226 ItemImpl::aboutToDestruct()
227 {
228 if(mKeychain.get()) {
229 mKeychain->forceRemoveFromCache(this);
230 }
231 }
232
233
234
235 void
236 ItemImpl::didModify()
237 {
238 StLock<Mutex>_(mMutex);
239 mData = NULL;
240 mDbAttributes.reset(NULL);
241 }
242
243 const CSSM_DATA &
244 ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info)
245 {
246 static const uint32 zeroInt = 0;
247 static const double zeroDouble = 0.0;
248 static const char timeBytes[] = "20010101000000Z";
249
250 static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt };
251 static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble };
252 static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes };
253 static const CSSM_DATA defaultZeroBytes = { 0, NULL };
254
255 switch (info.AttributeFormat)
256 {
257 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
258 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
259 return defaultFourBytes;
260
261 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
262 return defaultEightBytes;
263
264 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
265 return defaultTime;
266
267 default:
268 return defaultZeroBytes;
269 }
270 }
271
272 void ItemImpl::fillDbAttributesFromSchema(DbAttributes& dbAttributes, CSSM_DB_RECORDTYPE recordType, Keychain keychain) {
273 // If we weren't passed a keychain, use our own.
274 keychain = !!keychain ? keychain : mKeychain;
275
276 // Without a keychain, there's no one to ask.
277 if(!keychain) {
278 return;
279 }
280
281 SecKeychainAttributeInfo* infos;
282 keychain->getAttributeInfoForItemID(recordType, &infos);
283
284 secinfo("integrity", "filling %u attributes for type %u", (unsigned int)infos->count, recordType);
285
286 for (uint32 i = 0; i < infos->count; i++) {
287 CSSM_DB_ATTRIBUTE_INFO info;
288 memset(&info, 0, sizeof(info));
289
290 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
291 info.Label.AttributeID = infos->tag[i];
292 info.AttributeFormat = infos->format[i];
293
294 dbAttributes.add(info);
295 }
296
297 keychain->freeAttributeInfo(infos);
298 }
299
300 DbAttributes* ItemImpl::getCurrentAttributes() {
301 DbAttributes* dbAttributes;
302 secinfo("integrity", "getting current attributes...");
303
304 if(mUniqueId.get()) {
305 // If we have a unique id, there's an item in the database backing us. Ask for its attributes.
306 dbAttributes = new DbAttributes(dbUniqueRecord()->database(), 1);
307 fillDbAttributesFromSchema(*dbAttributes, recordType());
308 mUniqueId->get(dbAttributes, NULL);
309
310 // and fold in any updates.
311 if(mDbAttributes.get()) {
312 secinfo("integrity", "adding %d attributes from mDbAttributes", mDbAttributes->size());
313 dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get()));
314 }
315 } else if (mDbAttributes.get()) {
316 // We don't have a backing item, so all our attributes are in mDbAttributes. Copy them.
317 secnotice("integrity", "no unique id, using %d attributes from mDbAttributes", mDbAttributes->size());
318 dbAttributes = new DbAttributes();
319 dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get()));
320 } else {
321 // No attributes at all. We should maybe throw here, but let's not.
322 secnotice("integrity", "no attributes at all");
323 dbAttributes = new DbAttributes();
324 }
325 dbAttributes->recordType(recordType());
326 // TODO: We don't set semanticInformation. Issue?
327
328 return dbAttributes;
329 }
330
331
332 void ItemImpl::encodeAttributes(CssmOwnedData &attributeBlob) {
333 // Sometimes we don't have our attributes. Find them.
334 auto_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
335 encodeAttributesFromDictionary(attributeBlob, dbAttributes.get());
336
337 }
338
339 void ItemImpl::encodeAttributesFromDictionary(CssmOwnedData &attributeBlob, DbAttributes* dbAttributes) {
340 // Create a CFDictionary from dbAttributes and call der_encode_dictionary on it
341 CFRef<CFMutableDictionaryRef> attributes;
342 attributes.take(CFDictionaryCreateMutable(NULL, dbAttributes->size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
343
344 secinfo("integrity", "looking at %d attributes", dbAttributes->size());
345 // TODO: include record type and semantic information?
346
347 for(int i = 0; i < dbAttributes->size(); i++) {
348 CssmDbAttributeData& data = dbAttributes->attributes()[i];
349 CssmDbAttributeInfo& datainfo = data.info();
350
351 // Sometimes we need to normalize the info. Calling Schema::attributeInfo is the best way to do that.
352 // There's no avoiding the try-catch structure here, since only some of the names are in Schema::attributeInfo,
353 // but it will only indicate that by throwing.
354 CssmDbAttributeInfo& actualInfo = datainfo;
355 try {
356 if(datainfo.nameFormat() == CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER && Schema::haveAttributeInfo(datainfo.intName())) {
357 actualInfo = Schema::attributeInfo(datainfo.intName());
358 }
359 } catch(...) {
360 actualInfo = datainfo;
361 }
362
363 // Pull the label/name out of this data
364 CFRef<CFDataRef> label = NULL;
365
366 switch(actualInfo.nameFormat()) {
367 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: {
368 const char* stringname = actualInfo.stringName();
369 label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(stringname), strlen(stringname)));
370 break;
371 }
372 case CSSM_DB_ATTRIBUTE_NAME_AS_OID: {
373 const CssmOid& oidname = actualInfo.oidName();
374 label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(oidname.data()), oidname.length()));
375 break;
376 }
377 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: {
378 uint32 iname = actualInfo.intName();
379 label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&(iname)), sizeof(uint32)));
380 break;
381 }
382 }
383
384 if(data.size() == 0) {
385 // This attribute doesn't have a value, and so shouldn't be included in the digest.
386 continue;
387 }
388
389 // Do not include the Creation or Modification date attributes in the hash.
390 // Use this complicated method of checking so we'll catch string and integer names.
391 SecKeychainAttrType cdat = kSecCreationDateItemAttr;
392 SecKeychainAttrType cmod = kSecModDateItemAttr;
393 if((CFDataGetLength(label) == sizeof(SecKeychainAttrType)) &&
394 ((memcmp(CFDataGetBytePtr(label), &cdat, sizeof(SecKeychainAttrType)) == 0) ||
395 (memcmp(CFDataGetBytePtr(label), &cmod, sizeof(SecKeychainAttrType)) == 0))) {
396 continue;
397 }
398
399 // Collect the raw data for each value of this CssmDbAttributeData
400 CFRef<CFMutableArrayRef> attributeDataContainer;
401 attributeDataContainer.take(CFArrayCreateMutable(NULL, data.size(), &kCFTypeArrayCallBacks));
402
403 for(int j = 0; j < data.size(); j++) {
404 CssmData& entry = data.values()[j];
405
406 CFRef<CFDataRef> datadata = NULL;
407 switch(actualInfo.format()) {
408 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
409 case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
410 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
411 datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(data.values()[j].data()), data.values()[j].length()));
412 break;
413
414 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: {
415 uint32 x = entry.length() == 1 ? *reinterpret_cast<uint8 *>(entry.Data) :
416 entry.length() == 2 ? *reinterpret_cast<uint16 *>(entry.Data) :
417 entry.length() == 4 ? *reinterpret_cast<uint32 *>(entry.Data) : 0;
418 datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&x), sizeof(x)));
419 break;
420 }
421
422 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: {
423 sint32 x = entry.length() == 1 ? *reinterpret_cast<sint8 *>(entry.Data) :
424 entry.length() == 2 ? *reinterpret_cast<sint16 *>(entry.Data) :
425 entry.length() == 4 ? *reinterpret_cast<sint32 *>(entry.Data) : 0;
426 datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&x), sizeof(x)));
427 break;
428 }
429 // CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM is unimplemented here but
430 // has some canonicalization requirements, see DbValue.cpp
431
432 default:
433 continue;
434 }
435
436 CFArrayAppendValue(attributeDataContainer, datadata);
437 }
438 CFDictionaryAddValue(attributes, label, attributeDataContainer);
439 }
440
441 // Now that we have a CFDictionary containing a bunch of CFDatas, turn that
442 // into a der blob.
443
444 CFErrorRef error;
445 CFRef<CFDataRef> derBlob;
446 derBlob.take(CFPropertyListCreateDERData(NULL, attributes, &error));
447
448 // TODO: How do we check error here?
449
450 if(!derBlob) {
451 return;
452 }
453
454 attributeBlob.length(CFDataGetLength(derBlob));
455 attributeBlob.copy(CFDataGetBytePtr(derBlob), CFDataGetLength(derBlob));
456 }
457
458 void ItemImpl::computeDigest(CssmOwnedData &sha2) {
459 auto_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
460 ItemImpl::computeDigestFromDictionary(sha2, dbAttributes.get());
461 }
462
463 void ItemImpl::computeDigestFromDictionary(CssmOwnedData &sha2, DbAttributes* dbAttributes) {
464 try{
465 CssmAutoData attributeBlob(Allocator::standard());
466 encodeAttributesFromDictionary(attributeBlob, dbAttributes);
467
468 sha2.length(CC_SHA256_DIGEST_LENGTH);
469 CC_SHA256(attributeBlob.get().data(), static_cast<CC_LONG>(attributeBlob.get().length()), sha2);
470 secinfo("integrity", "finished: %s", sha2.get().toHex().c_str());
471 } catch (MacOSError mose) {
472 secnotice("integrity", "MacOSError: %d", (int)mose.osStatus());
473 } catch (...) {
474 secnotice("integrity", "unknown exception");
475 }
476 }
477
478 void ItemImpl::addIntegrity(Access &access, bool force) {
479 if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
480 secinfo("integrity", "skipping integrity add due to keychain version\n");
481 return;
482 }
483
484 ACL * acl = NULL;
485 CssmAutoData digest(Allocator::standard());
486 computeDigest(digest);
487
488 // First, check if this already has an integrity tag
489 vector<ACL *> acls;
490 access.findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY, acls);
491
492 if(acls.size() >= 1) {
493 // Use the existing ACL
494 acl = acls[0];
495 secinfo("integrity", "previous integrity acl exists; setting integrity");
496 acl->setIntegrity(digest.get());
497
498 // Delete all extra ACLs
499 for(int i = 1; i < acls.size(); i++) {
500 secnotice("integrity", "extra integrity acls exist; removing %d",i);
501 acls[i]->remove();
502 }
503 } else if(acls.size() == 0) {
504 // Make a new ACL
505 secnotice("integrity", "no previous integrity acl exists; making a new one");
506 acl = new ACL(digest.get());
507 access.add(acl);
508 }
509 }
510
511 void ItemImpl::setIntegrity(bool force) {
512 if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
513 secnotice("integrity", "skipping integrity set due to keychain version");
514 return;
515 }
516
517 // For Items, only passwords should have integrity
518 if(!(recordType() == CSSM_DL_DB_RECORD_GENERIC_PASSWORD || recordType() == CSSM_DL_DB_RECORD_INTERNET_PASSWORD)) {
519 return;
520 }
521
522 // If we're not on an SSDb, we shouldn't have integrity
523 Db db(mKeychain->database());
524 if (!useSecureStorage(db)) {
525 return;
526 }
527
528 setIntegrity(*group(), force);
529 }
530
531 void ItemImpl::setIntegrity(AclBearer &bearer, bool force) {
532 if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
533 secnotice("integrity", "skipping integrity acl set due to keychain version");
534 return;
535 }
536
537 SecPointer<Access> access = new Access(bearer);
538
539 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
540 addIntegrity(*access, force);
541 access->setAccess(bearer, true);
542 }
543
544 void ItemImpl::removeIntegrity(const AccessCredentials *cred) {
545 removeIntegrity(*group(), cred);
546 }
547
548 void ItemImpl::removeIntegrity(AclBearer &bearer, const AccessCredentials *cred) {
549 SecPointer<Access> access = new Access(bearer);
550 vector<ACL *> acls;
551
552 access->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY, acls);
553 for(int i = 0; i < acls.size(); i++) {
554 acls[i]->remove();
555 }
556
557 access->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID, acls);
558 for(int i = 0; i < acls.size(); i++) {
559 acls[i]->remove();
560 }
561
562 access->editAccess(bearer, true, cred);
563 }
564
565 bool ItemImpl::checkIntegrity() {
566 // Note: subclasses are responsible for checking themselves.
567
568 // If we don't have a keychain yet, we don't have any group. Return true?
569 if(!isPersistent()) {
570 secnotice("integrity", "no keychain, integrity is valid?");
571 return true;
572 }
573
574 if(!mKeychain || !mKeychain->hasIntegrityProtection()) {
575 secinfo("integrity", "skipping integrity check due to keychain version");
576 return true;
577 }
578
579 // Collect our SSGroup, if it exists.
580 dbUniqueRecord();
581 SSGroup ssGroup = group();
582 if(ssGroup) {
583 return checkIntegrity(*ssGroup);
584 }
585
586 // If we don't have an SSGroup, we can't be invalid. return true.
587 return true;
588 }
589
590 bool ItemImpl::checkIntegrity(AclBearer& aclBearer) {
591 if(!mKeychain || !mKeychain->hasIntegrityProtection()) {
592 secinfo("integrity", "skipping integrity check due to keychain version");
593 return true;
594 }
595
596 auto_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
597 return checkIntegrityFromDictionary(aclBearer, dbAttributes.get());
598 }
599
600 bool ItemImpl::checkIntegrityFromDictionary(AclBearer& aclBearer, DbAttributes* dbAttributes) {
601 try {
602 AutoAclEntryInfoList aclInfos;
603 aclBearer.getAcl(aclInfos, CSSM_APPLE_ACL_TAG_INTEGRITY);
604
605 // We should only expect there to be one integrity tag. If there's not,
606 // take the first one and ignore the rest. We should probably attempt delete
607 // them.
608
609 AclEntryInfo &info = aclInfos.at(0);
610 auto_ptr<ACL> acl(new ACL(info, Allocator::standard()));
611
612 for(int i = 1; i < aclInfos.count(); i++) {
613 secnotice("integrity", "*** DUPLICATE INTEGRITY ACL, something has gone wrong");
614 }
615
616 CssmAutoData digest(Allocator::standard());
617 computeDigestFromDictionary(digest, dbAttributes);
618 if (acl->integrity() == digest.get()) {
619 return true;
620 }
621 }
622 catch (CssmError cssme) {
623 const char* errStr = cssmErrorString(cssme.error);
624 secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
625
626 if(cssme.error == CSSMERR_CSP_ACL_ENTRY_TAG_NOT_FOUND) {
627 // TODO: No entry, run migrator?
628 return true;
629 }
630 if(cssme.error == CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE) {
631 // something went horribly wrong with fetching acl.
632
633 secnotice("integrity", "INVALID ITEM (too many integrity acls)");
634 return false;
635 }
636 if(cssme.error == CSSMERR_CSP_VERIFY_FAILED) {
637 secnotice("integrity", "MAC verification failed; something has gone very wrong");
638 return false; // No MAC, no integrity.
639 }
640
641 throw;
642 }
643
644 secnotice("integrity", "***** INVALID ITEM");
645 return false;
646 }
647
648 PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy)
649 {
650 StLock<Mutex>_(mMutex);
651 // If we already have a Keychain we can't be added.
652 if (mKeychain)
653 MacOSError::throwMe(errSecDuplicateItem);
654
655 // If we don't have any attributes we can't be added.
656 // (this might occur if attempting to add the item twice, since our attributes
657 // and data are set to NULL at the end of this function.)
658 if (!mDbAttributes.get())
659 MacOSError::throwMe(errSecDuplicateItem);
660
661 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
662
663 // update the creation and update dates on the new item
664 if (!isCopy)
665 {
666 KeychainSchema schema = keychain->keychainSchema();
667 SInt64 date;
668 GetCurrentMacLongDateTime(date);
669 if (schema->hasAttribute(recordType, kSecCreationDateItemAttr))
670 {
671 setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date);
672 }
673
674 if (schema->hasAttribute(recordType, kSecModDateItemAttr))
675 {
676 setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date);
677 }
678 }
679
680 // If the label (PrintName) attribute isn't specified, set a default label.
681 mDbAttributes->canonicalize(); // make sure we'll find the label with the thing Schema::attributeInfo returns
682 if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)))
683 {
684 // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here.
685 CssmDbAttributeData *label = NULL;
686 switch (recordType)
687 {
688 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
689 label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
690 break;
691
692 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
693 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
694 label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr));
695 // if AppleShare server name wasn't specified, try the server address
696 if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr));
697 break;
698
699 default:
700 break;
701 }
702 // if all else fails, use the account name.
703 if (!label)
704 label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr));
705
706 if (label && label->size())
707 setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0));
708 }
709
710 // get the attributes that are part of the primary key
711 const CssmAutoDbRecordAttributeInfo &primaryKeyInfos =
712 keychain->primaryKeyInfosFor(recordType);
713
714 // make sure each primary key element has a value in the item, otherwise
715 // the database will complain. we make a set of the provided attribute infos
716 // to avoid O(N^2) behavior.
717
718 DbAttributes *attributes = mDbAttributes.get();
719 typedef set<CssmDbAttributeInfo> InfoSet;
720 InfoSet infoSet;
721
722 if (!mDoNotEncrypt)
723 {
724 // make a set of all the attributes in the key
725 for (uint32 i = 0; i < attributes->size(); i++)
726 infoSet.insert(attributes->at(i).Info);
727
728 for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key
729 InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i));
730
731 if (it == infoSet.end()) { // not in the key? add the default
732 // we need to add a default value to the item attributes
733 attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i)));
734 }
735 }
736 }
737
738 try {
739 mKeychain = keychain;
740 StLock<Mutex>_(*(mKeychain->getKeychainMutex())); // must hold this mutex before calling db->insert
741
742 Db db(keychain->database());
743 if (mDoNotEncrypt)
744 {
745 mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get());
746 }
747 else if (useSecureStorage(db))
748 {
749 updateSSGroup(db, recordType, mData.get(), keychain, mAccess);
750 mAccess = NULL; // use them and lose them - TODO: should this only be unset if there's no error in saveToNewSSGroup? Unclear.
751 }
752 else
753 {
754 // add the item to the (regular) db
755 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
756 }
757
758 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
759 } catch(...) {
760 mKeychain = NULL;
761 throw;
762 }
763
764 // Forget our data and attributes.
765 mData = NULL;
766 mDbAttributes.reset(NULL);
767
768 return mPrimaryKey;
769 }
770
771
772
773 PrimaryKey
774 ItemImpl::add (Keychain &keychain)
775 {
776 return addWithCopyInfo (keychain, false);
777 }
778
779
780
781 Item
782 ItemImpl::copyTo(const Keychain &keychain, Access *newAccess)
783 {
784 // We'll be removing any Partition or Integrity ACLs from this item during
785 // the copy. Note that creating a new item from this one fetches the data,
786 // so this process must now be on the ACL/partition ID list for this item,
787 // and an attacker without access can't cause this removal.
788 //
789 // The integrity and partition ID acls will get re-added once the item lands
790 // in the new keychain, if it supports them. If it doesn't, removing the
791 // integrity acl as it leaves will prevent any issues if the item is
792 // modified in the unsupported keychain and then re-copied back into an
793 // integrity keychain.
794
795 StLock<Mutex>_(mMutex);
796 Item item(*this);
797 if (newAccess) {
798 newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
799 newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
800 item->setAccess(newAccess);
801 } else {
802 /* Attempt to copy the access from the current item to the newly created one. */
803 SSGroup myGroup = group();
804 if (myGroup)
805 {
806 SecPointer<Access> access = new Access(*myGroup);
807 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
808 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
809 item->setAccess(access);
810 }
811 }
812
813 keychain->addCopy(item);
814 return item;
815 }
816
817 void
818 ItemImpl::update()
819 {
820 StLock<Mutex>_(mMutex);
821 if (!mKeychain)
822 MacOSError::throwMe(errSecNoSuchKeychain);
823
824 // Don't update if nothing changed.
825 if (!isModified())
826 return;
827
828 // Hold this before modifying the db below
829 StLock<Mutex>__(*(mKeychain->getKeychainMutex()));
830
831 CSSM_DB_RECORDTYPE aRecordType = recordType();
832 KeychainSchema schema = mKeychain->keychainSchema();
833
834 // Update the modification date on the item if there is a mod date attribute.
835 if (schema->hasAttribute(aRecordType, kSecModDateItemAttr))
836 {
837 SInt64 date;
838 GetCurrentMacLongDateTime(date);
839 setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date);
840 }
841
842 Db db(dbUniqueRecord()->database());
843 if (mDoNotEncrypt)
844 {
845 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
846 memset (&attrData, 0, sizeof (attrData));
847 attrData.DataRecordType = aRecordType;
848
849 dbUniqueRecord()->modifyWithoutEncryption(aRecordType,
850 &attrData,
851 mData.get(),
852 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
853 }
854 else if (useSecureStorage(db))
855 {
856 // Pass mData to updateSSGroup. If we have any data to change (and it's
857 // therefore non-null), it'll save to a new SSGroup; otherwise, it will
858 // update the old ssgroup. This prevents a RAA on attribute update, while
859 // still protecting new data from being decrypted by old SSGroups with
860 // outdated attributes.
861 updateSSGroup(db, recordType(), mData.get());
862 }
863 else
864 {
865 dbUniqueRecord()->modify(aRecordType,
866 mDbAttributes.get(),
867 mData.get(),
868 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
869 }
870
871 if (!mDoNotEncrypt)
872 {
873 PrimaryKey oldPK = mPrimaryKey;
874 mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId);
875
876 // Forget our data and attributes.
877 mData = NULL;
878 mDbAttributes.reset(NULL);
879
880 // Let the Keychain update what it needs to.
881 mKeychain->didUpdate(this, oldPK, mPrimaryKey);
882 }
883 }
884
885 void
886 ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer* newdata, Keychain keychain, SecPointer<Access> access)
887 {
888 // hhs replaced with the new aclFactory class
889 AclFactory aclFactory;
890 const AccessCredentials *nullCred = aclFactory.nullCred();
891
892 bool haveOldUniqueId = !!mUniqueId.get();
893 SSDbUniqueRecord ssUniqueId(NULL);
894 SSGroup ssGroup(NULL);
895 if(haveOldUniqueId) {
896 ssUniqueId = SSDbUniqueRecord(dynamic_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
897 if (ssUniqueId.get() == NULL) {
898 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
899 }
900 ssGroup = ssUniqueId->group();
901 }
902
903 // If we have new data OR no old unique id, save to a new group
904 bool saveToNewSSGroup = (!!newdata) || (!haveOldUniqueId);
905
906 // If there aren't any attributes, make up some blank ones.
907 if (!mDbAttributes.get())
908 {
909 secinfo("integrity", "making new dbattributes");
910 mDbAttributes.reset(new DbAttributes());
911 mDbAttributes->recordType(mPrimaryKey->recordType());
912 }
913
914 // Add the item to the secure storage db
915 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db));
916 if (impl == NULL)
917 {
918 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
919 }
920
921 SSDb ssDb(impl);
922
923 TrackingAllocator allocator(Allocator::standard());
924
925 if ((!access) && (haveOldUniqueId)) {
926 // Copy the ACL from the old group.
927 secinfo("integrity", "copying old ACL");
928 access = new Access(*(ssGroup));
929
930 // We can't copy these over to the new item; they're going to be reset.
931 // Remove them before securityd complains.
932 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
933 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
934 } else if (!access) {
935 secinfo("integrity", "setting up new ACL");
936 // create default access controls for the new item
937 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
938 string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item";
939 access = new Access(printName);
940 } else {
941 secinfo("integrity", "passed an Access, use it");
942 // Access is non-null. Do nothing.
943 }
944
945 // If we have an old group and an old mUniqueId, then we're in the middle of an update.
946 // mDbAttributes contains the newest attributes, but not the old ones. Find
947 // them, merge them, and shove them all back into mDbAttributes. This lets
948 // us re-apply them all to the new item.
949 if(haveOldUniqueId) {
950 mDbAttributes.reset(getCurrentAttributes());
951 }
952
953 // Create a CSPDL transaction. Note that this does things when it goes out of scope.
954 CSPDLTransaction transaction(db);
955
956 Access::Maker maker;
957 ResourceControlContext prototype;
958 maker.initialOwner(prototype, nullCred);
959
960 if(saveToNewSSGroup) {
961 secinfo("integrity", "saving to a new SSGroup");
962
963 // If we're updating an item, it has an old group and possibly an
964 // old mUniqueId. Delete these from the database, so we can insert
965 // new ones.
966 if(haveOldUniqueId) {
967 secinfo("integrity", "deleting old mUniqueId");
968 mUniqueId->deleteRecord();
969 mUniqueId.release();
970 } else {
971 secinfo("integrity", "no old mUniqueId");
972 }
973
974 // Create a new SSGroup with temporary access controls
975 SSGroup newSSGroup(ssDb, &prototype);
976 const AccessCredentials * cred = maker.cred();
977
978 try {
979 doChange(keychain, recordType, ^{
980 mUniqueId = ssDb->ssInsert(recordType, mDbAttributes.get(), newdata, newSSGroup, cred);
981 });
982
983 // now finalize the access controls on the group
984 addIntegrity(*access);
985 access->setAccess(*newSSGroup, maker);
986
987 // We have to reset this after we add the integrity, since it needs the attributes
988 mDbAttributes.reset(NULL);
989
990 transaction.commit();
991 }
992 catch (CssmError cssme) {
993 const char* errStr = cssmErrorString(cssme.error);
994 secnotice("integrity", "caught CssmError during add: %d %s", (int) cssme.error, errStr);
995
996 // Delete the new SSGroup that we just created
997 deleteSSGroup(newSSGroup, nullCred);
998 throw;
999 }
1000 catch (MacOSError mose) {
1001 secnotice("integrity", "caught MacOSError during add: %d", (int) mose.osStatus());
1002
1003 deleteSSGroup(newSSGroup, nullCred);
1004 throw;
1005 }
1006 catch (...)
1007 {
1008 secnotice("integrity", "caught unknown exception during add");
1009
1010 deleteSSGroup(newSSGroup, nullCred);
1011 throw;
1012 }
1013 } else {
1014 // Modify the old SSGroup
1015 secinfo("integrity", "modifying the existing SSGroup");
1016
1017 try {
1018 doChange(keychain, recordType, ^{
1019 assert(!newdata);
1020 const AccessCredentials *autoPrompt = globals().itemCredentials();
1021 ssUniqueId->modify(recordType,
1022 mDbAttributes.get(),
1023 newdata,
1024 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE,
1025 autoPrompt);
1026 });
1027
1028 // Update the integrity on the SSGroup
1029 setIntegrity(*ssGroup);
1030
1031 // We have to reset this after we add the integrity, since it needs the attributes
1032 mDbAttributes.reset(NULL);
1033
1034 transaction.commit();
1035 }
1036 catch (CssmError cssme) {
1037 const char* errStr = cssmErrorString(cssme.error);
1038 secnotice("integrity", "caught CssmError during modify: %d %s", (int) cssme.error, errStr);
1039 throw;
1040 }
1041 catch (MacOSError mose) {
1042 secnotice("integrity", "caught MacOSError during modify: %d", (int) mose.osStatus());
1043 throw;
1044 }
1045 catch (...)
1046 {
1047 secnotice("integrity", "caught unknown exception during modify");
1048 throw;
1049 }
1050
1051 }
1052 }
1053
1054 // Helper function to delete a group and swallow all errors
1055 void ItemImpl::deleteSSGroup(SSGroup & ssgroup, const AccessCredentials* nullCred) {
1056 try{
1057 ssgroup->deleteKey(nullCred);
1058 } catch(CssmError error) {
1059 secnotice("integrity", "caught cssm error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1060 } catch(MacOSError error) {
1061 secnotice("integrity", "caught macos error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1062 } catch(UnixError error) {
1063 secnotice("integrity", "caught unix error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1064 }
1065 }
1066
1067 void
1068 ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryChange) ())
1069 {
1070 // Insert the record using the newly created group.
1071 try {
1072 tryChange();
1073 } catch (CssmError cssme) {
1074 // If there's a "duplicate" of this item, it might be an item with corrupt/invalid attributes
1075 // Try to extract the item and check its attributes, then try again if necessary
1076 auto_ptr<CssmClient::DbAttributes> primaryKeyAttrs;
1077 if(cssme.error == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
1078 secnotice("integrity", "possible duplicate, trying to delete invalid items");
1079
1080 Keychain kc = (keychain ? keychain : mKeychain);
1081 if(!kc) {
1082 secnotice("integrity", "no valid keychain");
1083 }
1084
1085 // Only check for corrupt items if the keychain supports them
1086 if((!kc) || !kc->hasIntegrityProtection()) {
1087 secinfo("integrity", "skipping integrity check for corrupt items due to keychain support");
1088 throw;
1089 } else {
1090 primaryKeyAttrs.reset(getCurrentAttributes());
1091 PrimaryKey pk = kc->makePrimaryKey(recordType, primaryKeyAttrs.get());
1092
1093 bool tryAgain = false;
1094
1095 // Because things are lazy, maybe our keychain has a version
1096 // of this item with different attributes. Ask it!
1097 ItemImpl* maybeItem = kc->_lookupItem(pk);
1098 if(maybeItem) {
1099 if(!maybeItem->checkIntegrity()) {
1100 Item item(maybeItem);
1101 kc->deleteItem(item);
1102 tryAgain = true;
1103 }
1104 } else {
1105 // Our keychain doesn't know about any item with this primary key, so maybe
1106 // we have a corrupt item in the database. Let's check.
1107
1108 secinfo("integrity", "making a cursor from primary key");
1109 CssmClient::DbCursor cursor = pk->createCursor(kc);
1110 DbUniqueRecord uniqueId;
1111
1112 StLock<Mutex> _mutexLocker(*kc->getKeychainMutex());
1113
1114 // The item on-disk might have more or different attributes than we do, since we're
1115 // only searching via primary key. Fetch all of its attributes.
1116 auto_ptr<DbAttributes>dbDupAttributes (new DbAttributes(kc->database(), 1));
1117 fillDbAttributesFromSchema(*dbDupAttributes, recordType, kc);
1118
1119 // Occasionally this cursor won't return the item attributes (for an unknown reason).
1120 // However, we know the attributes any item with this primary key should have, so use those instead.
1121 while (cursor->next(dbDupAttributes.get(), NULL, uniqueId)) {
1122 secinfo("integrity", "got an item...");
1123
1124 SSGroup group = safer_cast<SSDbUniqueRecordImpl &>(*uniqueId).group();
1125 if(!ItemImpl::checkIntegrityFromDictionary(*group, dbDupAttributes.get())) {
1126 secnotice("integrity", "item is invalid! deleting...");
1127 uniqueId->deleteRecord();
1128 tryAgain = true;
1129 }
1130 }
1131 }
1132
1133 if(tryAgain) {
1134 secnotice("integrity", "trying again...");
1135 tryChange();
1136 } else {
1137 // We didn't find an invalid item, the duplicate item exception is real
1138 secnotice("integrity", "duplicate item exception is real; throwing it on");
1139 throw;
1140 }
1141 }
1142 } else {
1143 throw;
1144 }
1145 }
1146 }
1147
1148 void
1149 ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength)
1150 {
1151 StLock<Mutex>_(mMutex);
1152 if (actualLength)
1153 *actualLength = sizeof(SecItemClass);
1154
1155 if (attr.length < sizeof(SecItemClass))
1156 MacOSError::throwMe(errSecBufferTooSmall);
1157
1158 SecItemClass aClass = Schema::itemClassFor(recordType());
1159 memcpy(attr.data, &aClass, sizeof(SecItemClass));
1160 }
1161
1162 void
1163 ItemImpl::setAttribute(SecKeychainAttribute& attr)
1164 {
1165 StLock<Mutex>_(mMutex);
1166 setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
1167 }
1168
1169 CSSM_DB_RECORDTYPE
1170 ItemImpl::recordType()
1171 {
1172 StLock<Mutex>_(mMutex);
1173 if (mDbAttributes.get())
1174 return mDbAttributes->recordType();
1175
1176 return mPrimaryKey->recordType();
1177 }
1178
1179 const DbAttributes *
1180 ItemImpl::modifiedAttributes()
1181 {
1182 StLock<Mutex>_(mMutex);
1183 return mDbAttributes.get();
1184 }
1185
1186 const CssmData *
1187 ItemImpl::modifiedData()
1188 {
1189 StLock<Mutex>_(mMutex);
1190 return mData.get();
1191 }
1192
1193 void
1194 ItemImpl::setData(UInt32 length,const void *data)
1195 {
1196 StLock<Mutex>_(mMutex);
1197 mData = new CssmDataContainer(data, length);
1198 }
1199
1200 void
1201 ItemImpl::setAccess(Access *newAccess)
1202 {
1203 StLock<Mutex>_(mMutex);
1204 mAccess = newAccess;
1205 }
1206
1207 CssmClient::DbUniqueRecord
1208 ItemImpl::dbUniqueRecord()
1209 {
1210 StLock<Mutex>_(mMutex);
1211 if (!isPersistent()) // is there no database attached?
1212 {
1213 MacOSError::throwMe(errSecNotAvailable);
1214 }
1215
1216 if (!mUniqueId)
1217 {
1218 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
1219 if (!cursor->next(NULL, NULL, mUniqueId))
1220 MacOSError::throwMe(errSecInvalidItemRef);
1221 }
1222
1223 // Check that our Db still matches our keychain's db. If not, find this item again in the new Db.
1224 // Why silly !(x == y) construction? Compiler calls operator bool() on each pointer otherwise.
1225 if(!(mUniqueId->database() == keychain()->database())) {
1226 secinfo("integrity", "updating db of mUniqueRecord");
1227
1228 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
1229 if (!cursor->next(NULL, NULL, mUniqueId))
1230 MacOSError::throwMe(errSecInvalidItemRef);
1231 }
1232
1233 return mUniqueId;
1234 }
1235
1236 PrimaryKey
1237 ItemImpl::primaryKey()
1238 {
1239 return mPrimaryKey;
1240 }
1241
1242 bool
1243 ItemImpl::isPersistent()
1244 {
1245 return mKeychain;
1246 }
1247
1248 bool
1249 ItemImpl::isModified()
1250 {
1251 StLock<Mutex>_(mMutex);
1252 return mData.get() || mDbAttributes.get();
1253 }
1254
1255 Keychain
1256 ItemImpl::keychain()
1257 {
1258 return mKeychain;
1259 }
1260
1261 bool
1262 ItemImpl::operator < (const ItemImpl &other)
1263 {
1264 if (mData && *mData)
1265 {
1266 // Pointer compare
1267 return this < &other;
1268 }
1269
1270 return mPrimaryKey < other.mPrimaryKey;
1271 }
1272
1273 void
1274 ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data)
1275 {
1276 StLock<Mutex>_(mMutex);
1277 if (!mDbAttributes.get())
1278 {
1279 mDbAttributes.reset(new DbAttributes());
1280 mDbAttributes->recordType(mPrimaryKey->recordType());
1281 }
1282
1283 size_t length = data.Length;
1284 const void *buf = reinterpret_cast<const void *>(data.Data);
1285 uint8 timeString[16];
1286
1287 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
1288 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
1289 // style attribute value.
1290 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1291 {
1292 if (length == sizeof(UInt32))
1293 {
1294 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 16, &timeString);
1295 buf = &timeString;
1296 length = 16;
1297 }
1298 else if (length == sizeof(SInt64))
1299 {
1300 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 16, &timeString);
1301 buf = &timeString;
1302 length = 16;
1303 }
1304 }
1305
1306 mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
1307 }
1308
1309 void
1310 ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
1311 {
1312 StLock<Mutex>_(mMutex);
1313 StMaybeLock<Mutex>__ (mKeychain == NULL ? NULL : mKeychain->getKeychainMutex());
1314
1315 if (!mDbAttributes.get())
1316 {
1317 mDbAttributes.reset(new DbAttributes());
1318 mDbAttributes->recordType(mPrimaryKey->recordType());
1319 }
1320
1321 if(attrList) // optional
1322 {
1323 for(UInt32 ix=0; ix < attrList->count; ix++)
1324 {
1325 SecKeychainAttrType attrTag = attrList->attr[ix].tag;
1326
1327 if (attrTag == APPLEDB_CSSM_PRINTNAME_ATTRIBUTE)
1328 {
1329 // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema
1330 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1331 attrTag = kSecLabelItemAttr;
1332 }
1333
1334 mDbAttributes->add(Schema::attributeInfo(attrTag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
1335 }
1336 }
1337
1338 if(inData)
1339 {
1340 mData = new CssmDataContainer(inData, dataLength);
1341 }
1342
1343 update();
1344 }
1345
1346 void
1347 ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
1348 {
1349 StLock<Mutex>_(mMutex);
1350 // If the data hasn't been set we can't return it.
1351 if (!mKeychain && outData)
1352 {
1353 CssmData *data = mData.get();
1354 if (!data)
1355 MacOSError::throwMe(errSecDataNotAvailable);
1356 }
1357 // TODO: need to check and make sure attrs are valid and handle error condition
1358
1359
1360 if (itemClass)
1361 *itemClass = Schema::itemClassFor(recordType());
1362
1363 bool getDataFromDatabase = mKeychain && mPrimaryKey;
1364 if (getDataFromDatabase) // are we attached to a database?
1365 {
1366 dbUniqueRecord();
1367
1368 // get the number of attributes requested by the caller
1369 UInt32 attrCount = attrList ? attrList->count : 0;
1370
1371 // make a DBAttributes structure and populate it
1372 DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
1373 for (UInt32 ix = 0; ix < attrCount; ++ix)
1374 {
1375 dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag));
1376 }
1377
1378 // request the data from the database (since we are a reference "item" and the data is really stored there)
1379 CssmDataContainer itemData;
1380 getContent(&dbAttributes, outData ? &itemData : NULL);
1381
1382 // retrieve the data from result
1383 for (UInt32 ix = 0; ix < attrCount; ++ix)
1384 {
1385 if (dbAttributes.at(ix).NumberOfValues > 0)
1386 {
1387 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
1388 attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
1389
1390 // We don't want the data released, it is up the client
1391 dbAttributes.at(ix).Value[0].Data = NULL;
1392 dbAttributes.at(ix).Value[0].Length = 0;
1393 }
1394 else
1395 {
1396 attrList->attr[ix].data = NULL;
1397 attrList->attr[ix].length = 0;
1398 }
1399 }
1400
1401 // clean up
1402 if (outData)
1403 {
1404 *outData=itemData.data();
1405 itemData.Data = NULL;
1406
1407 if (length)
1408 *length=(UInt32)itemData.length();
1409 itemData.Length = 0;
1410 }
1411 }
1412 else
1413 {
1414 getLocalContent(attrList, length, outData);
1415 }
1416 }
1417
1418 void
1419 ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data)
1420 {
1421 Allocator &allocator = Allocator::standard(Allocator::sensitive); // @@@ This might not match the one used originally
1422 if (data)
1423 allocator.free(data);
1424
1425 UInt32 attrCount = attrList ? attrList->count : 0;
1426 for (UInt32 ix = 0; ix < attrCount; ++ix)
1427 {
1428 allocator.free(attrList->attr[ix].data);
1429 attrList->attr[ix].data = NULL;
1430 }
1431 }
1432
1433 void
1434 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
1435 {
1436 StLock<Mutex>_(mMutex);
1437 if (!mKeychain)
1438 MacOSError::throwMe(errSecNoSuchKeychain);
1439
1440 if (!mDoNotEncrypt)
1441 {
1442 if (!mDbAttributes.get())
1443 {
1444 mDbAttributes.reset(new DbAttributes());
1445 mDbAttributes->recordType(mPrimaryKey->recordType());
1446 }
1447
1448 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
1449 UInt32 attrCount = attrList ? attrList->count : 0;
1450 for (UInt32 ix = 0; ix < attrCount; ix++)
1451 {
1452 SecKeychainAttrType attrTag = attrList->attr[ix].tag;
1453
1454 if (attrTag == kSecLabelItemAttr)
1455 {
1456 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1457 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1458 if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType) ))
1459 attrTag = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
1460 }
1461
1462 CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrTag);
1463
1464 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
1465 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
1466 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
1467 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
1468 else
1469 mDbAttributes->add(info);
1470 }
1471 }
1472
1473 if(inData)
1474 {
1475 mData = new CssmDataContainer(inData, dataLength);
1476 }
1477
1478 update();
1479 }
1480
1481 void
1482 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass,
1483 SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
1484 {
1485 StLock<Mutex>_(mMutex);
1486 // If the data hasn't been set we can't return it.
1487 if (!mKeychain && outData)
1488 {
1489 CssmData *data = mData.get();
1490 if (!data)
1491 MacOSError::throwMe(errSecDataNotAvailable);
1492 }
1493 // TODO: need to check and make sure attrs are valid and handle error condition
1494
1495 SecItemClass myItemClass = Schema::itemClassFor(recordType());
1496 if (itemClass)
1497 *itemClass = myItemClass;
1498
1499 // @@@ This call won't work for floating items (like certificates).
1500 dbUniqueRecord();
1501
1502 UInt32 attrCount = info ? info->count : 0;
1503 DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
1504 for (UInt32 ix = 0; ix < attrCount; ix++)
1505 {
1506 CssmDbAttributeData &record = dbAttributes.add();
1507 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
1508 record.Info.Label.AttributeID=info->tag[ix];
1509
1510 if (record.Info.Label.AttributeID == kSecLabelItemAttr)
1511 {
1512 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1513 if (IS_PASSWORD_ITEM_CLASS( myItemClass ))
1514 record.Info.Label.AttributeID = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
1515 }
1516 }
1517
1518 CssmDataContainer itemData;
1519 getContent(&dbAttributes, outData ? &itemData : NULL);
1520
1521 if (info && attrList)
1522 {
1523 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
1524
1525 if(attrCount == 0) {
1526 theList->count = 0;
1527 theList->attr = NULL;
1528 } else {
1529 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(calloc(attrCount, sizeof(SecKeychainAttribute)));
1530 theList->count=attrCount;
1531 theList->attr=attr;
1532
1533 for (UInt32 ix = 0; ix < attrCount; ++ix)
1534 {
1535 attr[ix].tag=info->tag[ix];
1536
1537 if (dbAttributes.at(ix).NumberOfValues > 0)
1538 {
1539 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
1540 attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
1541
1542 // We don't want the data released, it is up the client
1543 dbAttributes.at(ix).Value[0].Data = NULL;
1544 dbAttributes.at(ix).Value[0].Length = 0;
1545 }
1546 else
1547 {
1548 attr[ix].data = NULL;
1549 attr[ix].length = 0;
1550 }
1551 }
1552 }
1553 *attrList=theList;
1554 }
1555
1556 if (outData)
1557 {
1558 *outData=itemData.data();
1559 itemData.Data=NULL;
1560
1561 if (length) *length=(UInt32)itemData.length();
1562 itemData.Length=0;
1563 }
1564
1565 }
1566
1567 void
1568 ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
1569 {
1570 Allocator &allocator = Allocator::standard(Allocator::sensitive); // @@@ This might not match the one used originally
1571
1572 if (data)
1573 allocator.free(data);
1574
1575 if (attrList)
1576 {
1577 for (UInt32 ix = 0; ix < attrList->count; ++ix)
1578 {
1579 allocator.free(attrList->attr[ix].data);
1580 }
1581 free(attrList->attr);
1582 free(attrList);
1583 }
1584 }
1585
1586 void
1587 ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
1588 {
1589 StLock<Mutex>_(mMutex);
1590 if (attr.tag == kSecClassItemAttr)
1591 return getClass(attr, actualLength);
1592
1593 if (mDbAttributes.get())
1594 {
1595 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
1596 if (data)
1597 {
1598 getAttributeFrom(data, attr, actualLength);
1599 return;
1600 }
1601 }
1602
1603 if (!mKeychain)
1604 MacOSError::throwMe(errSecNoSuchAttr);
1605
1606 DbAttributes dbAttributes(dbUniqueRecord()->database(), 1);
1607 dbAttributes.add(Schema::attributeInfo(attr.tag));
1608 dbUniqueRecord()->get(&dbAttributes, NULL);
1609 getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
1610 }
1611
1612 void
1613 ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
1614 {
1615 StLock<Mutex>_(mMutex);
1616 static const uint32 zero = 0;
1617 UInt32 length;
1618 const void *buf = NULL;
1619
1620 // Temporary storage for buf.
1621 sint64 macLDT;
1622 uint32 macSeconds;
1623 sint16 svalue16;
1624 uint16 uvalue16;
1625 sint8 svalue8;
1626 uint8 uvalue8;
1627
1628 if (!data)
1629 length = 0;
1630 else if (data->size() < 1) // Attribute has no values.
1631 {
1632 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
1633 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1634 {
1635 length = sizeof(zero);
1636 buf = &zero;
1637 }
1638 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1639 length = 0; // Should we throw here?
1640 else // All other formats
1641 length = 0;
1642 }
1643 else // Get the first value
1644 {
1645 length = (UInt32)data->Value[0].Length;
1646 buf = data->Value[0].Data;
1647
1648 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
1649 {
1650 if (attr.length == sizeof(sint8))
1651 {
1652 length = attr.length;
1653 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
1654 buf = &svalue8;
1655 }
1656 else if (attr.length == sizeof(sint16))
1657 {
1658 length = attr.length;
1659 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
1660 buf = &svalue16;
1661 }
1662 }
1663 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1664 {
1665 if (attr.length == sizeof(uint8))
1666 {
1667 length = attr.length;
1668 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
1669 buf = &uvalue8;
1670 }
1671 else if (attr.length == sizeof(uint16))
1672 {
1673 length = attr.length;
1674 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
1675 buf = &uvalue16;
1676 }
1677 }
1678 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1679 {
1680 if (attr.length == sizeof(uint32))
1681 {
1682 TimeStringToMacSeconds(data->Value[0], macSeconds);
1683 buf = &macSeconds;
1684 length = attr.length;
1685 }
1686 else if (attr.length == sizeof(sint64))
1687 {
1688 TimeStringToMacLongDateTime(data->Value[0], macLDT);
1689 buf = &macLDT;
1690 length = attr.length;
1691 }
1692 }
1693 }
1694
1695 if (actualLength)
1696 *actualLength = length;
1697
1698 if (length)
1699 {
1700 if (attr.length < length)
1701 MacOSError::throwMe(errSecBufferTooSmall);
1702
1703 memcpy(attr.data, buf, length);
1704 }
1705 }
1706
1707 void
1708 ItemImpl::getData(CssmDataContainer& outData)
1709 {
1710 StLock<Mutex>_(mMutex);
1711 if (!mKeychain)
1712 {
1713 CssmData *data = mData.get();
1714 // If the data hasn't been set we can't return it.
1715 if (!data)
1716 MacOSError::throwMe(errSecDataNotAvailable);
1717
1718 outData = *data;
1719 return;
1720 }
1721
1722 getContent(NULL, &outData);
1723 }
1724
1725 SSGroup
1726 ItemImpl::group()
1727 {
1728 StLock<Mutex>_(mMutex);
1729 SSGroup group;
1730 if (!!mUniqueId)
1731 {
1732 Db db(mKeychain->database());
1733 if (useSecureStorage(db))
1734 {
1735 group = safer_cast<SSDbUniqueRecordImpl &>(*dbUniqueRecord()).group();
1736 }
1737 }
1738
1739 return group;
1740 }
1741
1742 void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData)
1743 {
1744 StLock<Mutex>_(mMutex);
1745 willRead();
1746 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally
1747 if (outData)
1748 {
1749 CssmData *data = mData.get();
1750 if (!data)
1751 MacOSError::throwMe(errSecDataNotAvailable);
1752
1753 // Copy the data out of our internal cached copy.
1754 UInt32 length = (UInt32)data->Length;
1755 *outData = allocator.malloc(length);
1756 memcpy(*outData, data->Data, length);
1757 if (outLength)
1758 *outLength = length;
1759 }
1760
1761 if (attributeList)
1762 {
1763 if (!mDbAttributes.get())
1764 MacOSError::throwMe(errSecDataNotAvailable);
1765
1766 // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database
1767 for (UInt32 ix = 0; ix < attributeList->count; ++ix)
1768 {
1769 SecKeychainAttribute &attribute = attributeList->attr[ix];
1770 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attribute.tag));
1771 if (data && data->NumberOfValues > 0)
1772 {
1773 // Copy the data out of our internal cached copy.
1774 UInt32 length = (UInt32)data->Value[0].Length;
1775 attribute.data = allocator.malloc(length);
1776 memcpy(attribute.data, data->Value[0].Data, length);
1777 attribute.length = length;
1778 }
1779 else
1780 {
1781 attribute.length = 0;
1782 attribute.data = NULL;
1783 }
1784 }
1785 }
1786 }
1787
1788 void
1789 ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
1790 {
1791 StLock<Mutex>_(mMutex);
1792 if (itemData)
1793 {
1794 Db db(dbUniqueRecord()->database());
1795 if (mDoNotEncrypt)
1796 {
1797 dbUniqueRecord()->getWithoutEncryption (dbAttributes, itemData);
1798 return;
1799 }
1800 if (useSecureStorage(db))
1801 {
1802 try {
1803 if(!checkIntegrity()) {
1804 secnotice("integrity", "item has no integrity, denying access");
1805 CssmError::throwMe(errSecInvalidItemRef);
1806 }
1807 } catch(CssmError cssme) {
1808 secnotice("integrity", "error while checking integrity, denying access: %s", cssme.what());
1809 throw;
1810 }
1811
1812 SSDbUniqueRecordImpl* impl = dynamic_cast<SSDbUniqueRecordImpl *>(&(*dbUniqueRecord()));
1813 if (impl == NULL)
1814 {
1815 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1816 }
1817
1818 SSDbUniqueRecord ssUniqueId(impl);
1819 const AccessCredentials *autoPrompt = globals().itemCredentials();
1820 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
1821 return;
1822 }
1823 }
1824
1825 dbUniqueRecord()->get(dbAttributes, itemData);
1826 }
1827
1828 bool
1829 ItemImpl::useSecureStorage(const Db &db)
1830 {
1831 StLock<Mutex>_(mMutex);
1832 switch (recordType())
1833 {
1834 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
1835 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
1836 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
1837 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
1838 return true;
1839 break;
1840 default:
1841 break;
1842 }
1843 return false;
1844 }
1845
1846 void ItemImpl::willRead()
1847 {
1848 }
1849
1850 Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef)
1851 {
1852 CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef));
1853 NameValueDictionary dict(dictData);
1854
1855 Keychain keychain;
1856 Item item = (ItemImpl *) NULL;
1857
1858 if (isIdentityRef) {
1859 *isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false;
1860 }
1861
1862 // make sure we have a database identifier
1863 if (dict.FindByName(SSUID_KEY) != 0)
1864 {
1865 DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict);
1866 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
1867 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(),
1868 dlDbIdentifier.dbLocation());
1869
1870 keychain = globals().storageManager.keychain(newDlDbIdentifier);
1871
1872 const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY);
1873 if (aDictItem && keychain)
1874 {
1875 PrimaryKey primaryKey(aDictItem->Value());
1876 item = keychain->item(primaryKey);
1877 }
1878 }
1879 KCThrowIf_( !item, errSecItemNotFound );
1880 return item;
1881 }
1882
1883 void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef)
1884 {
1885 if (secd_PersistentRef) {
1886 outDataRef = secd_PersistentRef;
1887 return;
1888 }
1889 StLock<Mutex>_(mMutex);
1890 // item must be in a keychain and have a primary key to be persistent
1891 if (!mKeychain || !mPrimaryKey) {
1892 MacOSError::throwMe(errSecItemNotFound);
1893 }
1894 DLDbIdentifier dlDbIdentifier = mKeychain->dlDbIdentifier();
1895 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
1896 DLDbListCFPref::AbbreviatedPath(mKeychain->name()).c_str(),
1897 dlDbIdentifier.dbLocation());
1898 NameValueDictionary dict;
1899 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict);
1900
1901 CssmData* pKey = mPrimaryKey;
1902 dict.Insert (new NameValuePair(ITEM_KEY, *pKey));
1903
1904 if (isSecIdentityRef) {
1905 uint32_t value = -1;
1906 CssmData valueData((void*)&value, sizeof(value));
1907 dict.Insert (new NameValuePair(IDENTITY_KEY, valueData));
1908 }
1909
1910 // flatten the NameValueDictionary
1911 CssmData dictData;
1912 dict.Export(dictData);
1913 outDataRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length);
1914 free (dictData.Data);
1915 }
1916
1917 void ItemImpl::copyRecordIdentifier(CSSM_DATA &data)
1918 {
1919 StLock<Mutex>_(mMutex);
1920 CssmClient::DbUniqueRecord uniqueRecord = dbUniqueRecord ();
1921 uniqueRecord->getRecordIdentifier(data);
1922 }
1923
1924 /*
1925 * Obtain blob used to bind a keychain item to an Extended Attribute record.
1926 * We just use the PrimaryKey blob as the default. Note that for standard Items,
1927 * this can cause the loss of extended attribute bindings if a Primary Key
1928 * attribute changes.
1929 */
1930 const CssmData &ItemImpl::itemID()
1931 {
1932 StLock<Mutex>_(mMutex);
1933 if(mPrimaryKey->length() == 0) {
1934 /* not in a keychain; we don't have a primary key */
1935 MacOSError::throwMe(errSecNoSuchAttr);
1936 }
1937 return *mPrimaryKey;
1938 }
1939
1940 bool ItemImpl::equal(SecCFObject &other)
1941 {
1942 // First check to see if both items have a primary key and
1943 // if the primary key is the same. If so then these
1944 // items must be equal
1945 ItemImpl& other_item = (ItemImpl&)other;
1946 if (mPrimaryKey != NULL && mPrimaryKey == other_item.mPrimaryKey)
1947 {
1948 return true;
1949 }
1950
1951 // The primary keys do not match so do a CFHash of the
1952 // data of the item and compare those for equality
1953 CFHashCode this_hash = hash();
1954 CFHashCode other_hash = other.hash();
1955 return (this_hash == other_hash);
1956 }
1957
1958 CFHashCode ItemImpl::hash()
1959 {
1960 CFHashCode result = SecCFObject::hash();
1961
1962 StLock<Mutex>_(mMutex);
1963 RefPointer<CssmDataContainer> data_to_hash;
1964
1965 // Use the item data for the hash
1966 if (mData && *mData)
1967 {
1968 data_to_hash = mData;
1969 }
1970
1971 // If there is no primary key AND not data ????
1972 // just return the 'old' hash value which is the
1973 // object pointer.
1974 if (NULL != data_to_hash.get())
1975 {
1976 CFDataRef temp_data = NULL;
1977 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1978
1979 if (data_to_hash->length() < 80)
1980 {
1981 // If it is less than 80 bytes then CFData can be used
1982 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1983 (const UInt8 *)data_to_hash->data(), data_to_hash->length(), kCFAllocatorNull);
1984
1985 }
1986 // CFData truncates its hash value to 80 bytes. ????
1987 // In order to do the 'right thing' a SHA 256 hash will be used to
1988 // include all of the data
1989 else
1990 {
1991 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1992
1993 CC_SHA256((const void *)data_to_hash->data(), (CC_LONG)data_to_hash->length(), digest);
1994
1995 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1996 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
1997 }
1998
1999 if (NULL != temp_data)
2000 {
2001 result = CFHash(temp_data);
2002 CFRelease(temp_data);
2003 }
2004
2005 }
2006
2007 return result;
2008 }
2009
2010
2011 void ItemImpl::postItemEvent(SecKeychainEvent theEvent)
2012 {
2013 mKeychain->postEvent(theEvent, this);
2014 }
2015
2016
2017
2018 //
2019 // Item -- This class is here to magically create the right subclass of ItemImpl
2020 // when constructing new items.
2021 //
2022 Item::Item()
2023 {
2024 }
2025
2026 Item::Item(ItemImpl *impl) : SecPointer<ItemImpl>(impl)
2027 {
2028 }
2029
2030 Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool inhibitCheck)
2031 {
2032 if (!inhibitCheck)
2033 {
2034 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2035 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
2036 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
2037 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2038 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
2039 }
2040
2041 *this = new ItemImpl(itemClass, itemCreator, length, data, inhibitCheck);
2042 }
2043
2044 Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
2045 {
2046 *this = new ItemImpl(itemClass, attrList, length, data);
2047 }
2048
2049 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
2050 : SecPointer<ItemImpl>(
2051 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2052 ? Certificate::make(keychain, primaryKey, uniqueId)
2053 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2054 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2055 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2056 ? KeyItem::make(keychain, primaryKey, uniqueId)
2057 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2058 ? ExtendedAttribute::make(keychain, primaryKey, uniqueId)
2059 : ItemImpl::make(keychain, primaryKey, uniqueId))
2060 {
2061 }
2062
2063 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey)
2064 : SecPointer<ItemImpl>(
2065 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2066 ? Certificate::make(keychain, primaryKey)
2067 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2068 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2069 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2070 ? KeyItem::make(keychain, primaryKey)
2071 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2072 ? ExtendedAttribute::make(keychain, primaryKey)
2073 : ItemImpl::make(keychain, primaryKey))
2074 {
2075 }
2076
2077 Item::Item(ItemImpl &item)
2078 : SecPointer<ItemImpl>(
2079 item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2080 ? new Certificate(safer_cast<Certificate &>(item))
2081 : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2082 || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2083 || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2084 ? new KeyItem(safer_cast<KeyItem &>(item))
2085 : item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2086 ? new ExtendedAttribute(safer_cast<ExtendedAttribute &>(item))
2087 : new ItemImpl(item))
2088 {
2089 }
2090
2091 CFIndex KeychainCore::GetItemRetainCount(Item& item)
2092 {
2093 return CFGetRetainCount(item->handle(false));
2094 }
2095
2096 void ItemImpl::setPersistentRef(CFDataRef ref)
2097 {
2098 if (secd_PersistentRef) {
2099 CFRelease(secd_PersistentRef);
2100 }
2101 secd_PersistentRef = ref;
2102 CFRetain(ref);
2103 }
2104
2105 CFDataRef ItemImpl::getPersistentRef()
2106 {
2107 return secd_PersistentRef;
2108 }
2109
2110
2111
2112 bool ItemImpl::mayDelete()
2113 {
2114 ObjectImpl* uniqueIDImpl = mUniqueId.get();
2115
2116 if (uniqueIDImpl != NULL)
2117 {
2118 bool result = mUniqueId->isIdle();
2119 return result;
2120 }
2121 else
2122 {
2123 return true;
2124 }
2125 }