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