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