]> git.saurik.com Git - apple/security.git/blame - libsecurity_keychain/lib/Keychains.cpp
Security-55471.14.18.tar.gz
[apple/security.git] / libsecurity_keychain / lib / Keychains.cpp
CommitLineData
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
56static dispatch_once_t SecKeychainSystemKeychainChecked;
57
58OSStatus SecKeychainSystemKeychainCheckWouldDeadlock()
59{
60 dispatch_once(&SecKeychainSystemKeychainChecked, ^{});
427c49bc 61 return errSecSuccess;
b1ab9ed8
A
62}
63
64using namespace KeychainCore;
65using namespace CssmClient;
66
67
68typedef struct EventItem
69{
70 SecKeychainEvent kcEvent;
71 Item item;
72} EventItem;
73
74typedef std::list<EventItem> EventBufferSuper;
75class EventBuffer : public EventBufferSuper
76{
77public:
78 EventBuffer () {}
79 virtual ~EventBuffer ();
80};
81
82
83EventBuffer::~EventBuffer ()
84{
85}
86
87
88
89//
90// KeychainSchemaImpl
91//
92KeychainSchemaImpl::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
153KeychainSchemaImpl::~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
170const KeychainSchemaImpl::RelationInfoMap &
171KeychainSchemaImpl::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
179bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const
180{
181 DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType);
182 return it != mDatabaseInfoMap.end();
183}
184
185bool
186KeychainSchemaImpl::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
207CSSM_DB_ATTRIBUTE_FORMAT
208KeychainSchemaImpl::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
218CssmDbAttributeInfo
219KeychainSchemaImpl::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
229void
230KeychainSchemaImpl::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
262const CssmAutoDbRecordAttributeInfo &
263KeychainSchemaImpl::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
274bool
275KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const
276{
277 return mDatabaseInfoMap < other.mDatabaseInfoMap;
278}
279
280bool
281KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const
282{
283 return mDatabaseInfoMap == other.mDatabaseInfoMap;
284}
285
286void
287KeychainSchemaImpl::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
327KeychainSchema::~KeychainSchema()
328
329{
330}
331
332
333
b1ab9ed8
A
334struct Event
335{
336 SecKeychainEvent eventCode;
337 PrimaryKey primaryKey;
338};
339typedef 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
345static 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//
395KeychainImpl::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
405KeychainImpl::~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
419Mutex*
420KeychainImpl::getMutexForObject()
421{
422 return globals().storageManager.getStorageManagerMutex();
423}
424
425Mutex*
426KeychainImpl::getKeychainMutex()
427{
428 return &mMutex;
429}
430
431void 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
439bool
440KeychainImpl::operator ==(const KeychainImpl &keychain) const
441{
442 return dlDbIdentifier() == keychain.dlDbIdentifier();
443}
444
445KCCursor
446KeychainImpl::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
455KCCursor
456KeychainImpl::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
465void
466KeychainImpl::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
486void 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
496void
497KeychainImpl::create()
498{
499 StLock<Mutex>_(mMutex);
500
501 AclFactory aclFactory;
502 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
503 create(&rcc);
504}
505
506void 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
531void
532KeychainImpl::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
553void
554KeychainImpl::open()
555{
556 StLock<Mutex>_(mMutex);
557
558 mDb->open();
559}
560
561void
562KeychainImpl::lock()
563{
564 StLock<Mutex>_(mMutex);
565
566 mDb->lock();
567}
568
569void
570KeychainImpl::unlock()
571{
572 StLock<Mutex>_(mMutex);
573
574 mDb->unlock();
575}
576
577void
578KeychainImpl::unlock(const CssmData &password)
579{
580 StLock<Mutex>_(mMutex);
581
582 mDb->unlock(password);
583}
584
585void
586KeychainImpl::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
599void
427c49bc 600KeychainImpl::stash()
b1ab9ed8 601{
427c49bc 602 StLock<Mutex>_(mMutex);
b1ab9ed8 603
427c49bc 604 mDb->stash();
b1ab9ed8
A
605}
606
427c49bc
A
607void
608KeychainImpl::stashCheck()
b1ab9ed8 609{
427c49bc 610 StLock<Mutex>_(mMutex);
b1ab9ed8 611
427c49bc
A
612 mDb->stashCheck();
613}
b1ab9ed8 614
427c49bc
A
615void
616KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
617{
618 StLock<Mutex>_(mMutex);
b1ab9ed8 619
427c49bc 620 mDb->getSettings(outIdleTimeOut, outLockOnSleep);
b1ab9ed8
A
621}
622
b1ab9ed8
A
623void
624KeychainImpl::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
643void
644KeychainImpl::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
679void
680KeychainImpl::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
711void
712KeychainImpl::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
722UInt32
723KeychainImpl::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
732bool
733KeychainImpl::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
753bool
754KeychainImpl::isActive() const
755{
756 return mDb->isActive();
757}
758
759void 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
792void
793KeychainImpl::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
801void
802KeychainImpl::add(Item &inItem)
803{
804 Keychain keychain(this);
805 PrimaryKey primaryKey = inItem->add(keychain);
806 completeAdd(inItem, primaryKey);
807 postEvent(kSecAddEvent, inItem);
808}
809
810void
811KeychainImpl::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
854void
855KeychainImpl::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
884CssmClient::CSP
885KeychainImpl::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
911PrimaryKey
912KeychainImpl::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
923const CssmAutoDbRecordAttributeInfo &
924KeychainImpl::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
946void 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
958ItemImpl *
959KeychainImpl::_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
978Item
979KeychainImpl::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
1013Item
1014KeychainImpl::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
1053KeychainSchema
1054KeychainImpl::keychainSchema()
1055{
1056 StLock<Mutex>_(mMutex);
1057 if (!mKeychainSchema)
1058 mKeychainSchema = KeychainSchema(mDb);
1059
1060 return mKeychainSchema;
1061}
1062
1063void 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.
1071void
1072KeychainImpl::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
1097void
1098KeychainImpl::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
1107void
1108KeychainImpl::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
1123void
1124KeychainImpl::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
1147void
1148KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1149{
1150 free(Info->tag);
1151 free(Info->format);
1152 free(Info);
1153}
1154
1155CssmDbAttributeInfo
1156KeychainImpl::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
1178void
1179KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1180{
1181 StLock<Mutex>_(mMutex);
1182
1183 mDb->recode(data, extraData);
1184}
1185
1186void
1187KeychainImpl::copyBlob(CssmData &data)
1188{
1189 StLock<Mutex>_(mMutex);
1190
1191 mDb->copyBlob(data);
1192}
1193
1194void
1195KeychainImpl::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
1232void
1233KeychainImpl::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
1265Keychain::Keychain()
1266{
1267 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1268 check_system_keychain();
1269 });
1270}
1271
427c49bc
A
1272Keychain::~Keychain()
1273{
1274}
1275
1276
1277
b1ab9ed8
A
1278Keychain
1279Keychain::optional(SecKeychainRef handle)
1280{
1281 if (handle)
1282 return KeychainImpl::required(handle);
1283 else
1284 return globals().storageManager.defaultKeychain();
1285}
1286
1287
427c49bc 1288CFIndex 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//
1301const AccessCredentials *
1302KeychainImpl::makeCredentials()
1303{
1304 return defaultCredentials();
1305}
1306
1307
1308const AccessCredentials *
1309KeychainImpl::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
1327bool KeychainImpl::mayDelete()
1328{
1329 return true;
1330}