]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/Keychains.cpp
Security-57740.31.2.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / Keychains.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 // Keychains.cpp
27 //
28
29 #include "KCEventNotifier.h"
30 #include "Keychains.h"
31
32 #include "Item.h"
33 #include "KCCursor.h"
34 #include "Globals.h"
35 #include <security_cdsa_utilities/Schema.h>
36 #include <security_cdsa_client/keychainacl.h>
37 #include <security_cdsa_utilities/cssmacl.h>
38 #include <security_cdsa_utilities/cssmdb.h>
39 #include <security_utilities/trackingallocator.h>
40 #include <security_utilities/FileLockTransaction.h>
41 #include <security_keychain/SecCFTypes.h>
42 #include <securityd_client/ssblob.h>
43 #include <Security/TrustSettingsSchema.h>
44
45 #include "SecKeychainPriv.h"
46
47 #include <Security/SecKeychainItemPriv.h>
48 #include <CoreFoundation/CoreFoundation.h>
49 #include "DLDbListCFPref.h"
50 #include <fcntl.h>
51 #include <glob.h>
52 #include <sys/param.h>
53 #include <syslog.h>
54 #include <sys/stat.h>
55 #include <sys/socket.h>
56 #include <sys/un.h>
57 #include <sys/types.h>
58 #include <sys/time.h>
59
60 static dispatch_once_t SecKeychainSystemKeychainChecked;
61
62 OSStatus SecKeychainSystemKeychainCheckWouldDeadlock()
63 {
64 dispatch_once(&SecKeychainSystemKeychainChecked, ^{});
65 return errSecSuccess;
66 }
67
68 using namespace KeychainCore;
69 using namespace CssmClient;
70
71
72 typedef struct EventItem
73 {
74 SecKeychainEvent kcEvent;
75 Item item;
76 } EventItem;
77
78 typedef std::list<EventItem> EventBufferSuper;
79 class EventBuffer : public EventBufferSuper
80 {
81 public:
82 EventBuffer () {}
83 virtual ~EventBuffer ();
84 };
85
86
87 EventBuffer::~EventBuffer ()
88 {
89 }
90
91
92
93 //
94 // KeychainSchemaImpl
95 //
96 KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive)
97 {
98 DbCursor relations(db);
99 relations->recordType(CSSM_DL_DB_SCHEMA_INFO);
100 DbAttributes relationRecord(db, 1);
101 relationRecord.add(Schema::RelationID);
102 DbUniqueRecord outerUniqueId(db);
103
104 while (relations->next(&relationRecord, NULL, outerUniqueId))
105 {
106 DbUniqueRecord uniqueId(db);
107
108 uint32 relationID = relationRecord.at(0);
109 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
110 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
111 continue;
112
113 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with
114 // RelationID == relationID
115 DbCursor attributes(db);
116 attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
117 attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
118
119 // Set up a record for retriving the SCHEMA_ATTRIBUTES
120 DbAttributes attributeRecord(db, 2);
121 attributeRecord.add(Schema::AttributeFormat);
122 attributeRecord.add(Schema::AttributeID);
123
124 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
125 while (attributes->next(&attributeRecord, NULL, uniqueId))
126 rim[attributeRecord.at(1)] = attributeRecord.at(0);
127
128 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
129 // with RelationID == relationID
130 DbCursor indexes(db);
131 indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES);
132 indexes->conjunctive(CSSM_DB_AND);
133 indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
134 indexes->add(CSSM_DB_EQUAL, Schema::IndexType,
135 uint32(CSSM_DB_INDEX_UNIQUE));
136
137 // Set up a record for retriving the SCHEMA_INDEXES
138 DbAttributes indexRecord(db, 1);
139 indexRecord.add(Schema::AttributeID);
140
141 CssmAutoDbRecordAttributeInfo &infos =
142 *new CssmAutoDbRecordAttributeInfo();
143 mPrimaryKeyInfoMap.
144 insert(PrimaryKeyInfoMap::value_type(relationID, &infos));
145 infos.DataRecordType = relationID;
146 while (indexes->next(&indexRecord, NULL, uniqueId))
147 {
148 CssmDbAttributeInfo &info = infos.add();
149 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
150 info.Label.AttributeID = indexRecord.at(0);
151 // @@@ Might insert bogus value if DB is corrupt
152 info.AttributeFormat = rim[info.Label.AttributeID];
153 }
154 }
155 }
156
157 KeychainSchemaImpl::~KeychainSchemaImpl()
158 {
159 try
160 {
161 map<CSSM_DB_RECORDTYPE, CssmAutoDbRecordAttributeInfo *>::iterator it = mPrimaryKeyInfoMap.begin();
162 while (it != mPrimaryKeyInfoMap.end())
163 {
164 delete it->second;
165 it++;
166 }
167 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
168 }
169 catch(...)
170 {
171 }
172 }
173
174 const KeychainSchemaImpl::RelationInfoMap &
175 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const
176 {
177 DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
178 if (dit == mDatabaseInfoMap.end())
179 MacOSError::throwMe(errSecNoSuchClass);
180 return dit->second;
181 }
182
183 bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const
184 {
185 DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType);
186 return it != mDatabaseInfoMap.end();
187 }
188
189 bool
190 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
191 {
192 try
193 {
194 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
195 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
196 return rit != rmap.end();
197 }
198 catch (MacOSError result)
199 {
200 if (result.osStatus () == errSecNoSuchClass)
201 {
202 return false;
203 }
204 else
205 {
206 throw;
207 }
208 }
209 }
210
211 CSSM_DB_ATTRIBUTE_FORMAT
212 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
213 {
214 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
215 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
216 if (rit == rmap.end())
217 MacOSError::throwMe(errSecNoSuchAttr);
218
219 return rit->second;
220 }
221
222 CssmDbAttributeInfo
223 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
224 {
225 CSSM_DB_ATTRIBUTE_INFO info;
226 info.AttributeFormat = attributeFormatFor(recordType, attributeId);
227 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
228 info.Label.AttributeID = attributeId;
229
230 return info;
231 }
232
233 void
234 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const
235 {
236 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
237
238 SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
239
240 size_t capacity=rmap.size();
241 UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
242 UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
243 UInt32 i=0;
244
245
246 for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit)
247 {
248 if (i>=capacity)
249 {
250 capacity *= 2;
251 if (capacity <= i) capacity = i + 1;
252 tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
253 formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
254 }
255 tagBuf[i]=rit->first;
256 formatBuf[i++]=rit->second;
257 }
258
259 theList->count=i;
260 theList->tag=tagBuf;
261 theList->format=formatBuf;
262 *Info=theList;
263 }
264
265
266 const CssmAutoDbRecordAttributeInfo &
267 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const
268 {
269 PrimaryKeyInfoMap::const_iterator it;
270 it = mPrimaryKeyInfoMap.find(recordType);
271
272 if (it == mPrimaryKeyInfoMap.end())
273 MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever.
274
275 return *it->second;
276 }
277
278 bool
279 KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const
280 {
281 return mDatabaseInfoMap < other.mDatabaseInfoMap;
282 }
283
284 bool
285 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const
286 {
287 return mDatabaseInfoMap == other.mDatabaseInfoMap;
288 }
289
290 void
291 KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID,
292 const char *inRelationName,
293 uint32 inNumberOfAttributes,
294 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo,
295 uint32 inNumberOfIndexes,
296 const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo)
297 {
298 StLock<Mutex>_(mMutex);
299
300 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
301 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
302 return;
303
304 // if our schema is already in the map, return
305 if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end())
306 {
307 return;
308 }
309
310 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
311 for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix)
312 rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType;
313
314 CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo();
315
316 mPrimaryKeyInfoMap.
317 insert(PrimaryKeyInfoMap::value_type(relationID, infos));
318 infos->DataRecordType = relationID;
319 for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix)
320 if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE)
321 {
322 CssmDbAttributeInfo &info = infos->add();
323 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
324 info.Label.AttributeID = pIndexInfo[ix].AttributeId;
325 info.AttributeFormat = rim[info.Label.AttributeID];
326 }
327 }
328
329
330
331 KeychainSchema::~KeychainSchema()
332
333 {
334 }
335
336
337
338 struct Event
339 {
340 SecKeychainEvent eventCode;
341 PrimaryKey primaryKey;
342 };
343 typedef std::list<Event> EventList;
344
345 #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
346 #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
347 #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
348
349 static void check_system_keychain()
350 {
351 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the
352 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets.
353
354 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
355 // In theory a system might be able to recover from this state if we let it try to muddle along, and
356 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a
357 // sandbox'ed process. So we just do our best and let another process try again.
358
359 struct stat keycheck_file_info;
360 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) {
361 int server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
362 if (server_fd < 0) {
363 syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked");
364 return;
365 }
366
367 struct sockaddr_un keychain_check_server_address;
368 keychain_check_server_address.sun_family = AF_UNIX;
369 if (strlcpy(keychain_check_server_address.sun_path, SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME, sizeof(keychain_check_server_address.sun_path)) > sizeof(keychain_check_server_address.sun_path)) {
370 // It would be nice if we could compile time assert this
371 syslog(LOG_ERR, "Socket path too long, max length %lu, your length %lu", (unsigned long)sizeof(keychain_check_server_address.sun_path), (unsigned long)strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME));
372 close(server_fd);
373 return;
374 }
375 keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address);
376
377 int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len);
378 if (rc < 0) {
379 syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME);
380 close(server_fd);
381 return;
382 }
383
384 // this read lets us block until the EOF comes, we don't ever get a byte (and if we do, we don't care about it)
385 char byte;
386 ssize_t read_size = read(server_fd, &byte, 1);
387 if (read_size < 0) {
388 syslog(LOG_ERR, "Error reading from system keychain checker: %m");
389 }
390
391 close(server_fd);
392 return;
393 }
394 }
395
396 //
397 // KeychainImpl
398 //
399 KeychainImpl::KeychainImpl(const Db &db)
400 : mCacheTimer(NULL), mSuppressTickle(false), mAttemptedUpgrade(false), mDbItemMapMutex(Mutex::recursive), mDbDeletedItemMapMutex(Mutex::recursive),
401 mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive)
402 {
403 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
404 check_system_keychain();
405 });
406 mDb->defaultCredentials(this); // install activation hook
407 mEventBuffer = new EventBuffer;
408 }
409
410 KeychainImpl::~KeychainImpl()
411 {
412 try
413 {
414 // Remove ourselves from the cache if we are in it.
415 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
416 globals().storageManager.removeKeychain(dlDbIdentifier(), this);
417 delete mEventBuffer;
418 }
419 catch(...)
420 {
421 }
422 }
423
424 Mutex*
425 KeychainImpl::getMutexForObject() const
426 {
427 return globals().storageManager.getStorageManagerMutex();
428 }
429
430 Mutex*
431 KeychainImpl::getKeychainMutex()
432 {
433 return &mMutex;
434 }
435
436 ReadWriteLock*
437 KeychainImpl::getKeychainReadWriteLock()
438 {
439 return &mRWLock;
440 }
441
442 void KeychainImpl::aboutToDestruct()
443 {
444 // remove me from the global cache, we are done
445 // fprintf(stderr, "Destructing keychain object\n");
446 DLDbIdentifier identifier = dlDbIdentifier();
447 globals().storageManager.removeKeychain(identifier, this);
448 }
449
450 bool
451 KeychainImpl::operator ==(const KeychainImpl &keychain) const
452 {
453 return dlDbIdentifier() == keychain.dlDbIdentifier();
454 }
455
456 KCCursor
457 KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
458 {
459 StLock<Mutex>_(mMutex);
460
461 StorageManager::KeychainList keychains;
462 keychains.push_back(Keychain(this));
463 return KCCursor(keychains, itemClass, attrList);
464 }
465
466 KCCursor
467 KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
468 {
469 StLock<Mutex>_(mMutex);
470
471 StorageManager::KeychainList keychains;
472 keychains.push_back(Keychain(this));
473 return KCCursor(keychains, attrList);
474 }
475
476 void
477 KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
478 {
479 StLock<Mutex>_(mMutex);
480
481 if (!inPassword)
482 {
483 create();
484 return;
485 }
486
487 Allocator &alloc = Allocator::standard();
488
489 // @@@ Share this instance
490
491 const CssmData password(const_cast<void *>(inPassword), passwordLength);
492 AclFactory::PasswordChangeCredentials pCreds (password, alloc);
493 AclFactory::AnyResourceContext rcc(pCreds);
494 create(&rcc);
495
496 // Now that we've created, trigger setting the defaultCredentials
497 mDb->open();
498 }
499
500 void KeychainImpl::create(ConstStringPtr inPassword)
501 {
502 StLock<Mutex>_(mMutex);
503
504 if ( inPassword )
505 create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
506 else
507 create();
508 }
509
510 void
511 KeychainImpl::create()
512 {
513 StLock<Mutex>_(mMutex);
514
515 AclFactory aclFactory;
516 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
517 create(&rcc);
518
519 // Now that we've created, trigger setting the defaultCredentials
520 mDb->open();
521 }
522
523 void KeychainImpl::createWithBlob(CssmData &blob)
524 {
525 StLock<Mutex>_(mMutex);
526
527 mDb->dbInfo(&Schema::DBInfo);
528 AclFactory aclFactory;
529 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
530 mDb->resourceControlContext (&rcc);
531 try
532 {
533 mDb->createWithBlob(blob);
534 }
535 catch (...)
536 {
537 mDb->resourceControlContext(NULL);
538 mDb->dbInfo(NULL);
539 throw;
540 }
541 mDb->resourceControlContext(NULL);
542 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
543 globals().storageManager.created(Keychain(this));
544
545 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
546 }
547
548 void
549 KeychainImpl::create(const ResourceControlContext *rcc)
550 {
551 StLock<Mutex>_(mMutex);
552
553 mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create)
554 mDb->resourceControlContext(rcc);
555 try
556 {
557 mDb->create();
558 }
559 catch (...)
560 {
561 mDb->resourceControlContext(NULL);
562 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
563 throw;
564 }
565 mDb->resourceControlContext(NULL);
566 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
567 globals().storageManager.created(Keychain(this));
568 }
569
570 void
571 KeychainImpl::open()
572 {
573 StLock<Mutex>_(mMutex);
574
575 mDb->open();
576 }
577
578 void
579 KeychainImpl::lock()
580 {
581 StLock<Mutex>_(mMutex);
582
583 mDb->lock();
584 }
585
586 void
587 KeychainImpl::unlock()
588 {
589 StLock<Mutex>_(mMutex);
590
591 mDb->unlock();
592 }
593
594 void
595 KeychainImpl::unlock(const CssmData &password)
596 {
597 StLock<Mutex>_(mMutex);
598
599 mDb->unlock(password);
600 }
601
602 void
603 KeychainImpl::unlock(ConstStringPtr password)
604 {
605 StLock<Mutex>_(mMutex);
606
607 if (password)
608 {
609 const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
610 unlock(data);
611 }
612 else
613 unlock();
614 }
615
616 void
617 KeychainImpl::stash()
618 {
619 StLock<Mutex>_(mMutex);
620
621 mDb->stash();
622 }
623
624 void
625 KeychainImpl::stashCheck()
626 {
627 StLock<Mutex>_(mMutex);
628
629 mDb->stashCheck();
630 }
631
632 void
633 KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
634 {
635 StLock<Mutex>_(mMutex);
636
637 mDb->getSettings(outIdleTimeOut, outLockOnSleep);
638 }
639
640 void
641 KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
642 {
643 StLock<Mutex>_(mMutex);
644
645 // The .Mac syncing code only makes sense for the AppleFile CSP/DL,
646 // but other DLs such as the OCSP and LDAP DLs do not expose a way to
647 // change settings or the password. To make a minimal change that only affects
648 // the smartcard case, we only look for that CSP/DL
649
650 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL);
651
652 // get the old keychain blob so that we can tell .Mac to resync it
653 CssmAutoData oldBlob(mDb ->allocator());
654 if (!isSmartcard)
655 mDb->copyBlob(oldBlob.get());
656
657 mDb->setSettings(inIdleTimeOut, inLockOnSleep);
658 }
659
660 void
661 KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
662 UInt32 newPasswordLength, const void *newPassword)
663 {
664 StLock<Mutex>_(mMutex);
665
666 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL);
667
668 TrackingAllocator allocator(Allocator::standard());
669 AutoCredentials cred = AutoCredentials(allocator);
670 if (oldPassword)
671 {
672 const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength);
673 TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK);
674 oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
675 oldList.append(new(allocator) ListElement(oldPass));
676 cred += oldList;
677 }
678
679 if (newPassword)
680 {
681 const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength);
682 TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK);
683 newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
684 newList.append(new(allocator) ListElement(newPass));
685 cred += newList;
686 }
687
688 // get the old keychain blob so that we can tell .Mac to resync it
689 CssmAutoData oldBlob(mDb->allocator());
690 if (!isSmartcard)
691 mDb->copyBlob(oldBlob.get());
692
693 mDb->changePassphrase(&cred);
694 }
695
696 void
697 KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
698 {
699 StLock<Mutex>_(mMutex);
700
701 const void *oldPtr, *newPtr;
702 UInt32 oldLen, newLen;
703 if (oldPassword)
704 {
705 oldLen = oldPassword[0];
706 oldPtr = oldPassword + 1;
707 }
708 else
709 {
710 oldLen = 0;
711 oldPtr = NULL;
712 }
713
714 if (newPassword)
715 {
716 newLen = newPassword[0];
717 newPtr = newPassword + 1;
718 }
719 else
720 {
721 newLen = 0;
722 newPtr = NULL;
723 }
724
725 changePassphrase(oldLen, oldPtr, newLen, newPtr);
726 }
727
728 void
729 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred)
730 {
731 StLock<Mutex>_(mMutex);
732
733 if (!exists())
734 MacOSError::throwMe(errSecNoSuchKeychain);
735
736 MacOSError::throwMe(errSecUnimplemented);
737 }
738
739 UInt32
740 KeychainImpl::status() const
741 {
742 StLock<Mutex>_(mMutex);
743
744 // @@@ We should figure out the read/write status though a DL passthrough
745 // or some other way. Also should locked be unlocked read only or just
746 // read-only?
747 return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus)
748 | kSecReadPermStatus;
749 }
750
751 bool
752 KeychainImpl::exists()
753 {
754 StLock<Mutex>_(mMutex);
755
756 bool exists = true;
757 try
758 {
759 open();
760 // Ok to leave the mDb open since it will get closed when it goes away.
761 }
762 catch (const CssmError &e)
763 {
764 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
765 throw;
766 exists = false;
767 }
768
769 return exists;
770 }
771
772 bool
773 KeychainImpl::isActive() const
774 {
775 return mDb->isActive();
776 }
777
778 void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey)
779 {
780 // The inItem shouldn't be in the cache yet
781 assert(!inItem->inCache());
782
783 // Insert inItem into mDbItemMap with key primaryKey. p.second will be
784 // true if it got inserted. If not p.second will be false and p.first
785 // will point to the current entry with key primaryKey.
786 StLock<Mutex> _(mDbItemMapMutex);
787 pair<DbItemMap::iterator, bool> p =
788 mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
789 if (!p.second)
790 {
791 // There was already an ItemImpl * in mDbItemMap with key
792 // primaryKey. Remove it, and try the add again.
793 ItemImpl *oldItem = p.first->second;
794
795 // @@@ If this happens we are breaking our API contract of
796 // uniquifying items. We really need to insert the item into the
797 // map before we start the add. And have the item be in an
798 // "is being added" state.
799 secnotice("keychain", "add of new item %p somehow replaced %p",
800 inItem.get(), oldItem);
801
802 mDbItemMap.erase(p.first);
803 oldItem->inCache(false);
804 forceRemoveFromCache(oldItem);
805 mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
806 }
807
808 inItem->inCache(true);
809 }
810
811 void
812 KeychainImpl::addCopy(Item &inItem)
813 {
814 StReadWriteLock _(mRWLock, StReadWriteLock::Write);
815
816 Keychain keychain(this);
817 PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true);
818 completeAdd(inItem, primaryKey);
819 postEvent(kSecAddEvent, inItem);
820 }
821
822 void
823 KeychainImpl::add(Item &inItem)
824 {
825 // Make sure we hold a write lock on ourselves when we do this
826 StReadWriteLock _(mRWLock, StReadWriteLock::Write);
827
828 Keychain keychain(this);
829 PrimaryKey primaryKey = inItem->add(keychain);
830 completeAdd(inItem, primaryKey);
831 postEvent(kSecAddEvent, inItem);
832 }
833
834 void
835 KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK,
836 PrimaryKey &newPK)
837 {
838 // If the primary key hasn't changed we don't need to update mDbItemMap.
839 if (oldPK != newPK)
840 {
841 // If inItem isn't in the cache we don't need to update mDbItemMap.
842 assert(inItem->inCache());
843 if (inItem->inCache())
844 {
845 StLock<Mutex> _(mDbItemMapMutex);
846 // First remove the entry for inItem in mDbItemMap with key oldPK.
847 DbItemMap::iterator it = mDbItemMap.find(oldPK);
848 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
849 mDbItemMap.erase(it);
850
851 // Insert inItem into mDbItemMap with key newPK. p.second will be
852 // true if it got inserted. If not p.second will be false and
853 // p.first will point to the current entry with key newPK.
854 pair<DbItemMap::iterator, bool> p =
855 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
856 if (!p.second)
857 {
858 // There was already an ItemImpl * in mDbItemMap with key
859 // primaryKey. Remove it, and try the add again.
860 ItemImpl *oldItem = p.first->second;
861
862 // @@@ If this happens we are breaking our API contract of
863 // uniquifying items. We really need to insert the item into
864 // the map with the new primary key before we start the update.
865 // And have the item be in an "is being updated" state.
866 secnotice("keychain", "update of item %p somehow replaced %p",
867 inItem.get(), oldItem);
868
869 mDbItemMap.erase(p.first);
870 oldItem->inCache(false);
871 forceRemoveFromCache(oldItem);
872 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
873 }
874 }
875 }
876
877 // Item updates now are technically a delete and re-add, so post these events instead of kSecUpdateEvent
878 postEvent(kSecDeleteEvent, inItem, oldPK);
879 postEvent(kSecAddEvent, inItem);
880 }
881
882 void
883 KeychainImpl::deleteItem(Item &inoutItem)
884 {
885 StReadWriteLock _(mRWLock, StReadWriteLock::Write);
886
887 {
888 // item must be persistent
889 if (!inoutItem->isPersistent())
890 MacOSError::throwMe(errSecInvalidItemRef);
891
892 secinfo("kcnotify", "starting deletion of item %p", inoutItem.get());
893
894 DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
895 PrimaryKey primaryKey = inoutItem->primaryKey();
896 uniqueId->deleteRecord();
897
898 // Move the item from mDbItemMap to mDbDeletedItemMap. We need the item
899 // to give to the client process when we receive the kSecDeleteEvent
900 // notification, but if that notification never arrives, we don't want
901 // the item hanging around. When didDeleteItem is called by CCallbackMgr,
902 // we'll remove all traces of the item.
903
904 if (inoutItem->inCache()) {
905 StLock<Mutex> _(mDbItemMapMutex);
906 StLock<Mutex> __(mDbDeletedItemMapMutex);
907 // Only look for it if it's in the cache
908 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
909
910 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inoutItem.get()) {
911 mDbDeletedItemMap.insert(DbItemMap::value_type(primaryKey, it->second));
912 mDbItemMap.erase(it);
913 }
914 }
915
916 // Post the notification for the item deletion with
917 // the primaryKey obtained when the item still existed
918 }
919
920 postEvent(kSecDeleteEvent, inoutItem);
921 }
922
923 void KeychainImpl::changeDatabase(CssmClient::Db db)
924 {
925 StLock<Mutex>_(mDbMutex);
926 mDb = db;
927 mDb->defaultCredentials(this);
928 }
929
930
931 CssmClient::CSP
932 KeychainImpl::csp()
933 {
934 StLock<Mutex>_(mMutex);
935
936 if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
937 MacOSError::throwMe(errSecInvalidKeychain);
938
939 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
940 try
941 {
942 CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
943 return CSP(cspdl);
944 }
945 catch (...)
946 {
947 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb));
948 if (impl == NULL)
949 {
950 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
951 }
952
953 SSDb ssDb(impl);
954 return ssDb->csp();
955 }
956 }
957
958 PrimaryKey
959 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
960 {
961 StLock<Mutex>_(mMutex);
962
963 DbAttributes primaryKeyAttrs(uniqueId->database());
964 primaryKeyAttrs.recordType(recordType);
965 gatherPrimaryKeyAttributes(primaryKeyAttrs);
966 uniqueId->get(&primaryKeyAttrs, NULL);
967 return PrimaryKey(primaryKeyAttrs);
968 }
969
970 PrimaryKey
971 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbAttributes* currentAttributes)
972 {
973 StLock<Mutex>_(mMutex);
974
975 DbAttributes primaryKeyAttrs;
976 primaryKeyAttrs.recordType(recordType);
977 gatherPrimaryKeyAttributes(primaryKeyAttrs);
978
979 for(int i = 0; i < primaryKeyAttrs.size(); i++) {
980 CssmDbAttributeData& attr = primaryKeyAttrs[i];
981
982 CssmDbAttributeData * actual = currentAttributes->find(attr.info());
983 if(actual) {
984 attr.set(*actual, Allocator::standard());
985 }
986 }
987 return PrimaryKey(primaryKeyAttrs);
988 }
989
990 const CssmAutoDbRecordAttributeInfo &
991 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
992 {
993 StLock<Mutex>_(mMutex);
994
995 try
996 {
997 return keychainSchema()->primaryKeyInfosFor(recordType);
998 }
999 catch (const CommonError &error)
1000 {
1001 switch (error.osStatus())
1002 {
1003 case errSecNoSuchClass:
1004 case CSSMERR_DL_INVALID_RECORDTYPE:
1005 resetSchema();
1006 return keychainSchema()->primaryKeyInfosFor(recordType);
1007 default:
1008 throw;
1009 }
1010 }
1011 }
1012
1013 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
1014 {
1015 StLock<Mutex> _(mMutex);
1016
1017 const CssmAutoDbRecordAttributeInfo &infos =
1018 primaryKeyInfosFor(primaryKeyAttrs.recordType());
1019
1020 // @@@ fix this to not copy info.
1021 for (uint32 i = 0; i < infos.size(); i++)
1022 primaryKeyAttrs.add(infos.at(i));
1023 }
1024
1025 ItemImpl *
1026 KeychainImpl::_lookupItem(const PrimaryKey &primaryKey)
1027 {
1028 StLock<Mutex> _(mDbItemMapMutex);
1029 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1030 if (it != mDbItemMap.end())
1031 {
1032 return it->second;
1033 }
1034
1035 return NULL;
1036 }
1037
1038 ItemImpl *
1039 KeychainImpl::_lookupDeletedItemOnly(const PrimaryKey &primaryKey)
1040 {
1041 StLock<Mutex> _(mDbDeletedItemMapMutex);
1042 DbItemMap::iterator it = mDbDeletedItemMap.find(primaryKey);
1043 if (it != mDbDeletedItemMap.end())
1044 {
1045 return it->second;
1046 }
1047
1048 return NULL;
1049 }
1050
1051 Item
1052 KeychainImpl::item(const PrimaryKey &primaryKey)
1053 {
1054 StLock<Mutex>_(mMutex);
1055
1056 // Lookup the item in the map while holding the apiLock.
1057 ItemImpl *itemImpl = _lookupItem(primaryKey);
1058 if (itemImpl) {
1059 return Item(itemImpl);
1060 }
1061
1062 try
1063 {
1064 // We didn't find it so create a new item with just a keychain and
1065 // a primary key. Some other thread might have beaten
1066 // us to creating this item and adding it to the cache. If that
1067 // happens we retry the lookup.
1068 return Item(this, primaryKey);
1069 }
1070 catch (const MacOSError &e)
1071 {
1072 // If the item creation failed because some other thread already
1073 // inserted this item into the cache we retry the lookup.
1074 if (e.osStatus() == errSecDuplicateItem)
1075 {
1076 // Lookup the item in the map while holding the apiLock.
1077 ItemImpl *itemImpl = _lookupItem(primaryKey);
1078 if (itemImpl)
1079 return Item(itemImpl);
1080 }
1081 throw;
1082 }
1083 }
1084 // Check for an item that may have been deleted.
1085 Item
1086 KeychainImpl::itemdeleted(const PrimaryKey& primaryKey) {
1087 StLock<Mutex>_(mMutex);
1088
1089 Item i = _lookupDeletedItemOnly(primaryKey);
1090 if(i.get()) {
1091 return i;
1092 } else {
1093 return item(primaryKey);
1094 }
1095 }
1096
1097
1098 Item
1099 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
1100 {
1101 StLock<Mutex>_(mMutex);
1102
1103 PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
1104 {
1105 // Lookup the item in the map while holding the apiLock.
1106 ItemImpl *itemImpl = _lookupItem(primaryKey);
1107
1108 if (itemImpl)
1109 {
1110 return Item(itemImpl);
1111 }
1112 }
1113
1114 try
1115 {
1116 // We didn't find it so create a new item with a keychain, a primary key
1117 // and a DbUniqueRecord. However since we aren't holding
1118 // globals().apiLock anymore some other thread might have beaten
1119 // us to creating this item and adding it to the cache. If that
1120 // happens we retry the lookup.
1121 return Item(this, primaryKey, uniqueId);
1122 }
1123 catch (const MacOSError &e)
1124 {
1125 // If the item creation failed because some other thread already
1126 // inserted this item into the cache we retry the lookup.
1127 if (e.osStatus() == errSecDuplicateItem)
1128 {
1129 // Lookup the item in the map while holding the apiLock.
1130 ItemImpl *itemImpl = _lookupItem(primaryKey);
1131 if (itemImpl)
1132 return Item(itemImpl);
1133 }
1134 throw;
1135 }
1136 }
1137
1138 KeychainSchema
1139 KeychainImpl::keychainSchema()
1140 {
1141 StLock<Mutex>_(mMutex);
1142 if (!mKeychainSchema)
1143 mKeychainSchema = KeychainSchema(mDb);
1144
1145 return mKeychainSchema;
1146 }
1147
1148 void KeychainImpl::resetSchema()
1149 {
1150 mKeychainSchema = NULL; // re-fetch it from db next time
1151 }
1152
1153
1154 // Called from DbItemImpl's constructor (so it is only partially constructed),
1155 // add it to the map.
1156 void
1157 KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
1158 {
1159 StLock<Mutex>_(mMutex);
1160
1161 // The dbItemImpl shouldn't be in the cache yet
1162 assert(!dbItemImpl->inCache());
1163
1164 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1165 // be true if it got inserted. If not p.second will be false and p.first
1166 // will point to the current entry with key primaryKey.
1167 StLock<Mutex> __(mDbItemMapMutex);
1168 pair<DbItemMap::iterator, bool> p =
1169 mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
1170
1171 if (!p.second)
1172 {
1173 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1174 // There is a race condition here when being called in multiple threads
1175 // We might have added an item using add and received a notification at
1176 // the same time.
1177 MacOSError::throwMe(errSecDuplicateItem);
1178 }
1179
1180 dbItemImpl->inCache(true);
1181 }
1182
1183 void
1184 KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
1185 {
1186 StLock<Mutex>_(mMutex);
1187
1188 // Called by CCallbackMgr
1189 secinfo("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
1190 removeItem(inItemImpl->primaryKey(), inItemImpl);
1191 }
1192
1193 void
1194 KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
1195 {
1196 StLock<Mutex>_(mMutex);
1197
1198 // If inItemImpl isn't in the cache to begin with we are done.
1199 if (!inItemImpl->inCache())
1200 return;
1201
1202 {
1203 StLock<Mutex> _(mDbItemMapMutex);
1204 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1205 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl) {
1206 mDbItemMap.erase(it);
1207 }
1208 } // drop mDbItemMapMutex
1209
1210 {
1211 StLock<Mutex> _(mDbDeletedItemMapMutex);
1212 DbItemMap::iterator it = mDbDeletedItemMap.find(primaryKey);
1213 if (it != mDbDeletedItemMap.end() && (ItemImpl*) it->second == inItemImpl) {
1214 mDbDeletedItemMap.erase(it);
1215 }
1216 } // drop mDbDeletedItemMapMutex
1217
1218 inItemImpl->inCache(false);
1219 }
1220
1221 void
1222 KeychainImpl::forceRemoveFromCache(ItemImpl* inItemImpl) {
1223 try {
1224 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
1225 {
1226 StLock<Mutex> _(mDbItemMapMutex);
1227 for(DbItemMap::iterator it = mDbItemMap.begin(); it != mDbItemMap.end(); ) {
1228 if(it->second == inItemImpl) {
1229 // Increment the iterator, but use its pre-increment value for the erase
1230 it->second->inCache(false);
1231 mDbItemMap.erase(it++);
1232 } else {
1233 it++;
1234 }
1235 }
1236 } // drop mDbItemMapMutex
1237
1238 {
1239 StLock<Mutex> _(mDbDeletedItemMapMutex);
1240 for(DbItemMap::iterator it = mDbDeletedItemMap.begin(); it != mDbDeletedItemMap.end(); ) {
1241 if(it->second == inItemImpl) {
1242 // Increment the iterator, but use its pre-increment value for the erase
1243 it->second->inCache(false);
1244 mDbDeletedItemMap.erase(it++);
1245 } else {
1246 it++;
1247 }
1248 }
1249 } // drop mDbDeletedItemMapMutex
1250 } catch(UnixError ue) {
1251 secnotice("keychain", "caught UnixError: %d %s", ue.unixError(), ue.what());
1252 } catch (CssmError cssme) {
1253 const char* errStr = cssmErrorString(cssme.error);
1254 secnotice("keychain", "caught CssmError: %d %s", (int) cssme.error, errStr);
1255 } catch (MacOSError mose) {
1256 secnotice("keychain", "MacOSError: %d", (int)mose.osStatus());
1257 } catch(...) {
1258 secnotice("keychain", "Unknown error");
1259 }
1260 }
1261
1262 void
1263 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID,
1264 SecKeychainAttributeInfo **Info)
1265 {
1266 StLock<Mutex>_(mMutex);
1267
1268 try
1269 {
1270 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1271 }
1272 catch (const CommonError &error)
1273 {
1274 switch (error.osStatus())
1275 {
1276 case errSecNoSuchClass:
1277 case CSSMERR_DL_INVALID_RECORDTYPE:
1278 resetSchema();
1279 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1280 default:
1281 throw;
1282 }
1283 }
1284 }
1285
1286 void
1287 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1288 {
1289 free(Info->tag);
1290 free(Info->format);
1291 free(Info);
1292 }
1293
1294 CssmDbAttributeInfo
1295 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
1296 {
1297 StLock<Mutex>_(mMutex);
1298
1299 try
1300 {
1301 return keychainSchema()->attributeInfoFor(recordType, tag);
1302 }
1303 catch (const CommonError &error)
1304 {
1305 switch (error.osStatus())
1306 {
1307 case errSecNoSuchClass:
1308 case CSSMERR_DL_INVALID_RECORDTYPE:
1309 resetSchema();
1310 return keychainSchema()->attributeInfoFor(recordType, tag);
1311 default:
1312 throw;
1313 }
1314 }
1315 }
1316
1317 void
1318 KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1319 {
1320 StLock<Mutex>_(mMutex);
1321
1322 mDb->recode(data, extraData);
1323 }
1324
1325 void
1326 KeychainImpl::copyBlob(CssmData &data)
1327 {
1328 StLock<Mutex>_(mMutex);
1329
1330 mDb->copyBlob(data);
1331 }
1332
1333 void
1334 KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
1335 {
1336 StLock<Mutex>_(mMutex);
1337
1338 mDb->setBatchMode(mode, rollback);
1339 mIsInBatchMode = mode;
1340 if (!mode)
1341 {
1342 if (!rollback) // was batch mode being turned off without an abort?
1343 {
1344 // dump the buffer
1345 EventBuffer::iterator it = mEventBuffer->begin();
1346 while (it != mEventBuffer->end())
1347 {
1348 PrimaryKey primaryKey;
1349 if (it->item)
1350 {
1351 primaryKey = it->item->primaryKey();
1352 }
1353
1354 KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey);
1355
1356 ++it;
1357 }
1358
1359 }
1360
1361 // notify that a keychain has changed in too many ways to count
1362 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent);
1363 mEventBuffer->clear();
1364 }
1365 else
1366 {
1367 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent);
1368 }
1369 }
1370 void
1371 KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
1372 {
1373 postEvent(kcEvent, item, NULL);
1374 }
1375
1376 void
1377 KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item, PrimaryKey pk)
1378 {
1379 PrimaryKey primaryKey;
1380
1381 if(pk.get()) {
1382 primaryKey = pk;
1383 } else {
1384 StLock<Mutex>_(mMutex);
1385
1386 if (item != NULL)
1387 {
1388 primaryKey = item->primaryKey();
1389 }
1390 }
1391
1392 if (!mIsInBatchMode)
1393 {
1394 KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
1395 }
1396 else
1397 {
1398 StLock<Mutex>_(mMutex);
1399
1400 EventItem it;
1401 it.kcEvent = kcEvent;
1402 if (item != NULL)
1403 {
1404 it.item = item;
1405 }
1406
1407 mEventBuffer->push_back (it);
1408 }
1409 }
1410
1411 void KeychainImpl::tickle() {
1412 if(!mSuppressTickle) {
1413 globals().storageManager.tickleKeychain(this);
1414 }
1415 }
1416
1417
1418 bool KeychainImpl::performKeychainUpgradeIfNeeded() {
1419 // Grab this keychain's mutex. This might not be sufficient, since the
1420 // keychain might have outstanding cursors. We'll grab the RWLock later if needed.
1421 StLock<Mutex>_(mMutex);
1422
1423 if(!globals().integrityProtection()) {
1424 secnotice("integrity", "skipping upgrade for %s due to global integrity protection being disabled", mDb->name());
1425 return false;
1426 }
1427
1428 // We need a CSP database for 'upgrade' to be meaningful
1429 if((mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP) == 0) {
1430 return false;
1431 }
1432
1433 // We only want to upgrade file-based Apple keychains. Check the GUID.
1434 if(mDb->dl()->guid() != gGuidAppleCSPDL) {
1435 secinfo("integrity", "skipping upgrade for %s due to guid mismatch\n", mDb->name());
1436 return false;
1437 }
1438
1439 // If we've already attempted an upgrade on this keychain, don't bother again
1440 if(mAttemptedUpgrade) {
1441 return false;
1442 }
1443
1444 // Don't upgrade the System root certificate keychain (to make old tp code happy)
1445 if(strncmp(mDb->name(), SYSTEM_ROOT_STORE_PATH, strlen(SYSTEM_ROOT_STORE_PATH)) == 0) {
1446 secinfo("integrity", "skipping upgrade for %s\n", mDb->name());
1447 return false;
1448 }
1449
1450 uint32 dbBlobVersion = SecurityServer::DbBlob::version_MacOS_10_0;
1451
1452 try {
1453 dbBlobVersion = mDb->dbBlobVersion();
1454 } catch (CssmError cssme) {
1455 if(cssme.error == CSSMERR_DL_DATASTORE_DOESNOT_EXIST) {
1456 // oh well! We tried to get the blob version of a database
1457 // that doesn't exist. It doesn't need migration, so do nothing.
1458 secnotice("integrity", "dbBlobVersion() failed for a non-existent database");
1459 return false;
1460 } else {
1461 // Some other error occurred. We can't upgrade this keychain, so fail.
1462 const char* errStr = cssmErrorString(cssme.error);
1463 secnotice("integrity", "dbBlobVersion() failed for a CssmError: %d %s", (int) cssme.error, errStr);
1464 return false;
1465 }
1466 } catch (...) {
1467 secnotice("integrity", "dbBlobVersion() failed for an unknown reason");
1468 return false;
1469 }
1470
1471
1472
1473 // Check the location of this keychain
1474 string path = mDb->name();
1475 string keychainDbPath = StorageManager::makeKeychainDbFilename(path);
1476
1477 bool inHomeLibraryKeychains = StorageManager::pathInHomeLibraryKeychains(path);
1478
1479 string keychainDbSuffix = "-db";
1480 bool endsWithKeychainDb = (path.size() > keychainDbSuffix.size() && (0 == path.compare(path.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
1481
1482 bool isSystemKeychain = (0 == path.compare("/Library/Keychains/System.keychain"));
1483
1484 bool result = false;
1485
1486 if(inHomeLibraryKeychains && endsWithKeychainDb && dbBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0) {
1487 // something has gone horribly wrong: an old-versioned keychain has a .keychain-db name. Rename it.
1488 string basePath = path;
1489 basePath.erase(basePath.end()-3, basePath.end());
1490
1491 attemptKeychainRename(path, basePath, dbBlobVersion);
1492
1493 // If we moved to a good path, we might still want to perform the upgrade. Update our variables.
1494 path = mDb->name();
1495
1496 try {
1497 dbBlobVersion = mDb->dbBlobVersion();
1498 } catch (CssmError cssme) {
1499 const char* errStr = cssmErrorString(cssme.error);
1500 secnotice("integrity", "dbBlobVersion() after a rename failed for a CssmError: %d %s", (int) cssme.error, errStr);
1501 return false;
1502 } catch (...) {
1503 secnotice("integrity", "dbBlobVersion() failed for an unknown reason after a rename");
1504 return false;
1505 }
1506
1507 endsWithKeychainDb = (path.size() > keychainDbSuffix.size() && (0 == path.compare(path.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
1508 keychainDbPath = StorageManager::makeKeychainDbFilename(path);
1509 secnotice("integrity", "after rename, our database thinks that it is %s", path.c_str());
1510 }
1511
1512 // Migrate an old keychain in ~/Library/Keychains
1513 if(inHomeLibraryKeychains && dbBlobVersion != SecurityServer::DbBlob::version_partition && !endsWithKeychainDb) {
1514 // We can only attempt to migrate an unlocked keychain.
1515 if(mDb->isLocked()) {
1516 // However, it's possible that while we weren't doing any keychain operations, someone upgraded the keychain,
1517 // and then locked it. No way around hitting the filesystem here: check for the existence of a new file and,
1518 // if no new file exists, quit.
1519 DLDbIdentifier mungedDLDbIdentifier = StorageManager::mungeDLDbIdentifier(mDb->dlDbIdentifier(), false);
1520 string mungedPath(mungedDLDbIdentifier.dbName());
1521
1522 // If this matches the file we already have, skip the upgrade. Otherwise, continue.
1523 if(mungedPath == path) {
1524 secnotice("integrity", "skipping upgrade for locked keychain %s\n", mDb->name());
1525 return false;
1526 }
1527 }
1528
1529 result = keychainMigration(path, dbBlobVersion, keychainDbPath, SecurityServer::DbBlob::version_partition);
1530 } else if(inHomeLibraryKeychains && dbBlobVersion == SecurityServer::DbBlob::version_partition && !endsWithKeychainDb) {
1531 // This is a new-style keychain with the wrong name, try to rename it
1532 attemptKeychainRename(path, keychainDbPath, dbBlobVersion);
1533 result = true;
1534 } else if(isSystemKeychain && dbBlobVersion == SecurityServer::DbBlob::version_partition) {
1535 // Try to "unupgrade" the system keychain, to clean up our old issues
1536 secnotice("integrity", "attempting downgrade for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
1537
1538 // First step: acquire the credentials to allow for ACL modification
1539 SecurityServer::SystemKeychainKey skk(kSystemUnlockFile);
1540 if(skk.valid()) {
1541 // We've managed to read the key; now, create credentials using it
1542 CssmClient::Key systemKeychainMasterKey(csp(), skk.key(), true);
1543 CssmClient::AclFactory::MasterKeyUnlockCredentials creds(systemKeychainMasterKey, Allocator::standard(Allocator::sensitive));
1544
1545 // Attempt the downgrade, using our master key as the ACL override
1546 result = keychainMigration(path, dbBlobVersion, path, SecurityServer::DbBlob::version_MacOS_10_0, creds.getAccessCredentials());
1547 } else {
1548 secnotice("integrity", "Couldn't read System.keychain key, skipping update");
1549 }
1550 } else {
1551 secinfo("integrity", "not attempting migration for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
1552
1553 // Since we don't believe any migration needs to be done here, mark the
1554 // migration as "attempted" to short-circuit future checks.
1555 mAttemptedUpgrade = true;
1556 }
1557
1558 // We might have changed our location on disk. Let StorageManager know.
1559 globals().storageManager.registerKeychainImpl(this);
1560
1561 // if we attempted a migration, try to clean up leftover files from <rdar://problem/23950408> XARA backup have provided me with 12GB of login keychain copies
1562 if(result) {
1563 string pattern = path + "_*_backup";
1564 glob_t pglob = {};
1565 secnotice("integrity", "globbing for %s", pattern.c_str());
1566 int globresult = glob(pattern.c_str(), GLOB_MARK, NULL, &pglob);
1567 if(globresult == 0) {
1568 secnotice("integrity", "glob: %lu results", pglob.gl_pathc);
1569 if(pglob.gl_pathc > 10) {
1570 // There are more than 10 backup files, indicating a problem.
1571 // Delete all but one of them. Under rdar://23950408, they should all be identical.
1572 secnotice("integrity", "saving backup file: %s", pglob.gl_pathv[0]);
1573 for(int i = 1; i < pglob.gl_pathc; i++) {
1574 secnotice("integrity", "cleaning up backup file: %s", pglob.gl_pathv[i]);
1575 // ignore return code; this is a best-effort cleanup
1576 unlink(pglob.gl_pathv[i]);
1577 }
1578 }
1579
1580 struct stat st;
1581 bool pathExists = (::stat(path.c_str(), &st) == 0);
1582 bool keychainDbPathExists = (::stat(keychainDbPath.c_str(), &st) == 0);
1583
1584 if(!pathExists && keychainDbPathExists && pglob.gl_pathc >= 1) {
1585 // We have a file at keychainDbPath, no file at path, and at least one backup keychain file.
1586 //
1587 // Move the backup file to path, to simulate the current "split-world" view,
1588 // which copies from path to keychainDbPath, then modifies keychainDbPath.
1589 secnotice("integrity", "moving backup file %s to %s", pglob.gl_pathv[0], path.c_str());
1590 ::rename(pglob.gl_pathv[0], path.c_str());
1591 }
1592 }
1593
1594 globfree(&pglob);
1595 }
1596
1597 return result;
1598 }
1599
1600 bool KeychainImpl::keychainMigration(const string oldPath, const uint32 dbBlobVersion, const string newPath, const uint32 newBlobVersion, const AccessCredentials *cred) {
1601 secnotice("integrity", "going to migrate %s at version %d to", oldPath.c_str(), dbBlobVersion);
1602 secnotice("integrity", " %s at version %d", newPath.c_str(), newBlobVersion);
1603
1604 // We need to opportunistically perform the upgrade/reload dance.
1605 //
1606 // If the keychain is unlocked, try to upgrade it.
1607 // In either case, reload the database from disk.
1608 // We need this keychain's read/write lock.
1609
1610 // Try to grab the keychain write lock.
1611 StReadWriteLock lock(mRWLock, StReadWriteLock::TryWrite);
1612
1613 // If we didn't manage to grab the lock, there's readers out there
1614 // currently reading this keychain. Abort the upgrade.
1615 if(!lock.isLocked()) {
1616 secnotice("integrity", "couldn't get read-write lock, aborting upgrade");
1617 return false;
1618 }
1619
1620 // Take the file lock on the existing database. We don't need to commit this txion, because we're not planning to
1621 // change the original keychain.
1622 FileLockTransaction fileLockmDb(mDb);
1623
1624 // Let's reload this keychain to see if someone changed it on disk
1625 globals().storageManager.reloadKeychain(this);
1626
1627 bool result = false;
1628
1629 try {
1630 // We can only attempt an upgrade if the keychain is currently unlocked
1631 // There's a TOCTTOU issue here, but it's going to be rare in practice, and the upgrade will simply fail.
1632 if(!mDb->isLocked()) {
1633 secnotice("integrity", "have a plan to migrate database %s", mDb->name());
1634 // Database blob is out of date. Attempt a migration.
1635 uint32 convertedVersion = attemptKeychainMigration(oldPath, dbBlobVersion, newPath, newBlobVersion, cred);
1636 if(convertedVersion == newBlobVersion) {
1637 secnotice("integrity", "conversion succeeded");
1638 result = true;
1639 } else {
1640 secnotice("integrity", "conversion failed, keychain is still %d", convertedVersion);
1641 }
1642 } else {
1643 secnotice("integrity", "keychain is locked, can't upgrade");
1644 }
1645 } catch (CssmError cssme) {
1646 const char* errStr = cssmErrorString(cssme.error);
1647 secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
1648 } catch (...) {
1649 // Something went wrong, but don't worry about it.
1650 secnotice("integrity", "caught unknown error");
1651 }
1652
1653 // No matter if the migrator succeeded, we need to reload this keychain from disk.
1654 secnotice("integrity", "reloading keychain after migration");
1655 globals().storageManager.reloadKeychain(this);
1656 secnotice("integrity", "database %s is now version %d", mDb->name(), mDb->dbBlobVersion());
1657
1658 return result;
1659 }
1660
1661 // Make sure you have this keychain's mutex and write lock when you call this function!
1662 uint32 KeychainImpl::attemptKeychainMigration(const string oldPath, const uint32 oldBlobVersion, const string newPath, const uint32 newBlobVersion, const AccessCredentials* cred) {
1663 if(mDb->dbBlobVersion() == newBlobVersion) {
1664 // Someone else upgraded this, hurray!
1665 secnotice("integrity", "reloaded keychain version %d, quitting", mDb->dbBlobVersion());
1666 return newBlobVersion;
1667 }
1668
1669 mAttemptedUpgrade = true;
1670 uint32 newDbVersion = oldBlobVersion;
1671
1672 if( (oldBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0 && newBlobVersion == SecurityServer::DbBlob::version_partition) ||
1673 (oldBlobVersion == SecurityServer::DbBlob::version_partition && newBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0 && cred != NULL)) {
1674 // Here's the upgrade outline:
1675 //
1676 // 1. Make a copy of the keychain with the new file path
1677 // 2. Open that keychain database.
1678 // 3. Recode it to use the new version.
1679 // 4. Notify the StorageManager that the DLDB identifier for this keychain has changed.
1680 //
1681 // If we're creating a new keychain file, on failure, try to delete the new file. Otherwise,
1682 // everyone will try to use it.
1683
1684 secnotice("integrity", "attempting migration from version %d to %d", oldBlobVersion, newBlobVersion);
1685
1686 Db db;
1687 bool newFile = (oldPath != newPath);
1688
1689 try {
1690 DLDbIdentifier dldbi(dlDbIdentifier().ssuid(), newPath.c_str(), dlDbIdentifier().dbLocation());
1691 if(newFile) {
1692 secnotice("integrity", "creating a new keychain at %s", newPath.c_str());
1693 db = mDb->cloneTo(dldbi);
1694 } else {
1695 secnotice("integrity", "using old keychain at %s", newPath.c_str());
1696 db = mDb;
1697 }
1698 FileLockTransaction fileLockDb(db);
1699
1700 if(newFile) {
1701 // since we're creating a completely new file, if this migration fails, delete the new file
1702 fileLockDb.setDeleteOnFailure();
1703 }
1704
1705 // Let the upgrade begin.
1706 newDbVersion = db->recodeDbToVersion(newBlobVersion);
1707 if(newDbVersion != newBlobVersion) {
1708 // Recoding failed. Don't proceed.
1709 secnotice("integrity", "recodeDbToVersion failed, version is still %d", newDbVersion);
1710 return newDbVersion;
1711 }
1712
1713 secnotice("integrity", "recoded db successfully, adding extra integrity");
1714
1715 Keychain keychain(db);
1716
1717 // Breaking abstraction, but what're you going to do?
1718 // Don't upgrade this keychain, since we just upgraded the DB
1719 // But the DB won't return any new data until the txion commits
1720 keychain->mAttemptedUpgrade = true;
1721 keychain->mSuppressTickle = true;
1722
1723 SecItemClass classes[] = {kSecGenericPasswordItemClass,
1724 kSecInternetPasswordItemClass,
1725 kSecPublicKeyItemClass,
1726 kSecPrivateKeyItemClass,
1727 kSecSymmetricKeyItemClass};
1728
1729 for(int i = 0; i < sizeof(classes) / sizeof(classes[0]); i++) {
1730 Item item;
1731 KCCursor kcc = keychain->createCursor(classes[i], NULL);
1732
1733 // During recoding, we might have deleted some corrupt keys.
1734 // Because of this, we might have zombie SSGroup records left in
1735 // the database that have no matching key. Tell the KCCursor to
1736 // delete these if found.
1737 // This will also try to suppress any other invalid items.
1738 kcc->setDeleteInvalidRecords(true);
1739
1740 while(kcc->next(item)) {
1741 try {
1742 if(newBlobVersion == SecurityServer::DbBlob::version_partition) {
1743 // Force the item to set integrity. The keychain is confused about its version because it hasn't written to disk yet,
1744 // but if we've reached this point, the keychain supports integrity.
1745 item->setIntegrity(true);
1746 } else if(newBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0) {
1747 // We're downgrading this keychain. Pass in whatever credentials our caller thinks will allow this ACL modification.
1748 item->removeIntegrity(cred);
1749 }
1750 } catch(CssmError cssme) {
1751 // During recoding, we might have deleted some corrupt keys. Because of this, we might have zombie SSGroup records left in
1752 // the database that have no matching key. If we get a DL_RECORD_NOT_FOUND error, delete the matching item record.
1753 if (cssme.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND) {
1754 secnotice("integrity", "deleting corrupt (Not Found) record");
1755 keychain->deleteItem(item);
1756 } else if(cssme.osStatus() == CSSMERR_CSP_INVALID_KEY) {
1757 secnotice("integrity", "deleting corrupt key record");
1758 keychain->deleteItem(item);
1759 } else {
1760 throw;
1761 }
1762 }
1763 }
1764 }
1765
1766 // Tell securityd we're done with the upgrade, to re-enable all protections
1767 db->recodeFinished();
1768
1769 // If we reach here, tell the file locks to commit the transaction and return the new blob version
1770 fileLockDb.success();
1771
1772 secnotice("integrity", "success, returning version %d", newDbVersion);
1773 return newDbVersion;
1774 } catch(UnixError ue) {
1775 secnotice("integrity", "caught UnixError: %d %s", ue.unixError(), ue.what());
1776 } catch (CssmError cssme) {
1777 const char* errStr = cssmErrorString(cssme.error);
1778 secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
1779 } catch (MacOSError mose) {
1780 secnotice("integrity", "MacOSError: %d", (int)mose.osStatus());
1781 } catch (const std::bad_cast & e) {
1782 secnotice("integrity", "***** bad cast: %s", e.what());
1783 } catch (...) {
1784 // We failed to migrate. We won't commit the transaction, so the blob on-disk stays the same.
1785 secnotice("integrity", "***** unknown error");
1786 }
1787 } else {
1788 secnotice("integrity", "no migration path for %s at version %d to", oldPath.c_str(), oldBlobVersion);
1789 secnotice("integrity", " %s at version %d", newPath.c_str(), newBlobVersion);
1790 return oldBlobVersion;
1791 }
1792
1793 // If we reached here, the migration failed. Return the old version.
1794 return oldBlobVersion;
1795 }
1796
1797 void KeychainImpl::attemptKeychainRename(const string oldPath, const string newPath, uint32 blobVersion) {
1798 secnotice("integrity", "attempting to rename keychain (%d) from %s to %s", blobVersion, oldPath.c_str(), newPath.c_str());
1799
1800 // Take the file lock on this database, so other people won't try to move it before we do
1801 // NOTE: during a migration from a v256 to a v512 keychain, the db is first copied from the .keychain to the
1802 // .keychain-db path. Other non-migrating processes, if they open the keychain, enter this function to
1803 // try to move it back. These will attempt to take the .keychain-db file lock, but they will not succeed
1804 // until the migration is finished. Once they acquire that, they might try to take the .keychain file lock.
1805 // This is technically lock inversion, but deadlocks will not happen since the migrating process creates the
1806 // .keychain-db file lock before creating the .keychain-db file, so other processes will not try to grab the
1807 // .keychain-db lock in this function before the migrating process already has it.
1808 FileLockTransaction fileLockmDb(mDb);
1809
1810 // first, check if someone renamed this keychain while we were grabbing the file lock
1811 globals().storageManager.reloadKeychain(this);
1812
1813 uint32 dbBlobVersion = SecurityServer::DbBlob::version_MacOS_10_0;
1814
1815 try {
1816 dbBlobVersion = mDb->dbBlobVersion();
1817 } catch (...) {
1818 secnotice("integrity", "dbBlobVersion() failed for an unknown reason while renaming, aborting rename");
1819 return;
1820 }
1821
1822 if(dbBlobVersion != blobVersion) {
1823 secnotice("integrity", "database version changed while we were grabbing the file lock; aborting rename");
1824 return;
1825 }
1826
1827 if(oldPath != mDb->name()) {
1828 secnotice("integrity", "database location changed while we were grabbing the file lock; aborting rename");
1829 return;
1830 }
1831
1832 // we're still at the original location and version; go ahead and do the move
1833 globals().storageManager.rename(this, newPath.c_str());
1834 }
1835
1836 Keychain::Keychain()
1837 {
1838 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1839 check_system_keychain();
1840 });
1841 }
1842
1843 Keychain::~Keychain()
1844 {
1845 }
1846
1847
1848
1849 Keychain
1850 Keychain::optional(SecKeychainRef handle)
1851 {
1852 if (handle)
1853 return KeychainImpl::required(handle);
1854 else
1855 return globals().storageManager.defaultKeychain();
1856 }
1857
1858
1859 CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
1860 {
1861 CFTypeRef ref = kc->handle(false);
1862 return CFGetRetainCount(ref);
1863 }
1864
1865
1866 //
1867 // Create default credentials for this keychain.
1868 // This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1869 //
1870 // This function embodies the "default credentials" logic for Keychain-layer databases.
1871 //
1872 const AccessCredentials *
1873 KeychainImpl::makeCredentials()
1874 {
1875 return defaultCredentials();
1876 }
1877
1878
1879 const AccessCredentials *
1880 KeychainImpl::defaultCredentials()
1881 {
1882 StLock<Mutex>_(mMutex);
1883
1884 // Use custom unlock credentials for file keychains which have a referral
1885 // record and the standard credentials for all others.
1886
1887 if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
1888 return &mCustomUnlockCreds;
1889 else
1890 if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
1891 return globals().smartcardCredentials();
1892 else
1893 return globals().keychainCredentials();
1894 }
1895
1896
1897
1898 bool KeychainImpl::mayDelete()
1899 {
1900 return true;
1901 }
1902
1903 bool KeychainImpl::hasIntegrityProtection() {
1904 StLock<Mutex>_(mMutex);
1905
1906 // This keychain only supports integrity if there's a database attached, that database is an Apple CSPDL, and the blob version is high enough
1907 if(mDb && (mDb->dl()->guid() == gGuidAppleCSPDL)) {
1908 if(mDb->dbBlobVersion() >= SecurityServer::DbBlob::version_partition) {
1909 return true;
1910 } else {
1911 secnotice("integrity", "keychain blob version does not support integrity");
1912 return false;
1913 }
1914 } else {
1915 secnotice("integrity", "keychain guid does not support integrity");
1916 return false;
1917 }
1918 return false;
1919 }
1920