]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/StorageManager.cpp
Security-59754.80.3.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
1363 login(name[0], name + 1, password[0], password + 1, false);
1364 }
1365
1366 void StorageManager::login(UInt32 nameLength, const void *name,
1367 UInt32 passwordLength, const void *password, bool isReset)
1368 {
1369 if (passwordLength != 0 && password == NULL)
1370 {
1371 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1372 MacOSError::throwMe(errSecParam);
1373 }
1374
1375 DLDbIdentifier loginDLDbIdentifier;
1376 {
1377 mSavedList.revert(true);
1378 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1379 }
1380
1381 secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1382 if (!loginDLDbIdentifier)
1383 MacOSError::throwMe(errSecNoSuchKeychain);
1384
1385
1386 //***************************************************************
1387 // gather keychain information
1388 //***************************************************************
1389
1390 // user name
1391 int uid = geteuid();
1392 struct passwd *pw = getpwuid(uid);
1393 if (pw == NULL) {
1394 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1395 MacOSError::throwMe(errSecParam);
1396 }
1397 std::string userName = pw->pw_name;
1398
1399 // make keychain path strings
1400 std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix);
1401 std::string shortnameKeychain = keychainPath + userName;
1402 std::string shortnameDotKeychain = shortnameKeychain + ".keychain";
1403 std::string loginDotKeychain = keychainPath + "login.keychain";
1404 std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain";
1405 std::string loginKeychainDb = keychainPath + "login.keychain-db";
1406
1407 // check for existence of keychain files
1408 bool shortnameKeychainExists = false;
1409 bool shortnameDotKeychainExists = false;
1410 bool loginKeychainExists = false;
1411 bool loginRenamed1KeychainExists = false;
1412 bool loginKeychainDbExists = false;
1413 {
1414 struct stat st;
1415 int stat_result;
1416 stat_result = ::stat(shortnameKeychain.c_str(), &st);
1417 shortnameKeychainExists = (stat_result == 0);
1418 stat_result = ::stat(shortnameDotKeychain.c_str(), &st);
1419 shortnameDotKeychainExists = (stat_result == 0);
1420 stat_result = ::stat(loginDotKeychain.c_str(), &st);
1421 loginKeychainExists = (stat_result == 0);
1422 stat_result = ::stat(loginRenamed1Keychain.c_str(), &st);
1423 loginRenamed1KeychainExists = (stat_result == 0);
1424 stat_result = ::stat(loginKeychainDb.c_str(), &st);
1425 loginKeychainDbExists = (stat_result == 0);
1426 }
1427
1428 // login.keychain-db is considered to be the same as login.keychain.
1429 // Our transparent keychain promotion on open will handle opening the right version of this file.
1430 loginKeychainExists |= loginKeychainDbExists;
1431
1432 bool loginUnlocked = false;
1433
1434 // make the keychain identifiers
1435 CSSM_VERSION version = {0, 0};
1436 DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL);
1437 DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL);
1438 DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL);
1439
1440 //***************************************************************
1441 // make file renaming changes first
1442 //***************************************************************
1443
1444 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1445 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1446 if (shortnameKeychainExists) {
1447 int rename_stat = 0;
1448 if (loginKeychainExists) {
1449 struct stat st;
1450 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1451 if (tmp_result == 0) {
1452 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1453 tmp_result = ::unlink(loginDotKeychain.c_str());
1454 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1455 shortnameKeychainExists = (rename_stat != 0);
1456 }
1457 }
1458 }
1459 if (shortnameKeychainExists) {
1460 if (loginKeychainExists && !shortnameDotKeychainExists) {
1461 rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str());
1462 shortnameDotKeychainExists = (rename_stat == 0);
1463 } else if (!loginKeychainExists) {
1464 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1465 loginKeychainExists = (rename_stat == 0);
1466 } else {
1467 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1468 // on Leopard we never want a shortname keychain, so we must move it aside.
1469 char pathbuf[MAXPATHLEN];
1470 std::string shortnameRenamedXXXKeychain = keychainPath;
1471 shortnameRenamedXXXKeychain += userName;
1472 shortnameRenamedXXXKeychain += "_renamed_XXX.keychain";
1473 ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1474 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1475 rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf);
1476 shortnameKeychainExists = (rename_stat != 0);
1477 }
1478 }
1479 if (rename_stat != 0) {
1480 MacOSError::throwMe(errno);
1481 }
1482 }
1483
1484 //***************************************************************
1485 // handle special case where user previously reset the keychain
1486 //***************************************************************
1487 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1488 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1489 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1490 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1491 // "shortname.keychain" if it is not.
1492
1493 if (loginRenamed1KeychainExists && (!loginKeychainExists ||
1494 (mSavedList.searchList().size() == 1 && mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) )) {
1495 try
1496 {
1497 Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier));
1498 secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1499 (loginRenamed1KC) ? loginRenamed1KC->name() : "<NULL>");
1500 loginRenamed1KC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1501 // if we get here, we unlocked it
1502 if (loginKeychainExists) {
1503 struct stat st;
1504 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1505 if (tmp_result == 0) {
1506 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1507 tmp_result = ::unlink(loginDotKeychain.c_str());
1508 tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1509 } else if (!shortnameDotKeychainExists) {
1510 tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str());
1511 shortnameDotKeychainExists = (tmp_result == 0);
1512 } else {
1513 throw 1; // can't do anything with it except move it out of the way
1514 }
1515 }
1516 } else {
1517 int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1518 loginKeychainExists = (tmp_result == 0);
1519 }
1520 }
1521 catch(...)
1522 {
1523 // we failed to unlock the login_renamed1.keychain file with the login password.
1524 // move it aside so we don't try to deal with it again.
1525 char pathbuf[MAXPATHLEN];
1526 std::string loginRenamedXXXKeychain = keychainPath;
1527 loginRenamedXXXKeychain += "login_renamed_XXX.keychain";
1528 ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1529 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1530 ::rename(loginRenamed1Keychain.c_str(), pathbuf);
1531 }
1532 }
1533
1534 // is it token login?
1535 CFRef<CFDictionaryRef> tokenLoginContext;
1536 CFRef<CFStringRef> smartCardPassword;
1537 OSStatus tokenContextStatus = TokenLoginGetContext(password, passwordLength, tokenLoginContext.take());
1538 // if login.keychain does not exist at this point, create it
1539 if (!loginKeychainExists || (isReset && !loginKeychainDbExists)) {
1540 // when we creating new KC and user is logged using token (i.e. smart card), we have to get
1541 // the password for that account first
1542 if (tokenContextStatus == errSecSuccess) {
1543 secnotice("KCLogin", "Going to create login keychain for sc login");
1544 AuthorizationRef authRef;
1545 OSStatus status = AuthorizationCreate(NULL, NULL, 0, &authRef);
1546 if (status == errSecSuccess) {
1547 AuthorizationItem right = { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL, 0 };
1548 AuthorizationItemSet rightSet = { 1, &right };
1549
1550 uint32_t reason, tries;
1551 reason = 0;
1552 tries = 0;
1553 AuthorizationItem envRights[] = {
1554 { AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 },
1555 { AGENT_HINT_TRIES, sizeof(tries), &tries, 0 }};
1556
1557 AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights };
1558 status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL);
1559 if (status == errSecSuccess) {
1560 AuthorizationItemSet *returnedInfo;
1561 status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo);
1562 if (status == errSecSuccess) {
1563 if (returnedInfo && (returnedInfo->count > 0)) {
1564 for (uint32_t index = 0; index < returnedInfo->count; index++) {
1565 AuthorizationItem &item = returnedInfo->items[index];
1566 if (!strcmp(AGENT_PASSWORD, item.name)) {
1567 CFIndex len = item.valueLength;
1568 if (len) {
1569 secnotice("KCLogin", "User entered pwd");
1570 smartCardPassword = CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8 *)item.value, (CFIndex)len, kCFStringEncodingUTF8, TRUE);
1571 memset(item.value, 0, len);
1572 }
1573 }
1574 }
1575 }
1576 }
1577 if(returnedInfo) {
1578 AuthorizationFreeItemSet(returnedInfo);
1579 }
1580 }
1581 AuthorizationFree(authRef, 0);
1582 }
1583 }
1584
1585 // but don't add it to the search list yet; we'll do that later
1586 Keychain theKeychain = makeKeychain(loginDLDbIdentifier, false, true);
1587 secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1588 if (tokenContextStatus == errSecSuccess) {
1589 if (smartCardPassword.get()) {
1590 CFIndex length = CFStringGetLength(smartCardPassword);
1591 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1592 char *buffer = (char *)malloc(maxSize);
1593 if (CFStringGetCString(smartCardPassword, buffer, maxSize, kCFStringEncodingUTF8)) {
1594 secnotice("KCLogin", "Keychain is created using password provided by sc user");
1595 theKeychain->create((UInt32)strlen(buffer), buffer);
1596 memset(buffer, 0, maxSize);
1597 } else {
1598 secnotice("KCLogin", "Conversion failed");
1599 MacOSError::throwMe(errSecNotAvailable);
1600 }
1601 } else {
1602 secnotice("KCLogin", "User did not provide kc password");
1603 MacOSError::throwMe(errSecNotAvailable);
1604 }
1605 } else {
1606 theKeychain->create(passwordLength, password);
1607 }
1608 secnotice("KCLogin", "Login keychain created successfully");
1609 loginKeychainExists = true;
1610 // Set the prefs for this new login keychain.
1611 loginKeychain(theKeychain);
1612 // Login Keychain does not lock on sleep nor lock after timeout by default.
1613 theKeychain->setSettings(INT_MAX, false);
1614 loginUnlocked = true;
1615 mSavedList.revert(true);
1616 }
1617
1618 //***************************************************************
1619 // make plist changes after files have been renamed or created
1620 //***************************************************************
1621
1622 // if the shortname keychain exists in the search list, either rename or remove the entry
1623 if (mSavedList.member(demungeDLDbIdentifier(shortnameDLDbIdentifier))) {
1624 if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1625 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1626 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1627 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1628 (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>");
1629 mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1630 demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1631 } else if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1632 // change shortname to login.keychain
1633 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1634 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1635 (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1636 mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1637 demungeDLDbIdentifier(loginDLDbIdentifier));
1638 } else {
1639 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1640 // so just remove the entry
1641 secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>");
1642 mSavedList.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier));
1643 }
1644
1645 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1646 mSavedList.save();
1647 mSavedList.revert(true);
1648 }
1649
1650 // make sure that login.keychain is in the search list
1651 if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1652 secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1653 mSavedList.add(demungeDLDbIdentifier(loginDLDbIdentifier));
1654 mSavedList.save();
1655 mSavedList.revert(true);
1656 }
1657
1658 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1659 if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1660 mSavedList.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1661 mSavedList.save();
1662 mSavedList.revert(true);
1663 }
1664
1665 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1666 if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) {
1667 secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1668 mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier));
1669 mSavedList.save();
1670 mSavedList.revert(true);
1671 }
1672
1673 //***************************************************************
1674 // auto-unlock the login keychain(s)
1675 //***************************************************************
1676 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1677
1678 OSStatus loginResult = errSecSuccess;
1679 if (!loginUnlocked) {
1680 try
1681 {
1682 Keychain theKeychain(keychain(loginDLDbIdentifier));
1683 secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1684 (theKeychain) ? theKeychain->name() : "<NULL>");
1685 theKeychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
1686 loginUnlocked = true;
1687 }
1688 catch(const CssmError &e)
1689 {
1690 loginResult = e.osStatus(); // save this result
1691 }
1692 }
1693
1694 if (!loginUnlocked || tokenContextStatus == errSecSuccess) {
1695 Keychain theKeychain(keychain(loginDLDbIdentifier));
1696 bool tokenLoginDataUpdated = false;
1697
1698 for (UInt32 i = 0; i < 2; i++) {
1699 loginResult = errSecSuccess;
1700
1701 CFRef<CFDictionaryRef> tokenLoginData;
1702 if (tokenLoginContext) {
1703 OSStatus status = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1704 if (status != errSecSuccess) {
1705 if (tokenLoginDataUpdated) {
1706 loginResult = status;
1707 break;
1708 }
1709 // updating unlock key fails if it is not token login
1710 secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status);
1711 status = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1712 if (status == errSecSuccess) {
1713 loginResult = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1714 if (loginResult != errSecSuccess) {
1715 break;
1716 }
1717 tokenLoginDataUpdated = true;
1718 }
1719 }
1720 }
1721
1722 try {
1723 // first try to unlock login keychain because if this fails, token keychain unlock fails as well
1724 if (tokenLoginData) {
1725 secnotice("KCLogin", "Going to unlock keybag using scBlob");
1726 OSStatus status = TokenLoginUnlockKeybag(tokenLoginContext, tokenLoginData);
1727 secnotice("KCLogin", "Keybag unlock result %d", (int)status);
1728 if (status)
1729 CssmError::throwMe(status); // to trigger login data regeneration
1730 }
1731
1732 // build a fake key
1733 CssmKey key;
1734 key.header().BlobType = CSSM_KEYBLOB_RAW;
1735 key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
1736 key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY;
1737 key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
1738 key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE;
1739 key.header().KeyAttr = 0;
1740 CFRef<CFDataRef> tokenLoginUnlockKey;
1741 if (tokenLoginData) {
1742 OSStatus status = TokenLoginGetUnlockKey(tokenLoginContext, tokenLoginUnlockKey.take());
1743 if (status)
1744 CssmError::throwMe(status); // to trigger login data regeneration
1745 key.KeyData = CssmData(tokenLoginUnlockKey.get());
1746 } else {
1747 key.KeyData = CssmData(const_cast<void *>(password), passwordLength);
1748 }
1749 // unwrap it into the CSP (but keep it raw)
1750 UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE);
1751 CssmKey masterKey;
1752 CssmData descriptiveData;
1753 unwrap(key,
1754 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE),
1755 masterKey, &descriptiveData, NULL);
1756
1757 CssmClient::Db db = theKeychain->database();
1758
1759 // create the keychain, using appropriate credentials
1760 Allocator &alloc = db->allocator();
1761 AutoCredentials cred(alloc); // will leak, but we're quitting soon :-)
1762
1763 // use this passphrase
1764 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
1765 new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY),
1766 new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())),
1767 new(alloc) ListElement(CssmData::wrap(masterKey)),
1768 new(alloc) ListElement(CssmData()));
1769 db->authenticate(CSSM_DB_ACCESS_READ, &cred);
1770 db->unlock();
1771 loginUnlocked = true;
1772 } catch (const CssmError &e) {
1773 if (tokenLoginData && !tokenLoginDataUpdated) {
1774 // token login unlock key was invalid
1775 loginResult = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1776 if (loginResult == errSecSuccess) {
1777 tokenLoginDataUpdated = true;
1778 continue;
1779 }
1780 }
1781 else {
1782 loginResult = e.osStatus();
1783 }
1784 }
1785 break;
1786 }
1787 }
1788
1789 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1790 if (shortnameDotKeychainExists && mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1791 try
1792 {
1793 Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier));
1794 secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1795 (shortnameDotKC) ? shortnameDotKC->name() : "<NULL>");
1796 shortnameDotKC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1797 }
1798 catch(const CssmError &e)
1799 {
1800 // ignore; failure to unlock this keychain is not considered an error
1801 }
1802 }
1803
1804 if (loginResult != errSecSuccess) {
1805 MacOSError::throwMe(loginResult);
1806 }
1807 }
1808
1809 void StorageManager::stashLogin()
1810 {
1811 OSStatus loginResult = errSecSuccess;
1812
1813 DLDbIdentifier loginDLDbIdentifier;
1814 {
1815 mSavedList.revert(true);
1816 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1817 }
1818
1819 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1820 if (!loginDLDbIdentifier)
1821 MacOSError::throwMe(errSecNoSuchKeychain);
1822
1823 try
1824 {
1825 CssmData empty;
1826 Keychain theKeychain(keychain(loginDLDbIdentifier));
1827 secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1828 (theKeychain) ? theKeychain->name() : "<NULL>");
1829 theKeychain->stashCheck();
1830 }
1831 catch(const CssmError &e)
1832 {
1833 loginResult = e.osStatus(); // save this result
1834 }
1835
1836
1837 if (loginResult != errSecSuccess) {
1838 MacOSError::throwMe(loginResult);
1839 }
1840 }
1841
1842 void StorageManager::stashKeychain()
1843 {
1844 OSStatus loginResult = errSecSuccess;
1845
1846 DLDbIdentifier loginDLDbIdentifier;
1847 {
1848 mSavedList.revert(true);
1849 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1850 }
1851
1852 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1853 if (!loginDLDbIdentifier)
1854 MacOSError::throwMe(errSecNoSuchKeychain);
1855
1856 try
1857 {
1858 Keychain theKeychain(keychain(loginDLDbIdentifier));
1859 secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1860 (theKeychain) ? theKeychain->name() : "<NULL>");
1861 theKeychain->stash();
1862 }
1863 catch(const CssmError &e)
1864 {
1865 loginResult = e.osStatus(); // save this result
1866 }
1867
1868
1869 if (loginResult != errSecSuccess) {
1870 MacOSError::throwMe(loginResult);
1871 }
1872 }
1873
1874 void StorageManager::logout()
1875 {
1876 // nothing left to do here
1877 }
1878
1879 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
1880 {
1881 StLock<Mutex>_(mMutex);
1882
1883 loginKeychain()->changePassphrase(oldPassword, newPassword);
1884 secnotice("KClogin", "Changed login keychain password successfully");
1885 }
1886
1887
1888 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
1889 {
1890 StLock<Mutex>_(mMutex);
1891
1892 loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
1893 secnotice("KClogin", "Changed login keychain password successfully");
1894 }
1895
1896 // Clear out the keychain search list and rename the existing login.keychain.
1897 //
1898 void StorageManager::resetKeychain(Boolean resetSearchList)
1899 {
1900 StLock<Mutex>_(mMutex);
1901
1902 // Clear the keychain search list.
1903 Keychain keychain = NULL;
1904 DLDbIdentifier dldbi;
1905 try
1906 {
1907 if ( resetSearchList )
1908 {
1909 StorageManager::KeychainList keychainList;
1910 setSearchList(keychainList);
1911 }
1912 // Get a reference to the existing login keychain...
1913 // If we don't have one, we throw (not requiring a rename).
1914 //
1915 keychain = loginKeychain();
1916 } catch(const CommonError& e) {
1917 secnotice("KClogin", "Failed to open login keychain due to an error: %s", e.what());
1918
1919 // Set up fallback rename.
1920 dldbi = loginKeychainDLDbIdentifer();
1921
1922 struct stat exists;
1923 if(::stat(dldbi.dbName(), &exists) != 0) {
1924 // no file exists, everything is fine
1925 secnotice("KClogin", "no file exists; resetKeychain() is done");
1926 return;
1927 }
1928 }
1929
1930 try{
1931 //
1932 // Rename the existing login.keychain (i.e. put it aside).
1933 //
1934 CFMutableStringRef newName = NULL;
1935 newName = CFStringCreateMutable(NULL, 0);
1936 CFStringRef currName = NULL;
1937 if(keychain) {
1938 currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8);
1939 } else {
1940 currName = CFStringCreateWithCString(NULL, dldbi.dbName(), kCFStringEncodingUTF8);
1941 }
1942 if ( newName && currName )
1943 {
1944 CFStringAppend(newName, currName);
1945 CFStringRef kcSuffix = CFSTR(kKeychainSuffix);
1946 CFStringRef kcDbSuffix = CFSTR(kKeychainDbSuffix);
1947 bool hasDbSuffix = false;
1948 if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension
1949 {
1950 CFRange suffixRange = CFStringFind(newName, kcSuffix, 0);
1951 CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0);
1952 }
1953 if (CFStringHasSuffix(newName, kcDbSuffix)) {
1954 hasDbSuffix = true;
1955 CFRange suffixRange = CFStringFind(newName, kcDbSuffix, 0);
1956 CFStringFindAndReplace(newName, kcDbSuffix, CFSTR(""), suffixRange, 0);
1957 }
1958
1959 CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_"
1960 try
1961 {
1962 secnotice("KClogin", "attempting keychain rename to %@", newName);
1963 renameUnique(keychain, currName, newName, hasDbSuffix);
1964 }
1965 catch(const CommonError& e)
1966 {
1967 // we need to release 'newName' & 'currName'
1968 secnotice("KClogin", "Failed to renameUnique due to an error: %s", e.what());
1969 }
1970 catch(...)
1971 {
1972 secnotice("KClogin", "Failed to renameUnique due to an unknown error");
1973 }
1974 } // else, let the login call report a duplicate
1975 else {
1976 secnotice("KClogin", "don't have paths, quitting");
1977 }
1978 if ( newName )
1979 CFRelease(newName);
1980 if ( currName )
1981 CFRelease(currName);
1982 }
1983 catch(const CommonError& e) {
1984 secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e.what());
1985 }
1986 catch(...)
1987 {
1988 // We either don't have a login keychain, or there was a
1989 // failure to rename the existing one.
1990 secnotice("KClogin", "Failed to reset keychain due to an unknown error");
1991 }
1992 }
1993
1994 #pragma mark ____ File Related ____
1995
1996 Keychain StorageManager::make(const char *pathName)
1997 {
1998 return make(pathName, true);
1999 }
2000
2001 Keychain StorageManager::make(const char *pathName, bool add)
2002 {
2003 return make(pathName, add, false);
2004 }
2005
2006 Keychain StorageManager::make(const char *pathName, bool add, bool isReset) {
2007 return makeKeychain(makeDLDbIdentifier(pathName), add, isReset);
2008 }
2009
2010 DLDbIdentifier StorageManager::makeDLDbIdentifier(const char *pathName) {
2011 StLock<Mutex>_(mMutex);
2012
2013 string fullPathName;
2014 if ( pathName[0] == '/' )
2015 fullPathName = pathName;
2016 else
2017 {
2018 // Get Home directory from environment.
2019 switch (mDomain)
2020 {
2021 case kSecPreferencesDomainUser:
2022 {
2023 const char *homeDir = getenv("HOME");
2024 if (homeDir == NULL)
2025 {
2026 // If $HOME is unset get the current user's home directory
2027 // from the passwd file.
2028 uid_t uid = geteuid();
2029 if (!uid) uid = getuid();
2030 struct passwd *pw = getpwuid(uid);
2031 if (!pw)
2032 MacOSError::throwMe(errSecParam);
2033 homeDir = pw->pw_dir;
2034 }
2035 fullPathName = homeDir;
2036 }
2037 break;
2038 case kSecPreferencesDomainSystem:
2039 fullPathName = "";
2040 break;
2041 default:
2042 assert(false); // invalid domain for this
2043 }
2044
2045 fullPathName += "/Library/Keychains/";
2046 fullPathName += pathName;
2047 }
2048
2049 const CSSM_NET_ADDRESS *DbLocation = NULL; // NULL for keychains
2050 const CSSM_VERSION *version = NULL;
2051 uint32 subserviceId = 0;
2052 CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
2053 const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
2054 subserviceId, subserviceType);
2055 DLDbIdentifier dlDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
2056 return dlDbIdentifier;
2057 }
2058
2059 Keychain StorageManager::makeLoginAuthUI(const Item *item, bool isReset)
2060 {
2061 StLock<Mutex>_(mMutex);
2062
2063 // Create a login/default keychain for the user using UI.
2064 // The user can cancel out of the operation, or create a new login keychain.
2065 // If auto-login is turned off, the user will be asked for their login password.
2066 //
2067 OSStatus result = errSecSuccess;
2068 Keychain keychain; // We return this keychain.
2069 //
2070 // Set up the Auth ref to bring up UI.
2071 //
2072 AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL;
2073 AuthorizationRef authRef = NULL;
2074 try
2075 {
2076 result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef);
2077 if ( result )
2078 MacOSError::throwMe(result);
2079
2080 AuthorizationEnvironment envir;
2081 envir.count = 6; // up to 6 hints can be used.
2082 authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count);
2083 if ( !authEnvirItemArrayPtr )
2084 MacOSError::throwMe(errAuthorizationInternal);
2085
2086 currItem = envir.items = authEnvirItemArrayPtr;
2087
2088 //
2089 // 1st Hint (optional): The keychain item's account attribute string.
2090 // When item is specified, we assume an 'add' operation is being attempted.
2091 char buff[256];
2092 UInt32 actLen = 0;
2093 SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
2094 if ( item )
2095 {
2096 try
2097 {
2098 (*item)->getAttribute(attr, &actLen);
2099 }
2100 catch(...)
2101 {
2102 actLen = 0; // This item didn't have the account attribute, so don't display one in the UI.
2103 }
2104 }
2105 currItem->name = AGENT_HINT_ATTR_NAME; // name str that identifies this hint as attr name
2106 if ( actLen ) // Fill in the hint if we have an account attr
2107 {
2108 if ( actLen >= sizeof(buff) )
2109 buff[sizeof(buff)-1] = 0;
2110 else
2111 buff[actLen] = 0;
2112 currItem->valueLength = strlen(buff)+1;
2113 currItem->value = buff;
2114 }
2115 else
2116 {
2117 currItem->valueLength = 0;
2118 currItem->value = NULL;
2119 }
2120 currItem->flags = 0;
2121
2122 //
2123 // 2nd Hint (optional): The item's keychain full path.
2124 //
2125 currItem++;
2126 char* currDefaultName = NULL;
2127 try
2128 {
2129 currDefaultName = (char*)defaultKeychain()->name(); // Use the name if we have it.
2130 currItem->name = AGENT_HINT_LOGIN_KC_NAME; // Name str that identifies this hint as kc path
2131 currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0;
2132 currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)"";
2133 currItem->flags = 0;
2134 currItem++;
2135 }
2136 catch(...)
2137 {
2138 envir.count--;
2139 }
2140
2141 //
2142 // 3rd Hint (required): check if curr default keychain is unavailable.
2143 // This is determined by the parent not existing.
2144 //
2145 currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER;
2146 Boolean loginUnavail = false;
2147 try
2148 {
2149 Keychain defaultKC = defaultKeychain();
2150 if ( !defaultKC->exists() )
2151 loginUnavail = true;
2152 }
2153 catch(...) // login.keychain not present
2154 {
2155 }
2156 currItem->valueLength = sizeof(Boolean);
2157 currItem->value = (void*)&loginUnavail;
2158 currItem->flags = 0;
2159
2160 //
2161 // 4th Hint (required): userName
2162 //
2163 currItem++;
2164 currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME;
2165 char* uName = getenv("USER");
2166 string userName = uName ? uName : "";
2167 if ( userName.length() == 0 )
2168 {
2169 uid_t uid = geteuid();
2170 if (!uid) uid = getuid();
2171 struct passwd *pw = getpwuid(uid); // fallback case...
2172 if (pw)
2173 userName = pw->pw_name;
2174 endpwent();
2175 }
2176 if ( userName.length() == 0 ) // did we ultimately get one?
2177 MacOSError::throwMe(errAuthorizationInternal);
2178
2179 currItem->value = (void*)userName.c_str();
2180 currItem->valueLength = userName.length();
2181 currItem->flags = 0;
2182
2183 //
2184 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2185 //
2186 currItem++;
2187 currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
2188 Boolean moreThanOneKCExists = false;
2189 {
2190 // if item is NULL, then this is a user-initiated full reset
2191 if (item && mSavedList.searchList().size() > 1)
2192 moreThanOneKCExists = true;
2193 }
2194 currItem->value = &moreThanOneKCExists;
2195 currItem->valueLength = sizeof(Boolean);
2196 currItem->flags = 0;
2197
2198 //
2199 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2200 // We want to suppress the "do you want to reset to defaults?" panel in this case.
2201 //
2202 currItem++;
2203 currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL;
2204 Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE;
2205 currItem->valueLength = sizeof(Boolean);
2206 currItem->value = (void*)&suppressResetPanel;
2207 currItem->flags = 0;
2208
2209 //
2210 // Set up the auth rights and make the auth call.
2211 //
2212 AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 };
2213 AuthorizationRights rights = { 1, &authItem };
2214 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
2215 result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL);
2216 if ( result )
2217 MacOSError::throwMe(result);
2218 try
2219 {
2220 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2221 }
2222 catch (...) // can throw if no existing login.keychain is found
2223 {
2224 }
2225 login(authRef, (UInt32)userName.length(), userName.c_str(), isReset); // Create login.keychain
2226 keychain = loginKeychain(); // Get newly-created login keychain
2227 defaultKeychain(keychain); // Set it to be the default
2228
2229 free(authEnvirItemArrayPtr);
2230 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2231 }
2232
2233 catch (...)
2234 {
2235 // clean up allocations, then rethrow error
2236 if ( authEnvirItemArrayPtr )
2237 free(authEnvirItemArrayPtr);
2238 if ( authRef )
2239 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2240 throw;
2241 }
2242
2243 return keychain;
2244 }
2245
2246 Keychain StorageManager::defaultKeychainUI(Item &item)
2247 {
2248 StLock<Mutex>_(mMutex);
2249
2250 Keychain returnedKeychain;
2251 try
2252 {
2253 returnedKeychain = defaultKeychain(); // If we have one, return it.
2254 if ( returnedKeychain->exists() )
2255 return returnedKeychain;
2256 }
2257 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
2258 {
2259 }
2260 if ( globals().getUserInteractionAllowed() )
2261 {
2262 returnedKeychain = makeLoginAuthUI(&item, false); // If no Keychains is present, one will be created.
2263 if ( !returnedKeychain )
2264 MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong...
2265 }
2266 else
2267 MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error.
2268
2269 return returnedKeychain;
2270 }
2271
2272 void
2273 StorageManager::addToDomainList(SecPreferencesDomain domain,
2274 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2275 {
2276 StLock<Mutex>_(mMutex);
2277
2278 if (domain == kSecPreferencesDomainDynamic)
2279 MacOSError::throwMe(errSecInvalidPrefsDomain);
2280
2281 // make the identifier
2282 CSSM_VERSION version = {0, 0};
2283 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2284 subServiceType, dbName, NULL);
2285
2286 if (domain == mDomain)
2287 {
2288 // manipulate the user's list
2289 {
2290 mSavedList.revert(true);
2291 mSavedList.add(demungeDLDbIdentifier(id));
2292 mSavedList.save();
2293 }
2294
2295 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2296 }
2297 else
2298 {
2299 // manipulate the other list
2300 DLDbListCFPref(domain).add(id);
2301 }
2302 }
2303
2304 void
2305 StorageManager::isInDomainList(SecPreferencesDomain domain,
2306 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2307 {
2308 StLock<Mutex>_(mMutex);
2309
2310 if (domain == kSecPreferencesDomainDynamic)
2311 MacOSError::throwMe(errSecInvalidPrefsDomain);
2312
2313 CSSM_VERSION version = {0, 0};
2314 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2315 subServiceType, dbName, NULL);
2316
2317 // determine the list to search
2318 bool result;
2319 if (domain == mDomain)
2320 {
2321 result = mSavedList.member(demungeDLDbIdentifier(id));
2322 }
2323 else
2324 {
2325 result = DLDbListCFPref(domain).member(demungeDLDbIdentifier(id));
2326 }
2327
2328 // do the search
2329 if (!result)
2330 {
2331 MacOSError::throwMe(errSecNoSuchKeychain);
2332 }
2333 }
2334
2335 void
2336 StorageManager::removeFromDomainList(SecPreferencesDomain domain,
2337 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2338 {
2339 StLock<Mutex>_(mMutex);
2340
2341 if (domain == kSecPreferencesDomainDynamic)
2342 MacOSError::throwMe(errSecInvalidPrefsDomain);
2343
2344 // make the identifier
2345 CSSM_VERSION version = {0, 0};
2346 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2347 subServiceType, dbName, NULL);
2348
2349 if (domain == mDomain)
2350 {
2351 // manipulate the user's list
2352 {
2353 mSavedList.revert(true);
2354 mSavedList.remove(demungeDLDbIdentifier(id));
2355 mSavedList.save();
2356 }
2357
2358 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2359 }
2360 else
2361 {
2362 // manipulate the other list
2363 DLDbListCFPref(domain).remove(id);
2364 }
2365 }
2366
2367 bool
2368 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain)
2369 {
2370 struct stat sb;
2371 mode_t perms;
2372 const char* sysPrefDir = "/Library/Preferences";
2373 const char* errMsg = "Will not set default";
2374 char* mustOwnDir = NULL;
2375 struct passwd* pw = NULL;
2376
2377 // get my uid
2378 uid_t uid = geteuid();
2379 if (!uid) uid = getuid();
2380
2381 // our (e)uid must own the appropriate preferences or home directory
2382 // for the specified preference domain whose default we will be modifying
2383 switch (domain) {
2384 case kSecPreferencesDomainUser:
2385 mustOwnDir = getenv("HOME");
2386 if (mustOwnDir == NULL) {
2387 pw = getpwuid(uid);
2388 if (!pw) return false;
2389 mustOwnDir = pw->pw_dir;
2390 }
2391 break;
2392 case kSecPreferencesDomainSystem:
2393 mustOwnDir = (char*)sysPrefDir;
2394 break;
2395 case kSecPreferencesDomainCommon:
2396 mustOwnDir = (char*)sysPrefDir;
2397 break;
2398 default:
2399 return false;
2400 }
2401
2402 if (mustOwnDir != NULL) {
2403 struct stat dsb;
2404 if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) {
2405 fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir);
2406 mustOwnDir = NULL; // will return below after calling endpwent()
2407 }
2408 }
2409
2410 if (pw != NULL)
2411 endpwent();
2412
2413 if (mustOwnDir == NULL)
2414 return false;
2415
2416 // check that file actually exists
2417 if (stat(path, &sb) != 0) {
2418 fprintf(stderr, "%s: file %s does not exist\n", errMsg, path);
2419 return false;
2420 }
2421
2422 // check flags
2423 if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
2424 fprintf(stderr, "%s: file %s is immutable\n", errMsg, path);
2425 return false;
2426 }
2427
2428 // check ownership
2429 if (sb.st_uid != uid) {
2430 fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2431 errMsg, path, (int)sb.st_uid, (int)uid);
2432 return false;
2433 }
2434
2435 // check mode
2436 perms = sb.st_mode;
2437 perms |= 0600; // must have owner read/write permission set
2438 if (sb.st_mode != perms) {
2439 fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path);
2440 return false;
2441 }
2442
2443 // user owns file and can read/write it
2444 return true;
2445 }