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