]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/Keychains.cpp
Security-55471.14.tar.gz
[apple/security.git] / libsecurity_keychain / lib / Keychains.cpp
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>
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 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
1272 Keychain::~Keychain()
1273 {
1274 }
1275
1276
1277
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
1288 CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
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
1327 bool KeychainImpl::mayDelete()
1328 {
1329 return true;
1330 }