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