]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/StorageManager.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / StorageManager.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 File: StorageManager.cpp
27
28 Contains: Working with multiple keychains
29
30 */
31
32 #include "StorageManager.h"
33 #include "KCEventNotifier.h"
34
35 #include <Security/cssmapple.h>
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <syslog.h>
39 #include <pwd.h>
40 #include <algorithm>
41 #include <string>
42 #include <stdio.h>
43 #include <security_utilities/debugging.h>
44 #include <security_keychain/SecCFTypes.h>
45 #include <securityd_client/ssclient.h>
46 #include <Security/AuthorizationTags.h>
47 #include <Security/AuthorizationTagsPriv.h>
48 #include <Security/SecTask.h>
49 #include <security_keychain/SecCFTypes.h>
50 #include <Security/SecCFAllocator.h>
51 #include "TrustSettingsSchema.h"
52 #include <security_cdsa_client/wrapkey.h>
53 #include <securityd_client/ssblob.h>
54 #include <Security/SecBasePriv.h>
55 #include "TokenLogin.h"
56
57 //%%% add this to AuthorizationTagsPriv.h later
58 #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
59 #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
60 #endif
61
62 #include "KCCursor.h"
63 #include "Globals.h"
64
65
66 using namespace CssmClient;
67 using namespace KeychainCore;
68
69 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
70 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
71 #define kEmptyKeychainSizeInBytes 20460
72
73 //-----------------------------------------------------------------------------------
74
75 static SecPreferencesDomain defaultPreferenceDomain()
76 {
77 SessionAttributeBits sessionAttrs;
78 if (gServerMode) {
79 secnotice("servermode", "StorageManager initialized in server mode");
80 sessionAttrs = sessionIsRoot;
81 } else {
82 MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs));
83 }
84
85 // If this is the root session, use system preferences.
86 // (In SecurityServer debug mode, you'll get a (fake) root session
87 // that has graphics access. Ignore that to help testing.)
88 if ((sessionAttrs & sessionIsRoot)
89 IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) {
90 secnotice("storagemgr", "using system preferences");
91 return kSecPreferencesDomainSystem;
92 }
93
94 // otherwise, use normal (user) preferences
95 return kSecPreferencesDomainUser;
96 }
97
98 static bool isAppSandboxed()
99 {
100 bool result = false;
101 SecTaskRef task = SecTaskCreateFromSelf(NULL);
102 if(task != NULL) {
103 CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task,
104 CFSTR("com.apple.security.app-sandbox"), NULL);
105 if(appSandboxValue != NULL) {
106 result = true;
107 CFRelease(appSandboxValue);
108 }
109 CFRelease(task);
110 }
111 return result;
112 }
113
114 static bool shouldAddToSearchList(const DLDbIdentifier &dLDbIdentifier)
115 {
116 // Creation of a private keychain should not modify the search list: rdar://13529331
117 // However, we want to ensure the login and System keychains are in
118 // the search list if that is not the case when they are created.
119 // Note that App Sandbox apps may not modify the list in either case.
120
121 bool loginOrSystemKeychain = false;
122 const char *dbname = dLDbIdentifier.dbName();
123 if (dbname) {
124 if ((!strcmp(dbname, "/Library/Keychains/System.keychain")) ||
125 (strstr(dbname, "/login.keychain")) ) {
126 loginOrSystemKeychain = true;
127 }
128 }
129 return (loginOrSystemKeychain && !isAppSandboxed());
130 }
131
132
133 StorageManager::StorageManager() :
134 mSavedList(defaultPreferenceDomain()),
135 mCommonList(kSecPreferencesDomainCommon),
136 mDomain(kSecPreferencesDomainUser),
137 mMutex(Mutex::recursive)
138 {
139 }
140
141
142 Mutex*
143 StorageManager::getStorageManagerMutex()
144 {
145 return &mKeychainMapMutex;
146 }
147
148
149 Keychain
150 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
151 {
152 StLock<Mutex>_(mKeychainMapMutex);
153
154 if (!dLDbIdentifier)
155 return Keychain();
156
157 KeychainMap::iterator it = mKeychainMap.end();
158
159 // If we have a keychain object for the munged keychain, return that.
160 // Don't hit the filesystem to check file status if we've already done that work...
161 DLDbIdentifier munge_dldbi = forceMungeDLDbIDentifier(dLDbIdentifier);
162 it = mKeychainMap.find(munge_dldbi);
163 if (it != mKeychainMap.end()) {
164 return it->second;
165 }
166
167 // If we have a keychain object for the un/demunged keychain, return that.
168 // We might be in the middle of an upgrade, where the -db file exists as a bit-perfect copy of the original file.
169 DLDbIdentifier demunge_dldbi = demungeDLDbIdentifier(dLDbIdentifier);
170 it = mKeychainMap.find(demunge_dldbi);
171 if (it != mKeychainMap.end()) {
172 return it->second;
173 }
174
175 // Okay, we haven't seen this keychain before. Do the full process...
176 DLDbIdentifier dldbi = mungeDLDbIdentifier(dLDbIdentifier, false);
177 it = mKeychainMap.find(dldbi); // Almost certain not to find it here
178 if (it != mKeychainMap.end())
179 {
180 return it->second;
181 }
182
183 if (gServerMode) {
184 secnotice("servermode", "keychain reference in server mode");
185 return Keychain();
186 }
187
188 // The keychain is not in our cache. Create it.
189 Db db(makeDb(dldbi));
190
191 Keychain keychain(db);
192 // Add the keychain to the cache.
193 registerKeychain(keychain);
194
195 return keychain;
196 }
197
198 // Note: this must be a munged DLDbidentifier.
199 CssmClient::Db
200 StorageManager::makeDb(DLDbIdentifier dLDbIdentifier) {
201 Module module(dLDbIdentifier.ssuid().guid());
202
203 DL dl;
204 if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
205 dl = SSCSPDL(module);
206 else
207 dl = DL(module);
208
209 dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
210 dl->version(dLDbIdentifier.ssuid().version());
211
212 CssmClient::Db db(dl, dLDbIdentifier.dbName());
213
214 return db;
215 }
216
217 // StorageManager is responsible for silently switching to newer-style keychains.
218 // If the keychain requested is in ~/Library/Keychains/, and there is a
219 // newer keychain available (with extension ".keychain-db"), open that one
220 // instead of the one requested.
221 //
222 // Because of backwards compatibility reasons, we can't update the plist
223 // files on disk to point to the upgraded keychains. We will be asked to
224 // load "/Users/account/Library/Keychains/login.keychain", hence this
225 // modification to 'login.keychain-db'.
226 DLDbIdentifier
227 StorageManager::mungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier, bool isReset) {
228 if(!dLDbIdentifier.dbName()) {
229 // If this DLDbIdentifier doesn't have a filename, don't munge it
230 return dLDbIdentifier;
231 }
232
233 string path = dLDbIdentifier.dbName();
234
235 bool shouldCreateProtected = globals().integrityProtection();
236
237 // If we don't have a DLDbIdentifier, we can't return one
238 if(dLDbIdentifier.mImpl == NULL) {
239 return DLDbIdentifier();
240 }
241
242 // Ensure we're in ~/Library/Keychains
243 if(pathInHomeLibraryKeychains(path)) {
244 string pathdb = makeKeychainDbFilename(path);
245
246 struct stat st;
247
248 int path_stat_err = 0;
249 bool path_exists = (::stat(path.c_str(), &st) == 0);
250 if(!path_exists) {
251 path_stat_err = errno;
252 }
253
254 int pathdb_stat_err = 0;
255 bool pathdb_exists = (::stat(pathdb.c_str(), &st) == 0);
256 if(!pathdb_exists) {
257 pathdb_stat_err = errno;
258 }
259
260 // If protections are off, don't change the requested filename.
261 // If protictions are on and the -db file exists, always use it.
262 //
263 // If we're resetting, and we're creating a new-style keychain, use the -db path.
264 // If we're resetting, and we're creating an old-style keychain, use the original path.
265 //
266 // Protection pathdb_exists path_exists resetting Result
267 // DISABLED X X X original
268 // ENABLED 1 X X -db
269 // ENABLED 0 0 X -db
270 // ENABLED 0 1 0 original
271 // ENABLED 0 1 1 -db
272 //
273 bool switchPaths = shouldCreateProtected && (pathdb_exists || (!pathdb_exists && !path_exists) || isReset);
274
275 if(switchPaths) {
276 secinfo("integrity", "switching to keychain-db: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err);
277 path = pathdb;
278 } else {
279 secinfo("integrity", "not switching: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err);
280 }
281 }
282
283 DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation());
284 return id;
285 }
286
287 DLDbIdentifier
288 StorageManager::forceMungeDLDbIDentifier(const DLDbIdentifier& dLDbIdentifier) {
289 if(!dLDbIdentifier.dbName() || dLDbIdentifier.mImpl == NULL) {
290 return dLDbIdentifier;
291 }
292
293 string path = dLDbIdentifier.dbName();
294 string pathdb = makeKeychainDbFilename(path);
295
296 DLDbIdentifier id(dLDbIdentifier.ssuid(), pathdb.c_str(), dLDbIdentifier.dbLocation());
297 return id;
298 }
299
300 DLDbIdentifier
301 StorageManager::demungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier) {
302 if(dLDbIdentifier.dbName() == NULL) {
303 return dLDbIdentifier;
304 }
305
306 string path = dLDbIdentifier.dbName();
307 string dbSuffix = "-db";
308 bool endsWithKeychainDb = (path.size() > dbSuffix.size() && (0 == path.compare(path.size() - dbSuffix.size(), dbSuffix.size(), dbSuffix)));
309
310 // Ensure we're in ~/Library/Keychains, and that the path ends in "-db"
311 if(pathInHomeLibraryKeychains(path) && endsWithKeychainDb) {
312 // remove "-db" from the end.
313 path.erase(path.end() - 3, path.end());
314 }
315
316 DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation());
317 return id;
318 }
319
320 string
321 StorageManager::makeKeychainDbFilename(const string& filename) {
322 string keychainDbSuffix = "-db";
323 bool endsWithKeychainDb = (filename.size() > keychainDbSuffix.size() && (0 == filename.compare(filename.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
324
325 if(endsWithKeychainDb) {
326 return filename;
327 } else {
328 return filename + keychainDbSuffix;
329 }
330 }
331
332 bool
333 StorageManager::pathInHomeLibraryKeychains(const string& path) {
334 return SecurityServer::CommonBlob::pathInHomeLibraryKeychains(path);
335 }
336
337 void
338 StorageManager::reloadKeychain(Keychain keychain) {
339 StLock<Mutex>_(mKeychainMapMutex);
340
341 DLDbIdentifier dLDbIdentifier = keychain->database()->dlDbIdentifier();
342
343 keychain->changeDatabase(makeDb(mungeDLDbIdentifier(dLDbIdentifier, false)));
344
345 // This keychain might have a different dldbidentifier now, depending on what
346 // other processes have been doing to the keychain files. Let's re-register it, just
347 // to be sure.
348 registerKeychain(keychain);
349 }
350
351 void
352 StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier,
353 KeychainImpl *keychainImpl)
354 {
355 StLock<Mutex>_(mKeychainMapMutex);
356
357 // Don't trust this dldbidentifier. Just look for the keychain and delete it.
358 forceRemoveFromCache(keychainImpl);
359 }
360
361 void
362 StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier)
363 {
364 // Lock the recursive mutex
365
366 StLock<Mutex>_(mKeychainMapMutex);
367
368 KeychainMap::iterator it = mKeychainMap.find(dLDbIdentifier);
369 if (it != mKeychainMap.end())
370 {
371 it->second->inCache(false);
372 mKeychainMap.erase(it);
373 }
374 }
375
376 // If the client does not keep references to keychains, they are destroyed on
377 // every API exit, and recreated on every API entrance.
378 //
379 // To improve performance, we'll cache keychains for some short period of time.
380 // We'll do this by CFRetaining the keychain object, and setting a timer to
381 // CFRelease it when time's up. This way, the client can still recover all its
382 // memory if it doesn't want the keychains around, but repeated API calls will
383 // be significantly faster.
384 //
385 void
386 StorageManager::tickleKeychain(KeychainImpl *keychainImpl) {
387 static dispatch_once_t onceToken = 0;
388 static dispatch_queue_t release_queue = NULL;
389 dispatch_once(&onceToken, ^{
390 release_queue = dispatch_queue_create("com.apple.security.keychain-cache-queue", DISPATCH_QUEUE_SERIAL);
391 });
392
393 __block KeychainImpl* kcImpl = keychainImpl;
394
395 if(!kcImpl) {
396 return;
397 }
398
399 // We really only want to cache CSPDL file-based keychains
400 if(kcImpl->dlDbIdentifier().ssuid().guid() != gGuidAppleCSPDL) {
401 return;
402 }
403
404 // Make a one-shot timer to release the keychain
405 uint32_t seconds = 1;
406
407 const string path = kcImpl->name();
408 bool isSystemKeychain = (0 == path.compare("/Library/Keychains/System.keychain"));
409 if(pathInHomeLibraryKeychains(path) || isSystemKeychain) {
410 // These keychains are important and likely aren't on removable media.
411 // Cache them longer.
412 seconds = 5;
413 }
414
415 __block CFTypeRef kcHandle = kcImpl->handle(); // calls retain; this keychain object will stay around until our dispatch block fires.
416
417 // You _must not_ call CFRelease while on this queue, due to the locking order mishmash. CFRelease takes a lock, so remember to do it later.
418 __block bool releaseImmediately = false;
419
420 dispatch_sync(release_queue, ^() {
421 if(kcImpl->mCacheTimer) {
422 // Update the cache timer to be seconds from now
423 dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2);
424 secdebug("keychain", "updating cache on %p %s", kcImpl, kcImpl->name());
425
426 // We've added an extra retain to this keychain right before invoking this block. Remember to release it.
427 releaseImmediately = true;
428 } else {
429 // No cache timer; make one.
430 kcImpl->mCacheTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, release_queue);
431 dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2);
432 secdebug("keychain", "taking cache on %p %s", kcImpl, kcImpl->name());
433
434 dispatch_source_set_event_handler(kcImpl->mCacheTimer, ^{
435 secdebug("keychain", "releasing cache on %p %s", kcImpl, kcImpl->name());
436 dispatch_source_cancel(kcImpl->mCacheTimer);
437 dispatch_release(kcImpl->mCacheTimer);
438 kcImpl->mCacheTimer = NULL;
439
440 // Since we're on the timer queue, we can't call CFRelease on the kcHandle (since that takes a lock). Dispatch_async it over to some other queue...
441 // This is better than using dispatch_async on the timer queue initially, since it's less dispatch_asyncs overall, even though it's more confusing.
442 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
443 CFRelease(kcHandle);
444 });
445 });
446
447 dispatch_resume(kcImpl->mCacheTimer);
448 }
449 });
450
451 if(releaseImmediately) {
452 CFRelease(kcHandle);
453 }
454 }
455
456 // Create keychain if it doesn't exist, and optionally add it to the search list.
457 Keychain
458 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add, bool isReset)
459 {
460 StLock<Mutex>_(mKeychainMapMutex);
461
462 Keychain theKeychain = keychain(mungeDLDbIdentifier(dLDbIdentifier, isReset));
463 bool post = false;
464 bool updateList = (add && shouldAddToSearchList(dLDbIdentifier));
465
466 if (updateList)
467 {
468 mSavedList.revert(false);
469 DLDbList searchList = mSavedList.searchList();
470 if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end())
471 return theKeychain; // theKeychain is already in the searchList.
472
473 mCommonList.revert(false);
474 searchList = mCommonList.searchList();
475 if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end())
476 return theKeychain; // theKeychain is already in the commonList don't add it to the searchList.
477
478 // If theKeychain doesn't exist don't bother adding it to the search list yet.
479 if (!theKeychain->exists())
480 return theKeychain;
481
482 // theKeychain exists and is not in our search list, so add it to the
483 // search list.
484 mSavedList.revert(true);
485 mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier));
486 mSavedList.save();
487 post = true;
488 }
489
490 if (post)
491 {
492 // Make sure we are not holding mStorageManagerLock anymore when we
493 // post this event.
494 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
495 }
496
497 return theKeychain;
498 }
499
500 // Be notified a Keychain just got created.
501 void
502 StorageManager::created(const Keychain &keychain)
503 {
504 StLock<Mutex>_(mKeychainMapMutex);
505
506 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
507 bool defaultChanged = false;
508 bool updateList = shouldAddToSearchList(dLDbIdentifier);
509
510 if (updateList)
511 {
512 mSavedList.revert(true);
513 // If we don't have a default Keychain yet. Make the newly created
514 // keychain the default.
515 if (!mSavedList.defaultDLDbIdentifier())
516 {
517 mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier));
518 defaultChanged = true;
519 }
520
521 // Add the keychain to the search list prefs.
522 mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier));
523 mSavedList.save();
524
525 // Make sure we are not holding mLock when we post these events.
526 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
527 }
528
529 if (defaultChanged)
530 {
531 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier);
532 }
533 }
534
535 KCCursor
536 StorageManager::createCursor(SecItemClass itemClass,
537 const SecKeychainAttributeList *attrList)
538 {
539 StLock<Mutex>_(mMutex);
540
541 KeychainList searchList;
542 getSearchList(searchList);
543 return KCCursor(searchList, itemClass, attrList);
544 }
545
546 KCCursor
547 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
548 {
549 StLock<Mutex>_(mMutex);
550
551 KeychainList searchList;
552 getSearchList(searchList);
553 return KCCursor(searchList, attrList);
554 }
555
556 void
557 StorageManager::lockAll()
558 {
559 StLock<Mutex>_(mMutex);
560
561 SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard());
562 ss.lockAll (false);
563 }
564
565 Keychain
566 StorageManager::defaultKeychain()
567 {
568 StLock<Mutex>_(mMutex);
569
570 Keychain theKeychain;
571 CFTypeRef ref;
572
573 {
574 mSavedList.revert(false);
575 DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier());
576 if (defaultDLDbIdentifier)
577 {
578 theKeychain = keychain(defaultDLDbIdentifier);
579 ref = theKeychain->handle(false);
580 }
581 }
582
583 if (theKeychain /* && theKeychain->exists() */)
584 return theKeychain;
585
586 MacOSError::throwMe(errSecNoDefaultKeychain);
587 }
588
589 void
590 StorageManager::defaultKeychain(const Keychain &keychain)
591 {
592 StLock<Mutex>_(mMutex);
593
594 // Only set a keychain as the default if we own it and can read/write it,
595 // and our uid allows modifying the directory for that preference domain.
596 if (!keychainOwnerPermissionsValidForDomain(keychain->name(), mDomain))
597 MacOSError::throwMe(errSecWrPerm);
598
599 DLDbIdentifier oldDefaultId;
600 DLDbIdentifier newDefaultId(keychain->dlDbIdentifier());
601 {
602 oldDefaultId = mSavedList.defaultDLDbIdentifier();
603 mSavedList.revert(true);
604 mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId));
605 mSavedList.save();
606 }
607
608 if (!(oldDefaultId == newDefaultId))
609 {
610 // Make sure we are not holding mLock when we post this event.
611 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId);
612 }
613 }
614
615 Keychain
616 StorageManager::defaultKeychain(SecPreferencesDomain domain)
617 {
618 StLock<Mutex>_(mMutex);
619
620 if (domain == kSecPreferencesDomainDynamic)
621 MacOSError::throwMe(errSecInvalidPrefsDomain);
622
623 if (domain == mDomain)
624 return defaultKeychain();
625 else
626 {
627 DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier());
628 if (defaultDLDbIdentifier)
629 return keychain(defaultDLDbIdentifier);
630
631 MacOSError::throwMe(errSecNoDefaultKeychain);
632 }
633 }
634
635 void
636 StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain)
637 {
638 StLock<Mutex>_(mMutex);
639
640 if (domain == kSecPreferencesDomainDynamic)
641 MacOSError::throwMe(errSecInvalidPrefsDomain);
642
643 if (domain == mDomain)
644 defaultKeychain(keychain);
645 else
646 DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier());
647 }
648
649 Keychain
650 StorageManager::loginKeychain()
651 {
652 StLock<Mutex>_(mMutex);
653
654 Keychain theKeychain;
655 {
656 mSavedList.revert(false);
657 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
658 if (loginDLDbIdentifier)
659 {
660 theKeychain = keychain(loginDLDbIdentifier);
661 }
662 }
663
664 if (theKeychain && theKeychain->exists())
665 return theKeychain;
666
667 MacOSError::throwMe(errSecNoSuchKeychain);
668 }
669
670 DLDbIdentifier
671 StorageManager::loginKeychainDLDbIdentifer()
672 {
673 StLock<Mutex>_(mMutex);
674 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
675 return mungeDLDbIdentifier(loginDLDbIdentifier, false);
676 }
677
678 void
679 StorageManager::loginKeychain(Keychain keychain)
680 {
681 StLock<Mutex>_(mMutex);
682
683 mSavedList.revert(true);
684 mSavedList.loginDLDbIdentifier(demungeDLDbIdentifier(keychain->dlDbIdentifier()));
685 mSavedList.save();
686 }
687
688 size_t
689 StorageManager::size()
690 {
691 StLock<Mutex>_(mMutex);
692
693 mSavedList.revert(false);
694 mCommonList.revert(false);
695 return mSavedList.searchList().size() + mCommonList.searchList().size();
696 }
697
698 Keychain
699 StorageManager::at(unsigned int ix)
700 {
701 StLock<Mutex>_(mMutex);
702
703 mSavedList.revert(false);
704 DLDbList dLDbList = mSavedList.searchList();
705 if (ix < dLDbList.size())
706 {
707 return keychain(dLDbList[ix]);
708 }
709 else
710 {
711 ix -= dLDbList.size();
712 mCommonList.revert(false);
713 DLDbList commonList = mCommonList.searchList();
714 if (ix >= commonList.size())
715 MacOSError::throwMe(errSecInvalidKeychain);
716
717 return keychain(commonList[ix]);
718 }
719 }
720
721 Keychain
722 StorageManager::operator[](unsigned int ix)
723 {
724 StLock<Mutex>_(mMutex);
725
726 return at(ix);
727 }
728
729 void StorageManager::rename(Keychain keychain, const char* newName)
730 {
731
732 StLock<Mutex>_(mKeychainMapMutex);
733
734 bool changedDefault = false;
735 DLDbIdentifier newDLDbIdentifier;
736 {
737 mSavedList.revert(true);
738 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
739
740 // Find the keychain object for the given ref
741 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
742
743 if(!keychain->database()->isLocked()) {
744 // Bring our unlock state with us
745 DLDbIdentifier dldbi(dLDbIdentifier.ssuid(), newName, dLDbIdentifier.dbLocation());
746 keychain->database()->transferTo(dldbi);
747 } else {
748 keychain->database()->rename(newName);
749 }
750
751 if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId)
752 changedDefault=true;
753
754 newDLDbIdentifier = keychain->dlDbIdentifier();
755 // Rename the keychain in the search list.
756 mSavedList.rename(demungeDLDbIdentifier(dLDbIdentifier), demungeDLDbIdentifier(newDLDbIdentifier));
757
758 // If this was the default keychain change it accordingly
759 if (changedDefault)
760 mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier));
761
762 mSavedList.save();
763
764 // If the keychain wasn't in the cache, don't touch the cache.
765 // Otherwise, update the cache to use its current identifier.
766 if(keychain->inCache()) {
767 registerKeychain(keychain);
768 }
769 }
770
771 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
772
773 if (changedDefault)
774 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent,
775 newDLDbIdentifier);
776 }
777
778 void StorageManager::registerKeychain(Keychain& kc) {
779 registerKeychainImpl(kc.get());
780 }
781
782 void StorageManager::registerKeychainImpl(KeychainImpl* kcimpl) {
783 if(!kcimpl) {
784 return;
785 }
786
787 {
788 StLock<Mutex> _(mKeychainMapMutex);
789
790 // First, iterate through the cache to see if this keychain is there. If so, remove it.
791 forceRemoveFromCache(kcimpl);
792
793 // If we renamed this keychain on top of an existing one, let's drop the old one from the cache.
794 KeychainMap::iterator it = mKeychainMap.find(kcimpl->dlDbIdentifier());
795 if (it != mKeychainMap.end())
796 {
797 Keychain oldKeychain(it->second);
798 oldKeychain->inCache(false);
799 // @@@ Ideally we should invalidate or fault this keychain object.
800 }
801
802 mKeychainMap.insert(KeychainMap::value_type(kcimpl->dlDbIdentifier(), kcimpl));
803 kcimpl->inCache(true);
804 } // drop mKeychainMapMutex
805 }
806
807 void StorageManager::forceRemoveFromCache(KeychainImpl* inKeychainImpl) {
808 try {
809 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
810 {
811 StLock<Mutex> _(mKeychainMapMutex);
812 for(KeychainMap::iterator it = mKeychainMap.begin(); it != mKeychainMap.end(); ) {
813 if(it->second == inKeychainImpl) {
814 // Increment the iterator, but use its pre-increment value for the erase
815 it->second->inCache(false);
816 mKeychainMap.erase(it++);
817 } else {
818 it++;
819 }
820 }
821 } // drop mKeychainMapMutex
822 } catch(UnixError ue) {
823 secnotice("storagemgr", "caught UnixError: %d %s", ue.unixError(), ue.what());
824 } catch (CssmError cssme) {
825 const char* errStr = cssmErrorString(cssme.error);
826 secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme.error, errStr);
827 } catch (MacOSError mose) {
828 secnotice("storagemgr", "MacOSError: %d", (int)mose.osStatus());
829 } catch(...) {
830 secnotice("storagemgr", "Unknown error");
831 }
832 }
833
834 // If you pass NULL as the keychain, you must pass an oldName.
835 void StorageManager::renameUnique(Keychain keychain, CFStringRef oldName, CFStringRef newName, bool appendDbSuffix)
836 {
837 StLock<Mutex>_(mMutex);
838
839 bool doneCreating = false;
840 int index = 1;
841 do
842 {
843 char newNameCString[MAXPATHLEN];
844 if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc.
845 {
846 // Construct the new name...
847 //
848 CFMutableStringRef newNameCFStr = NULL;
849 newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN);
850 if ( newNameCFStr )
851 {
852 CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), newNameCString, index);
853 if(appendDbSuffix) {
854 CFStringAppend(newNameCFStr, CFSTR(kKeychainDbSuffix));
855 } else {
856 CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix)); // add .keychain
857 }
858 char toUseBuff2[MAXPATHLEN];
859 if ( CFStringGetCString(newNameCFStr, toUseBuff2, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc.
860 {
861 struct stat filebuf;
862 if ( lstat(toUseBuff2, &filebuf) )
863 {
864 if(keychain) {
865 rename(keychain, toUseBuff2);
866 KeychainList kcList;
867 kcList.push_back(keychain);
868 remove(kcList, false);
869 } else {
870 // We don't have a Keychain object, so force the rename here if possible
871 char oldNameCString[MAXPATHLEN];
872 if ( CFStringGetCString(oldName, oldNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) {
873 int result = ::rename(oldNameCString, toUseBuff2);
874 secnotice("KClogin", "keychain force rename to %s: %d %d", newNameCString, result, (result == 0) ? 0 : errno);
875 if(result != 0) {
876 UnixError::throwMe(errno);
877 }
878 } else {
879 secnotice("KClogin", "path is wrong, quitting");
880 }
881 }
882 doneCreating = true;
883 }
884 else
885 index++;
886 }
887 else
888 doneCreating = true; // failure to get c string.
889 CFRelease(newNameCFStr);
890 }
891 else
892 doneCreating = false; // failure to create mutable string.
893 }
894 else
895 doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?)
896 }
897 while (!doneCreating && index != INT_MAX);
898 }
899
900 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
901 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
902
903 static CFStringRef MakeExpandedPath (const char* path)
904 {
905 std::string name = DLDbListCFPref::ExpandTildesInPath (std::string (path));
906 CFStringRef expanded = CFStringCreateWithCString (NULL, name.c_str (), 0);
907 return expanded;
908 }
909
910 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier &id)
911 {
912 StLock<Mutex>_(mMutex);
913
914 // make a CFString of our identifier
915 const char* idname = id.dbName ();
916 if (idname == NULL)
917 {
918 return;
919 }
920
921 CFRef<CFStringRef> idString = MakeExpandedPath (idname);
922
923 // check and see if this keychain is in the keychain syncing list
924 CFArrayRef value =
925 (CFArrayRef) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY,
926 KEYCHAIN_SYNC_DOMAIN,
927 kCFPreferencesCurrentUser,
928 kCFPreferencesAnyHost);
929 if (value == NULL)
930 {
931 return;
932 }
933
934 // make a mutable copy of the dictionary
935 CFRef<CFMutableArrayRef> mtValue = CFArrayCreateMutableCopy (NULL, 0, value);
936 CFRelease (value);
937
938 // walk the array, looking for the value
939 CFIndex i;
940 CFIndex limit = CFArrayGetCount (mtValue.get());
941 bool found = false;
942
943 for (i = 0; i < limit; ++i)
944 {
945 CFDictionaryRef idx = (CFDictionaryRef) CFArrayGetValueAtIndex (mtValue.get(), i);
946 CFStringRef v = (CFStringRef) CFDictionaryGetValue (idx, CFSTR("DbName"));
947 if (v == NULL)
948 {
949 return; // something is really wrong if this is taken
950 }
951
952 char* stringBuffer = NULL;
953 const char* pathString = CFStringGetCStringPtr(v, 0);
954 if (pathString == 0)
955 {
956 CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(v), kCFStringEncodingUTF8) + 1;
957 stringBuffer = (char*) malloc(maxLen);
958 CFStringGetCString(v, stringBuffer, maxLen, kCFStringEncodingUTF8);
959 pathString = stringBuffer;
960 }
961
962 CFStringRef vExpanded = MakeExpandedPath(pathString);
963 CFComparisonResult result = CFStringCompare (vExpanded, idString.get(), 0);
964 if (stringBuffer != NULL)
965 {
966 free(stringBuffer);
967 }
968
969 CFRelease (vExpanded);
970
971 if (result == 0)
972 {
973 CFArrayRemoveValueAtIndex (mtValue.get(), i);
974 found = true;
975 break;
976 }
977 }
978
979 if (found)
980 {
981 #ifndef NDEBUG
982 CFShow (mtValue.get());
983 #endif
984
985 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY,
986 mtValue,
987 KEYCHAIN_SYNC_DOMAIN,
988 kCFPreferencesCurrentUser,
989 kCFPreferencesAnyHost);
990 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
991 }
992 }
993
994 void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
995 {
996 StLock<Mutex>_(mMutex);
997
998 bool unsetDefault = false;
999 bool updateList = (!isAppSandboxed());
1000
1001 if (updateList)
1002 {
1003 mSavedList.revert(true);
1004 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
1005 for (KeychainList::const_iterator ix = kcsToRemove.begin();
1006 ix != kcsToRemove.end(); ++ix)
1007 {
1008 // Find the keychain object for the given ref
1009 Keychain theKeychain = *ix;
1010 DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier();
1011
1012 // Remove it from the saved list
1013 mSavedList.remove(demungeDLDbIdentifier(dLDbIdentifier));
1014 if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId) {
1015 unsetDefault=true;
1016 }
1017
1018 if (deleteDb)
1019 {
1020 removeKeychainFromSyncList (dLDbIdentifier);
1021
1022 // Now remove it from the cache
1023 removeKeychain(dLDbIdentifier, theKeychain.get());
1024 }
1025 }
1026
1027 if (unsetDefault)
1028 mSavedList.defaultDLDbIdentifier(DLDbIdentifier());
1029
1030 mSavedList.save();
1031 }
1032
1033 if (deleteDb)
1034 {
1035 // Delete the actual databases without holding any locks.
1036 for (KeychainList::const_iterator ix = kcsToRemove.begin();
1037 ix != kcsToRemove.end(); ++ix)
1038 {
1039 (*ix)->database()->deleteDb();
1040 }
1041 }
1042
1043 if (updateList) {
1044 // Make sure we are not holding mLock when we post these events.
1045 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1046 }
1047
1048 if (unsetDefault)
1049 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent);
1050 }
1051
1052 void
1053 StorageManager::getSearchList(KeychainList &keychainList)
1054 {
1055 // hold the global lock since we make keychain objects in this function
1056
1057 // to do: each of the items in this list must be retained, otherwise mayhem will occur
1058 StLock<Mutex>_(mMutex);
1059
1060 if (gServerMode) {
1061 keychainList.clear();
1062 return;
1063 }
1064
1065 mSavedList.revert(false);
1066 mCommonList.revert(false);
1067
1068 // Merge mSavedList, mDynamicList and mCommonList
1069 DLDbList dLDbList = mSavedList.searchList();
1070 DLDbList dynamicList = mDynamicList.searchList();
1071 DLDbList commonList = mCommonList.searchList();
1072 KeychainList result;
1073 result.reserve(dLDbList.size() + dynamicList.size() + commonList.size());
1074
1075 {
1076 for (DLDbList::const_iterator it = dynamicList.begin();
1077 it != dynamicList.end(); ++it)
1078 {
1079 Keychain k = keychain(*it);
1080 result.push_back(k);
1081 }
1082
1083 for (DLDbList::const_iterator it = dLDbList.begin();
1084 it != dLDbList.end(); ++it)
1085 {
1086 Keychain k = keychain(*it);
1087 result.push_back(k);
1088 }
1089
1090 for (DLDbList::const_iterator it = commonList.begin();
1091 it != commonList.end(); ++it)
1092 {
1093 Keychain k = keychain(*it);
1094 result.push_back(k);
1095 }
1096 }
1097
1098 keychainList.swap(result);
1099 }
1100
1101 void
1102 StorageManager::setSearchList(const KeychainList &keychainList)
1103 {
1104 StLock<Mutex>_(mMutex);
1105
1106 DLDbList searchList, oldSearchList(mSavedList.searchList());
1107 for (KeychainList::const_iterator it = keychainList.begin(); it != keychainList.end(); ++it)
1108 {
1109 DLDbIdentifier dldbi = demungeDLDbIdentifier((*it)->dlDbIdentifier());
1110
1111 // If this keychain is not in the common or dynamic lists, add it to the new search list
1112 DLDbList commonList = mCommonList.searchList();
1113 bool found = false;
1114 for(DLDbList::const_iterator jt = commonList.begin(); jt != commonList.end(); ++jt) {
1115 if((*jt) == dldbi) {
1116 found = true;
1117 }
1118 }
1119
1120 DLDbList dynamicList = mDynamicList.searchList();
1121 for(DLDbList::const_iterator jt = dynamicList.begin(); jt != dynamicList.end(); ++jt) {
1122 if((*jt) == dldbi) {
1123 found = true;
1124 }
1125 }
1126
1127 if(found) {
1128 continue;
1129 }
1130
1131 searchList.push_back(dldbi);
1132 }
1133
1134 {
1135 // Set the current searchlist to be what was passed in, the old list will be freed
1136 // upon exit of this stackframe.
1137 mSavedList.revert(true);
1138 mSavedList.searchList(searchList);
1139 mSavedList.save();
1140 }
1141
1142 if (!(oldSearchList == searchList))
1143 {
1144 // Make sure we are not holding mLock when we post this event.
1145 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1146 }
1147 }
1148
1149 void
1150 StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList)
1151 {
1152 StLock<Mutex>_(mMutex);
1153
1154 if (gServerMode) {
1155 keychainList.clear();
1156 return;
1157 }
1158
1159 if (domain == kSecPreferencesDomainDynamic)
1160 {
1161 convertList(keychainList, mDynamicList.searchList());
1162 }
1163 else if (domain == mDomain)
1164 {
1165 mSavedList.revert(false);
1166 convertList(keychainList, mSavedList.searchList());
1167 }
1168 else
1169 {
1170 convertList(keychainList, DLDbListCFPref(domain).searchList());
1171 }
1172 }
1173
1174 void StorageManager::forceUserSearchListReread()
1175 {
1176 mSavedList.forceUserSearchListReread();
1177 }
1178
1179 void
1180 StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList)
1181 {
1182 StLock<Mutex>_(mMutex);
1183
1184 if (domain == kSecPreferencesDomainDynamic)
1185 MacOSError::throwMe(errSecInvalidPrefsDomain);
1186
1187 DLDbList searchList;
1188 convertList(searchList, keychainList);
1189
1190 if (domain == mDomain)
1191 {
1192 DLDbList oldSearchList(mSavedList.searchList());
1193 {
1194 // Set the current searchlist to be what was passed in, the old list will be freed
1195 // upon exit of this stackframe.
1196 mSavedList.revert(true);
1197 mSavedList.searchList(searchList);
1198 mSavedList.save();
1199 }
1200
1201 if (!(oldSearchList == searchList))
1202 {
1203 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1204 }
1205 }
1206 else
1207 {
1208 DLDbListCFPref(domain).searchList(searchList);
1209 }
1210 }
1211
1212 void
1213 StorageManager::domain(SecPreferencesDomain domain)
1214 {
1215 StLock<Mutex>_(mMutex);
1216
1217 if (domain == kSecPreferencesDomainDynamic)
1218 MacOSError::throwMe(errSecInvalidPrefsDomain);
1219
1220 if (domain == mDomain)
1221 return; // no change
1222
1223 #if !defined(NDEBUG)
1224 switch (domain)
1225 {
1226 case kSecPreferencesDomainSystem:
1227 secnotice("storagemgr", "switching to system domain"); break;
1228 case kSecPreferencesDomainUser:
1229 secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break;
1230 default:
1231 secnotice("storagemgr", "switching to weird prefs domain %d", domain); break;
1232 }
1233 #endif
1234
1235 mDomain = domain;
1236 mSavedList.set(domain);
1237 }
1238
1239 void
1240 StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
1241 {
1242 StLock<Mutex>_(mMutex);
1243
1244 if (!keychainOrArray)
1245 getSearchList(keychainList);
1246 else
1247 {
1248 CFTypeID typeID = CFGetTypeID(keychainOrArray);
1249 if (typeID == CFArrayGetTypeID())
1250 convertToKeychainList(CFArrayRef(keychainOrArray), keychainList);
1251 else if (typeID == gTypes().KeychainImpl.typeID)
1252 keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray)));
1253 else
1254 MacOSError::throwMe(errSecParam);
1255 }
1256 }
1257
1258 // static methods.
1259 void
1260 StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
1261 {
1262 CFIndex count = CFArrayGetCount(keychainArray);
1263 if (!(count > 0))
1264 return;
1265
1266 KeychainList keychains(count);
1267 for (CFIndex ix = 0; ix < count; ++ix)
1268 {
1269 keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
1270 }
1271
1272 keychainList.swap(keychains);
1273 }
1274
1275 CFArrayRef
1276 StorageManager::convertFromKeychainList(const KeychainList &keychainList)
1277 {
1278 CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
1279
1280 for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
1281 {
1282 SecKeychainRef keychainRef = (*ix)->handle();
1283 CFArrayAppendValue(keychainArray, keychainRef);
1284 CFRelease(keychainRef);
1285 }
1286
1287 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1288 CFRetain(keychainArray);
1289 return keychainArray;
1290 }
1291
1292 void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs)
1293 {
1294 DLDbList result;
1295 result.reserve(kcs.size());
1296 for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix)
1297 {
1298 result.push_back(demungeDLDbIdentifier((*ix)->dlDbIdentifier()));
1299 }
1300 ids.swap(result);
1301 }
1302
1303 void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids)
1304 {
1305 StLock<Mutex>_(mMutex);
1306
1307 KeychainList result;
1308 result.reserve(ids.size());
1309 {
1310 for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix)
1311 result.push_back(keychain(*ix));
1312 }
1313 kcs.swap(result);
1314 }
1315
1316 #pragma mark ____ Login Functions ____
1317
1318 void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name, bool isReset)
1319 {
1320 StLock<Mutex>_(mMutex);
1321
1322 AuthorizationItemSet* info = NULL;
1323 OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info); // get the results of the copy rights call.
1324 Boolean created = false;
1325 if ( result == errSecSuccess && info->count )
1326 {
1327 // Grab the password from the auth context (info) and create the keychain...
1328 //
1329 AuthorizationItem* currItem = info->items;
1330 for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context.
1331 {
1332 if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0)
1333 {
1334 // creates the login keychain with the specified password
1335 try
1336 {
1337 login(nameLength, name, (UInt32)currItem->valueLength, currItem->value, isReset);
1338 created = true;
1339 }
1340 catch(...)
1341 {
1342 }
1343 break;
1344 }
1345 currItem++;
1346 }
1347 }
1348 if ( info )
1349 AuthorizationFreeItemSet(info);
1350
1351 if ( !created )
1352 MacOSError::throwMe(errAuthorizationInternal);
1353 }
1354
1355 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
1356 {
1357 StLock<Mutex>_(mMutex);
1358
1359 if ( name == NULL || password == NULL )
1360 MacOSError::throwMe(errSecParam);
1361
1362 login(name[0], name + 1, password[0], password + 1, false);
1363 }
1364
1365 void StorageManager::login(UInt32 nameLength, const void *name,
1366 UInt32 passwordLength, const void *password, bool isReset)
1367 {
1368 if (passwordLength != 0 && password == NULL)
1369 {
1370 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1371 MacOSError::throwMe(errSecParam);
1372 }
1373
1374 DLDbIdentifier loginDLDbIdentifier;
1375 {
1376 mSavedList.revert(true);
1377 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1378 }
1379
1380 secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1381 if (!loginDLDbIdentifier)
1382 MacOSError::throwMe(errSecNoSuchKeychain);
1383
1384
1385 //***************************************************************
1386 // gather keychain information
1387 //***************************************************************
1388
1389 // user name
1390 int uid = geteuid();
1391 struct passwd *pw = getpwuid(uid);
1392 if (pw == NULL) {
1393 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1394 MacOSError::throwMe(errSecParam);
1395 }
1396 std::string userName = pw->pw_name;
1397
1398 // make keychain path strings
1399 std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix);
1400 std::string shortnameKeychain = keychainPath + userName;
1401 std::string shortnameDotKeychain = shortnameKeychain + ".keychain";
1402 std::string loginDotKeychain = keychainPath + "login.keychain";
1403 std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain";
1404 std::string loginKeychainDb = keychainPath + "login.keychain-db";
1405
1406 // check for existence of keychain files
1407 bool shortnameKeychainExists = false;
1408 bool shortnameDotKeychainExists = false;
1409 bool loginKeychainExists = false;
1410 bool loginRenamed1KeychainExists = false;
1411 bool loginKeychainDbExists = false;
1412 {
1413 struct stat st;
1414 int stat_result;
1415 stat_result = ::stat(shortnameKeychain.c_str(), &st);
1416 shortnameKeychainExists = (stat_result == 0);
1417 stat_result = ::stat(shortnameDotKeychain.c_str(), &st);
1418 shortnameDotKeychainExists = (stat_result == 0);
1419 stat_result = ::stat(loginDotKeychain.c_str(), &st);
1420 loginKeychainExists = (stat_result == 0);
1421 stat_result = ::stat(loginRenamed1Keychain.c_str(), &st);
1422 loginRenamed1KeychainExists = (stat_result == 0);
1423 stat_result = ::stat(loginKeychainDb.c_str(), &st);
1424 loginKeychainDbExists = (stat_result == 0);
1425 }
1426
1427 // login.keychain-db is considered to be the same as login.keychain.
1428 // Our transparent keychain promotion on open will handle opening the right version of this file.
1429 loginKeychainExists |= loginKeychainDbExists;
1430
1431 bool loginUnlocked = false;
1432
1433 // make the keychain identifiers
1434 CSSM_VERSION version = {0, 0};
1435 DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL);
1436 DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL);
1437 DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL);
1438
1439 //***************************************************************
1440 // make file renaming changes first
1441 //***************************************************************
1442
1443 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1444 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1445 if (shortnameKeychainExists) {
1446 int rename_stat = 0;
1447 if (loginKeychainExists) {
1448 struct stat st;
1449 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1450 if (tmp_result == 0) {
1451 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1452 tmp_result = ::unlink(loginDotKeychain.c_str());
1453 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1454 shortnameKeychainExists = (rename_stat != 0);
1455 }
1456 }
1457 }
1458 if (shortnameKeychainExists) {
1459 if (loginKeychainExists && !shortnameDotKeychainExists) {
1460 rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str());
1461 shortnameDotKeychainExists = (rename_stat == 0);
1462 } else if (!loginKeychainExists) {
1463 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1464 loginKeychainExists = (rename_stat == 0);
1465 } else {
1466 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1467 // on Leopard we never want a shortname keychain, so we must move it aside.
1468 char pathbuf[MAXPATHLEN];
1469 std::string shortnameRenamedXXXKeychain = keychainPath;
1470 shortnameRenamedXXXKeychain += userName;
1471 shortnameRenamedXXXKeychain += "_renamed_XXX.keychain";
1472 ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1473 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1474 rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf);
1475 shortnameKeychainExists = (rename_stat != 0);
1476 }
1477 }
1478 if (rename_stat != 0) {
1479 MacOSError::throwMe(errno);
1480 }
1481 }
1482
1483 //***************************************************************
1484 // handle special case where user previously reset the keychain
1485 //***************************************************************
1486 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1487 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1488 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1489 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1490 // "shortname.keychain" if it is not.
1491
1492 if (loginRenamed1KeychainExists && (!loginKeychainExists ||
1493 (mSavedList.searchList().size() == 1 && mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) )) {
1494 try
1495 {
1496 Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier));
1497 secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1498 (loginRenamed1KC) ? loginRenamed1KC->name() : "<NULL>");
1499 loginRenamed1KC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1500 // if we get here, we unlocked it
1501 if (loginKeychainExists) {
1502 struct stat st;
1503 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1504 if (tmp_result == 0) {
1505 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1506 tmp_result = ::unlink(loginDotKeychain.c_str());
1507 tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1508 } else if (!shortnameDotKeychainExists) {
1509 tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str());
1510 shortnameDotKeychainExists = (tmp_result == 0);
1511 } else {
1512 throw 1; // can't do anything with it except move it out of the way
1513 }
1514 }
1515 } else {
1516 int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1517 loginKeychainExists = (tmp_result == 0);
1518 }
1519 }
1520 catch(...)
1521 {
1522 // we failed to unlock the login_renamed1.keychain file with the login password.
1523 // move it aside so we don't try to deal with it again.
1524 char pathbuf[MAXPATHLEN];
1525 std::string loginRenamedXXXKeychain = keychainPath;
1526 loginRenamedXXXKeychain += "login_renamed_XXX.keychain";
1527 ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1528 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1529 ::rename(loginRenamed1Keychain.c_str(), pathbuf);
1530 }
1531 }
1532
1533 // is it token login?
1534 CFRef<CFDictionaryRef> tokenLoginContext;
1535 CFRef<CFStringRef> smartCardPassword;
1536 OSStatus tokenContextStatus = TokenLoginGetContext(password, passwordLength, tokenLoginContext.take());
1537 // if login.keychain does not exist at this point, create it
1538 if (!loginKeychainExists || (isReset && !loginKeychainDbExists)) {
1539 // when we creating new KC and user is logged using token (i.e. smart card), we have to get
1540 // the password for that account first
1541 if (tokenContextStatus == errSecSuccess) {
1542 secnotice("KCLogin", "Going to create login keychain for sc login");
1543 AuthorizationRef authRef;
1544 OSStatus status = AuthorizationCreate(NULL, NULL, 0, &authRef);
1545 if (status == errSecSuccess) {
1546 AuthorizationItem right = { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL, 0 };
1547 AuthorizationItemSet rightSet = { 1, &right };
1548
1549 uint32_t reason, tries;
1550 reason = 0;
1551 tries = 0;
1552 AuthorizationItem envRights[] = {
1553 { AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 },
1554 { AGENT_HINT_TRIES, sizeof(tries), &tries, 0 }};
1555
1556 AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights };
1557 status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL);
1558 if (status == errSecSuccess) {
1559 AuthorizationItemSet *returnedInfo;
1560 status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo);
1561 if (status == errSecSuccess) {
1562 if (returnedInfo && (returnedInfo->count > 0)) {
1563 for (uint32_t index = 0; index < returnedInfo->count; index++) {
1564 AuthorizationItem &item = returnedInfo->items[index];
1565 if (!strcmp(AGENT_PASSWORD, item.name)) {
1566 CFIndex len = item.valueLength;
1567 if (len) {
1568 secnotice("KCLogin", "User entered pwd");
1569 smartCardPassword = CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8 *)item.value, (CFIndex)len, kCFStringEncodingUTF8, TRUE);
1570 memset(item.value, 0, len);
1571 }
1572 }
1573 }
1574 }
1575 }
1576 if(returnedInfo) {
1577 AuthorizationFreeItemSet(returnedInfo);
1578 }
1579 }
1580 AuthorizationFree(authRef, 0);
1581 }
1582 }
1583
1584 // but don't add it to the search list yet; we'll do that later
1585 Keychain theKeychain = makeKeychain(loginDLDbIdentifier, false, true);
1586 secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1587 if (tokenContextStatus == errSecSuccess) {
1588 if (smartCardPassword.get()) {
1589 CFIndex length = CFStringGetLength(smartCardPassword);
1590 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1591 char *buffer = (char *)malloc(maxSize);
1592 if (CFStringGetCString(smartCardPassword, buffer, maxSize, kCFStringEncodingUTF8)) {
1593 secnotice("KCLogin", "Keychain is created using password provided by sc user");
1594 theKeychain->create((UInt32)strlen(buffer), buffer);
1595 memset(buffer, 0, maxSize);
1596 } else {
1597 secnotice("KCLogin", "Conversion failed");
1598 MacOSError::throwMe(errSecNotAvailable);
1599 }
1600 } else {
1601 secnotice("KCLogin", "User did not provide kc password");
1602 MacOSError::throwMe(errSecNotAvailable);
1603 }
1604 } else {
1605 theKeychain->create(passwordLength, password);
1606 }
1607 secnotice("KCLogin", "Login keychain created successfully");
1608 loginKeychainExists = true;
1609 // Set the prefs for this new login keychain.
1610 loginKeychain(theKeychain);
1611 // Login Keychain does not lock on sleep nor lock after timeout by default.
1612 theKeychain->setSettings(INT_MAX, false);
1613 loginUnlocked = true;
1614 mSavedList.revert(true);
1615 }
1616
1617 //***************************************************************
1618 // make plist changes after files have been renamed or created
1619 //***************************************************************
1620
1621 // if the shortname keychain exists in the search list, either rename or remove the entry
1622 if (mSavedList.member(demungeDLDbIdentifier(shortnameDLDbIdentifier))) {
1623 if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1624 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1625 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1626 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1627 (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>");
1628 mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1629 demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1630 } else if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1631 // change shortname to login.keychain
1632 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1633 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1634 (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1635 mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1636 demungeDLDbIdentifier(loginDLDbIdentifier));
1637 } else {
1638 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1639 // so just remove the entry
1640 secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>");
1641 mSavedList.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier));
1642 }
1643
1644 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1645 mSavedList.save();
1646 mSavedList.revert(true);
1647 }
1648
1649 // make sure that login.keychain is in the search list
1650 if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1651 secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1652 mSavedList.add(demungeDLDbIdentifier(loginDLDbIdentifier));
1653 mSavedList.save();
1654 mSavedList.revert(true);
1655 }
1656
1657 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1658 if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1659 mSavedList.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1660 mSavedList.save();
1661 mSavedList.revert(true);
1662 }
1663
1664 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1665 if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) {
1666 secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1667 mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier));
1668 mSavedList.save();
1669 mSavedList.revert(true);
1670 }
1671
1672 //***************************************************************
1673 // auto-unlock the login keychain(s)
1674 //***************************************************************
1675 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1676
1677 OSStatus loginResult = errSecSuccess;
1678 if (!loginUnlocked) {
1679 try
1680 {
1681 Keychain theKeychain(keychain(loginDLDbIdentifier));
1682 secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1683 (theKeychain) ? theKeychain->name() : "<NULL>");
1684 theKeychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
1685 loginUnlocked = true;
1686 }
1687 catch(const CssmError &e)
1688 {
1689 loginResult = e.osStatus(); // save this result
1690 }
1691 }
1692
1693 if (!loginUnlocked || tokenContextStatus == errSecSuccess) {
1694 Keychain theKeychain(keychain(loginDLDbIdentifier));
1695 bool tokenLoginDataUpdated = false;
1696
1697 for (UInt32 i = 0; i < 2; i++) {
1698 loginResult = errSecSuccess;
1699
1700 CFRef<CFDictionaryRef> tokenLoginData;
1701 if (tokenLoginContext) {
1702 OSStatus status = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1703 if (status != errSecSuccess) {
1704 if (tokenLoginDataUpdated) {
1705 loginResult = status;
1706 break;
1707 }
1708 // updating unlock key fails if it is not token login
1709 secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status);
1710 status = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1711 if (status == errSecSuccess) {
1712 loginResult = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1713 if (loginResult != errSecSuccess) {
1714 break;
1715 }
1716 tokenLoginDataUpdated = true;
1717 }
1718 }
1719 }
1720
1721 try {
1722 // first try to unlock login keychain because if this fails, token keychain unlock fails as well
1723 if (tokenLoginData) {
1724 secnotice("KCLogin", "Going to unlock keybag using scBlob");
1725 OSStatus status = TokenLoginUnlockKeybag(tokenLoginContext, tokenLoginData);
1726 secnotice("KCLogin", "Keybag unlock result %d", (int)status);
1727 if (status)
1728 CssmError::throwMe(status); // to trigger login data regeneration
1729 }
1730
1731 // build a fake key
1732 CssmKey key;
1733 key.header().BlobType = CSSM_KEYBLOB_RAW;
1734 key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
1735 key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY;
1736 key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
1737 key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE;
1738 key.header().KeyAttr = 0;
1739 CFRef<CFDataRef> tokenLoginUnlockKey;
1740 if (tokenLoginData) {
1741 OSStatus status = TokenLoginGetUnlockKey(tokenLoginContext, tokenLoginUnlockKey.take());
1742 if (status)
1743 CssmError::throwMe(status); // to trigger login data regeneration
1744 key.KeyData = CssmData(tokenLoginUnlockKey.get());
1745 } else {
1746 key.KeyData = CssmData(const_cast<void *>(password), passwordLength);
1747 }
1748 // unwrap it into the CSP (but keep it raw)
1749 UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE);
1750 CssmKey masterKey;
1751 CssmData descriptiveData;
1752 unwrap(key,
1753 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE),
1754 masterKey, &descriptiveData, NULL);
1755
1756 CssmClient::Db db = theKeychain->database();
1757
1758 // create the keychain, using appropriate credentials
1759 Allocator &alloc = db->allocator();
1760 AutoCredentials cred(alloc); // will leak, but we're quitting soon :-)
1761
1762 // use this passphrase
1763 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
1764 new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY),
1765 new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())),
1766 new(alloc) ListElement(CssmData::wrap(masterKey)),
1767 new(alloc) ListElement(CssmData()));
1768 db->authenticate(CSSM_DB_ACCESS_READ, &cred);
1769 db->unlock();
1770 loginUnlocked = true;
1771 } catch (const CssmError &e) {
1772 if (tokenLoginData && !tokenLoginDataUpdated) {
1773 // token login unlock key was invalid
1774 loginResult = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1775 if (loginResult == errSecSuccess) {
1776 tokenLoginDataUpdated = true;
1777 continue;
1778 }
1779 }
1780 else {
1781 loginResult = e.osStatus();
1782 }
1783 }
1784 break;
1785 }
1786 }
1787
1788 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1789 if (shortnameDotKeychainExists && mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1790 try
1791 {
1792 Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier));
1793 secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1794 (shortnameDotKC) ? shortnameDotKC->name() : "<NULL>");
1795 shortnameDotKC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1796 }
1797 catch(const CssmError &e)
1798 {
1799 // ignore; failure to unlock this keychain is not considered an error
1800 }
1801 }
1802
1803 if (loginResult != errSecSuccess) {
1804 MacOSError::throwMe(loginResult);
1805 }
1806 }
1807
1808 void StorageManager::stashLogin()
1809 {
1810 OSStatus loginResult = errSecSuccess;
1811
1812 DLDbIdentifier loginDLDbIdentifier;
1813 {
1814 mSavedList.revert(true);
1815 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1816 }
1817
1818 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1819 if (!loginDLDbIdentifier)
1820 MacOSError::throwMe(errSecNoSuchKeychain);
1821
1822 try
1823 {
1824 CssmData empty;
1825 Keychain theKeychain(keychain(loginDLDbIdentifier));
1826 secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1827 (theKeychain) ? theKeychain->name() : "<NULL>");
1828 theKeychain->stashCheck();
1829 }
1830 catch(const CssmError &e)
1831 {
1832 loginResult = e.osStatus(); // save this result
1833 }
1834
1835
1836 if (loginResult != errSecSuccess) {
1837 MacOSError::throwMe(loginResult);
1838 }
1839 }
1840
1841 void StorageManager::stashKeychain()
1842 {
1843 OSStatus loginResult = errSecSuccess;
1844
1845 DLDbIdentifier loginDLDbIdentifier;
1846 {
1847 mSavedList.revert(true);
1848 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1849 }
1850
1851 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1852 if (!loginDLDbIdentifier)
1853 MacOSError::throwMe(errSecNoSuchKeychain);
1854
1855 try
1856 {
1857 Keychain theKeychain(keychain(loginDLDbIdentifier));
1858 secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1859 (theKeychain) ? theKeychain->name() : "<NULL>");
1860 theKeychain->stash();
1861 }
1862 catch(const CssmError &e)
1863 {
1864 loginResult = e.osStatus(); // save this result
1865 }
1866
1867
1868 if (loginResult != errSecSuccess) {
1869 MacOSError::throwMe(loginResult);
1870 }
1871 }
1872
1873 void StorageManager::logout()
1874 {
1875 // nothing left to do here
1876 }
1877
1878 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
1879 {
1880 StLock<Mutex>_(mMutex);
1881
1882 loginKeychain()->changePassphrase(oldPassword, newPassword);
1883 secnotice("KClogin", "Changed login keychain password successfully");
1884 }
1885
1886
1887 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
1888 {
1889 StLock<Mutex>_(mMutex);
1890
1891 loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
1892 secnotice("KClogin", "Changed login keychain password successfully");
1893 }
1894
1895 // Clear out the keychain search list and rename the existing login.keychain.
1896 //
1897 void StorageManager::resetKeychain(Boolean resetSearchList)
1898 {
1899 StLock<Mutex>_(mMutex);
1900
1901 // Clear the keychain search list.
1902 Keychain keychain = NULL;
1903 DLDbIdentifier dldbi;
1904 try
1905 {
1906 if ( resetSearchList )
1907 {
1908 StorageManager::KeychainList keychainList;
1909 setSearchList(keychainList);
1910 }
1911 // Get a reference to the existing login keychain...
1912 // If we don't have one, we throw (not requiring a rename).
1913 //
1914 keychain = loginKeychain();
1915 } catch(const CommonError& e) {
1916 secnotice("KClogin", "Failed to open login keychain due to an error: %s", e.what());
1917
1918 // Set up fallback rename.
1919 dldbi = loginKeychainDLDbIdentifer();
1920
1921 struct stat exists;
1922 if(::stat(dldbi.dbName(), &exists) != 0) {
1923 // no file exists, everything is fine
1924 secnotice("KClogin", "no file exists; resetKeychain() is done");
1925 return;
1926 }
1927 }
1928
1929 try{
1930 //
1931 // Rename the existing login.keychain (i.e. put it aside).
1932 //
1933 CFMutableStringRef newName = NULL;
1934 newName = CFStringCreateMutable(NULL, 0);
1935 CFStringRef currName = NULL;
1936 if(keychain) {
1937 currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8);
1938 } else {
1939 currName = CFStringCreateWithCString(NULL, dldbi.dbName(), kCFStringEncodingUTF8);
1940 }
1941 if ( newName && currName )
1942 {
1943 CFStringAppend(newName, currName);
1944 CFStringRef kcSuffix = CFSTR(kKeychainSuffix);
1945 CFStringRef kcDbSuffix = CFSTR(kKeychainDbSuffix);
1946 bool hasDbSuffix = false;
1947 if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension
1948 {
1949 CFRange suffixRange = CFStringFind(newName, kcSuffix, 0);
1950 CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0);
1951 }
1952 if (CFStringHasSuffix(newName, kcDbSuffix)) {
1953 hasDbSuffix = true;
1954 CFRange suffixRange = CFStringFind(newName, kcDbSuffix, 0);
1955 CFStringFindAndReplace(newName, kcDbSuffix, CFSTR(""), suffixRange, 0);
1956 }
1957
1958 CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_"
1959 try
1960 {
1961 secnotice("KClogin", "attempting keychain rename to %@", newName);
1962 renameUnique(keychain, currName, newName, hasDbSuffix);
1963 }
1964 catch(const CommonError& e)
1965 {
1966 // we need to release 'newName' & 'currName'
1967 secnotice("KClogin", "Failed to renameUnique due to an error: %s", e.what());
1968 }
1969 catch(...)
1970 {
1971 secnotice("KClogin", "Failed to renameUnique due to an unknown error");
1972 }
1973 } // else, let the login call report a duplicate
1974 else {
1975 secnotice("KClogin", "don't have paths, quitting");
1976 }
1977 if ( newName )
1978 CFRelease(newName);
1979 if ( currName )
1980 CFRelease(currName);
1981 }
1982 catch(const CommonError& e) {
1983 secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e.what());
1984 }
1985 catch(...)
1986 {
1987 // We either don't have a login keychain, or there was a
1988 // failure to rename the existing one.
1989 secnotice("KClogin", "Failed to reset keychain due to an unknown error");
1990 }
1991 }
1992
1993 #pragma mark ____ File Related ____
1994
1995 Keychain StorageManager::make(const char *pathName)
1996 {
1997 return make(pathName, true);
1998 }
1999
2000 Keychain StorageManager::make(const char *pathName, bool add)
2001 {
2002 return make(pathName, add, false);
2003 }
2004
2005 Keychain StorageManager::make(const char *pathName, bool add, bool isReset) {
2006 return makeKeychain(makeDLDbIdentifier(pathName), add, isReset);
2007 }
2008
2009 DLDbIdentifier StorageManager::makeDLDbIdentifier(const char *pathName) {
2010 StLock<Mutex>_(mMutex);
2011
2012 string fullPathName;
2013 if ( pathName[0] == '/' )
2014 fullPathName = pathName;
2015 else
2016 {
2017 // Get Home directory from environment.
2018 switch (mDomain)
2019 {
2020 case kSecPreferencesDomainUser:
2021 {
2022 const char *homeDir = getenv("HOME");
2023 if (homeDir == NULL)
2024 {
2025 // If $HOME is unset get the current user's home directory
2026 // from the passwd file.
2027 uid_t uid = geteuid();
2028 if (!uid) uid = getuid();
2029 struct passwd *pw = getpwuid(uid);
2030 if (!pw)
2031 MacOSError::throwMe(errSecParam);
2032 homeDir = pw->pw_dir;
2033 }
2034 fullPathName = homeDir;
2035 }
2036 break;
2037 case kSecPreferencesDomainSystem:
2038 fullPathName = "";
2039 break;
2040 default:
2041 assert(false); // invalid domain for this
2042 }
2043
2044 fullPathName += "/Library/Keychains/";
2045 fullPathName += pathName;
2046 }
2047
2048 const CSSM_NET_ADDRESS *DbLocation = NULL; // NULL for keychains
2049 const CSSM_VERSION *version = NULL;
2050 uint32 subserviceId = 0;
2051 CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
2052 const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
2053 subserviceId, subserviceType);
2054 DLDbIdentifier dlDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
2055 return dlDbIdentifier;
2056 }
2057
2058 Keychain StorageManager::makeLoginAuthUI(const Item *item, bool isReset)
2059 {
2060 StLock<Mutex>_(mMutex);
2061
2062 // Create a login/default keychain for the user using UI.
2063 // The user can cancel out of the operation, or create a new login keychain.
2064 // If auto-login is turned off, the user will be asked for their login password.
2065 //
2066 OSStatus result = errSecSuccess;
2067 Keychain keychain; // We return this keychain.
2068 //
2069 // Set up the Auth ref to bring up UI.
2070 //
2071 AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL;
2072 AuthorizationRef authRef = NULL;
2073 try
2074 {
2075 result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef);
2076 if ( result )
2077 MacOSError::throwMe(result);
2078
2079 AuthorizationEnvironment envir;
2080 envir.count = 6; // up to 6 hints can be used.
2081 authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count);
2082 if ( !authEnvirItemArrayPtr )
2083 MacOSError::throwMe(errAuthorizationInternal);
2084
2085 currItem = envir.items = authEnvirItemArrayPtr;
2086
2087 //
2088 // 1st Hint (optional): The keychain item's account attribute string.
2089 // When item is specified, we assume an 'add' operation is being attempted.
2090 char buff[256];
2091 UInt32 actLen = 0;
2092 SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
2093 if ( item )
2094 {
2095 try
2096 {
2097 (*item)->getAttribute(attr, &actLen);
2098 }
2099 catch(...)
2100 {
2101 actLen = 0; // This item didn't have the account attribute, so don't display one in the UI.
2102 }
2103 }
2104 currItem->name = AGENT_HINT_ATTR_NAME; // name str that identifies this hint as attr name
2105 if ( actLen ) // Fill in the hint if we have an account attr
2106 {
2107 if ( actLen >= sizeof(buff) )
2108 buff[sizeof(buff)-1] = 0;
2109 else
2110 buff[actLen] = 0;
2111 currItem->valueLength = strlen(buff)+1;
2112 currItem->value = buff;
2113 }
2114 else
2115 {
2116 currItem->valueLength = 0;
2117 currItem->value = NULL;
2118 }
2119 currItem->flags = 0;
2120
2121 //
2122 // 2nd Hint (optional): The item's keychain full path.
2123 //
2124 currItem++;
2125 char* currDefaultName = NULL;
2126 try
2127 {
2128 currDefaultName = (char*)defaultKeychain()->name(); // Use the name if we have it.
2129 currItem->name = AGENT_HINT_LOGIN_KC_NAME; // Name str that identifies this hint as kc path
2130 currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0;
2131 currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)"";
2132 currItem->flags = 0;
2133 currItem++;
2134 }
2135 catch(...)
2136 {
2137 envir.count--;
2138 }
2139
2140 //
2141 // 3rd Hint (required): check if curr default keychain is unavailable.
2142 // This is determined by the parent not existing.
2143 //
2144 currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER;
2145 Boolean loginUnavail = false;
2146 try
2147 {
2148 Keychain defaultKC = defaultKeychain();
2149 if ( !defaultKC->exists() )
2150 loginUnavail = true;
2151 }
2152 catch(...) // login.keychain not present
2153 {
2154 }
2155 currItem->valueLength = sizeof(Boolean);
2156 currItem->value = (void*)&loginUnavail;
2157 currItem->flags = 0;
2158
2159 //
2160 // 4th Hint (required): userName
2161 //
2162 currItem++;
2163 currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME;
2164 char* uName = getenv("USER");
2165 string userName = uName ? uName : "";
2166 if ( userName.length() == 0 )
2167 {
2168 uid_t uid = geteuid();
2169 if (!uid) uid = getuid();
2170 struct passwd *pw = getpwuid(uid); // fallback case...
2171 if (pw)
2172 userName = pw->pw_name;
2173 endpwent();
2174 }
2175 if ( userName.length() == 0 ) // did we ultimately get one?
2176 MacOSError::throwMe(errAuthorizationInternal);
2177
2178 currItem->value = (void*)userName.c_str();
2179 currItem->valueLength = userName.length();
2180 currItem->flags = 0;
2181
2182 //
2183 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2184 //
2185 currItem++;
2186 currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
2187 Boolean moreThanOneKCExists = false;
2188 {
2189 // if item is NULL, then this is a user-initiated full reset
2190 if (item && mSavedList.searchList().size() > 1)
2191 moreThanOneKCExists = true;
2192 }
2193 currItem->value = &moreThanOneKCExists;
2194 currItem->valueLength = sizeof(Boolean);
2195 currItem->flags = 0;
2196
2197 //
2198 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2199 // We want to suppress the "do you want to reset to defaults?" panel in this case.
2200 //
2201 currItem++;
2202 currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL;
2203 Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE;
2204 currItem->valueLength = sizeof(Boolean);
2205 currItem->value = (void*)&suppressResetPanel;
2206 currItem->flags = 0;
2207
2208 //
2209 // Set up the auth rights and make the auth call.
2210 //
2211 AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 };
2212 AuthorizationRights rights = { 1, &authItem };
2213 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
2214 result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL);
2215 if ( result )
2216 MacOSError::throwMe(result);
2217 try
2218 {
2219 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2220 }
2221 catch (...) // can throw if no existing login.keychain is found
2222 {
2223 }
2224 login(authRef, (UInt32)userName.length(), userName.c_str(), isReset); // Create login.keychain
2225 keychain = loginKeychain(); // Get newly-created login keychain
2226 defaultKeychain(keychain); // Set it to be the default
2227
2228 free(authEnvirItemArrayPtr);
2229 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2230 }
2231
2232 catch (...)
2233 {
2234 // clean up allocations, then rethrow error
2235 if ( authEnvirItemArrayPtr )
2236 free(authEnvirItemArrayPtr);
2237 if ( authRef )
2238 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2239 throw;
2240 }
2241
2242 return keychain;
2243 }
2244
2245 Keychain StorageManager::defaultKeychainUI(Item &item)
2246 {
2247 StLock<Mutex>_(mMutex);
2248
2249 Keychain returnedKeychain;
2250 try
2251 {
2252 returnedKeychain = defaultKeychain(); // If we have one, return it.
2253 if ( returnedKeychain->exists() )
2254 return returnedKeychain;
2255 }
2256 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
2257 {
2258 }
2259 if ( globals().getUserInteractionAllowed() )
2260 {
2261 returnedKeychain = makeLoginAuthUI(&item, false); // If no Keychains is present, one will be created.
2262 if ( !returnedKeychain )
2263 MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong...
2264 }
2265 else
2266 MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error.
2267
2268 return returnedKeychain;
2269 }
2270
2271 void
2272 StorageManager::addToDomainList(SecPreferencesDomain domain,
2273 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2274 {
2275 StLock<Mutex>_(mMutex);
2276
2277 if (domain == kSecPreferencesDomainDynamic)
2278 MacOSError::throwMe(errSecInvalidPrefsDomain);
2279
2280 // make the identifier
2281 CSSM_VERSION version = {0, 0};
2282 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2283 subServiceType, dbName, NULL);
2284
2285 if (domain == mDomain)
2286 {
2287 // manipulate the user's list
2288 {
2289 mSavedList.revert(true);
2290 mSavedList.add(demungeDLDbIdentifier(id));
2291 mSavedList.save();
2292 }
2293
2294 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2295 }
2296 else
2297 {
2298 // manipulate the other list
2299 DLDbListCFPref(domain).add(id);
2300 }
2301 }
2302
2303 void
2304 StorageManager::isInDomainList(SecPreferencesDomain domain,
2305 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2306 {
2307 StLock<Mutex>_(mMutex);
2308
2309 if (domain == kSecPreferencesDomainDynamic)
2310 MacOSError::throwMe(errSecInvalidPrefsDomain);
2311
2312 CSSM_VERSION version = {0, 0};
2313 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2314 subServiceType, dbName, NULL);
2315
2316 // determine the list to search
2317 bool result;
2318 if (domain == mDomain)
2319 {
2320 result = mSavedList.member(demungeDLDbIdentifier(id));
2321 }
2322 else
2323 {
2324 result = DLDbListCFPref(domain).member(demungeDLDbIdentifier(id));
2325 }
2326
2327 // do the search
2328 if (!result)
2329 {
2330 MacOSError::throwMe(errSecNoSuchKeychain);
2331 }
2332 }
2333
2334 void
2335 StorageManager::removeFromDomainList(SecPreferencesDomain domain,
2336 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2337 {
2338 StLock<Mutex>_(mMutex);
2339
2340 if (domain == kSecPreferencesDomainDynamic)
2341 MacOSError::throwMe(errSecInvalidPrefsDomain);
2342
2343 // make the identifier
2344 CSSM_VERSION version = {0, 0};
2345 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2346 subServiceType, dbName, NULL);
2347
2348 if (domain == mDomain)
2349 {
2350 // manipulate the user's list
2351 {
2352 mSavedList.revert(true);
2353 mSavedList.remove(demungeDLDbIdentifier(id));
2354 mSavedList.save();
2355 }
2356
2357 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2358 }
2359 else
2360 {
2361 // manipulate the other list
2362 DLDbListCFPref(domain).remove(id);
2363 }
2364 }
2365
2366 bool
2367 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain)
2368 {
2369 struct stat sb;
2370 mode_t perms;
2371 const char* sysPrefDir = "/Library/Preferences";
2372 const char* errMsg = "Will not set default";
2373 char* mustOwnDir = NULL;
2374 struct passwd* pw = NULL;
2375
2376 // get my uid
2377 uid_t uid = geteuid();
2378 if (!uid) uid = getuid();
2379
2380 // our (e)uid must own the appropriate preferences or home directory
2381 // for the specified preference domain whose default we will be modifying
2382 switch (domain) {
2383 case kSecPreferencesDomainUser:
2384 mustOwnDir = getenv("HOME");
2385 if (mustOwnDir == NULL) {
2386 pw = getpwuid(uid);
2387 if (!pw) return false;
2388 mustOwnDir = pw->pw_dir;
2389 }
2390 break;
2391 case kSecPreferencesDomainSystem:
2392 mustOwnDir = (char*)sysPrefDir;
2393 break;
2394 case kSecPreferencesDomainCommon:
2395 mustOwnDir = (char*)sysPrefDir;
2396 break;
2397 default:
2398 return false;
2399 }
2400
2401 if (mustOwnDir != NULL) {
2402 struct stat dsb;
2403 if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) {
2404 fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir);
2405 mustOwnDir = NULL; // will return below after calling endpwent()
2406 }
2407 }
2408
2409 if (pw != NULL)
2410 endpwent();
2411
2412 if (mustOwnDir == NULL)
2413 return false;
2414
2415 // check that file actually exists
2416 if (stat(path, &sb) != 0) {
2417 fprintf(stderr, "%s: file %s does not exist\n", errMsg, path);
2418 return false;
2419 }
2420
2421 // check flags
2422 if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
2423 fprintf(stderr, "%s: file %s is immutable\n", errMsg, path);
2424 return false;
2425 }
2426
2427 // check ownership
2428 if (sb.st_uid != uid) {
2429 fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2430 errMsg, path, (int)sb.st_uid, (int)uid);
2431 return false;
2432 }
2433
2434 // check mode
2435 perms = sb.st_mode;
2436 perms |= 0600; // must have owner read/write permission set
2437 if (sb.st_mode != perms) {
2438 fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path);
2439 return false;
2440 }
2441
2442 // user owns file and can read/write it
2443 return true;
2444 }