]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2000-2004 Apple Computer, 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> | |
b1ab9ed8 A |
38 | #include <security_cdsa_utilities/cssmdb.h> |
39 | #include <security_utilities/trackingallocator.h> | |
40 | #include <security_keychain/SecCFTypes.h> | |
41 | ||
42 | #include "SecKeychainPriv.h" | |
43 | ||
44 | #include <Security/SecKeychainItemPriv.h> | |
45 | #include <CoreFoundation/CoreFoundation.h> | |
46 | #include "DLDbListCFPref.h" | |
47 | #include <fcntl.h> | |
48 | #include <sys/param.h> | |
49 | #include <syslog.h> | |
50 | #include <sys/stat.h> | |
51 | #include <sys/socket.h> | |
52 | #include <sys/un.h> | |
53 | #include <sys/types.h> | |
54 | #include <sys/time.h> | |
55 | ||
56 | static dispatch_once_t SecKeychainSystemKeychainChecked; | |
57 | ||
58 | OSStatus SecKeychainSystemKeychainCheckWouldDeadlock() | |
59 | { | |
60 | dispatch_once(&SecKeychainSystemKeychainChecked, ^{}); | |
427c49bc | 61 | return errSecSuccess; |
b1ab9ed8 A |
62 | } |
63 | ||
64 | using namespace KeychainCore; | |
65 | using namespace CssmClient; | |
66 | ||
67 | ||
68 | typedef struct EventItem | |
69 | { | |
70 | SecKeychainEvent kcEvent; | |
71 | Item item; | |
72 | } EventItem; | |
73 | ||
74 | typedef std::list<EventItem> EventBufferSuper; | |
75 | class EventBuffer : public EventBufferSuper | |
76 | { | |
77 | public: | |
78 | EventBuffer () {} | |
79 | virtual ~EventBuffer (); | |
80 | }; | |
81 | ||
82 | ||
83 | EventBuffer::~EventBuffer () | |
84 | { | |
85 | } | |
86 | ||
87 | ||
88 | ||
89 | // | |
90 | // KeychainSchemaImpl | |
91 | // | |
92 | KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive) | |
93 | { | |
94 | DbCursor relations(db); | |
95 | relations->recordType(CSSM_DL_DB_SCHEMA_INFO); | |
96 | DbAttributes relationRecord(db, 1); | |
97 | relationRecord.add(Schema::RelationID); | |
98 | DbUniqueRecord outerUniqueId(db); | |
99 | ||
100 | while (relations->next(&relationRecord, NULL, outerUniqueId)) | |
101 | { | |
102 | DbUniqueRecord uniqueId(db); | |
103 | ||
104 | uint32 relationID = relationRecord.at(0); | |
105 | if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID | |
106 | && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) | |
107 | continue; | |
108 | ||
109 | // Create a cursor on the SCHEMA_ATTRIBUTES table for records with | |
110 | // RelationID == relationID | |
111 | DbCursor attributes(db); | |
112 | attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES); | |
113 | attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); | |
114 | ||
115 | // Set up a record for retriving the SCHEMA_ATTRIBUTES | |
116 | DbAttributes attributeRecord(db, 2); | |
117 | attributeRecord.add(Schema::AttributeFormat); | |
118 | attributeRecord.add(Schema::AttributeID); | |
119 | ||
120 | RelationInfoMap &rim = mDatabaseInfoMap[relationID]; | |
121 | while (attributes->next(&attributeRecord, NULL, uniqueId)) | |
122 | rim[attributeRecord.at(1)] = attributeRecord.at(0); | |
123 | ||
124 | // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records | |
125 | // with RelationID == relationID | |
126 | DbCursor indexes(db); | |
127 | indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES); | |
128 | indexes->conjunctive(CSSM_DB_AND); | |
129 | indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); | |
130 | indexes->add(CSSM_DB_EQUAL, Schema::IndexType, | |
131 | uint32(CSSM_DB_INDEX_UNIQUE)); | |
132 | ||
133 | // Set up a record for retriving the SCHEMA_INDEXES | |
134 | DbAttributes indexRecord(db, 1); | |
135 | indexRecord.add(Schema::AttributeID); | |
136 | ||
137 | CssmAutoDbRecordAttributeInfo &infos = | |
138 | *new CssmAutoDbRecordAttributeInfo(); | |
139 | mPrimaryKeyInfoMap. | |
140 | insert(PrimaryKeyInfoMap::value_type(relationID, &infos)); | |
141 | infos.DataRecordType = relationID; | |
142 | while (indexes->next(&indexRecord, NULL, uniqueId)) | |
143 | { | |
144 | CssmDbAttributeInfo &info = infos.add(); | |
145 | info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; | |
146 | info.Label.AttributeID = indexRecord.at(0); | |
147 | // @@@ Might insert bogus value if DB is corrupt | |
148 | info.AttributeFormat = rim[info.Label.AttributeID]; | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | KeychainSchemaImpl::~KeychainSchemaImpl() | |
154 | { | |
155 | try | |
156 | { | |
427c49bc A |
157 | map<CSSM_DB_RECORDTYPE, CssmAutoDbRecordAttributeInfo *>::iterator it = mPrimaryKeyInfoMap.begin(); |
158 | while (it != mPrimaryKeyInfoMap.end()) | |
159 | { | |
160 | delete it->second; | |
161 | it++; | |
162 | } | |
163 | // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end()); | |
b1ab9ed8 A |
164 | } |
165 | catch(...) | |
166 | { | |
167 | } | |
168 | } | |
169 | ||
170 | const KeychainSchemaImpl::RelationInfoMap & | |
171 | KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const | |
172 | { | |
173 | DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType); | |
174 | if (dit == mDatabaseInfoMap.end()) | |
175 | MacOSError::throwMe(errSecNoSuchClass); | |
176 | return dit->second; | |
177 | } | |
178 | ||
179 | bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const | |
180 | { | |
181 | DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType); | |
182 | return it != mDatabaseInfoMap.end(); | |
183 | } | |
184 | ||
185 | bool | |
186 | KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const | |
187 | { | |
188 | try | |
189 | { | |
190 | const RelationInfoMap &rmap = relationInfoMapFor(recordType); | |
191 | RelationInfoMap::const_iterator rit = rmap.find(attributeId); | |
192 | return rit != rmap.end(); | |
193 | } | |
194 | catch (MacOSError result) | |
195 | { | |
196 | if (result.osStatus () == errSecNoSuchClass) | |
197 | { | |
198 | return false; | |
199 | } | |
200 | else | |
201 | { | |
202 | throw; | |
203 | } | |
204 | } | |
205 | } | |
206 | ||
207 | CSSM_DB_ATTRIBUTE_FORMAT | |
208 | KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const | |
209 | { | |
210 | const RelationInfoMap &rmap = relationInfoMapFor(recordType); | |
211 | RelationInfoMap::const_iterator rit = rmap.find(attributeId); | |
212 | if (rit == rmap.end()) | |
213 | MacOSError::throwMe(errSecNoSuchAttr); | |
214 | ||
215 | return rit->second; | |
216 | } | |
217 | ||
218 | CssmDbAttributeInfo | |
219 | KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const | |
220 | { | |
221 | CSSM_DB_ATTRIBUTE_INFO info; | |
222 | info.AttributeFormat = attributeFormatFor(recordType, attributeId); | |
223 | info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; | |
224 | info.Label.AttributeID = attributeId; | |
225 | ||
226 | return info; | |
227 | } | |
228 | ||
229 | void | |
230 | KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const | |
231 | { | |
232 | const RelationInfoMap &rmap = relationInfoMapFor(recordType); | |
233 | ||
234 | SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo))); | |
235 | ||
427c49bc | 236 | size_t capacity=rmap.size(); |
b1ab9ed8 A |
237 | UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32))); |
238 | UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32))); | |
239 | UInt32 i=0; | |
240 | ||
241 | ||
242 | for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit) | |
243 | { | |
244 | if (i>=capacity) | |
245 | { | |
246 | capacity *= 2; | |
247 | if (capacity <= i) capacity = i + 1; | |
248 | tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32)))); | |
249 | formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32)))); | |
250 | } | |
251 | tagBuf[i]=rit->first; | |
252 | formatBuf[i++]=rit->second; | |
253 | } | |
254 | ||
255 | theList->count=i; | |
256 | theList->tag=tagBuf; | |
257 | theList->format=formatBuf; | |
258 | *Info=theList; | |
259 | } | |
260 | ||
261 | ||
262 | const CssmAutoDbRecordAttributeInfo & | |
263 | KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const | |
264 | { | |
265 | PrimaryKeyInfoMap::const_iterator it; | |
266 | it = mPrimaryKeyInfoMap.find(recordType); | |
267 | ||
268 | if (it == mPrimaryKeyInfoMap.end()) | |
269 | MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever. | |
270 | ||
271 | return *it->second; | |
272 | } | |
273 | ||
274 | bool | |
275 | KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const | |
276 | { | |
277 | return mDatabaseInfoMap < other.mDatabaseInfoMap; | |
278 | } | |
279 | ||
280 | bool | |
281 | KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const | |
282 | { | |
283 | return mDatabaseInfoMap == other.mDatabaseInfoMap; | |
284 | } | |
285 | ||
286 | void | |
287 | KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID, | |
288 | const char *inRelationName, | |
289 | uint32 inNumberOfAttributes, | |
290 | const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo, | |
291 | uint32 inNumberOfIndexes, | |
292 | const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo) | |
293 | { | |
294 | StLock<Mutex>_(mMutex); | |
295 | ||
296 | if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID | |
297 | && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) | |
298 | return; | |
299 | ||
427c49bc A |
300 | // if our schema is already in the map, return |
301 | if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end()) | |
302 | { | |
303 | return; | |
304 | } | |
305 | ||
b1ab9ed8 A |
306 | RelationInfoMap &rim = mDatabaseInfoMap[relationID]; |
307 | for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix) | |
308 | rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType; | |
309 | ||
427c49bc A |
310 | CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo(); |
311 | ||
b1ab9ed8 | 312 | mPrimaryKeyInfoMap. |
427c49bc A |
313 | insert(PrimaryKeyInfoMap::value_type(relationID, infos)); |
314 | infos->DataRecordType = relationID; | |
b1ab9ed8 A |
315 | for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix) |
316 | if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE) | |
317 | { | |
427c49bc | 318 | CssmDbAttributeInfo &info = infos->add(); |
b1ab9ed8 A |
319 | info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; |
320 | info.Label.AttributeID = pIndexInfo[ix].AttributeId; | |
321 | info.AttributeFormat = rim[info.Label.AttributeID]; | |
322 | } | |
323 | } | |
324 | ||
325 | ||
427c49bc A |
326 | |
327 | KeychainSchema::~KeychainSchema() | |
328 | ||
329 | { | |
330 | } | |
331 | ||
332 | ||
333 | ||
b1ab9ed8 A |
334 | struct Event |
335 | { | |
336 | SecKeychainEvent eventCode; | |
337 | PrimaryKey primaryKey; | |
338 | }; | |
339 | typedef std::list<Event> EventList; | |
340 | ||
341 | #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck" | |
342 | #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket") | |
343 | #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done") | |
344 | ||
345 | static void check_system_keychain() | |
346 | { | |
347 | // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the | |
348 | // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets. | |
349 | ||
350 | // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain. | |
351 | // In theory a system might be able to recover from this state if we let it try to muddle along, and | |
352 | // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a | |
353 | // sandbox'ed process. So we just do our best and let another process try again. | |
354 | ||
355 | struct stat keycheck_file_info; | |
356 | if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) { | |
357 | int server_fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
358 | if (server_fd < 0) { | |
359 | syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked"); | |
360 | return; | |
361 | } | |
362 | ||
363 | struct sockaddr_un keychain_check_server_address; | |
364 | keychain_check_server_address.sun_family = AF_UNIX; | |
365 | 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)) { | |
366 | // It would be nice if we could compile time assert this | |
427c49bc | 367 | 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)); |
b1ab9ed8 A |
368 | close(server_fd); |
369 | return; | |
370 | } | |
371 | keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address); | |
372 | ||
373 | int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len); | |
374 | if (rc < 0) { | |
375 | syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME); | |
376 | close(server_fd); | |
377 | return; | |
378 | } | |
379 | ||
380 | // 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) | |
381 | char byte; | |
382 | ssize_t read_size = read(server_fd, &byte, 1); | |
383 | if (read_size < 0) { | |
384 | syslog(LOG_ERR, "Error reading from system keychain checker: %m"); | |
385 | } | |
386 | ||
387 | close(server_fd); | |
388 | return; | |
389 | } | |
390 | } | |
391 | ||
392 | // | |
393 | // KeychainImpl | |
394 | // | |
395 | KeychainImpl::KeychainImpl(const Db &db) | |
396 | : mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive) | |
397 | { | |
398 | dispatch_once(&SecKeychainSystemKeychainChecked, ^{ | |
399 | check_system_keychain(); | |
400 | }); | |
401 | mDb->defaultCredentials(this); // install activation hook | |
402 | mEventBuffer = new EventBuffer; | |
403 | } | |
404 | ||
405 | KeychainImpl::~KeychainImpl() | |
406 | { | |
407 | try | |
408 | { | |
409 | // Remove ourselves from the cache if we are in it. | |
410 | // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false)); | |
411 | globals().storageManager.removeKeychain(dlDbIdentifier(), this); | |
412 | delete mEventBuffer; | |
413 | } | |
414 | catch(...) | |
415 | { | |
416 | } | |
417 | } | |
418 | ||
419 | Mutex* | |
420 | KeychainImpl::getMutexForObject() | |
421 | { | |
422 | return globals().storageManager.getStorageManagerMutex(); | |
423 | } | |
424 | ||
425 | Mutex* | |
426 | KeychainImpl::getKeychainMutex() | |
427 | { | |
428 | return &mMutex; | |
429 | } | |
430 | ||
431 | void KeychainImpl::aboutToDestruct() | |
432 | { | |
433 | // remove me from the global cache, we are done | |
434 | // fprintf(stderr, "Destructing keychain object\n"); | |
435 | DLDbIdentifier identifier = dlDbIdentifier(); | |
436 | globals().storageManager.removeKeychain(identifier, this); | |
437 | } | |
438 | ||
439 | bool | |
440 | KeychainImpl::operator ==(const KeychainImpl &keychain) const | |
441 | { | |
442 | return dlDbIdentifier() == keychain.dlDbIdentifier(); | |
443 | } | |
444 | ||
445 | KCCursor | |
446 | KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList) | |
447 | { | |
448 | StLock<Mutex>_(mMutex); | |
449 | ||
450 | StorageManager::KeychainList keychains; | |
451 | keychains.push_back(Keychain(this)); | |
452 | return KCCursor(keychains, itemClass, attrList); | |
453 | } | |
454 | ||
455 | KCCursor | |
456 | KeychainImpl::createCursor(const SecKeychainAttributeList *attrList) | |
457 | { | |
458 | StLock<Mutex>_(mMutex); | |
459 | ||
460 | StorageManager::KeychainList keychains; | |
461 | keychains.push_back(Keychain(this)); | |
462 | return KCCursor(keychains, attrList); | |
463 | } | |
464 | ||
465 | void | |
466 | KeychainImpl::create(UInt32 passwordLength, const void *inPassword) | |
467 | { | |
468 | StLock<Mutex>_(mMutex); | |
469 | ||
470 | if (!inPassword) | |
471 | { | |
472 | create(); | |
473 | return; | |
474 | } | |
475 | ||
476 | Allocator &alloc = Allocator::standard(); | |
477 | ||
478 | // @@@ Share this instance | |
479 | ||
480 | const CssmData password(const_cast<void *>(inPassword), passwordLength); | |
481 | AclFactory::PasswordChangeCredentials pCreds (password, alloc); | |
482 | AclFactory::AnyResourceContext rcc(pCreds); | |
483 | create(&rcc); | |
484 | } | |
485 | ||
486 | void KeychainImpl::create(ConstStringPtr inPassword) | |
487 | { | |
488 | StLock<Mutex>_(mMutex); | |
489 | ||
490 | if ( inPassword ) | |
491 | create(static_cast<UInt32>(inPassword[0]), &inPassword[1]); | |
492 | else | |
493 | create(); | |
494 | } | |
495 | ||
496 | void | |
497 | KeychainImpl::create() | |
498 | { | |
499 | StLock<Mutex>_(mMutex); | |
500 | ||
501 | AclFactory aclFactory; | |
502 | AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); | |
503 | create(&rcc); | |
504 | } | |
505 | ||
506 | void KeychainImpl::createWithBlob(CssmData &blob) | |
507 | { | |
508 | StLock<Mutex>_(mMutex); | |
509 | ||
510 | mDb->dbInfo(&Schema::DBInfo); | |
511 | AclFactory aclFactory; | |
512 | AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); | |
513 | mDb->resourceControlContext (&rcc); | |
514 | try | |
515 | { | |
516 | mDb->createWithBlob(blob); | |
517 | } | |
518 | catch (...) | |
519 | { | |
520 | mDb->resourceControlContext(NULL); | |
521 | mDb->dbInfo(NULL); | |
522 | throw; | |
523 | } | |
524 | mDb->resourceControlContext(NULL); | |
525 | mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) | |
526 | globals().storageManager.created(Keychain(this)); | |
527 | ||
528 | KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL); | |
529 | } | |
530 | ||
531 | void | |
532 | KeychainImpl::create(const ResourceControlContext *rcc) | |
533 | { | |
534 | StLock<Mutex>_(mMutex); | |
535 | ||
536 | mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create) | |
537 | mDb->resourceControlContext(rcc); | |
538 | try | |
539 | { | |
540 | mDb->create(); | |
541 | } | |
542 | catch (...) | |
543 | { | |
544 | mDb->resourceControlContext(NULL); | |
545 | mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) | |
546 | throw; | |
547 | } | |
548 | mDb->resourceControlContext(NULL); | |
549 | mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) | |
550 | globals().storageManager.created(Keychain(this)); | |
551 | } | |
552 | ||
553 | void | |
554 | KeychainImpl::open() | |
555 | { | |
556 | StLock<Mutex>_(mMutex); | |
557 | ||
558 | mDb->open(); | |
559 | } | |
560 | ||
561 | void | |
562 | KeychainImpl::lock() | |
563 | { | |
564 | StLock<Mutex>_(mMutex); | |
565 | ||
566 | mDb->lock(); | |
567 | } | |
568 | ||
569 | void | |
570 | KeychainImpl::unlock() | |
571 | { | |
572 | StLock<Mutex>_(mMutex); | |
573 | ||
574 | mDb->unlock(); | |
575 | } | |
576 | ||
577 | void | |
578 | KeychainImpl::unlock(const CssmData &password) | |
579 | { | |
580 | StLock<Mutex>_(mMutex); | |
581 | ||
582 | mDb->unlock(password); | |
583 | } | |
584 | ||
585 | void | |
586 | KeychainImpl::unlock(ConstStringPtr password) | |
587 | { | |
588 | StLock<Mutex>_(mMutex); | |
589 | ||
590 | if (password) | |
591 | { | |
592 | const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]); | |
593 | unlock(data); | |
594 | } | |
595 | else | |
596 | unlock(); | |
597 | } | |
598 | ||
599 | void | |
427c49bc | 600 | KeychainImpl::stash() |
b1ab9ed8 | 601 | { |
427c49bc | 602 | StLock<Mutex>_(mMutex); |
b1ab9ed8 | 603 | |
427c49bc | 604 | mDb->stash(); |
b1ab9ed8 A |
605 | } |
606 | ||
427c49bc A |
607 | void |
608 | KeychainImpl::stashCheck() | |
b1ab9ed8 | 609 | { |
427c49bc | 610 | StLock<Mutex>_(mMutex); |
b1ab9ed8 | 611 | |
427c49bc A |
612 | mDb->stashCheck(); |
613 | } | |
b1ab9ed8 | 614 | |
427c49bc A |
615 | void |
616 | KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep) | |
617 | { | |
618 | StLock<Mutex>_(mMutex); | |
b1ab9ed8 | 619 | |
427c49bc | 620 | mDb->getSettings(outIdleTimeOut, outLockOnSleep); |
b1ab9ed8 A |
621 | } |
622 | ||
b1ab9ed8 A |
623 | void |
624 | KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep) | |
625 | { | |
626 | StLock<Mutex>_(mMutex); | |
627 | ||
628 | // The .Mac syncing code only makes sense for the AppleFile CSP/DL, | |
629 | // but other DLs such as the OCSP and LDAP DLs do not expose a way to | |
630 | // change settings or the password. To make a minimal change that only affects | |
631 | // the smartcard case, we only look for that CSP/DL | |
632 | ||
633 | bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); | |
634 | ||
635 | // get the old keychain blob so that we can tell .Mac to resync it | |
636 | CssmAutoData oldBlob(mDb ->allocator()); | |
637 | if (!isSmartcard) | |
638 | mDb->copyBlob(oldBlob.get()); | |
639 | ||
640 | mDb->setSettings(inIdleTimeOut, inLockOnSleep); | |
b1ab9ed8 A |
641 | } |
642 | ||
643 | void | |
644 | KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword, | |
645 | UInt32 newPasswordLength, const void *newPassword) | |
646 | { | |
647 | StLock<Mutex>_(mMutex); | |
648 | ||
649 | bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); | |
650 | ||
651 | TrackingAllocator allocator(Allocator::standard()); | |
652 | AutoCredentials cred = AutoCredentials(allocator); | |
653 | if (oldPassword) | |
654 | { | |
655 | const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength); | |
656 | TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK); | |
657 | oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); | |
658 | oldList.append(new(allocator) ListElement(oldPass)); | |
659 | cred += oldList; | |
660 | } | |
661 | ||
662 | if (newPassword) | |
663 | { | |
664 | const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength); | |
665 | TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK); | |
666 | newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); | |
667 | newList.append(new(allocator) ListElement(newPass)); | |
668 | cred += newList; | |
669 | } | |
670 | ||
671 | // get the old keychain blob so that we can tell .Mac to resync it | |
672 | CssmAutoData oldBlob(mDb->allocator()); | |
673 | if (!isSmartcard) | |
674 | mDb->copyBlob(oldBlob.get()); | |
675 | ||
676 | mDb->changePassphrase(&cred); | |
b1ab9ed8 A |
677 | } |
678 | ||
679 | void | |
680 | KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword) | |
681 | { | |
682 | StLock<Mutex>_(mMutex); | |
683 | ||
684 | const void *oldPtr, *newPtr; | |
685 | UInt32 oldLen, newLen; | |
686 | if (oldPassword) | |
687 | { | |
688 | oldLen = oldPassword[0]; | |
689 | oldPtr = oldPassword + 1; | |
690 | } | |
691 | else | |
692 | { | |
693 | oldLen = 0; | |
694 | oldPtr = NULL; | |
695 | } | |
696 | ||
697 | if (newPassword) | |
698 | { | |
699 | newLen = newPassword[0]; | |
700 | newPtr = newPassword + 1; | |
701 | } | |
702 | else | |
703 | { | |
704 | newLen = 0; | |
705 | newPtr = NULL; | |
706 | } | |
707 | ||
708 | changePassphrase(oldLen, oldPtr, newLen, newPtr); | |
709 | } | |
710 | ||
711 | void | |
712 | KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred) | |
713 | { | |
714 | StLock<Mutex>_(mMutex); | |
715 | ||
716 | if (!exists()) | |
717 | MacOSError::throwMe(errSecNoSuchKeychain); | |
718 | ||
427c49bc | 719 | MacOSError::throwMe(errSecUnimplemented); |
b1ab9ed8 A |
720 | } |
721 | ||
722 | UInt32 | |
723 | KeychainImpl::status() const | |
724 | { | |
725 | // @@@ We should figure out the read/write status though a DL passthrough | |
726 | // or some other way. Also should locked be unlocked read only or just | |
727 | // read-only? | |
728 | return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus) | |
729 | | kSecReadPermStatus; | |
730 | } | |
731 | ||
732 | bool | |
733 | KeychainImpl::exists() | |
734 | { | |
735 | StLock<Mutex>_(mMutex); | |
736 | ||
737 | bool exists = true; | |
738 | try | |
739 | { | |
740 | open(); | |
741 | // Ok to leave the mDb open since it will get closed when it goes away. | |
742 | } | |
743 | catch (const CssmError &e) | |
744 | { | |
745 | if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) | |
746 | throw; | |
747 | exists = false; | |
748 | } | |
749 | ||
750 | return exists; | |
751 | } | |
752 | ||
753 | bool | |
754 | KeychainImpl::isActive() const | |
755 | { | |
756 | return mDb->isActive(); | |
757 | } | |
758 | ||
759 | void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey) | |
760 | { | |
761 | ||
762 | ||
763 | // The inItem shouldn't be in the cache yet | |
764 | assert(!inItem->inCache()); | |
765 | ||
766 | // Insert inItem into mDbItemMap with key primaryKey. p.second will be | |
767 | // true if it got inserted. If not p.second will be false and p.first | |
768 | // will point to the current entry with key primaryKey. | |
769 | pair<DbItemMap::iterator, bool> p = | |
770 | mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get())); | |
771 | if (!p.second) | |
772 | { | |
773 | // There was already an ItemImpl * in mDbItemMap with key | |
774 | // primaryKey. Get a ref to the pointer to it so we can assign a | |
775 | // new value to it below. | |
776 | ItemImpl *oldItem = p.first->second; | |
777 | ||
778 | // @@@ If this happens we are breaking our API contract of | |
779 | // uniquifying items. We really need to insert the item into the | |
780 | // map before we start the add. And have the item be in an | |
781 | // "is being added" state. | |
782 | assert(oldItem->inCache()); | |
783 | secdebug("keychain", "add of new item %p somehow replaced %p", | |
784 | inItem.get(), oldItem); | |
785 | oldItem->inCache(false); | |
786 | oldItem = inItem.get(); | |
787 | } | |
788 | ||
789 | inItem->inCache(true); | |
790 | } | |
791 | ||
792 | void | |
793 | KeychainImpl::addCopy(Item &inItem) | |
794 | { | |
795 | Keychain keychain(this); | |
796 | PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true); | |
797 | completeAdd(inItem, primaryKey); | |
798 | postEvent(kSecAddEvent, inItem); | |
799 | } | |
800 | ||
801 | void | |
802 | KeychainImpl::add(Item &inItem) | |
803 | { | |
804 | Keychain keychain(this); | |
805 | PrimaryKey primaryKey = inItem->add(keychain); | |
806 | completeAdd(inItem, primaryKey); | |
807 | postEvent(kSecAddEvent, inItem); | |
808 | } | |
809 | ||
810 | void | |
811 | KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK, | |
812 | PrimaryKey &newPK) | |
813 | { | |
814 | // If the primary key hasn't changed we don't need to update mDbItemMap. | |
815 | if (oldPK != newPK) | |
816 | { | |
817 | // If inItem isn't in the cache we don't need to update mDbItemMap. | |
818 | assert(inItem->inCache()); | |
819 | if (inItem->inCache()) | |
820 | { | |
821 | // First remove the entry for inItem in mDbItemMap with key oldPK. | |
822 | DbItemMap::iterator it = mDbItemMap.find(oldPK); | |
823 | if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get()) | |
824 | mDbItemMap.erase(it); | |
825 | ||
826 | // Insert inItem into mDbItemMap with key newPK. p.second will be | |
827 | // true if it got inserted. If not p.second will be false and | |
828 | // p.first will point to the current entry with key newPK. | |
829 | pair<DbItemMap::iterator, bool> p = | |
830 | mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get())); | |
831 | if (!p.second) | |
832 | { | |
833 | // There was already an ItemImpl * in mDbItemMap with key | |
834 | // primaryKey. Get a ref to the pointer to it so we can assign | |
835 | // a new value to it below. | |
836 | ItemImpl *oldItem = p.first->second; | |
837 | ||
838 | // @@@ If this happens we are breaking our API contract of | |
839 | // uniquifying items. We really need to insert the item into | |
840 | // the map with the new primary key before we start the update. | |
841 | // And have the item be in an "is being updated" state. | |
842 | assert(oldItem->inCache()); | |
843 | secdebug("keychain", "update of item %p somehow replaced %p", | |
844 | inItem.get(), oldItem); | |
845 | oldItem->inCache(false); | |
846 | oldItem = inItem.get(); | |
847 | } | |
848 | } | |
849 | } | |
850 | ||
851 | postEvent(kSecUpdateEvent, inItem); | |
852 | } | |
853 | ||
854 | void | |
855 | KeychainImpl::deleteItem(Item &inoutItem) | |
856 | { | |
857 | { | |
858 | // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock. | |
859 | // Hold it only as long as needed, instead. | |
860 | ||
861 | ||
862 | // item must be persistent | |
863 | if (!inoutItem->isPersistent()) | |
864 | MacOSError::throwMe(errSecInvalidItemRef); | |
865 | ||
866 | DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord(); | |
867 | PrimaryKey primaryKey = inoutItem->primaryKey(); | |
868 | uniqueId->deleteRecord(); | |
869 | ||
870 | // Don't remove the item from the mDbItemMap here since this would cause | |
871 | // us to report a new item to our caller when we receive the | |
872 | // kSecDeleteEvent notification. | |
873 | // It will be removed before we post the notification, because | |
874 | // CCallbackMgr will call didDeleteItem() | |
875 | ||
876 | // Post the notification for the item deletion with | |
877 | // the primaryKey obtained when the item still existed | |
878 | } | |
879 | ||
880 | postEvent(kSecDeleteEvent, inoutItem); | |
881 | } | |
882 | ||
883 | ||
884 | CssmClient::CSP | |
885 | KeychainImpl::csp() | |
886 | { | |
887 | StLock<Mutex>_(mMutex); | |
888 | ||
889 | if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP) | |
890 | MacOSError::throwMe(errSecInvalidKeychain); | |
891 | ||
892 | // Try to cast first to a CSPDL to handle case where we don't have an SSDb | |
893 | try | |
894 | { | |
895 | CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl())); | |
896 | return CSP(cspdl); | |
897 | } | |
898 | catch (...) | |
899 | { | |
900 | SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb)); | |
901 | if (impl == NULL) | |
902 | { | |
903 | CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); | |
904 | } | |
905 | ||
906 | SSDb ssDb(impl); | |
907 | return ssDb->csp(); | |
908 | } | |
909 | } | |
910 | ||
911 | PrimaryKey | |
912 | KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) | |
913 | { | |
914 | StLock<Mutex>_(mMutex); | |
915 | ||
916 | DbAttributes primaryKeyAttrs(uniqueId->database()); | |
917 | primaryKeyAttrs.recordType(recordType); | |
918 | gatherPrimaryKeyAttributes(primaryKeyAttrs); | |
919 | uniqueId->get(&primaryKeyAttrs, NULL); | |
920 | return PrimaryKey(primaryKeyAttrs); | |
921 | } | |
922 | ||
923 | const CssmAutoDbRecordAttributeInfo & | |
924 | KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) | |
925 | { | |
926 | StLock<Mutex>_(mMutex); | |
927 | ||
928 | try | |
929 | { | |
930 | return keychainSchema()->primaryKeyInfosFor(recordType); | |
931 | } | |
932 | catch (const CommonError &error) | |
933 | { | |
934 | switch (error.osStatus()) | |
935 | { | |
936 | case errSecNoSuchClass: | |
937 | case CSSMERR_DL_INVALID_RECORDTYPE: | |
938 | resetSchema(); | |
939 | return keychainSchema()->primaryKeyInfosFor(recordType); | |
940 | default: | |
941 | throw; | |
942 | } | |
943 | } | |
944 | } | |
945 | ||
946 | void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs) | |
947 | { | |
948 | StLock<Mutex>_(mMutex); | |
949 | ||
950 | const CssmAutoDbRecordAttributeInfo &infos = | |
951 | primaryKeyInfosFor(primaryKeyAttrs.recordType()); | |
952 | ||
953 | // @@@ fix this to not copy info. | |
954 | for (uint32 i = 0; i < infos.size(); i++) | |
955 | primaryKeyAttrs.add(infos.at(i)); | |
956 | } | |
957 | ||
958 | ItemImpl * | |
959 | KeychainImpl::_lookupItem(const PrimaryKey &primaryKey) | |
960 | { | |
961 | DbItemMap::iterator it = mDbItemMap.find(primaryKey); | |
962 | if (it != mDbItemMap.end()) | |
963 | { | |
964 | if (it->second == NULL) | |
965 | { | |
966 | // we've been weak released... | |
967 | mDbItemMap.erase(it); | |
968 | } | |
969 | else | |
970 | { | |
971 | return it->second; | |
972 | } | |
973 | } | |
974 | ||
975 | return NULL; | |
976 | } | |
977 | ||
978 | Item | |
979 | KeychainImpl::item(const PrimaryKey &primaryKey) | |
980 | { | |
981 | StLock<Mutex>_(mMutex); | |
982 | ||
983 | // Lookup the item in the map while holding the apiLock. | |
984 | ItemImpl *itemImpl = _lookupItem(primaryKey); | |
985 | if (itemImpl) | |
986 | return Item(itemImpl); | |
987 | ||
988 | try | |
989 | { | |
990 | // We didn't find it so create a new item with just a keychain and | |
991 | // a primary key. However since we aren't holding | |
992 | // globals().apiLock anymore some other thread might have beaten | |
993 | // us to creating this item and adding it to the cache. If that | |
994 | // happens we retry the lookup. | |
995 | return Item(this, primaryKey); | |
996 | } | |
997 | catch (const MacOSError &e) | |
998 | { | |
999 | // If the item creation failed because some other thread already | |
1000 | // inserted this item into the cache we retry the lookup. | |
1001 | if (e.osStatus() == errSecDuplicateItem) | |
1002 | { | |
1003 | // Lookup the item in the map while holding the apiLock. | |
1004 | ItemImpl *itemImpl = _lookupItem(primaryKey); | |
1005 | if (itemImpl) | |
1006 | return Item(itemImpl); | |
1007 | } | |
1008 | throw; | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | ||
1013 | Item | |
1014 | KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) | |
1015 | { | |
1016 | StLock<Mutex>_(mMutex); | |
1017 | ||
1018 | PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId); | |
1019 | { | |
1020 | // Lookup the item in the map while holding the apiLock. | |
1021 | ItemImpl *itemImpl = _lookupItem(primaryKey); | |
1022 | ||
1023 | if (itemImpl) | |
1024 | { | |
1025 | return Item(itemImpl); | |
1026 | } | |
1027 | } | |
1028 | ||
1029 | try | |
1030 | { | |
1031 | // We didn't find it so create a new item with a keychain, a primary key | |
1032 | // and a DbUniqueRecord. However since we aren't holding | |
1033 | // globals().apiLock anymore some other thread might have beaten | |
1034 | // us to creating this item and adding it to the cache. If that | |
1035 | // happens we retry the lookup. | |
1036 | return Item(this, primaryKey, uniqueId); | |
1037 | } | |
1038 | catch (const MacOSError &e) | |
1039 | { | |
1040 | // If the item creation failed because some other thread already | |
1041 | // inserted this item into the cache we retry the lookup. | |
1042 | if (e.osStatus() == errSecDuplicateItem) | |
1043 | { | |
1044 | // Lookup the item in the map while holding the apiLock. | |
1045 | ItemImpl *itemImpl = _lookupItem(primaryKey); | |
1046 | if (itemImpl) | |
1047 | return Item(itemImpl); | |
1048 | } | |
1049 | throw; | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | KeychainSchema | |
1054 | KeychainImpl::keychainSchema() | |
1055 | { | |
1056 | StLock<Mutex>_(mMutex); | |
1057 | if (!mKeychainSchema) | |
1058 | mKeychainSchema = KeychainSchema(mDb); | |
1059 | ||
1060 | return mKeychainSchema; | |
1061 | } | |
1062 | ||
1063 | void KeychainImpl::resetSchema() | |
1064 | { | |
1065 | mKeychainSchema = NULL; // re-fetch it from db next time | |
1066 | } | |
1067 | ||
1068 | ||
1069 | // Called from DbItemImpl's constructor (so it is only partially constructed), | |
1070 | // add it to the map. | |
1071 | void | |
1072 | KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl) | |
1073 | { | |
1074 | StLock<Mutex>_(mMutex); | |
1075 | ||
1076 | // The dbItemImpl shouldn't be in the cache yet | |
1077 | assert(!dbItemImpl->inCache()); | |
1078 | ||
1079 | // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will | |
1080 | // be true if it got inserted. If not p.second will be false and p.first | |
1081 | // will point to the current entry with key primaryKey. | |
1082 | pair<DbItemMap::iterator, bool> p = | |
1083 | mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl)); | |
1084 | ||
1085 | if (!p.second) | |
1086 | { | |
1087 | // There was already an ItemImpl * in mDbItemMap with key primaryKey. | |
1088 | // There is a race condition here when being called in multiple threads | |
1089 | // We might have added an item using add and received a notification at | |
1090 | // the same time. | |
1091 | MacOSError::throwMe(errSecDuplicateItem); | |
1092 | } | |
1093 | ||
1094 | dbItemImpl->inCache(true); | |
1095 | } | |
1096 | ||
1097 | void | |
1098 | KeychainImpl::didDeleteItem(ItemImpl *inItemImpl) | |
1099 | { | |
1100 | StLock<Mutex>_(mMutex); | |
1101 | ||
1102 | // Called by CCallbackMgr | |
1103 | secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl); | |
1104 | removeItem(inItemImpl->primaryKey(), inItemImpl); | |
1105 | } | |
1106 | ||
1107 | void | |
1108 | KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl) | |
1109 | { | |
1110 | StLock<Mutex>_(mMutex); | |
1111 | ||
1112 | // If inItemImpl isn't in the cache to begin with we are done. | |
1113 | if (!inItemImpl->inCache()) | |
1114 | return; | |
1115 | ||
1116 | DbItemMap::iterator it = mDbItemMap.find(primaryKey); | |
1117 | if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl) | |
1118 | mDbItemMap.erase(it); | |
1119 | ||
1120 | inItemImpl->inCache(false); | |
1121 | } | |
1122 | ||
1123 | void | |
1124 | KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, | |
1125 | SecKeychainAttributeInfo **Info) | |
1126 | { | |
1127 | StLock<Mutex>_(mMutex); | |
1128 | ||
1129 | try | |
1130 | { | |
1131 | keychainSchema()->getAttributeInfoForRecordType(itemID, Info); | |
1132 | } | |
1133 | catch (const CommonError &error) | |
1134 | { | |
1135 | switch (error.osStatus()) | |
1136 | { | |
1137 | case errSecNoSuchClass: | |
1138 | case CSSMERR_DL_INVALID_RECORDTYPE: | |
1139 | resetSchema(); | |
1140 | keychainSchema()->getAttributeInfoForRecordType(itemID, Info); | |
1141 | default: | |
1142 | throw; | |
1143 | } | |
1144 | } | |
1145 | } | |
1146 | ||
1147 | void | |
1148 | KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info) | |
1149 | { | |
1150 | free(Info->tag); | |
1151 | free(Info->format); | |
1152 | free(Info); | |
1153 | } | |
1154 | ||
1155 | CssmDbAttributeInfo | |
1156 | KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag) | |
1157 | { | |
1158 | StLock<Mutex>_(mMutex); | |
1159 | ||
1160 | try | |
1161 | { | |
1162 | return keychainSchema()->attributeInfoFor(recordType, tag); | |
1163 | } | |
1164 | catch (const CommonError &error) | |
1165 | { | |
1166 | switch (error.osStatus()) | |
1167 | { | |
1168 | case errSecNoSuchClass: | |
1169 | case CSSMERR_DL_INVALID_RECORDTYPE: | |
1170 | resetSchema(); | |
1171 | return keychainSchema()->attributeInfoFor(recordType, tag); | |
1172 | default: | |
1173 | throw; | |
1174 | } | |
1175 | } | |
1176 | } | |
1177 | ||
1178 | void | |
1179 | KeychainImpl::recode(const CssmData &data, const CssmData &extraData) | |
1180 | { | |
1181 | StLock<Mutex>_(mMutex); | |
1182 | ||
1183 | mDb->recode(data, extraData); | |
1184 | } | |
1185 | ||
1186 | void | |
1187 | KeychainImpl::copyBlob(CssmData &data) | |
1188 | { | |
1189 | StLock<Mutex>_(mMutex); | |
1190 | ||
1191 | mDb->copyBlob(data); | |
1192 | } | |
1193 | ||
1194 | void | |
1195 | KeychainImpl::setBatchMode(Boolean mode, Boolean rollback) | |
1196 | { | |
1197 | StLock<Mutex>_(mMutex); | |
1198 | ||
1199 | mDb->setBatchMode(mode, rollback); | |
1200 | mIsInBatchMode = mode; | |
1201 | if (!mode) | |
1202 | { | |
1203 | if (!rollback) // was batch mode being turned off without an abort? | |
1204 | { | |
1205 | // dump the buffer | |
1206 | EventBuffer::iterator it = mEventBuffer->begin(); | |
1207 | while (it != mEventBuffer->end()) | |
1208 | { | |
1209 | PrimaryKey primaryKey; | |
1210 | if (it->item) | |
1211 | { | |
1212 | primaryKey = it->item->primaryKey(); | |
1213 | } | |
1214 | ||
1215 | KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey); | |
1216 | ||
1217 | ++it; | |
1218 | } | |
1219 | ||
1220 | } | |
1221 | ||
1222 | // notify that a keychain has changed in too many ways to count | |
1223 | KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent); | |
1224 | mEventBuffer->clear(); | |
1225 | } | |
1226 | else | |
1227 | { | |
1228 | KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent); | |
1229 | } | |
1230 | } | |
1231 | ||
1232 | void | |
1233 | KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item) | |
1234 | { | |
1235 | PrimaryKey primaryKey; | |
1236 | ||
1237 | { | |
1238 | StLock<Mutex>_(mMutex); | |
1239 | ||
1240 | if (item != NULL) | |
1241 | { | |
1242 | primaryKey = item->primaryKey(); | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | if (!mIsInBatchMode) | |
1247 | { | |
1248 | KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey); | |
1249 | } | |
1250 | else | |
1251 | { | |
1252 | StLock<Mutex>_(mMutex); | |
1253 | ||
1254 | EventItem it; | |
1255 | it.kcEvent = kcEvent; | |
1256 | if (item != NULL) | |
1257 | { | |
1258 | it.item = item; | |
1259 | } | |
1260 | ||
1261 | mEventBuffer->push_back (it); | |
1262 | } | |
1263 | } | |
1264 | ||
1265 | Keychain::Keychain() | |
1266 | { | |
1267 | dispatch_once(&SecKeychainSystemKeychainChecked, ^{ | |
1268 | check_system_keychain(); | |
1269 | }); | |
1270 | } | |
1271 | ||
427c49bc A |
1272 | Keychain::~Keychain() |
1273 | { | |
1274 | } | |
1275 | ||
1276 | ||
1277 | ||
b1ab9ed8 A |
1278 | Keychain |
1279 | Keychain::optional(SecKeychainRef handle) | |
1280 | { | |
1281 | if (handle) | |
1282 | return KeychainImpl::required(handle); | |
1283 | else | |
1284 | return globals().storageManager.defaultKeychain(); | |
1285 | } | |
1286 | ||
1287 | ||
427c49bc | 1288 | CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc) |
b1ab9ed8 A |
1289 | { |
1290 | CFTypeRef ref = kc->handle(false); | |
1291 | return CFGetRetainCount(ref); | |
1292 | } | |
1293 | ||
1294 | ||
1295 | // | |
1296 | // Create default credentials for this keychain. | |
1297 | // This is triggered upon default open (i.e. a Db::activate() with no set credentials). | |
1298 | // | |
1299 | // This function embodies the "default credentials" logic for Keychain-layer databases. | |
1300 | // | |
1301 | const AccessCredentials * | |
1302 | KeychainImpl::makeCredentials() | |
1303 | { | |
1304 | return defaultCredentials(); | |
1305 | } | |
1306 | ||
1307 | ||
1308 | const AccessCredentials * | |
1309 | KeychainImpl::defaultCredentials() | |
1310 | { | |
1311 | StLock<Mutex>_(mMutex); | |
1312 | ||
1313 | // Use custom unlock credentials for file keychains which have a referral | |
1314 | // record and the standard credentials for all others. | |
1315 | ||
1316 | if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb)) | |
1317 | return &mCustomUnlockCreds; | |
1318 | else | |
1319 | if (mDb->dl()->guid() == gGuidAppleSdCSPDL) | |
1320 | return globals().smartcardCredentials(); | |
1321 | else | |
1322 | return globals().keychainCredentials(); | |
1323 | } | |
1324 | ||
1325 | ||
1326 | ||
427c49bc A |
1327 | bool KeychainImpl::mayDelete() |
1328 | { | |
1329 | return true; | |
1330 | } |