+ result = keychainMigration(path, dbBlobVersion, keychainDbPath, SecurityServer::DbBlob::version_partition);
+ } else if(inHomeLibraryKeychains && dbBlobVersion == SecurityServer::DbBlob::version_partition && !endsWithKeychainDb) {
+ // This is a new-style keychain with the wrong name, try to rename it
+ attemptKeychainRename(path, keychainDbPath, dbBlobVersion);
+ result = true;
+ } else if(isSystemKeychain && dbBlobVersion == SecurityServer::DbBlob::version_partition) {
+ // Try to "unupgrade" the system keychain, to clean up our old issues
+ secnotice("integrity", "attempting downgrade for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
+
+ // First step: acquire the credentials to allow for ACL modification
+ SecurityServer::SystemKeychainKey skk(kSystemUnlockFile);
+ if(skk.valid()) {
+ // We've managed to read the key; now, create credentials using it
+ CssmClient::Key systemKeychainMasterKey(csp(), skk.key(), true);
+ CssmClient::AclFactory::MasterKeyUnlockCredentials creds(systemKeychainMasterKey, Allocator::standard(Allocator::sensitive));
+
+ // Attempt the downgrade, using our master key as the ACL override
+ result = keychainMigration(path, dbBlobVersion, path, SecurityServer::DbBlob::version_MacOS_10_0, creds.getAccessCredentials());
+ } else {
+ secnotice("integrity", "Couldn't read System.keychain key, skipping update");
+ }
+ } else {
+ secinfo("integrity", "not attempting migration for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
+
+ // Since we don't believe any migration needs to be done here, mark the
+ // migration as "attempted" to short-circuit future checks.
+ mAttemptedUpgrade = true;
+ }
+
+ // We might have changed our location on disk. Let StorageManager know.
+ globals().storageManager.registerKeychainImpl(this);
+
+ // if we attempted a migration, try to clean up leftover files from <rdar://problem/23950408> XARA backup have provided me with 12GB of login keychain copies
+ if(result) {
+ string pattern = path + "_*_backup";
+ glob_t pglob = {};
+ secnotice("integrity", "globbing for %s", pattern.c_str());
+ int globresult = glob(pattern.c_str(), GLOB_MARK, NULL, &pglob);
+ if(globresult == 0) {
+ secnotice("integrity", "glob: %lu results", pglob.gl_pathc);
+ if(pglob.gl_pathc > 10) {
+ // There are more than 10 backup files, indicating a problem.
+ // Delete all but one of them. Under rdar://23950408, they should all be identical.
+ secnotice("integrity", "saving backup file: %s", pglob.gl_pathv[0]);
+ for(int i = 1; i < pglob.gl_pathc; i++) {
+ secnotice("integrity", "cleaning up backup file: %s", pglob.gl_pathv[i]);
+ // ignore return code; this is a best-effort cleanup
+ unlink(pglob.gl_pathv[i]);
+ }
+ }
+
+ struct stat st;
+ bool pathExists = (::stat(path.c_str(), &st) == 0);
+ bool keychainDbPathExists = (::stat(keychainDbPath.c_str(), &st) == 0);
+
+ if(!pathExists && keychainDbPathExists && pglob.gl_pathc >= 1) {
+ // We have a file at keychainDbPath, no file at path, and at least one backup keychain file.
+ //
+ // Move the backup file to path, to simulate the current "split-world" view,
+ // which copies from path to keychainDbPath, then modifies keychainDbPath.
+ secnotice("integrity", "moving backup file %s to %s", pglob.gl_pathv[0], path.c_str());
+ ::rename(pglob.gl_pathv[0], path.c_str());
+ }
+ }