]> git.saurik.com Git - apple/security.git/blob - Keychain/Keychains.cpp
Security-163.tar.gz
[apple/security.git] / Keychain / Keychains.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // Keychains.cpp
21 //
22
23 #include "Keychains.h"
24 #include "KCEventNotifier.h"
25
26 #include "Item.h"
27 #include "KCCursor.h"
28 #include "Globals.h"
29 #include "Schema.h"
30 #include <Security/keychainacl.h>
31 #include <Security/cssmacl.h>
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
33 #include <Security/cssmdb.h>
34 #include <Security/trackingallocator.h>
35 #include <Security/SecCFTypes.h>
36
37 using namespace KeychainCore;
38 using namespace CssmClient;
39
40
41 //
42 // KeychainSchemaImpl
43 //
44 KeychainSchemaImpl::KeychainSchemaImpl(const Db &db)
45 {
46 DbCursor relations(db);
47 relations->recordType(CSSM_DL_DB_SCHEMA_INFO);
48 DbAttributes relationRecord(db, 1);
49 relationRecord.add(Schema::RelationID);
50 DbUniqueRecord outerUniqueId(db);
51
52 while (relations->next(&relationRecord, NULL, outerUniqueId))
53 {
54 DbUniqueRecord uniqueId(db);
55
56 uint32 relationID = relationRecord.at(0);
57 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
58 continue;
59
60 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with RelationID == relationID
61 DbCursor attributes(db);
62 attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
63 attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
64
65 // Set up a record for retriving the SCHEMA_ATTRIBUTES
66 DbAttributes attributeRecord(db, 2);
67 attributeRecord.add(Schema::AttributeFormat);
68 attributeRecord.add(Schema::AttributeID);
69 attributeRecord.add(Schema::AttributeNameFormat);
70
71
72 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
73 while (attributes->next(&attributeRecord, NULL, uniqueId))
74 {
75 // @@@ this if statement was blocking tags of different naming conventions
76 //if(CSSM_DB_ATTRIBUTE_FORMAT(attributeRecord.at(2))==CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER)
77 rim[attributeRecord.at(1)] = attributeRecord.at(0);
78 }
79
80 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records with RelationID == relationID
81 DbCursor indexes(db);
82 indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES);
83 indexes->conjunctive(CSSM_DB_AND);
84 indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
85 indexes->add(CSSM_DB_EQUAL, Schema::IndexType, uint32(CSSM_DB_INDEX_UNIQUE));
86
87 // Set up a record for retriving the SCHEMA_INDEXES
88 DbAttributes indexRecord(db, 1);
89 indexRecord.add(Schema::AttributeID);
90
91 CssmAutoDbRecordAttributeInfo &infos = *new CssmAutoDbRecordAttributeInfo();
92 mPrimaryKeyInfoMap.insert(PrimaryKeyInfoMap::value_type(relationID, &infos));
93 infos.DataRecordType = relationID;
94 while (indexes->next(&indexRecord, NULL, uniqueId))
95 {
96 CssmDbAttributeInfo &info = infos.add();
97 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
98 info.Label.AttributeID = indexRecord.at(0);
99 info.AttributeFormat = rim[info.Label.AttributeID]; // @@@ Might insert bogus value if DB is corrupt
100 }
101 }
102 }
103
104 KeychainSchemaImpl::~KeychainSchemaImpl()
105 {
106 for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
107 }
108
109 const KeychainSchemaImpl::RelationInfoMap &
110 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const
111 {
112 DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
113 if (dit == mDatabaseInfoMap.end())
114 MacOSError::throwMe(errSecNoSuchClass);
115 return dit->second;
116 }
117
118 bool
119 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
120 {
121 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
122 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
123 return rit != rmap.end();
124 }
125
126 CSSM_DB_ATTRIBUTE_FORMAT
127 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
128 {
129 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
130 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
131 if (rit == rmap.end())
132 MacOSError::throwMe(errSecNoSuchAttr);
133
134 return rit->second;
135 }
136
137 CssmDbAttributeInfo
138 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
139 {
140 CSSM_DB_ATTRIBUTE_INFO info;
141 info.AttributeFormat = attributeFormatFor(recordType, attributeId);
142 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
143 info.Label.AttributeID = attributeId;
144
145 return info;
146 }
147
148 void
149 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const
150 {
151 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
152
153 SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
154
155 UInt32 capacity=rmap.size();
156 UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
157 UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
158 UInt32 i=0;
159
160
161 for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit)
162 {
163 if (i>=capacity)
164 {
165 capacity *= 2;
166 if (capacity <= i) capacity = i + 1;
167 tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
168 formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
169 }
170 tagBuf[i]=rit->first;
171 formatBuf[i++]=rit->second;
172 }
173
174 theList->count=i;
175 theList->tag=tagBuf;
176 theList->format=formatBuf;
177 *Info=theList;
178 }
179
180
181 const CssmAutoDbRecordAttributeInfo &
182 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const
183 {
184 PrimaryKeyInfoMap::const_iterator it;
185 it = mPrimaryKeyInfoMap.find(recordType);
186
187 if (it == mPrimaryKeyInfoMap.end())
188 MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever.
189
190 return *it->second;
191 }
192
193 bool
194 KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const
195 {
196 return mDatabaseInfoMap < other.mDatabaseInfoMap;
197 }
198
199 bool
200 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const
201 {
202 return mDatabaseInfoMap == other.mDatabaseInfoMap;
203 }
204
205
206 //
207 // KeychainImpl
208 //
209 KeychainImpl::KeychainImpl(const Db &db)
210 : mDb(db)
211 {
212 }
213
214 KeychainImpl::~KeychainImpl() throw()
215 {
216 globals().storageManager.removeKeychain(dLDbIdentifier(), this);
217 }
218
219 bool
220 KeychainImpl::operator ==(const KeychainImpl &keychain) const
221 {
222 return dLDbIdentifier() == keychain.dLDbIdentifier();
223 }
224
225 KCCursor
226 KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
227 {
228 StorageManager::KeychainList keychains;
229 keychains.push_back(Keychain(this));
230 return KCCursor(keychains, itemClass, attrList);
231 }
232
233 KCCursor
234 KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
235 {
236 StorageManager::KeychainList keychains;
237 keychains.push_back(Keychain(this));
238 return KCCursor(keychains, attrList);
239 }
240
241 void
242 KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
243 {
244 if (!inPassword)
245 {
246 create();
247 return;
248 }
249
250 CssmAllocator &alloc = CssmAllocator::standard();
251
252 // @@@ Share this instance
253
254 const CssmData password(const_cast<void *>(inPassword), passwordLength);
255 AclFactory::PasswordChangeCredentials pCreds (password, alloc);
256 AclFactory::AnyResourceContext rcc(pCreds);
257 create(&rcc);
258 }
259
260 void KeychainImpl::create(ConstStringPtr inPassword)
261 {
262 if ( inPassword )
263 create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
264 else
265 create();
266 }
267
268 void
269 KeychainImpl::create()
270 {
271 AclFactory aclFactory;
272 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
273 create(&rcc);
274 }
275
276 void
277 KeychainImpl::create(const ResourceControlContext *rcc)
278 {
279 mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create)
280 mDb->resourceControlContext(rcc);
281 try
282 {
283 mDb->create();
284 }
285 catch (...)
286 {
287 mDb->resourceControlContext(NULL);
288 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
289 throw;
290 }
291 mDb->resourceControlContext(NULL);
292 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
293 globals().storageManager.created(Keychain(this));
294
295 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
296 }
297
298 void
299 KeychainImpl::open()
300 {
301 mDb->open();
302 }
303
304 void
305 KeychainImpl::lock()
306 {
307 mDb->lock();
308 }
309
310 void
311 KeychainImpl::unlock()
312 {
313 mDb->unlock();
314 }
315
316 void
317 KeychainImpl::unlock(const CssmData &password)
318 {
319 mDb->unlock(password);
320 }
321
322 void
323 KeychainImpl::unlock(ConstStringPtr password)
324 {
325 if (password)
326 {
327 const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
328 unlock(data);
329 }
330 else
331 unlock();
332 }
333
334 void
335 KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
336 {
337 mDb->getSettings(outIdleTimeOut, outLockOnSleep);
338 }
339
340 void
341 KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
342 {
343 mDb->setSettings(inIdleTimeOut, inLockOnSleep);
344 }
345 void
346 KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
347 UInt32 newPasswordLength, const void *newPassword)
348 {
349 // @@@ When AutoCredentials is actually finished we should no logner use a tracking allocator.
350 TrackingAllocator allocator(CssmAllocator::standard());
351 AutoCredentials cred = AutoCredentials(allocator);
352 if (oldPassword)
353 {
354 const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength);
355 TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK);
356 oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
357 oldList.append(new(allocator) ListElement(oldPass));
358 cred += oldList;
359 }
360
361 if (newPassword)
362 {
363 const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength);
364 TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK);
365 newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
366 newList.append(new(allocator) ListElement(newPass));
367 cred += newList;
368 }
369
370 mDb->changePassphrase(&cred);
371 }
372
373 void
374 KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
375 {
376 const void *oldPtr, *newPtr;
377 UInt32 oldLen, newLen;
378 if (oldPassword)
379 {
380 oldLen = oldPassword[0];
381 oldPtr = oldPassword + 1;
382 }
383 else
384 {
385 oldLen = 0;
386 oldPtr = NULL;
387 }
388
389 if (newPassword)
390 {
391 newLen = newPassword[0];
392 newPtr = newPassword + 1;
393 }
394 else
395 {
396 newLen = 0;
397 newPtr = NULL;
398 }
399
400 changePassphrase(oldLen, oldPtr, newLen, newPtr);
401 }
402
403 void
404 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred)
405 {
406 // @@@ This should do an authenticate which is not the same as unlock.
407 if (!exists())
408 MacOSError::throwMe(errSecNoSuchKeychain);
409
410 MacOSError::throwMe(unimpErr);
411 }
412
413 UInt32
414 KeychainImpl::status() const
415 {
416 // @@@ We should figure out the read/write status though a DL passthrough or some other way.
417 // @@@ Also should locked be unlocked read only or just read-only?
418 return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus) | kSecReadPermStatus;
419 }
420
421 bool
422 KeychainImpl::exists()
423 {
424 bool exists = true;
425 try
426 {
427 open();
428 // Ok to leave the mDb open since it will get closed when it goes away.
429 }
430 catch (const CssmError &e)
431 {
432 if (e.cssmError() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
433 throw;
434 exists = false;
435 }
436
437 return exists;
438 }
439
440 bool
441 KeychainImpl::isActive() const
442 {
443 return mDb->isActive();
444 }
445
446 void
447 KeychainImpl::add(Item &inItem)
448 {
449 Keychain keychain(this);
450 PrimaryKey primaryKey = inItem->add(keychain);
451 {
452 StLock<Mutex> _(mDbItemMapLock);
453 mDbItemMap[primaryKey] = inItem.get();
454 }
455
456 KCEventNotifier::PostKeychainEvent(kSecAddEvent, this, inItem);
457 }
458
459 void
460 KeychainImpl::didUpdate(ItemImpl *inItemImpl, PrimaryKey &oldPK,
461 PrimaryKey &newPK)
462 {
463 // Make sure we only hold mDbItemMapLock as long as we need to.
464 {
465 StLock<Mutex> _(mDbItemMapLock);
466 DbItemMap::iterator it = mDbItemMap.find(oldPK);
467 if (it != mDbItemMap.end() && it->second == inItemImpl)
468 mDbItemMap.erase(it);
469 mDbItemMap[newPK] = inItemImpl;
470 }
471
472 KCEventNotifier::PostKeychainEvent( kSecUpdateEvent, this, inItemImpl );
473 }
474
475 void
476 KeychainImpl::deleteItem(Item &inoutItem)
477 {
478 // item must be persistant.
479 if (!inoutItem->isPersistant())
480 MacOSError::throwMe(errSecInvalidItemRef);
481
482 DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
483 PrimaryKey primaryKey = inoutItem->primaryKey();
484 uniqueId->deleteRecord();
485
486 // Don't kill the ref or clear the Item() since this potentially
487 // messes up things for the receiver of the kSecDeleteEvent notification.
488 //inoutItem->killRef();
489 //inoutItem = Item();
490
491 // Post the notification for the item deletion with
492 // the primaryKey obtained when the item still existed
493 KCEventNotifier::PostKeychainEvent(kSecDeleteEvent, dLDbIdentifier(), primaryKey);
494 }
495
496
497 CssmClient::CSP
498 KeychainImpl::csp()
499 {
500 if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
501 MacOSError::throwMe(errSecInvalidKeychain);
502
503 SSDb ssDb(safe_cast<SSDbImpl *>(&(*mDb)));
504 return ssDb->csp();
505 }
506
507 PrimaryKey
508 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
509 {
510 DbAttributes primaryKeyAttrs(uniqueId->database());
511 primaryKeyAttrs.recordType(recordType);
512 gatherPrimaryKeyAttributes(primaryKeyAttrs);
513 uniqueId->get(&primaryKeyAttrs, NULL);
514 return PrimaryKey(primaryKeyAttrs);
515 }
516
517 const CssmAutoDbRecordAttributeInfo &
518 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
519 {
520 try {
521 return keychainSchema()->primaryKeyInfosFor(recordType);
522 } catch (const CssmCommonError &error) {
523 switch (error.cssmError()) {
524 case errSecNoSuchClass:
525 case CSSMERR_DL_INVALID_RECORDTYPE:
526 resetSchema();
527 return keychainSchema()->primaryKeyInfosFor(recordType);
528 default:
529 throw;
530 }
531 }
532 }
533
534 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
535 {
536 const CssmAutoDbRecordAttributeInfo &infos =
537 primaryKeyInfosFor(primaryKeyAttrs.recordType());
538
539 // @@@ fix this to not copy info.
540 for (uint32 i = 0; i < infos.size(); i++)
541 primaryKeyAttrs.add(infos.at(i));
542 }
543
544 Item
545 KeychainImpl::item(const PrimaryKey& primaryKey)
546 {
547 // @@@ This retry code isn't really the right way to do this,
548 // we need to redo the locking structure here in the future.
549 bool tried = false;
550 for (;;)
551 {
552 {
553 StLock<Mutex> _(mDbItemMapLock);
554 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
555 if (it != mDbItemMap.end())
556 {
557 return Item(it->second);
558 }
559 }
560
561 try
562 {
563 // Create an item with just a primary key
564 return Item(this, primaryKey);
565 }
566 catch (const MacOSError &e)
567 {
568 if (tried || e.osStatus() != errSecDuplicateItem)
569 throw;
570 tried = true;
571 }
572 }
573 }
574
575 Item
576 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
577 {
578 PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
579 {
580 StLock<Mutex> _(mDbItemMapLock);
581 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
582 if (it != mDbItemMap.end())
583 {
584 return Item(it->second);
585 }
586 }
587
588 // Create a new item
589 return Item(this, primaryKey, uniqueId);
590 }
591
592 KeychainSchema
593 KeychainImpl::keychainSchema()
594 {
595 if (!mKeychainSchema)
596 {
597 // @@@ Use cache in storageManager
598 mKeychainSchema = KeychainSchema(mDb);
599 }
600
601 return mKeychainSchema;
602 }
603
604 void KeychainImpl::resetSchema()
605 {
606 mKeychainSchema = NULL; // re-fetch it from db next time
607 }
608
609
610 // Called from DbItemImpl's constructor (so it is only paritally constructed), add it to the map.
611 void
612 KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
613 {
614 StLock<Mutex> _(mDbItemMapLock);
615 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
616 if (it != mDbItemMap.end())
617 {
618 // @@@ There is a race condition here when being called in multiple threads
619 // We might have added an item using add and received a notification at the same time
620 //assert(true);
621 MacOSError::throwMe(errSecDuplicateItem);
622 //mDbItemMap.erase(it);
623 // @@@ What to do here?
624 }
625
626 mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
627 }
628
629 void
630 KeychainImpl::didDeleteItem(const ItemImpl *inItemImpl)
631 {
632 // Sent sent by CCallbackMgr.
633 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
634 PrimaryKey primaryKey = inItemImpl->primaryKey();
635 StLock<Mutex> _(mDbItemMapLock);
636 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
637 if (it != mDbItemMap.end())
638 mDbItemMap.erase(it);
639 }
640
641 void
642 KeychainImpl::removeItem(const PrimaryKey &primaryKey, const ItemImpl *inItemImpl)
643 {
644 // Sent from DbItemImpl's destructor, remove it from the map.
645 StLock<Mutex> _(mDbItemMapLock);
646 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
647 if (it != mDbItemMap.end() && it->second == inItemImpl)
648 mDbItemMap.erase(it);
649 }
650
651 void
652 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, SecKeychainAttributeInfo **Info)
653 {
654 try {
655 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
656 } catch (const CssmCommonError &error) {
657 switch (error.cssmError()) {
658 case errSecNoSuchClass:
659 case CSSMERR_DL_INVALID_RECORDTYPE:
660 resetSchema();
661 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
662 default:
663 throw;
664 }
665 }
666 }
667
668 void
669 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
670 {
671 free(Info->tag);
672 free(Info->format);
673 free(Info);
674 }
675
676 CssmDbAttributeInfo
677 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
678 {
679 try {
680 return keychainSchema()->attributeInfoFor(recordType, tag);
681 } catch (const CssmCommonError &error) {
682 switch (error.cssmError()) {
683 case errSecNoSuchClass:
684 case CSSMERR_DL_INVALID_RECORDTYPE:
685 resetSchema();
686 return keychainSchema()->attributeInfoFor(recordType, tag);
687 default:
688 throw;
689 }
690 }
691
692 }
693
694 Keychain
695 Keychain::optional(SecKeychainRef handle)
696 {
697 if (handle)
698 return KeychainImpl::required(handle);
699 else
700 return globals().storageManager.defaultKeychain();
701 }
702