]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/Keychains.cpp
Security-57336.10.29.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_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, ^{});
61 return errSecSuccess;
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 {
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());
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
236 size_t capacity=rmap.size();
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
300 // if our schema is already in the map, return
301 if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end())
302 {
303 return;
304 }
305
306 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
307 for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix)
308 rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType;
309
310 CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo();
311
312 mPrimaryKeyInfoMap.
313 insert(PrimaryKeyInfoMap::value_type(relationID, infos));
314 infos->DataRecordType = relationID;
315 for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix)
316 if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE)
317 {
318 CssmDbAttributeInfo &info = infos->add();
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
326
327 KeychainSchema::~KeychainSchema()
328
329 {
330 }
331
332
333
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
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));
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
600 KeychainImpl::stash()
601 {
602 StLock<Mutex>_(mMutex);
603
604 mDb->stash();
605 }
606
607 void
608 KeychainImpl::stashCheck()
609 {
610 StLock<Mutex>_(mMutex);
611
612 mDb->stashCheck();
613 }
614
615 void
616 KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
617 {
618 StLock<Mutex>_(mMutex);
619
620 mDb->getSettings(outIdleTimeOut, outLockOnSleep);
621 }
622
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);
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);
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
719 MacOSError::throwMe(errSecUnimplemented);
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
786 // make sure that we both mark the item and remove the item from the cache
787 removeItem(oldItem->primaryKey(), oldItem);
788 oldItem = inItem.get();
789 }
790
791 inItem->inCache(true);
792 }
793
794 void
795 KeychainImpl::addCopy(Item &inItem)
796 {
797 Keychain keychain(this);
798 PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true);
799 completeAdd(inItem, primaryKey);
800 postEvent(kSecAddEvent, inItem);
801 }
802
803 void
804 KeychainImpl::add(Item &inItem)
805 {
806 Keychain keychain(this);
807 PrimaryKey primaryKey = inItem->add(keychain);
808 completeAdd(inItem, primaryKey);
809 postEvent(kSecAddEvent, inItem);
810 }
811
812 void
813 KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK,
814 PrimaryKey &newPK)
815 {
816 // If the primary key hasn't changed we don't need to update mDbItemMap.
817 if (oldPK != newPK)
818 {
819 // If inItem isn't in the cache we don't need to update mDbItemMap.
820 assert(inItem->inCache());
821 if (inItem->inCache())
822 {
823 // First remove the entry for inItem in mDbItemMap with key oldPK.
824 DbItemMap::iterator it = mDbItemMap.find(oldPK);
825 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
826 mDbItemMap.erase(it);
827
828 // Insert inItem into mDbItemMap with key newPK. p.second will be
829 // true if it got inserted. If not p.second will be false and
830 // p.first will point to the current entry with key newPK.
831 pair<DbItemMap::iterator, bool> p =
832 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
833 if (!p.second)
834 {
835 // There was already an ItemImpl * in mDbItemMap with key
836 // primaryKey. Get a ref to the pointer to it so we can assign
837 // a new value to it below.
838 ItemImpl *oldItem = p.first->second;
839
840 // @@@ If this happens we are breaking our API contract of
841 // uniquifying items. We really need to insert the item into
842 // the map with the new primary key before we start the update.
843 // And have the item be in an "is being updated" state.
844 assert(oldItem->inCache());
845 secdebug("keychain", "update of item %p somehow replaced %p",
846 inItem.get(), oldItem);
847 oldItem->inCache(false);
848 oldItem = inItem.get();
849 }
850 }
851 }
852
853 postEvent(kSecUpdateEvent, inItem);
854 }
855
856 void
857 KeychainImpl::deleteItem(Item &inoutItem)
858 {
859 {
860 // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
861 // Hold it only as long as needed, instead.
862
863
864 // item must be persistent
865 if (!inoutItem->isPersistent())
866 MacOSError::throwMe(errSecInvalidItemRef);
867
868 DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
869 PrimaryKey primaryKey = inoutItem->primaryKey();
870 uniqueId->deleteRecord();
871
872 // Don't remove the item from the mDbItemMap here since this would cause
873 // us to report a new item to our caller when we receive the
874 // kSecDeleteEvent notification.
875 // It will be removed before we post the notification, because
876 // CCallbackMgr will call didDeleteItem()
877
878 // Post the notification for the item deletion with
879 // the primaryKey obtained when the item still existed
880 }
881
882 postEvent(kSecDeleteEvent, inoutItem);
883 }
884
885
886 CssmClient::CSP
887 KeychainImpl::csp()
888 {
889 StLock<Mutex>_(mMutex);
890
891 if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
892 MacOSError::throwMe(errSecInvalidKeychain);
893
894 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
895 try
896 {
897 CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
898 return CSP(cspdl);
899 }
900 catch (...)
901 {
902 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb));
903 if (impl == NULL)
904 {
905 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
906 }
907
908 SSDb ssDb(impl);
909 return ssDb->csp();
910 }
911 }
912
913 PrimaryKey
914 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
915 {
916 StLock<Mutex>_(mMutex);
917
918 DbAttributes primaryKeyAttrs(uniqueId->database());
919 primaryKeyAttrs.recordType(recordType);
920 gatherPrimaryKeyAttributes(primaryKeyAttrs);
921 uniqueId->get(&primaryKeyAttrs, NULL);
922 return PrimaryKey(primaryKeyAttrs);
923 }
924
925 const CssmAutoDbRecordAttributeInfo &
926 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
927 {
928 StLock<Mutex>_(mMutex);
929
930 try
931 {
932 return keychainSchema()->primaryKeyInfosFor(recordType);
933 }
934 catch (const CommonError &error)
935 {
936 switch (error.osStatus())
937 {
938 case errSecNoSuchClass:
939 case CSSMERR_DL_INVALID_RECORDTYPE:
940 resetSchema();
941 return keychainSchema()->primaryKeyInfosFor(recordType);
942 default:
943 throw;
944 }
945 }
946 }
947
948 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
949 {
950 StLock<Mutex>_(mMutex);
951
952 const CssmAutoDbRecordAttributeInfo &infos =
953 primaryKeyInfosFor(primaryKeyAttrs.recordType());
954
955 // @@@ fix this to not copy info.
956 for (uint32 i = 0; i < infos.size(); i++)
957 primaryKeyAttrs.add(infos.at(i));
958 }
959
960 ItemImpl *
961 KeychainImpl::_lookupItem(const PrimaryKey &primaryKey)
962 {
963 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
964 if (it != mDbItemMap.end())
965 {
966 if (it->second == NULL)
967 {
968 // we've been weak released...
969 mDbItemMap.erase(it);
970 }
971 else
972 {
973 return it->second;
974 }
975 }
976
977 return NULL;
978 }
979
980 Item
981 KeychainImpl::item(const PrimaryKey &primaryKey)
982 {
983 StLock<Mutex>_(mMutex);
984
985 // Lookup the item in the map while holding the apiLock.
986 ItemImpl *itemImpl = _lookupItem(primaryKey);
987 if (itemImpl)
988 return Item(itemImpl);
989
990 try
991 {
992 // We didn't find it so create a new item with just a keychain and
993 // a primary key. However since we aren't holding
994 // globals().apiLock anymore some other thread might have beaten
995 // us to creating this item and adding it to the cache. If that
996 // happens we retry the lookup.
997 return Item(this, primaryKey);
998 }
999 catch (const MacOSError &e)
1000 {
1001 // If the item creation failed because some other thread already
1002 // inserted this item into the cache we retry the lookup.
1003 if (e.osStatus() == errSecDuplicateItem)
1004 {
1005 // Lookup the item in the map while holding the apiLock.
1006 ItemImpl *itemImpl = _lookupItem(primaryKey);
1007 if (itemImpl)
1008 return Item(itemImpl);
1009 }
1010 throw;
1011 }
1012 }
1013
1014
1015 Item
1016 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
1017 {
1018 StLock<Mutex>_(mMutex);
1019
1020 PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
1021 {
1022 // Lookup the item in the map while holding the apiLock.
1023 ItemImpl *itemImpl = _lookupItem(primaryKey);
1024
1025 if (itemImpl)
1026 {
1027 return Item(itemImpl);
1028 }
1029 }
1030
1031 try
1032 {
1033 // We didn't find it so create a new item with a keychain, a primary key
1034 // and a DbUniqueRecord. However since we aren't holding
1035 // globals().apiLock anymore some other thread might have beaten
1036 // us to creating this item and adding it to the cache. If that
1037 // happens we retry the lookup.
1038 return Item(this, primaryKey, uniqueId);
1039 }
1040 catch (const MacOSError &e)
1041 {
1042 // If the item creation failed because some other thread already
1043 // inserted this item into the cache we retry the lookup.
1044 if (e.osStatus() == errSecDuplicateItem)
1045 {
1046 // Lookup the item in the map while holding the apiLock.
1047 ItemImpl *itemImpl = _lookupItem(primaryKey);
1048 if (itemImpl)
1049 return Item(itemImpl);
1050 }
1051 throw;
1052 }
1053 }
1054
1055 KeychainSchema
1056 KeychainImpl::keychainSchema()
1057 {
1058 StLock<Mutex>_(mMutex);
1059 if (!mKeychainSchema)
1060 mKeychainSchema = KeychainSchema(mDb);
1061
1062 return mKeychainSchema;
1063 }
1064
1065 void KeychainImpl::resetSchema()
1066 {
1067 mKeychainSchema = NULL; // re-fetch it from db next time
1068 }
1069
1070
1071 // Called from DbItemImpl's constructor (so it is only partially constructed),
1072 // add it to the map.
1073 void
1074 KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
1075 {
1076 StLock<Mutex>_(mMutex);
1077
1078 // The dbItemImpl shouldn't be in the cache yet
1079 assert(!dbItemImpl->inCache());
1080
1081 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1082 // be true if it got inserted. If not p.second will be false and p.first
1083 // will point to the current entry with key primaryKey.
1084 pair<DbItemMap::iterator, bool> p =
1085 mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
1086
1087 if (!p.second)
1088 {
1089 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1090 // There is a race condition here when being called in multiple threads
1091 // We might have added an item using add and received a notification at
1092 // the same time.
1093 MacOSError::throwMe(errSecDuplicateItem);
1094 }
1095
1096 dbItemImpl->inCache(true);
1097 }
1098
1099 void
1100 KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
1101 {
1102 StLock<Mutex>_(mMutex);
1103
1104 // Called by CCallbackMgr
1105 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
1106 removeItem(inItemImpl->primaryKey(), inItemImpl);
1107 }
1108
1109 void
1110 KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
1111 {
1112 StLock<Mutex>_(mMutex);
1113
1114 // If inItemImpl isn't in the cache to begin with we are done.
1115 if (!inItemImpl->inCache())
1116 return;
1117
1118 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1119 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl)
1120 mDbItemMap.erase(it);
1121
1122 inItemImpl->inCache(false);
1123 }
1124
1125 void
1126 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID,
1127 SecKeychainAttributeInfo **Info)
1128 {
1129 StLock<Mutex>_(mMutex);
1130
1131 try
1132 {
1133 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1134 }
1135 catch (const CommonError &error)
1136 {
1137 switch (error.osStatus())
1138 {
1139 case errSecNoSuchClass:
1140 case CSSMERR_DL_INVALID_RECORDTYPE:
1141 resetSchema();
1142 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1143 default:
1144 throw;
1145 }
1146 }
1147 }
1148
1149 void
1150 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1151 {
1152 free(Info->tag);
1153 free(Info->format);
1154 free(Info);
1155 }
1156
1157 CssmDbAttributeInfo
1158 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
1159 {
1160 StLock<Mutex>_(mMutex);
1161
1162 try
1163 {
1164 return keychainSchema()->attributeInfoFor(recordType, tag);
1165 }
1166 catch (const CommonError &error)
1167 {
1168 switch (error.osStatus())
1169 {
1170 case errSecNoSuchClass:
1171 case CSSMERR_DL_INVALID_RECORDTYPE:
1172 resetSchema();
1173 return keychainSchema()->attributeInfoFor(recordType, tag);
1174 default:
1175 throw;
1176 }
1177 }
1178 }
1179
1180 void
1181 KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1182 {
1183 StLock<Mutex>_(mMutex);
1184
1185 mDb->recode(data, extraData);
1186 }
1187
1188 void
1189 KeychainImpl::copyBlob(CssmData &data)
1190 {
1191 StLock<Mutex>_(mMutex);
1192
1193 mDb->copyBlob(data);
1194 }
1195
1196 void
1197 KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
1198 {
1199 StLock<Mutex>_(mMutex);
1200
1201 mDb->setBatchMode(mode, rollback);
1202 mIsInBatchMode = mode;
1203 if (!mode)
1204 {
1205 if (!rollback) // was batch mode being turned off without an abort?
1206 {
1207 // dump the buffer
1208 EventBuffer::iterator it = mEventBuffer->begin();
1209 while (it != mEventBuffer->end())
1210 {
1211 PrimaryKey primaryKey;
1212 if (it->item)
1213 {
1214 primaryKey = it->item->primaryKey();
1215 }
1216
1217 KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey);
1218
1219 ++it;
1220 }
1221
1222 }
1223
1224 // notify that a keychain has changed in too many ways to count
1225 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent);
1226 mEventBuffer->clear();
1227 }
1228 else
1229 {
1230 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent);
1231 }
1232 }
1233
1234 void
1235 KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
1236 {
1237 PrimaryKey primaryKey;
1238
1239 {
1240 StLock<Mutex>_(mMutex);
1241
1242 if (item != NULL)
1243 {
1244 primaryKey = item->primaryKey();
1245 }
1246 }
1247
1248 if (!mIsInBatchMode)
1249 {
1250 KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
1251 }
1252 else
1253 {
1254 StLock<Mutex>_(mMutex);
1255
1256 EventItem it;
1257 it.kcEvent = kcEvent;
1258 if (item != NULL)
1259 {
1260 it.item = item;
1261 }
1262
1263 mEventBuffer->push_back (it);
1264 }
1265 }
1266
1267 Keychain::Keychain()
1268 {
1269 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1270 check_system_keychain();
1271 });
1272 }
1273
1274 Keychain::~Keychain()
1275 {
1276 }
1277
1278
1279
1280 Keychain
1281 Keychain::optional(SecKeychainRef handle)
1282 {
1283 if (handle)
1284 return KeychainImpl::required(handle);
1285 else
1286 return globals().storageManager.defaultKeychain();
1287 }
1288
1289
1290 CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
1291 {
1292 CFTypeRef ref = kc->handle(false);
1293 return CFGetRetainCount(ref);
1294 }
1295
1296
1297 //
1298 // Create default credentials for this keychain.
1299 // This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1300 //
1301 // This function embodies the "default credentials" logic for Keychain-layer databases.
1302 //
1303 const AccessCredentials *
1304 KeychainImpl::makeCredentials()
1305 {
1306 return defaultCredentials();
1307 }
1308
1309
1310 const AccessCredentials *
1311 KeychainImpl::defaultCredentials()
1312 {
1313 StLock<Mutex>_(mMutex);
1314
1315 // Use custom unlock credentials for file keychains which have a referral
1316 // record and the standard credentials for all others.
1317
1318 if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
1319 return &mCustomUnlockCreds;
1320 else
1321 if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
1322 return globals().smartcardCredentials();
1323 else
1324 return globals().keychainCredentials();
1325 }
1326
1327
1328
1329 bool KeychainImpl::mayDelete()
1330 {
1331 return true;
1332 }