]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/Item.cpp
Security-58286.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 <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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 secinfo("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 StLock<Mutex>_(*(mKeychain->getKeychainMutex())); // must hold this mutex before calling db->insert
735
736 Db db(keychain->database());
737 if (mDoNotEncrypt)
738 {
739 mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get());
740 }
741 else if (useSecureStorage(db))
742 {
743 updateSSGroup(db, recordType, mData.get(), keychain, mAccess);
744 mAccess = NULL; // use them and lose them - TODO: should this only be unset if there's no error in saveToNewSSGroup? Unclear.
745 }
746 else
747 {
748 // add the item to the (regular) db
749 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
750 }
751
752 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
753 } catch(...) {
754 mKeychain = NULL;
755 throw;
756 }
757
758 // Forget our data and attributes.
759 mData = NULL;
760 mDbAttributes.reset(NULL);
761
762 return mPrimaryKey;
763 }
764
765
766
767 PrimaryKey
768 ItemImpl::add (Keychain &keychain)
769 {
770 return addWithCopyInfo (keychain, false);
771 }
772
773
774
775 Item
776 ItemImpl::copyTo(const Keychain &keychain, Access *newAccess)
777 {
778 // We'll be removing any Partition or Integrity ACLs from this item during
779 // the copy. Note that creating a new item from this one fetches the data,
780 // so this process must now be on the ACL/partition ID list for this item,
781 // and an attacker without access can't cause this removal.
782 //
783 // The integrity and partition ID acls will get re-added once the item lands
784 // in the new keychain, if it supports them. If it doesn't, removing the
785 // integrity acl as it leaves will prevent any issues if the item is
786 // modified in the unsupported keychain and then re-copied back into an
787 // integrity keychain.
788
789 StLock<Mutex>_(mMutex);
790 Item item(*this);
791 if (newAccess) {
792 newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
793 newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
794 item->setAccess(newAccess);
795 } else {
796 /* Attempt to copy the access from the current item to the newly created one. */
797 SSGroup myGroup = group();
798 if (myGroup)
799 {
800 SecPointer<Access> access = new Access(*myGroup);
801 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
802 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
803 item->setAccess(access);
804 }
805 }
806
807 keychain->addCopy(item);
808 return item;
809 }
810
811 void
812 ItemImpl::update()
813 {
814 StLock<Mutex>_(mMutex);
815 if (!mKeychain)
816 MacOSError::throwMe(errSecNoSuchKeychain);
817
818 // Don't update if nothing changed.
819 if (!isModified())
820 return;
821
822 // Hold this before modifying the db below
823 StLock<Mutex>__(*(mKeychain->getKeychainMutex()));
824
825 CSSM_DB_RECORDTYPE aRecordType = recordType();
826 KeychainSchema schema = mKeychain->keychainSchema();
827
828 // Update the modification date on the item if there is a mod date attribute.
829 if (schema->hasAttribute(aRecordType, kSecModDateItemAttr))
830 {
831 SInt64 date;
832 GetCurrentMacLongDateTime(date);
833 setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date);
834 }
835
836 Db db(dbUniqueRecord()->database());
837 if (mDoNotEncrypt)
838 {
839 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
840 memset (&attrData, 0, sizeof (attrData));
841 attrData.DataRecordType = aRecordType;
842
843 dbUniqueRecord()->modifyWithoutEncryption(aRecordType,
844 &attrData,
845 mData.get(),
846 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
847 }
848 else if (useSecureStorage(db))
849 {
850 // Pass mData to updateSSGroup. If we have any data to change (and it's
851 // therefore non-null), it'll save to a new SSGroup; otherwise, it will
852 // update the old ssgroup. This prevents a RAA on attribute update, while
853 // still protecting new data from being decrypted by old SSGroups with
854 // outdated attributes.
855 updateSSGroup(db, recordType(), mData.get());
856 }
857 else
858 {
859 dbUniqueRecord()->modify(aRecordType,
860 mDbAttributes.get(),
861 mData.get(),
862 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
863 }
864
865 if (!mDoNotEncrypt)
866 {
867 PrimaryKey oldPK = mPrimaryKey;
868 mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId);
869
870 // Forget our data and attributes.
871 mData = NULL;
872 mDbAttributes.reset(NULL);
873
874 // Let the Keychain update what it needs to.
875 mKeychain->didUpdate(this, oldPK, mPrimaryKey);
876 }
877 }
878
879 void
880 ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer* newdata, Keychain keychain, SecPointer<Access> access)
881 {
882 // hhs replaced with the new aclFactory class
883 AclFactory aclFactory;
884 const AccessCredentials *nullCred = aclFactory.nullCred();
885
886 bool haveOldUniqueId = !!mUniqueId.get();
887 SSDbUniqueRecord ssUniqueId(NULL);
888 SSGroup ssGroup(NULL);
889 if(haveOldUniqueId) {
890 ssUniqueId = SSDbUniqueRecord(dynamic_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
891 if (ssUniqueId.get() == NULL) {
892 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
893 }
894 ssGroup = ssUniqueId->group();
895 }
896
897 // If we have new data OR no old unique id, save to a new group
898 bool saveToNewSSGroup = (!!newdata) || (!haveOldUniqueId);
899
900 // If there aren't any attributes, make up some blank ones.
901 if (!mDbAttributes.get())
902 {
903 secinfo("integrity", "making new dbattributes");
904 mDbAttributes.reset(new DbAttributes());
905 mDbAttributes->recordType(mPrimaryKey->recordType());
906 }
907
908 // Add the item to the secure storage db
909 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db));
910 if (impl == NULL)
911 {
912 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
913 }
914
915 SSDb ssDb(impl);
916
917 TrackingAllocator allocator(Allocator::standard());
918
919 if ((!access) && (haveOldUniqueId)) {
920 // Copy the ACL from the old group.
921 secinfo("integrity", "copying old ACL");
922 access = new Access(*(ssGroup));
923
924 // We can't copy these over to the new item; they're going to be reset.
925 // Remove them before securityd complains.
926 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
927 access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
928 } else if (!access) {
929 secinfo("integrity", "setting up new ACL");
930 // create default access controls for the new item
931 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
932 string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item";
933 access = new Access(printName);
934 } else {
935 secinfo("integrity", "passed an Access, use it");
936 // Access is non-null. Do nothing.
937 }
938
939 // If we have an old group and an old mUniqueId, then we're in the middle of an update.
940 // mDbAttributes contains the newest attributes, but not the old ones. Find
941 // them, merge them, and shove them all back into mDbAttributes. This lets
942 // us re-apply them all to the new item.
943 if(haveOldUniqueId) {
944 mDbAttributes.reset(getCurrentAttributes());
945 }
946
947 // Create a CSPDL transaction. Note that this does things when it goes out of scope.
948 CSPDLTransaction transaction(db);
949
950 Access::Maker maker;
951 ResourceControlContext prototype;
952 maker.initialOwner(prototype, nullCred);
953
954 if(saveToNewSSGroup) {
955 secinfo("integrity", "saving to a new SSGroup");
956
957 // If we're updating an item, it has an old group and possibly an
958 // old mUniqueId. Delete these from the database, so we can insert
959 // new ones.
960 if(haveOldUniqueId) {
961 secinfo("integrity", "deleting old mUniqueId");
962 mUniqueId->deleteRecord();
963 mUniqueId.release();
964 } else {
965 secinfo("integrity", "no old mUniqueId");
966 }
967
968 // Create a new SSGroup with temporary access controls
969 SSGroup newSSGroup(ssDb, &prototype);
970 const AccessCredentials * cred = maker.cred();
971
972 try {
973 doChange(keychain, recordType, ^{
974 mUniqueId = ssDb->ssInsert(recordType, mDbAttributes.get(), newdata, newSSGroup, cred);
975 });
976
977 // now finalize the access controls on the group
978 addIntegrity(*access);
979 access->setAccess(*newSSGroup, maker);
980
981 // We have to reset this after we add the integrity, since it needs the attributes
982 mDbAttributes.reset(NULL);
983
984 transaction.commit();
985 }
986 catch (CssmError cssme) {
987 const char* errStr = cssmErrorString(cssme.error);
988 secnotice("integrity", "caught CssmError during add: %d %s", (int) cssme.error, errStr);
989
990 // Delete the new SSGroup that we just created
991 deleteSSGroup(newSSGroup, nullCred);
992 throw;
993 }
994 catch (MacOSError mose) {
995 secnotice("integrity", "caught MacOSError during add: %d", (int) mose.osStatus());
996
997 deleteSSGroup(newSSGroup, nullCred);
998 throw;
999 }
1000 catch (...)
1001 {
1002 secnotice("integrity", "caught unknown exception during add");
1003
1004 deleteSSGroup(newSSGroup, nullCred);
1005 throw;
1006 }
1007 } else {
1008 // Modify the old SSGroup
1009 secinfo("integrity", "modifying the existing SSGroup");
1010
1011 try {
1012 doChange(keychain, recordType, ^{
1013 assert(!newdata);
1014 const AccessCredentials *autoPrompt = globals().itemCredentials();
1015 ssUniqueId->modify(recordType,
1016 mDbAttributes.get(),
1017 newdata,
1018 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE,
1019 autoPrompt);
1020 });
1021
1022 // Update the integrity on the SSGroup
1023 setIntegrity(*ssGroup);
1024
1025 // We have to reset this after we add the integrity, since it needs the attributes
1026 mDbAttributes.reset(NULL);
1027
1028 transaction.commit();
1029 }
1030 catch (CssmError cssme) {
1031 const char* errStr = cssmErrorString(cssme.error);
1032 secnotice("integrity", "caught CssmError during modify: %d %s", (int) cssme.error, errStr);
1033 throw;
1034 }
1035 catch (MacOSError mose) {
1036 secnotice("integrity", "caught MacOSError during modify: %d", (int) mose.osStatus());
1037 throw;
1038 }
1039 catch (...)
1040 {
1041 secnotice("integrity", "caught unknown exception during modify");
1042 throw;
1043 }
1044
1045 }
1046 }
1047
1048 // Helper function to delete a group and swallow all errors
1049 void ItemImpl::deleteSSGroup(SSGroup & ssgroup, const AccessCredentials* nullCred) {
1050 try{
1051 ssgroup->deleteKey(nullCred);
1052 } catch(CssmError error) {
1053 secnotice("integrity", "caught cssm error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1054 } catch(MacOSError error) {
1055 secnotice("integrity", "caught macos error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1056 } catch(UnixError error) {
1057 secnotice("integrity", "caught unix error during deletion of group: %d %s", (int) error.osStatus(), error.what());
1058 }
1059 }
1060
1061 void
1062 ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryChange) ())
1063 {
1064 // Insert the record using the newly created group.
1065 try {
1066 tryChange();
1067 } catch (CssmError cssme) {
1068 // If there's a "duplicate" of this item, it might be an item with corrupt/invalid attributes
1069 // Try to extract the item and check its attributes, then try again if necessary
1070 auto_ptr<CssmClient::DbAttributes> primaryKeyAttrs;
1071 if(cssme.error == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
1072 secnotice("integrity", "possible duplicate, trying to delete invalid items");
1073
1074 Keychain kc = (keychain ? keychain : mKeychain);
1075 if(!kc) {
1076 secnotice("integrity", "no valid keychain");
1077 }
1078
1079 // Only check for corrupt items if the keychain supports them
1080 if((!kc) || !kc->hasIntegrityProtection()) {
1081 secinfo("integrity", "skipping integrity check for corrupt items due to keychain support");
1082 throw;
1083 } else {
1084 primaryKeyAttrs.reset(getCurrentAttributes());
1085 PrimaryKey pk = kc->makePrimaryKey(recordType, primaryKeyAttrs.get());
1086
1087 bool tryAgain = false;
1088
1089 // Because things are lazy, maybe our keychain has a version
1090 // of this item with different attributes. Ask it!
1091 ItemImpl* maybeItem = kc->_lookupItem(pk);
1092 if(maybeItem) {
1093 if(!maybeItem->checkIntegrity()) {
1094 Item item(maybeItem);
1095 kc->deleteItem(item);
1096 tryAgain = true;
1097 }
1098 } else {
1099 // Our keychain doesn't know about any item with this primary key, so maybe
1100 // we have a corrupt item in the database. Let's check.
1101
1102 secinfo("integrity", "making a cursor from primary key");
1103 CssmClient::DbCursor cursor = pk->createCursor(kc);
1104 DbUniqueRecord uniqueId;
1105
1106 StLock<Mutex> _mutexLocker(*kc->getKeychainMutex());
1107
1108 // The item on-disk might have more or different attributes than we do, since we're
1109 // only searching via primary key. Fetch all of its attributes.
1110 auto_ptr<DbAttributes>dbDupAttributes (new DbAttributes(kc->database(), 1));
1111 fillDbAttributesFromSchema(*dbDupAttributes, recordType, kc);
1112
1113 // Occasionally this cursor won't return the item attributes (for an unknown reason).
1114 // However, we know the attributes any item with this primary key should have, so use those instead.
1115 while (cursor->next(dbDupAttributes.get(), NULL, uniqueId)) {
1116 secinfo("integrity", "got an item...");
1117
1118 SSGroup group = safer_cast<SSDbUniqueRecordImpl &>(*uniqueId).group();
1119 if(!ItemImpl::checkIntegrityFromDictionary(*group, dbDupAttributes.get())) {
1120 secnotice("integrity", "item is invalid! deleting...");
1121 uniqueId->deleteRecord();
1122 tryAgain = true;
1123 }
1124 }
1125 }
1126
1127 if(tryAgain) {
1128 secnotice("integrity", "trying again...");
1129 tryChange();
1130 } else {
1131 // We didn't find an invalid item, the duplicate item exception is real
1132 secnotice("integrity", "duplicate item exception is real; throwing it on");
1133 throw;
1134 }
1135 }
1136 } else {
1137 throw;
1138 }
1139 }
1140 }
1141
1142 void
1143 ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength)
1144 {
1145 StLock<Mutex>_(mMutex);
1146 if (actualLength)
1147 *actualLength = sizeof(SecItemClass);
1148
1149 if (attr.length < sizeof(SecItemClass))
1150 MacOSError::throwMe(errSecBufferTooSmall);
1151
1152 SecItemClass aClass = Schema::itemClassFor(recordType());
1153 memcpy(attr.data, &aClass, sizeof(SecItemClass));
1154 }
1155
1156 void
1157 ItemImpl::setAttribute(SecKeychainAttribute& attr)
1158 {
1159 StLock<Mutex>_(mMutex);
1160 setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
1161 }
1162
1163 CSSM_DB_RECORDTYPE
1164 ItemImpl::recordType()
1165 {
1166 StLock<Mutex>_(mMutex);
1167 if (mDbAttributes.get())
1168 return mDbAttributes->recordType();
1169
1170 return mPrimaryKey->recordType();
1171 }
1172
1173 const DbAttributes *
1174 ItemImpl::modifiedAttributes()
1175 {
1176 StLock<Mutex>_(mMutex);
1177 return mDbAttributes.get();
1178 }
1179
1180 const CssmData *
1181 ItemImpl::modifiedData()
1182 {
1183 StLock<Mutex>_(mMutex);
1184 return mData.get();
1185 }
1186
1187 void
1188 ItemImpl::setData(UInt32 length,const void *data)
1189 {
1190 StLock<Mutex>_(mMutex);
1191 mData = new CssmDataContainer(data, length);
1192 }
1193
1194 void
1195 ItemImpl::setAccess(Access *newAccess)
1196 {
1197 StLock<Mutex>_(mMutex);
1198 mAccess = newAccess;
1199 }
1200
1201 CssmClient::DbUniqueRecord
1202 ItemImpl::dbUniqueRecord()
1203 {
1204 StLock<Mutex>_(mMutex);
1205 if (!isPersistent()) // is there no database attached?
1206 {
1207 MacOSError::throwMe(errSecNotAvailable);
1208 }
1209
1210 if (!mUniqueId)
1211 {
1212 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
1213 if (!cursor->next(NULL, NULL, mUniqueId))
1214 MacOSError::throwMe(errSecInvalidItemRef);
1215 }
1216
1217 // Check that our Db still matches our keychain's db. If not, find this item again in the new Db.
1218 // Why silly !(x == y) construction? Compiler calls operator bool() on each pointer otherwise.
1219 if(!(mUniqueId->database() == keychain()->database())) {
1220 secinfo("integrity", "updating db of mUniqueRecord");
1221
1222 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
1223 if (!cursor->next(NULL, NULL, mUniqueId))
1224 MacOSError::throwMe(errSecInvalidItemRef);
1225 }
1226
1227 return mUniqueId;
1228 }
1229
1230 PrimaryKey
1231 ItemImpl::primaryKey()
1232 {
1233 return mPrimaryKey;
1234 }
1235
1236 bool
1237 ItemImpl::isPersistent()
1238 {
1239 return mKeychain;
1240 }
1241
1242 bool
1243 ItemImpl::isModified()
1244 {
1245 StLock<Mutex>_(mMutex);
1246 return mData.get() || mDbAttributes.get();
1247 }
1248
1249 Keychain
1250 ItemImpl::keychain()
1251 {
1252 return mKeychain;
1253 }
1254
1255 bool
1256 ItemImpl::operator < (const ItemImpl &other)
1257 {
1258 if (mData && *mData)
1259 {
1260 // Pointer compare
1261 return this < &other;
1262 }
1263
1264 return mPrimaryKey < other.mPrimaryKey;
1265 }
1266
1267 void
1268 ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data)
1269 {
1270 StLock<Mutex>_(mMutex);
1271 if (!mDbAttributes.get())
1272 {
1273 mDbAttributes.reset(new DbAttributes());
1274 mDbAttributes->recordType(mPrimaryKey->recordType());
1275 }
1276
1277 size_t length = data.Length;
1278 const void *buf = reinterpret_cast<const void *>(data.Data);
1279 uint8 timeString[16];
1280
1281 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
1282 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
1283 // style attribute value.
1284 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1285 {
1286 if (length == sizeof(UInt32))
1287 {
1288 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 16, &timeString);
1289 buf = &timeString;
1290 length = 16;
1291 }
1292 else if (length == sizeof(SInt64))
1293 {
1294 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 16, &timeString);
1295 buf = &timeString;
1296 length = 16;
1297 }
1298 }
1299
1300 mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
1301 }
1302
1303 void
1304 ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
1305 {
1306 StLock<Mutex>_(mMutex);
1307 StMaybeLock<Mutex>__ (mKeychain == NULL ? NULL : mKeychain->getKeychainMutex());
1308
1309 if (!mDbAttributes.get())
1310 {
1311 mDbAttributes.reset(new DbAttributes());
1312 mDbAttributes->recordType(mPrimaryKey->recordType());
1313 }
1314
1315 if(attrList) // optional
1316 {
1317 for(UInt32 ix=0; ix < attrList->count; ix++)
1318 {
1319 SecKeychainAttrType attrTag = attrList->attr[ix].tag;
1320
1321 if (attrTag == APPLEDB_CSSM_PRINTNAME_ATTRIBUTE)
1322 {
1323 // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema
1324 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1325 attrTag = kSecLabelItemAttr;
1326 }
1327
1328 mDbAttributes->add(Schema::attributeInfo(attrTag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
1329 }
1330 }
1331
1332 if(inData)
1333 {
1334 mData = new CssmDataContainer(inData, dataLength);
1335 }
1336
1337 update();
1338 }
1339
1340 void
1341 ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
1342 {
1343 StLock<Mutex>_(mMutex);
1344 // If the data hasn't been set we can't return it.
1345 if (!mKeychain && outData)
1346 {
1347 CssmData *data = mData.get();
1348 if (!data)
1349 MacOSError::throwMe(errSecDataNotAvailable);
1350 }
1351 // TODO: need to check and make sure attrs are valid and handle error condition
1352
1353
1354 if (itemClass)
1355 *itemClass = Schema::itemClassFor(recordType());
1356
1357 bool getDataFromDatabase = mKeychain && mPrimaryKey;
1358 if (getDataFromDatabase) // are we attached to a database?
1359 {
1360 dbUniqueRecord();
1361
1362 // get the number of attributes requested by the caller
1363 UInt32 attrCount = attrList ? attrList->count : 0;
1364
1365 // make a DBAttributes structure and populate it
1366 DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
1367 for (UInt32 ix = 0; ix < attrCount; ++ix)
1368 {
1369 dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag));
1370 }
1371
1372 // request the data from the database (since we are a reference "item" and the data is really stored there)
1373 CssmDataContainer itemData;
1374 getContent(&dbAttributes, outData ? &itemData : NULL);
1375
1376 // retrieve the data from result
1377 for (UInt32 ix = 0; ix < attrCount; ++ix)
1378 {
1379 if (dbAttributes.at(ix).NumberOfValues > 0)
1380 {
1381 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
1382 attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
1383
1384 // We don't want the data released, it is up the client
1385 dbAttributes.at(ix).Value[0].Data = NULL;
1386 dbAttributes.at(ix).Value[0].Length = 0;
1387 }
1388 else
1389 {
1390 attrList->attr[ix].data = NULL;
1391 attrList->attr[ix].length = 0;
1392 }
1393 }
1394
1395 // clean up
1396 if (outData)
1397 {
1398 *outData=itemData.data();
1399 itemData.Data = NULL;
1400
1401 if (length)
1402 *length=(UInt32)itemData.length();
1403 itemData.Length = 0;
1404 }
1405 }
1406 else
1407 {
1408 getLocalContent(attrList, length, outData);
1409 }
1410
1411 // Inform anyone interested that we are doing this
1412 #if SENDACCESSNOTIFICATIONS
1413 if (outData)
1414 {
1415 secinfo("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content",
1416 itemClass, attrList, length, outData);
1417
1418 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
1419 }
1420 #endif
1421 }
1422
1423 void
1424 ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data)
1425 {
1426 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally
1427 if (data)
1428 allocator.free(data);
1429
1430 UInt32 attrCount = attrList ? attrList->count : 0;
1431 for (UInt32 ix = 0; ix < attrCount; ++ix)
1432 {
1433 allocator.free(attrList->attr[ix].data);
1434 attrList->attr[ix].data = NULL;
1435 }
1436 }
1437
1438 void
1439 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
1440 {
1441 StLock<Mutex>_(mMutex);
1442 if (!mKeychain)
1443 MacOSError::throwMe(errSecNoSuchKeychain);
1444
1445 if (!mDoNotEncrypt)
1446 {
1447 if (!mDbAttributes.get())
1448 {
1449 mDbAttributes.reset(new DbAttributes());
1450 mDbAttributes->recordType(mPrimaryKey->recordType());
1451 }
1452
1453 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
1454 UInt32 attrCount = attrList ? attrList->count : 0;
1455 for (UInt32 ix = 0; ix < attrCount; ix++)
1456 {
1457 SecKeychainAttrType attrTag = attrList->attr[ix].tag;
1458
1459 if (attrTag == kSecLabelItemAttr)
1460 {
1461 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1462 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1463 if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType) ))
1464 attrTag = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
1465 }
1466
1467 CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrTag);
1468
1469 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
1470 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
1471 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
1472 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
1473 else
1474 mDbAttributes->add(info);
1475 }
1476 }
1477
1478 if(inData)
1479 {
1480 mData = new CssmDataContainer(inData, dataLength);
1481 }
1482
1483 update();
1484 }
1485
1486 void
1487 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass,
1488 SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
1489 {
1490 StLock<Mutex>_(mMutex);
1491 // If the data hasn't been set we can't return it.
1492 if (!mKeychain && outData)
1493 {
1494 CssmData *data = mData.get();
1495 if (!data)
1496 MacOSError::throwMe(errSecDataNotAvailable);
1497 }
1498 // TODO: need to check and make sure attrs are valid and handle error condition
1499
1500 SecItemClass myItemClass = Schema::itemClassFor(recordType());
1501 if (itemClass)
1502 *itemClass = myItemClass;
1503
1504 // @@@ This call won't work for floating items (like certificates).
1505 dbUniqueRecord();
1506
1507 UInt32 attrCount = info ? info->count : 0;
1508 DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
1509 for (UInt32 ix = 0; ix < attrCount; ix++)
1510 {
1511 CssmDbAttributeData &record = dbAttributes.add();
1512 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
1513 record.Info.Label.AttributeID=info->tag[ix];
1514
1515 if (record.Info.Label.AttributeID == kSecLabelItemAttr)
1516 {
1517 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1518 if (IS_PASSWORD_ITEM_CLASS( myItemClass ))
1519 record.Info.Label.AttributeID = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
1520 }
1521 }
1522
1523 CssmDataContainer itemData;
1524 getContent(&dbAttributes, outData ? &itemData : NULL);
1525
1526 if (info && attrList)
1527 {
1528 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
1529
1530 if(attrCount == 0) {
1531 theList->count = 0;
1532 theList->attr = NULL;
1533 } else {
1534 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(malloc(sizeof(SecKeychainAttribute)*attrCount));
1535 theList->count=attrCount;
1536 theList->attr=attr;
1537
1538 for (UInt32 ix = 0; ix < attrCount; ++ix)
1539 {
1540 attr[ix].tag=info->tag[ix];
1541
1542 if (dbAttributes.at(ix).NumberOfValues > 0)
1543 {
1544 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
1545 attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
1546
1547 // We don't want the data released, it is up the client
1548 dbAttributes.at(ix).Value[0].Data = NULL;
1549 dbAttributes.at(ix).Value[0].Length = 0;
1550 }
1551 else
1552 {
1553 attr[ix].data = NULL;
1554 attr[ix].length = 0;
1555 }
1556 }
1557 }
1558 *attrList=theList;
1559 }
1560
1561 if (outData)
1562 {
1563 *outData=itemData.data();
1564 itemData.Data=NULL;
1565
1566 if (length) *length=(UInt32)itemData.length();
1567 itemData.Length=0;
1568
1569 #if SENDACCESSNOTIFICATIONS
1570 secinfo("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data",
1571 info, itemClass, attrList, length, outData);
1572
1573 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
1574 #endif
1575 }
1576
1577 }
1578
1579 void
1580 ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
1581 {
1582 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally
1583
1584 if (data)
1585 allocator.free(data);
1586
1587 if (attrList)
1588 {
1589 for (UInt32 ix = 0; ix < attrList->count; ++ix)
1590 {
1591 allocator.free(attrList->attr[ix].data);
1592 }
1593 free(attrList->attr);
1594 free(attrList);
1595 }
1596 }
1597
1598 void
1599 ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
1600 {
1601 StLock<Mutex>_(mMutex);
1602 if (attr.tag == kSecClassItemAttr)
1603 return getClass(attr, actualLength);
1604
1605 if (mDbAttributes.get())
1606 {
1607 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
1608 if (data)
1609 {
1610 getAttributeFrom(data, attr, actualLength);
1611 return;
1612 }
1613 }
1614
1615 if (!mKeychain)
1616 MacOSError::throwMe(errSecNoSuchAttr);
1617
1618 DbAttributes dbAttributes(dbUniqueRecord()->database(), 1);
1619 dbAttributes.add(Schema::attributeInfo(attr.tag));
1620 dbUniqueRecord()->get(&dbAttributes, NULL);
1621 getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
1622 }
1623
1624 void
1625 ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
1626 {
1627 StLock<Mutex>_(mMutex);
1628 static const uint32 zero = 0;
1629 UInt32 length;
1630 const void *buf = NULL;
1631
1632 // Temporary storage for buf.
1633 sint64 macLDT;
1634 uint32 macSeconds;
1635 sint16 svalue16;
1636 uint16 uvalue16;
1637 sint8 svalue8;
1638 uint8 uvalue8;
1639
1640 if (!data)
1641 length = 0;
1642 else if (data->size() < 1) // Attribute has no values.
1643 {
1644 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
1645 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1646 {
1647 length = sizeof(zero);
1648 buf = &zero;
1649 }
1650 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1651 length = 0; // Should we throw here?
1652 else // All other formats
1653 length = 0;
1654 }
1655 else // Get the first value
1656 {
1657 length = (UInt32)data->Value[0].Length;
1658 buf = data->Value[0].Data;
1659
1660 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
1661 {
1662 if (attr.length == sizeof(sint8))
1663 {
1664 length = attr.length;
1665 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
1666 buf = &svalue8;
1667 }
1668 else if (attr.length == sizeof(sint16))
1669 {
1670 length = attr.length;
1671 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
1672 buf = &svalue16;
1673 }
1674 }
1675 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
1676 {
1677 if (attr.length == sizeof(uint8))
1678 {
1679 length = attr.length;
1680 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
1681 buf = &uvalue8;
1682 }
1683 else if (attr.length == sizeof(uint16))
1684 {
1685 length = attr.length;
1686 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
1687 buf = &uvalue16;
1688 }
1689 }
1690 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
1691 {
1692 if (attr.length == sizeof(uint32))
1693 {
1694 TimeStringToMacSeconds(data->Value[0], macSeconds);
1695 buf = &macSeconds;
1696 length = attr.length;
1697 }
1698 else if (attr.length == sizeof(sint64))
1699 {
1700 TimeStringToMacLongDateTime(data->Value[0], macLDT);
1701 buf = &macLDT;
1702 length = attr.length;
1703 }
1704 }
1705 }
1706
1707 if (actualLength)
1708 *actualLength = length;
1709
1710 if (length)
1711 {
1712 if (attr.length < length)
1713 MacOSError::throwMe(errSecBufferTooSmall);
1714
1715 memcpy(attr.data, buf, length);
1716 }
1717 }
1718
1719 void
1720 ItemImpl::getData(CssmDataContainer& outData)
1721 {
1722 StLock<Mutex>_(mMutex);
1723 if (!mKeychain)
1724 {
1725 CssmData *data = mData.get();
1726 // If the data hasn't been set we can't return it.
1727 if (!data)
1728 MacOSError::throwMe(errSecDataNotAvailable);
1729
1730 outData = *data;
1731 return;
1732 }
1733
1734 getContent(NULL, &outData);
1735
1736 #if SENDACCESSNOTIFICATIONS
1737 secinfo("kcnotify", "ItemImpl::getData retrieved data");
1738
1739 //%%%<might> be done elsewhere, but here is good for now
1740 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
1741 #endif
1742 }
1743
1744 SSGroup
1745 ItemImpl::group()
1746 {
1747 StLock<Mutex>_(mMutex);
1748 SSGroup group;
1749 if (!!mUniqueId)
1750 {
1751 Db db(mKeychain->database());
1752 if (useSecureStorage(db))
1753 {
1754 group = safer_cast<SSDbUniqueRecordImpl &>(*dbUniqueRecord()).group();
1755 }
1756 }
1757
1758 return group;
1759 }
1760
1761 void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData)
1762 {
1763 StLock<Mutex>_(mMutex);
1764 willRead();
1765 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally
1766 if (outData)
1767 {
1768 CssmData *data = mData.get();
1769 if (!data)
1770 MacOSError::throwMe(errSecDataNotAvailable);
1771
1772 // Copy the data out of our internal cached copy.
1773 UInt32 length = (UInt32)data->Length;
1774 *outData = allocator.malloc(length);
1775 memcpy(*outData, data->Data, length);
1776 if (outLength)
1777 *outLength = length;
1778 }
1779
1780 if (attributeList)
1781 {
1782 if (!mDbAttributes.get())
1783 MacOSError::throwMe(errSecDataNotAvailable);
1784
1785 // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database
1786 for (UInt32 ix = 0; ix < attributeList->count; ++ix)
1787 {
1788 SecKeychainAttribute &attribute = attributeList->attr[ix];
1789 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attribute.tag));
1790 if (data && data->NumberOfValues > 0)
1791 {
1792 // Copy the data out of our internal cached copy.
1793 UInt32 length = (UInt32)data->Value[0].Length;
1794 attribute.data = allocator.malloc(length);
1795 memcpy(attribute.data, data->Value[0].Data, length);
1796 attribute.length = length;
1797 }
1798 else
1799 {
1800 attribute.length = 0;
1801 attribute.data = NULL;
1802 }
1803 }
1804 }
1805 }
1806
1807 void
1808 ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
1809 {
1810 StLock<Mutex>_(mMutex);
1811 if (itemData)
1812 {
1813 Db db(dbUniqueRecord()->database());
1814 if (mDoNotEncrypt)
1815 {
1816 dbUniqueRecord()->getWithoutEncryption (dbAttributes, itemData);
1817 return;
1818 }
1819 if (useSecureStorage(db))
1820 {
1821 try {
1822 if(!checkIntegrity()) {
1823 secnotice("integrity", "item has no integrity, denying access");
1824 CssmError::throwMe(errSecInvalidItemRef);
1825 }
1826 } catch(CssmError cssme) {
1827 secnotice("integrity", "error while checking integrity, denying access: %s", cssme.what());
1828 throw cssme;
1829 }
1830
1831 SSDbUniqueRecordImpl* impl = dynamic_cast<SSDbUniqueRecordImpl *>(&(*dbUniqueRecord()));
1832 if (impl == NULL)
1833 {
1834 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1835 }
1836
1837 SSDbUniqueRecord ssUniqueId(impl);
1838 const AccessCredentials *autoPrompt = globals().itemCredentials();
1839 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
1840 return;
1841 }
1842 }
1843
1844 dbUniqueRecord()->get(dbAttributes, itemData);
1845 }
1846
1847 bool
1848 ItemImpl::useSecureStorage(const Db &db)
1849 {
1850 StLock<Mutex>_(mMutex);
1851 switch (recordType())
1852 {
1853 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
1854 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
1855 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
1856 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
1857 return true;
1858 break;
1859 default:
1860 break;
1861 }
1862 return false;
1863 }
1864
1865 void ItemImpl::willRead()
1866 {
1867 }
1868
1869 Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef)
1870 {
1871 CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef));
1872 NameValueDictionary dict(dictData);
1873
1874 Keychain keychain;
1875 Item item = (ItemImpl *) NULL;
1876
1877 if (isIdentityRef) {
1878 *isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false;
1879 }
1880
1881 // make sure we have a database identifier
1882 if (dict.FindByName(SSUID_KEY) != 0)
1883 {
1884 DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict);
1885 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
1886 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(),
1887 dlDbIdentifier.dbLocation());
1888
1889 keychain = globals().storageManager.keychain(newDlDbIdentifier);
1890
1891 const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY);
1892 if (aDictItem && keychain)
1893 {
1894 PrimaryKey primaryKey(aDictItem->Value());
1895 item = keychain->item(primaryKey);
1896 }
1897 }
1898 KCThrowIf_( !item, errSecItemNotFound );
1899 return item;
1900 }
1901
1902 void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef)
1903 {
1904 if (secd_PersistentRef) {
1905 outDataRef = secd_PersistentRef;
1906 return;
1907 }
1908 StLock<Mutex>_(mMutex);
1909 // item must be in a keychain and have a primary key to be persistent
1910 if (!mKeychain || !mPrimaryKey) {
1911 MacOSError::throwMe(errSecItemNotFound);
1912 }
1913 DLDbIdentifier dlDbIdentifier = mKeychain->dlDbIdentifier();
1914 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
1915 DLDbListCFPref::AbbreviatedPath(mKeychain->name()).c_str(),
1916 dlDbIdentifier.dbLocation());
1917 NameValueDictionary dict;
1918 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict);
1919
1920 CssmData* pKey = mPrimaryKey;
1921 dict.Insert (new NameValuePair(ITEM_KEY, *pKey));
1922
1923 if (isSecIdentityRef) {
1924 uint32_t value = -1;
1925 CssmData valueData((void*)&value, sizeof(value));
1926 dict.Insert (new NameValuePair(IDENTITY_KEY, valueData));
1927 }
1928
1929 // flatten the NameValueDictionary
1930 CssmData dictData;
1931 dict.Export(dictData);
1932 outDataRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length);
1933 free (dictData.Data);
1934 }
1935
1936 void ItemImpl::copyRecordIdentifier(CSSM_DATA &data)
1937 {
1938 StLock<Mutex>_(mMutex);
1939 CssmClient::DbUniqueRecord uniqueRecord = dbUniqueRecord ();
1940 uniqueRecord->getRecordIdentifier(data);
1941 }
1942
1943 /*
1944 * Obtain blob used to bind a keychain item to an Extended Attribute record.
1945 * We just use the PrimaryKey blob as the default. Note that for standard Items,
1946 * this can cause the loss of extended attribute bindings if a Primary Key
1947 * attribute changes.
1948 */
1949 const CssmData &ItemImpl::itemID()
1950 {
1951 StLock<Mutex>_(mMutex);
1952 if(mPrimaryKey->length() == 0) {
1953 /* not in a keychain; we don't have a primary key */
1954 MacOSError::throwMe(errSecNoSuchAttr);
1955 }
1956 return *mPrimaryKey;
1957 }
1958
1959 bool ItemImpl::equal(SecCFObject &other)
1960 {
1961 // First check to see if both items have a primary key and
1962 // if the primary key is the same. If so then these
1963 // items must be equal
1964 ItemImpl& other_item = (ItemImpl&)other;
1965 if (mPrimaryKey != NULL && mPrimaryKey == other_item.mPrimaryKey)
1966 {
1967 return true;
1968 }
1969
1970 // The primary keys do not match so do a CFHash of the
1971 // data of the item and compare those for equality
1972 CFHashCode this_hash = hash();
1973 CFHashCode other_hash = other.hash();
1974 return (this_hash == other_hash);
1975 }
1976
1977 CFHashCode ItemImpl::hash()
1978 {
1979 CFHashCode result = SecCFObject::hash();
1980
1981 StLock<Mutex>_(mMutex);
1982 RefPointer<CssmDataContainer> data_to_hash;
1983
1984 // Use the item data for the hash
1985 if (mData && *mData)
1986 {
1987 data_to_hash = mData;
1988 }
1989
1990 // If there is no primary key AND not data ????
1991 // just return the 'old' hash value which is the
1992 // object pointer.
1993 if (NULL != data_to_hash.get())
1994 {
1995 CFDataRef temp_data = NULL;
1996 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1997
1998 if (data_to_hash->length() < 80)
1999 {
2000 // If it is less than 80 bytes then CFData can be used
2001 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
2002 (const UInt8 *)data_to_hash->data(), data_to_hash->length(), kCFAllocatorNull);
2003
2004 }
2005 // CFData truncates its hash value to 80 bytes. ????
2006 // In order to do the 'right thing' a SHA 256 hash will be used to
2007 // include all of the data
2008 else
2009 {
2010 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
2011
2012 CC_SHA256((const void *)data_to_hash->data(), (CC_LONG)data_to_hash->length(), digest);
2013
2014 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
2015 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
2016 }
2017
2018 if (NULL != temp_data)
2019 {
2020 result = CFHash(temp_data);
2021 CFRelease(temp_data);
2022 }
2023
2024 }
2025
2026 return result;
2027 }
2028
2029
2030 void ItemImpl::postItemEvent(SecKeychainEvent theEvent)
2031 {
2032 mKeychain->postEvent(theEvent, this);
2033 }
2034
2035
2036
2037 //
2038 // Item -- This class is here to magically create the right subclass of ItemImpl
2039 // when constructing new items.
2040 //
2041 Item::Item()
2042 {
2043 }
2044
2045 Item::Item(ItemImpl *impl) : SecPointer<ItemImpl>(impl)
2046 {
2047 }
2048
2049 Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool inhibitCheck)
2050 {
2051 if (!inhibitCheck)
2052 {
2053 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2054 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
2055 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
2056 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2057 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
2058 }
2059
2060 *this = new ItemImpl(itemClass, itemCreator, length, data, inhibitCheck);
2061 }
2062
2063 Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
2064 {
2065 *this = new ItemImpl(itemClass, attrList, length, data);
2066 }
2067
2068 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
2069 : SecPointer<ItemImpl>(
2070 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2071 ? Certificate::make(keychain, primaryKey, uniqueId)
2072 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2073 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2074 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2075 ? KeyItem::make(keychain, primaryKey, uniqueId)
2076 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2077 ? ExtendedAttribute::make(keychain, primaryKey, uniqueId)
2078 : ItemImpl::make(keychain, primaryKey, uniqueId))
2079 {
2080 }
2081
2082 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey)
2083 : SecPointer<ItemImpl>(
2084 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2085 ? Certificate::make(keychain, primaryKey)
2086 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2087 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2088 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2089 ? KeyItem::make(keychain, primaryKey)
2090 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2091 ? ExtendedAttribute::make(keychain, primaryKey)
2092 : ItemImpl::make(keychain, primaryKey))
2093 {
2094 }
2095
2096 Item::Item(ItemImpl &item)
2097 : SecPointer<ItemImpl>(
2098 item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2099 ? new Certificate(safer_cast<Certificate &>(item))
2100 : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2101 || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2102 || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
2103 ? new KeyItem(safer_cast<KeyItem &>(item))
2104 : item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2105 ? new ExtendedAttribute(safer_cast<ExtendedAttribute &>(item))
2106 : new ItemImpl(item))
2107 {
2108 }
2109
2110 CFIndex KeychainCore::GetItemRetainCount(Item& item)
2111 {
2112 return CFGetRetainCount(item->handle(false));
2113 }
2114
2115 void ItemImpl::setPersistentRef(CFDataRef ref)
2116 {
2117 if (secd_PersistentRef) {
2118 CFRelease(secd_PersistentRef);
2119 }
2120 secd_PersistentRef = ref;
2121 CFRetain(ref);
2122 }
2123
2124 CFDataRef ItemImpl::getPersistentRef()
2125 {
2126 return secd_PersistentRef;
2127 }
2128
2129
2130
2131 bool ItemImpl::mayDelete()
2132 {
2133 ObjectImpl* uniqueIDImpl = mUniqueId.get();
2134
2135 if (uniqueIDImpl != NULL)
2136 {
2137 bool result = mUniqueId->isIdle();
2138 return result;
2139 }
2140 else
2141 {
2142 return true;
2143 }
2144 }