├── .gitignore ├── Common ├── AuthorizationTypes.h ├── Common.h └── Common.m ├── Docs ├── KeychainMinderKnownPw.png ├── KeychainMinderUnknownPw.png └── KeychainMinderWelcome.png ├── KeychainMinder.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── KeychainMinder.xcscheme │ └── KeychainMinderTests.xcscheme ├── KeychainMinder.xcworkspace └── contents.xcworkspacedata ├── KeychainMinder ├── Info.plist ├── main.m ├── uninstall.sh └── update_authdb.py ├── KeychainMinderAgent ├── KeychainMinderAgent.h ├── KeychainMinderAgent.m ├── KeychainMinderAgentProtocol.h └── main.m ├── KeychainMinderGUI ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── MainMenu.xib ├── Info.plist ├── Keychain_Locked.png ├── Keychain_Unlocked.png ├── PasswordKnownView.h ├── PasswordKnownView.m ├── PasswordKnownView.xib ├── PasswordNotKnownView.h ├── PasswordNotKnownView.m ├── PasswordNotKnownView.xib ├── PasswordViewController.h ├── PasswordViewController.m └── main.m ├── KeychainMinderTests ├── Info.plist ├── KMCallbackEngine.h ├── KMCallbackEngine.m └── KeychainMinderTests.m ├── Package ├── Makefile ├── com.google.corp.keychainminder.plist ├── com.google.corp.keychainminderagent.plist └── postinstall ├── Podfile ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Build 3 | build 4 | Pods 5 | *.pkg 6 | *KeychainMinder.bundle 7 | *.xcodeproj/xcuserdata 8 | *.xcworkspace/xcuserdata 9 | -------------------------------------------------------------------------------- /Common/AuthorizationTypes.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | 17 | // Context & Hint Keys 18 | #define kKMAuthAuthenticationAuthority "dsAttrTypeStandard:AuthenticationAuthority" 19 | #define kKMAuthUID "uid" 20 | #define kKMAuthGID "gid" 21 | #define kKMAuthTokenName "token-name" 22 | #define kKMAuthAuthorizeRight "authorize-right" 23 | #define kKMAuthSuggestedUser "suggested-user" 24 | #define kKMAuthClientPath "client-path" 25 | 26 | // Plugin Data Types 27 | enum { 28 | kMechanismMagic = 'Mchn', 29 | kPluginMagic = 'PlgN', 30 | }; 31 | 32 | typedef struct { 33 | OSType magic; 34 | const AuthorizationCallbacks *callbacks; 35 | } PluginRecord; 36 | 37 | typedef struct { 38 | OSType magic; 39 | AuthorizationEngineRef engineRef; 40 | const PluginRecord *pluginRecord; 41 | } MechanismRecord; -------------------------------------------------------------------------------- /Common/Common.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | 17 | /// 18 | /// Retrieves a mutable array from the plist on disk. 19 | /// If the file doesn't exist or is unreadable, returns an empty mutable array. 20 | /// 21 | NSMutableArray *GetUsers(); 22 | 23 | /// 24 | /// Writes out the provided array as a plist to disk. 25 | /// 26 | void SetUsers(NSMutableArray *usersArray); 27 | 28 | /// 29 | /// Validates that the provided password is the current user's login password. 30 | /// 31 | BOOL ValidateLoginPassword(NSString *newPassword); 32 | 33 | /// 34 | /// Validates that the provided password matches the password for the current user's 35 | /// default keychain. 36 | /// 37 | /// To attempt to avoid issues with the "Local Items" keychain, it makes a hardlink 38 | /// to the keychain file with the date appended, opens that 'new' keychain file, attempts 39 | /// to unlock it and then removes the hardlink. Attempting to unlock an unlocked keychain 40 | /// will always succeed and locking the login keychain also locks the Local Items keychain 41 | /// and so should be avoided. 42 | /// 43 | /// Returns YES if password matches the keychain. 44 | /// 45 | BOOL ValidateLoginKeychainPassword(NSString *OldPassword); -------------------------------------------------------------------------------- /Common/Common.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "Common.h" 16 | 17 | static NSString * const kPreferencePath = 18 | @"/Library/Preferences/com.google.corp.keychainminder.plist"; 19 | 20 | 21 | NSMutableArray *GetUsers() { 22 | NSMutableArray *currentUsers = [NSMutableArray arrayWithContentsOfFile:kPreferencePath]; 23 | return (currentUsers ? currentUsers : [NSMutableArray array]); 24 | } 25 | 26 | void SetUsers(NSMutableArray *usersArray) { 27 | if (!usersArray) return; 28 | [usersArray writeToFile:kPreferencePath atomically:YES]; 29 | } 30 | 31 | BOOL ValidateLoginPassword(NSString *newPassword) { 32 | AuthorizationItem right; 33 | right.name = "system.login.screensaver"; 34 | right.value = NULL; 35 | right.valueLength = 0; 36 | right.flags = 0; 37 | AuthorizationRights authRights; 38 | authRights.count = 1; 39 | authRights.items = &right; 40 | 41 | AuthorizationItem authEnvItems[2]; 42 | authEnvItems[0].name = kAuthorizationEnvironmentUsername; 43 | authEnvItems[0].valueLength = NSUserName().length; 44 | authEnvItems[0].value = (void *)[NSUserName() UTF8String]; 45 | authEnvItems[0].flags = 0; 46 | authEnvItems[1].name = kAuthorizationEnvironmentPassword; 47 | authEnvItems[1].valueLength = newPassword.length; 48 | authEnvItems[1].value = (void *)[newPassword UTF8String]; 49 | authEnvItems[1].flags = 0; 50 | AuthorizationEnvironment authEnv; 51 | authEnv.count = 2; 52 | authEnv.items = authEnvItems; 53 | 54 | AuthorizationFlags authFlags = (kAuthorizationFlagExtendRights | kAuthorizationFlagDestroyRights); 55 | 56 | // Create an authorization reference, retrieve rights and then release. 57 | // CopyRights is where the authorization actually takes place and the result lets us know 58 | // whether auth was successful. 59 | OSStatus authStatus = AuthorizationCreate(&authRights, &authEnv, authFlags, NULL); 60 | return (authStatus == errAuthorizationSuccess); 61 | } 62 | 63 | BOOL ValidateLoginKeychainPassword(NSString *oldPassword) { 64 | // Get default keychain path 65 | SecKeychainRef defaultKeychain = NULL; 66 | if (SecKeychainCopyDefault(&defaultKeychain) != errSecSuccess) { 67 | if (defaultKeychain) CFRelease(defaultKeychain); 68 | return YES; 69 | } 70 | UInt32 maxPathLen = MAXPATHLEN; 71 | char keychainPath[MAXPATHLEN]; 72 | SecKeychainGetPath(defaultKeychain, &maxPathLen, keychainPath); 73 | CFRelease(defaultKeychain); 74 | 75 | // Duplicate the default keychain file to a new location. 76 | NSString *path = @(keychainPath); 77 | NSString *newPath = [path stringByAppendingFormat:@".%d", 78 | (int)[[NSDate date] timeIntervalSince1970]]; 79 | if (link(path.UTF8String, newPath.UTF8String) != 0) { 80 | return NO; 81 | } 82 | 83 | // Open and unlock this new keychain file. 84 | SecKeychainRef keychainRef = NULL; 85 | SecKeychainOpen(newPath.UTF8String, &keychainRef); 86 | OSStatus err = SecKeychainUnlock(keychainRef, (UInt32)oldPassword.length, 87 | oldPassword.UTF8String, YES); 88 | CFRelease(keychainRef); 89 | 90 | // Delete the temporary keychain file. 91 | unlink(newPath.UTF8String); 92 | 93 | return (err == errSecSuccess); 94 | } -------------------------------------------------------------------------------- /Docs/KeychainMinderKnownPw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/macops-keychainminder/b6ab824321fc677aa80a217348023dcdf416b68a/Docs/KeychainMinderKnownPw.png -------------------------------------------------------------------------------- /Docs/KeychainMinderUnknownPw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/macops-keychainminder/b6ab824321fc677aa80a217348023dcdf416b68a/Docs/KeychainMinderUnknownPw.png -------------------------------------------------------------------------------- /Docs/KeychainMinderWelcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/macops-keychainminder/b6ab824321fc677aa80a217348023dcdf416b68a/Docs/KeychainMinderWelcome.png -------------------------------------------------------------------------------- /KeychainMinder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0D0EA9B51B62C0FC0041A897 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9B41B62C0FC0041A897 /* main.m */; }; 11 | 0D0EA9C51B62D9AE0041A897 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9C41B62D9AE0041A897 /* AppDelegate.m */; }; 12 | 0D0EA9C71B62D9AE0041A897 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9C61B62D9AE0041A897 /* main.m */; }; 13 | 0D0EA9CC1B62D9AE0041A897 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D0EA9CA1B62D9AE0041A897 /* MainMenu.xib */; }; 14 | 0D0EA9E61B62DB0E0041A897 /* Common.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9E51B62DB0E0041A897 /* Common.m */; }; 15 | 0D0EA9E71B62DB0E0041A897 /* Common.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9E51B62DB0E0041A897 /* Common.m */; }; 16 | 0D189CAE1B6BE4D60082B239 /* KeychainMinderGUI.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D0EA9BF1B62D9AE0041A897 /* KeychainMinderGUI.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 17 | 0D189CB11B6BE74A0082B239 /* update_authdb.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D189CB01B6BE7470082B239 /* update_authdb.py */; }; 18 | 0D189CB21B6BE74D0082B239 /* uninstall.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D189CAF1B6BE7470082B239 /* uninstall.sh */; }; 19 | 0D19C1521B6AB19E000D5475 /* Keychain_Locked.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D19C1511B6AB19E000D5475 /* Keychain_Locked.png */; }; 20 | 0D19C1541B6AB1A7000D5475 /* Keychain_Unlocked.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D19C1531B6AB1A7000D5475 /* Keychain_Unlocked.png */; }; 21 | 0D19C15A1B6AB320000D5475 /* PasswordKnownView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D19C1581B6AB320000D5475 /* PasswordKnownView.m */; }; 22 | 0D19C15B1B6AB320000D5475 /* PasswordKnownView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D19C1591B6AB320000D5475 /* PasswordKnownView.xib */; }; 23 | 0D19C15F1B6AB329000D5475 /* PasswordNotKnownView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D19C15D1B6AB329000D5475 /* PasswordNotKnownView.m */; }; 24 | 0D19C1601B6AB329000D5475 /* PasswordNotKnownView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D19C15E1B6AB329000D5475 /* PasswordNotKnownView.xib */; }; 25 | 0D80140E1B6C0074006B8540 /* PasswordViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D80140D1B6C0074006B8540 /* PasswordViewController.m */; }; 26 | 30A9218BE1284B4B9F719DA2 /* libPods-KeychainMinderTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC95AD9E98FD902BBC1D3EA /* libPods-KeychainMinderTests.a */; }; 27 | C739E5161CA46B9200A863A3 /* KeychainMinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C739E5151CA46B9200A863A3 /* KeychainMinderTests.m */; }; 28 | C739E51D1CA46BAA00A863A3 /* KMCallbackEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = C739E51C1CA46BAA00A863A3 /* KMCallbackEngine.m */; }; 29 | C739E5211CA46BDB00A863A3 /* KeychainMinderAgent.m in Sources */ = {isa = PBXBuildFile; fileRef = C739E51F1CA46BDB00A863A3 /* KeychainMinderAgent.m */; }; 30 | C79331A31CA46CE200392B52 /* Common.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9E51B62DB0E0041A897 /* Common.m */; }; 31 | C79331A41CA46CE700392B52 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0EA9B41B62C0FC0041A897 /* main.m */; }; 32 | C7F073031C99BD9C0049A7C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7F073021C99BD9C0049A7C7 /* main.m */; }; 33 | C7F0730B1C99BDC90049A7C7 /* KeychainMinderAgent in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7F073001C99BD9C0049A7C7 /* KeychainMinderAgent */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34 | FC039A183291F172F2435D05 /* libPods-KeychainMinderAgent.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A6B3A3E2BC165ADE364C5F25 /* libPods-KeychainMinderAgent.a */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 0D84C9D41B66E7B200A8F499 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 0D0EA9A21B62BFE20041A897 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 0D0EA9BE1B62D9AE0041A897; 43 | remoteInfo = KeychainMinderGUI; 44 | }; 45 | C7F073071C99BDAC0049A7C7 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 0D0EA9A21B62BFE20041A897 /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = C7F072FF1C99BD9C0049A7C7; 50 | remoteInfo = KeychainMinderAgent; 51 | }; 52 | /* End PBXContainerItemProxy section */ 53 | 54 | /* Begin PBXCopyFilesBuildPhase section */ 55 | 0D0EA9E01B62D9CA0041A897 /* CopyFiles */ = { 56 | isa = PBXCopyFilesBuildPhase; 57 | buildActionMask = 2147483647; 58 | dstPath = ""; 59 | dstSubfolderSpec = 7; 60 | files = ( 61 | 0D189CB21B6BE74D0082B239 /* uninstall.sh in CopyFiles */, 62 | 0D189CB11B6BE74A0082B239 /* update_authdb.py in CopyFiles */, 63 | 0D189CAE1B6BE4D60082B239 /* KeychainMinderGUI.app in CopyFiles */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | C7F072FE1C99BD9C0049A7C7 /* CopyFiles */ = { 68 | isa = PBXCopyFilesBuildPhase; 69 | buildActionMask = 2147483647; 70 | dstPath = /usr/share/man/man1/; 71 | dstSubfolderSpec = 0; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 1; 75 | }; 76 | C7F0730A1C99BDBE0049A7C7 /* CopyFiles */ = { 77 | isa = PBXCopyFilesBuildPhase; 78 | buildActionMask = 2147483647; 79 | dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; 80 | dstSubfolderSpec = 16; 81 | files = ( 82 | C7F0730B1C99BDC90049A7C7 /* KeychainMinderAgent in CopyFiles */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXCopyFilesBuildPhase section */ 87 | 88 | /* Begin PBXFileReference section */ 89 | 0D0EA9AA1B62BFE20041A897 /* KeychainMinder.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainMinder.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 0D0EA9AE1B62BFE20041A897 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 91 | 0D0EA9B41B62C0FC0041A897 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 92 | 0D0EA9B61B62C12B0041A897 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 93 | 0D0EA9B81B62C12E0041A897 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 94 | 0D0EA9BF1B62D9AE0041A897 /* KeychainMinderGUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeychainMinderGUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | 0D0EA9C21B62D9AE0041A897 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 96 | 0D0EA9C31B62D9AE0041A897 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 97 | 0D0EA9C41B62D9AE0041A897 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 98 | 0D0EA9C61B62D9AE0041A897 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 99 | 0D0EA9CB1B62D9AE0041A897 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 100 | 0D0EA9E41B62DB0E0041A897 /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Common.h; path = Common/Common.h; sourceTree = ""; }; 101 | 0D0EA9E51B62DB0E0041A897 /* Common.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Common.m; path = Common/Common.m; sourceTree = ""; }; 102 | 0D189CAF1B6BE7470082B239 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = uninstall.sh; sourceTree = ""; }; 103 | 0D189CB01B6BE7470082B239 /* update_authdb.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = update_authdb.py; sourceTree = ""; }; 104 | 0D19C1511B6AB19E000D5475 /* Keychain_Locked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Keychain_Locked.png; sourceTree = ""; }; 105 | 0D19C1531B6AB1A7000D5475 /* Keychain_Unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Keychain_Unlocked.png; sourceTree = ""; }; 106 | 0D19C1571B6AB320000D5475 /* PasswordKnownView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasswordKnownView.h; sourceTree = ""; }; 107 | 0D19C1581B6AB320000D5475 /* PasswordKnownView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PasswordKnownView.m; sourceTree = ""; }; 108 | 0D19C1591B6AB320000D5475 /* PasswordKnownView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PasswordKnownView.xib; sourceTree = ""; }; 109 | 0D19C15C1B6AB329000D5475 /* PasswordNotKnownView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasswordNotKnownView.h; sourceTree = ""; }; 110 | 0D19C15D1B6AB329000D5475 /* PasswordNotKnownView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PasswordNotKnownView.m; sourceTree = ""; }; 111 | 0D19C15E1B6AB329000D5475 /* PasswordNotKnownView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PasswordNotKnownView.xib; sourceTree = ""; }; 112 | 0D80140C1B6C0074006B8540 /* PasswordViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasswordViewController.h; sourceTree = ""; }; 113 | 0D80140D1B6C0074006B8540 /* PasswordViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PasswordViewController.m; sourceTree = ""; }; 114 | 11F3EEBEBC8A87689D4180F9 /* Pods-KeychainMinderTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeychainMinderTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-KeychainMinderTests/Pods-KeychainMinderTests.release.xcconfig"; sourceTree = ""; }; 115 | 6E66D2C8B758DD6C070BA4D5 /* Pods-KeychainMinderTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeychainMinderTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KeychainMinderTests/Pods-KeychainMinderTests.debug.xcconfig"; sourceTree = ""; }; 116 | 9AC95AD9E98FD902BBC1D3EA /* libPods-KeychainMinderTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KeychainMinderTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 117 | A6B3A3E2BC165ADE364C5F25 /* libPods-KeychainMinderAgent.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KeychainMinderAgent.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 118 | A9A7BB8BD2328D9F3836BCEA /* Pods-KeychainMinderAgent.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeychainMinderAgent.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KeychainMinderAgent/Pods-KeychainMinderAgent.debug.xcconfig"; sourceTree = ""; }; 119 | C713ADD51CAC2F410011ADD7 /* AuthorizationTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AuthorizationTypes.h; path = Common/AuthorizationTypes.h; sourceTree = ""; }; 120 | C739E5131CA46B9200A863A3 /* KeychainMinderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainMinderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 121 | C739E5151CA46B9200A863A3 /* KeychainMinderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KeychainMinderTests.m; sourceTree = ""; }; 122 | C739E5171CA46B9200A863A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 123 | C739E51B1CA46BAA00A863A3 /* KMCallbackEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KMCallbackEngine.h; sourceTree = ""; }; 124 | C739E51C1CA46BAA00A863A3 /* KMCallbackEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KMCallbackEngine.m; sourceTree = ""; }; 125 | C739E51E1CA46BDB00A863A3 /* KeychainMinderAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeychainMinderAgent.h; sourceTree = ""; }; 126 | C739E51F1CA46BDB00A863A3 /* KeychainMinderAgent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeychainMinderAgent.m; sourceTree = ""; }; 127 | C739E5201CA46BDB00A863A3 /* KeychainMinderAgentProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeychainMinderAgentProtocol.h; sourceTree = ""; }; 128 | C7F073001C99BD9C0049A7C7 /* KeychainMinderAgent */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = KeychainMinderAgent; sourceTree = BUILT_PRODUCTS_DIR; }; 129 | C7F073021C99BD9C0049A7C7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 130 | FBD3F60D5C773D146528EFF2 /* Pods-KeychainMinderAgent.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeychainMinderAgent.release.xcconfig"; path = "Pods/Target Support Files/Pods-KeychainMinderAgent/Pods-KeychainMinderAgent.release.xcconfig"; sourceTree = ""; }; 131 | /* End PBXFileReference section */ 132 | 133 | /* Begin PBXFrameworksBuildPhase section */ 134 | 0D0EA9A71B62BFE20041A897 /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | 0D0EA9BC1B62D9AE0041A897 /* Frameworks */ = { 142 | isa = PBXFrameworksBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | C739E5101CA46B9200A863A3 /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | 30A9218BE1284B4B9F719DA2 /* libPods-KeychainMinderTests.a in Frameworks */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | C7F072FD1C99BD9C0049A7C7 /* Frameworks */ = { 157 | isa = PBXFrameworksBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | FC039A183291F172F2435D05 /* libPods-KeychainMinderAgent.a in Frameworks */, 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | /* End PBXFrameworksBuildPhase section */ 165 | 166 | /* Begin PBXGroup section */ 167 | 0D0EA9A11B62BFE20041A897 = { 168 | isa = PBXGroup; 169 | children = ( 170 | 0D0EA9E81B62DCFB0041A897 /* Common */, 171 | 0D0EA9AC1B62BFE20041A897 /* Plugin */, 172 | 0D0EA9C01B62D9AE0041A897 /* App */, 173 | C7F073011C99BD9C0049A7C7 /* KeychainMinderAgent */, 174 | C739E5141CA46B9200A863A3 /* KeychainMinderTests */, 175 | 0D0EA9BA1B62C1380041A897 /* Frameworks */, 176 | 0D0EA9AB1B62BFE20041A897 /* Products */, 177 | 2BF00F6175E09DA1081D098E /* Pods */, 178 | ); 179 | sourceTree = ""; 180 | }; 181 | 0D0EA9AB1B62BFE20041A897 /* Products */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 0D0EA9AA1B62BFE20041A897 /* KeychainMinder.bundle */, 185 | 0D0EA9BF1B62D9AE0041A897 /* KeychainMinderGUI.app */, 186 | C7F073001C99BD9C0049A7C7 /* KeychainMinderAgent */, 187 | C739E5131CA46B9200A863A3 /* KeychainMinderTests.xctest */, 188 | ); 189 | name = Products; 190 | sourceTree = ""; 191 | }; 192 | 0D0EA9AC1B62BFE20041A897 /* Plugin */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 0D0EA9B41B62C0FC0041A897 /* main.m */, 196 | 0D0EA9AE1B62BFE20041A897 /* Info.plist */, 197 | 0D189CAF1B6BE7470082B239 /* uninstall.sh */, 198 | 0D189CB01B6BE7470082B239 /* update_authdb.py */, 199 | ); 200 | name = Plugin; 201 | path = KeychainMinder; 202 | sourceTree = ""; 203 | }; 204 | 0D0EA9BA1B62C1380041A897 /* Frameworks */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 0D0EA9B81B62C12E0041A897 /* Security.framework */, 208 | 0D0EA9B61B62C12B0041A897 /* Foundation.framework */, 209 | A6B3A3E2BC165ADE364C5F25 /* libPods-KeychainMinderAgent.a */, 210 | 9AC95AD9E98FD902BBC1D3EA /* libPods-KeychainMinderTests.a */, 211 | ); 212 | name = Frameworks; 213 | sourceTree = ""; 214 | }; 215 | 0D0EA9C01B62D9AE0041A897 /* App */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | 0D0EA9C31B62D9AE0041A897 /* AppDelegate.h */, 219 | 0D0EA9C41B62D9AE0041A897 /* AppDelegate.m */, 220 | 0D0EA9CA1B62D9AE0041A897 /* MainMenu.xib */, 221 | 0D19C1571B6AB320000D5475 /* PasswordKnownView.h */, 222 | 0D19C1581B6AB320000D5475 /* PasswordKnownView.m */, 223 | 0D19C1591B6AB320000D5475 /* PasswordKnownView.xib */, 224 | 0D19C15C1B6AB329000D5475 /* PasswordNotKnownView.h */, 225 | 0D19C15D1B6AB329000D5475 /* PasswordNotKnownView.m */, 226 | 0D19C15E1B6AB329000D5475 /* PasswordNotKnownView.xib */, 227 | 0D80140C1B6C0074006B8540 /* PasswordViewController.h */, 228 | 0D80140D1B6C0074006B8540 /* PasswordViewController.m */, 229 | 0DD79FFA1B6AC5C900739089 /* Supporting Files */, 230 | ); 231 | name = App; 232 | path = KeychainMinderGUI; 233 | sourceTree = ""; 234 | }; 235 | 0D0EA9E81B62DCFB0041A897 /* Common */ = { 236 | isa = PBXGroup; 237 | children = ( 238 | C713ADD51CAC2F410011ADD7 /* AuthorizationTypes.h */, 239 | 0D0EA9E41B62DB0E0041A897 /* Common.h */, 240 | 0D0EA9E51B62DB0E0041A897 /* Common.m */, 241 | ); 242 | name = Common; 243 | sourceTree = ""; 244 | }; 245 | 0DD79FFA1B6AC5C900739089 /* Supporting Files */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | 0D0EA9C61B62D9AE0041A897 /* main.m */, 249 | 0D0EA9C21B62D9AE0041A897 /* Info.plist */, 250 | 0D19C1531B6AB1A7000D5475 /* Keychain_Unlocked.png */, 251 | 0D19C1511B6AB19E000D5475 /* Keychain_Locked.png */, 252 | ); 253 | name = "Supporting Files"; 254 | sourceTree = ""; 255 | }; 256 | 2BF00F6175E09DA1081D098E /* Pods */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | A9A7BB8BD2328D9F3836BCEA /* Pods-KeychainMinderAgent.debug.xcconfig */, 260 | FBD3F60D5C773D146528EFF2 /* Pods-KeychainMinderAgent.release.xcconfig */, 261 | 6E66D2C8B758DD6C070BA4D5 /* Pods-KeychainMinderTests.debug.xcconfig */, 262 | 11F3EEBEBC8A87689D4180F9 /* Pods-KeychainMinderTests.release.xcconfig */, 263 | ); 264 | name = Pods; 265 | sourceTree = ""; 266 | }; 267 | C739E5141CA46B9200A863A3 /* KeychainMinderTests */ = { 268 | isa = PBXGroup; 269 | children = ( 270 | C739E5151CA46B9200A863A3 /* KeychainMinderTests.m */, 271 | C739E51B1CA46BAA00A863A3 /* KMCallbackEngine.h */, 272 | C739E51C1CA46BAA00A863A3 /* KMCallbackEngine.m */, 273 | C739E5171CA46B9200A863A3 /* Info.plist */, 274 | ); 275 | path = KeychainMinderTests; 276 | sourceTree = ""; 277 | }; 278 | C7F073011C99BD9C0049A7C7 /* KeychainMinderAgent */ = { 279 | isa = PBXGroup; 280 | children = ( 281 | C739E51E1CA46BDB00A863A3 /* KeychainMinderAgent.h */, 282 | C739E51F1CA46BDB00A863A3 /* KeychainMinderAgent.m */, 283 | C739E5201CA46BDB00A863A3 /* KeychainMinderAgentProtocol.h */, 284 | C7F073021C99BD9C0049A7C7 /* main.m */, 285 | ); 286 | path = KeychainMinderAgent; 287 | sourceTree = ""; 288 | }; 289 | /* End PBXGroup section */ 290 | 291 | /* Begin PBXNativeTarget section */ 292 | 0D0EA9A91B62BFE20041A897 /* KeychainMinder */ = { 293 | isa = PBXNativeTarget; 294 | buildConfigurationList = 0D0EA9B11B62BFE20041A897 /* Build configuration list for PBXNativeTarget "KeychainMinder" */; 295 | buildPhases = ( 296 | 0D0EA9A61B62BFE20041A897 /* Sources */, 297 | 0D0EA9A71B62BFE20041A897 /* Frameworks */, 298 | 0D0EA9E01B62D9CA0041A897 /* CopyFiles */, 299 | C7F0730A1C99BDBE0049A7C7 /* CopyFiles */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | C7F073081C99BDAC0049A7C7 /* PBXTargetDependency */, 305 | 0D84C9D51B66E7B200A8F499 /* PBXTargetDependency */, 306 | ); 307 | name = KeychainMinder; 308 | productName = KeychainMinder; 309 | productReference = 0D0EA9AA1B62BFE20041A897 /* KeychainMinder.bundle */; 310 | productType = "com.apple.product-type.bundle"; 311 | }; 312 | 0D0EA9BE1B62D9AE0041A897 /* KeychainMinderGUI */ = { 313 | isa = PBXNativeTarget; 314 | buildConfigurationList = 0D0EA9D91B62D9AE0041A897 /* Build configuration list for PBXNativeTarget "KeychainMinderGUI" */; 315 | buildPhases = ( 316 | 0D0EA9BB1B62D9AE0041A897 /* Sources */, 317 | 0D0EA9BC1B62D9AE0041A897 /* Frameworks */, 318 | 0D0EA9BD1B62D9AE0041A897 /* Resources */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | ); 324 | name = KeychainMinderGUI; 325 | productName = KeychainMinderGUI; 326 | productReference = 0D0EA9BF1B62D9AE0041A897 /* KeychainMinderGUI.app */; 327 | productType = "com.apple.product-type.application"; 328 | }; 329 | C739E5121CA46B9200A863A3 /* KeychainMinderTests */ = { 330 | isa = PBXNativeTarget; 331 | buildConfigurationList = C739E51A1CA46B9200A863A3 /* Build configuration list for PBXNativeTarget "KeychainMinderTests" */; 332 | buildPhases = ( 333 | D35A9EE82A9F05C5D1A9FE4E /* Check Pods Manifest.lock */, 334 | C739E50F1CA46B9200A863A3 /* Sources */, 335 | C739E5101CA46B9200A863A3 /* Frameworks */, 336 | C739E5111CA46B9200A863A3 /* Resources */, 337 | 16B2BAD01E1C1A00D367EB72 /* Embed Pods Frameworks */, 338 | FD038DFFBA69A092925DA1CB /* Copy Pods Resources */, 339 | ); 340 | buildRules = ( 341 | ); 342 | dependencies = ( 343 | ); 344 | name = KeychainMinderTests; 345 | productName = KeychainMinderTests; 346 | productReference = C739E5131CA46B9200A863A3 /* KeychainMinderTests.xctest */; 347 | productType = "com.apple.product-type.bundle.unit-test"; 348 | }; 349 | C7F072FF1C99BD9C0049A7C7 /* KeychainMinderAgent */ = { 350 | isa = PBXNativeTarget; 351 | buildConfigurationList = C7F073061C99BD9C0049A7C7 /* Build configuration list for PBXNativeTarget "KeychainMinderAgent" */; 352 | buildPhases = ( 353 | 42D82900ED8F54B11848C462 /* Check Pods Manifest.lock */, 354 | C7F072FC1C99BD9C0049A7C7 /* Sources */, 355 | C7F072FD1C99BD9C0049A7C7 /* Frameworks */, 356 | C7F072FE1C99BD9C0049A7C7 /* CopyFiles */, 357 | 021642EEBDB1D78FBF1DD38F /* Copy Pods Resources */, 358 | ); 359 | buildRules = ( 360 | ); 361 | dependencies = ( 362 | ); 363 | name = KeychainMinderAgent; 364 | productName = KeychainMinderAgent; 365 | productReference = C7F073001C99BD9C0049A7C7 /* KeychainMinderAgent */; 366 | productType = "com.apple.product-type.tool"; 367 | }; 368 | /* End PBXNativeTarget section */ 369 | 370 | /* Begin PBXProject section */ 371 | 0D0EA9A21B62BFE20041A897 /* Project object */ = { 372 | isa = PBXProject; 373 | attributes = { 374 | LastUpgradeCheck = 0720; 375 | ORGANIZATIONNAME = "Google Inc"; 376 | TargetAttributes = { 377 | 0D0EA9A91B62BFE20041A897 = { 378 | CreatedOnToolsVersion = 6.1.1; 379 | }; 380 | 0D0EA9BE1B62D9AE0041A897 = { 381 | CreatedOnToolsVersion = 6.1.1; 382 | }; 383 | C739E5121CA46B9200A863A3 = { 384 | CreatedOnToolsVersion = 7.2.1; 385 | }; 386 | C7F072FF1C99BD9C0049A7C7 = { 387 | CreatedOnToolsVersion = 7.2; 388 | }; 389 | }; 390 | }; 391 | buildConfigurationList = 0D0EA9A51B62BFE20041A897 /* Build configuration list for PBXProject "KeychainMinder" */; 392 | compatibilityVersion = "Xcode 3.2"; 393 | developmentRegion = English; 394 | hasScannedForEncodings = 0; 395 | knownRegions = ( 396 | en, 397 | Base, 398 | ); 399 | mainGroup = 0D0EA9A11B62BFE20041A897; 400 | productRefGroup = 0D0EA9AB1B62BFE20041A897 /* Products */; 401 | projectDirPath = ""; 402 | projectRoot = ""; 403 | targets = ( 404 | 0D0EA9A91B62BFE20041A897 /* KeychainMinder */, 405 | 0D0EA9BE1B62D9AE0041A897 /* KeychainMinderGUI */, 406 | C7F072FF1C99BD9C0049A7C7 /* KeychainMinderAgent */, 407 | C739E5121CA46B9200A863A3 /* KeychainMinderTests */, 408 | ); 409 | }; 410 | /* End PBXProject section */ 411 | 412 | /* Begin PBXResourcesBuildPhase section */ 413 | 0D0EA9BD1B62D9AE0041A897 /* Resources */ = { 414 | isa = PBXResourcesBuildPhase; 415 | buildActionMask = 2147483647; 416 | files = ( 417 | 0D19C1541B6AB1A7000D5475 /* Keychain_Unlocked.png in Resources */, 418 | 0D19C15B1B6AB320000D5475 /* PasswordKnownView.xib in Resources */, 419 | 0D19C1521B6AB19E000D5475 /* Keychain_Locked.png in Resources */, 420 | 0D19C1601B6AB329000D5475 /* PasswordNotKnownView.xib in Resources */, 421 | 0D0EA9CC1B62D9AE0041A897 /* MainMenu.xib in Resources */, 422 | ); 423 | runOnlyForDeploymentPostprocessing = 0; 424 | }; 425 | C739E5111CA46B9200A863A3 /* Resources */ = { 426 | isa = PBXResourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | ); 430 | runOnlyForDeploymentPostprocessing = 0; 431 | }; 432 | /* End PBXResourcesBuildPhase section */ 433 | 434 | /* Begin PBXShellScriptBuildPhase section */ 435 | 021642EEBDB1D78FBF1DD38F /* Copy Pods Resources */ = { 436 | isa = PBXShellScriptBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | ); 440 | inputPaths = ( 441 | ); 442 | name = "Copy Pods Resources"; 443 | outputPaths = ( 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | shellPath = /bin/sh; 447 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-KeychainMinderAgent/Pods-KeychainMinderAgent-resources.sh\"\n"; 448 | showEnvVarsInLog = 0; 449 | }; 450 | 16B2BAD01E1C1A00D367EB72 /* Embed Pods Frameworks */ = { 451 | isa = PBXShellScriptBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | ); 455 | inputPaths = ( 456 | ); 457 | name = "Embed Pods Frameworks"; 458 | outputPaths = ( 459 | ); 460 | runOnlyForDeploymentPostprocessing = 0; 461 | shellPath = /bin/sh; 462 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-KeychainMinderTests/Pods-KeychainMinderTests-frameworks.sh\"\n"; 463 | showEnvVarsInLog = 0; 464 | }; 465 | 42D82900ED8F54B11848C462 /* Check Pods Manifest.lock */ = { 466 | isa = PBXShellScriptBuildPhase; 467 | buildActionMask = 2147483647; 468 | files = ( 469 | ); 470 | inputPaths = ( 471 | ); 472 | name = "Check Pods Manifest.lock"; 473 | outputPaths = ( 474 | ); 475 | runOnlyForDeploymentPostprocessing = 0; 476 | shellPath = /bin/sh; 477 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 478 | showEnvVarsInLog = 0; 479 | }; 480 | D35A9EE82A9F05C5D1A9FE4E /* Check Pods Manifest.lock */ = { 481 | isa = PBXShellScriptBuildPhase; 482 | buildActionMask = 2147483647; 483 | files = ( 484 | ); 485 | inputPaths = ( 486 | ); 487 | name = "Check Pods Manifest.lock"; 488 | outputPaths = ( 489 | ); 490 | runOnlyForDeploymentPostprocessing = 0; 491 | shellPath = /bin/sh; 492 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 493 | showEnvVarsInLog = 0; 494 | }; 495 | FD038DFFBA69A092925DA1CB /* Copy Pods Resources */ = { 496 | isa = PBXShellScriptBuildPhase; 497 | buildActionMask = 2147483647; 498 | files = ( 499 | ); 500 | inputPaths = ( 501 | ); 502 | name = "Copy Pods Resources"; 503 | outputPaths = ( 504 | ); 505 | runOnlyForDeploymentPostprocessing = 0; 506 | shellPath = /bin/sh; 507 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-KeychainMinderTests/Pods-KeychainMinderTests-resources.sh\"\n"; 508 | showEnvVarsInLog = 0; 509 | }; 510 | /* End PBXShellScriptBuildPhase section */ 511 | 512 | /* Begin PBXSourcesBuildPhase section */ 513 | 0D0EA9A61B62BFE20041A897 /* Sources */ = { 514 | isa = PBXSourcesBuildPhase; 515 | buildActionMask = 2147483647; 516 | files = ( 517 | 0D0EA9E61B62DB0E0041A897 /* Common.m in Sources */, 518 | 0D0EA9B51B62C0FC0041A897 /* main.m in Sources */, 519 | ); 520 | runOnlyForDeploymentPostprocessing = 0; 521 | }; 522 | 0D0EA9BB1B62D9AE0041A897 /* Sources */ = { 523 | isa = PBXSourcesBuildPhase; 524 | buildActionMask = 2147483647; 525 | files = ( 526 | 0D0EA9E71B62DB0E0041A897 /* Common.m in Sources */, 527 | 0D19C15A1B6AB320000D5475 /* PasswordKnownView.m in Sources */, 528 | 0D19C15F1B6AB329000D5475 /* PasswordNotKnownView.m in Sources */, 529 | 0D0EA9C71B62D9AE0041A897 /* main.m in Sources */, 530 | 0D0EA9C51B62D9AE0041A897 /* AppDelegate.m in Sources */, 531 | 0D80140E1B6C0074006B8540 /* PasswordViewController.m in Sources */, 532 | ); 533 | runOnlyForDeploymentPostprocessing = 0; 534 | }; 535 | C739E50F1CA46B9200A863A3 /* Sources */ = { 536 | isa = PBXSourcesBuildPhase; 537 | buildActionMask = 2147483647; 538 | files = ( 539 | C79331A31CA46CE200392B52 /* Common.m in Sources */, 540 | C739E51D1CA46BAA00A863A3 /* KMCallbackEngine.m in Sources */, 541 | C79331A41CA46CE700392B52 /* main.m in Sources */, 542 | C739E5161CA46B9200A863A3 /* KeychainMinderTests.m in Sources */, 543 | ); 544 | runOnlyForDeploymentPostprocessing = 0; 545 | }; 546 | C7F072FC1C99BD9C0049A7C7 /* Sources */ = { 547 | isa = PBXSourcesBuildPhase; 548 | buildActionMask = 2147483647; 549 | files = ( 550 | C739E5211CA46BDB00A863A3 /* KeychainMinderAgent.m in Sources */, 551 | C7F073031C99BD9C0049A7C7 /* main.m in Sources */, 552 | ); 553 | runOnlyForDeploymentPostprocessing = 0; 554 | }; 555 | /* End PBXSourcesBuildPhase section */ 556 | 557 | /* Begin PBXTargetDependency section */ 558 | 0D84C9D51B66E7B200A8F499 /* PBXTargetDependency */ = { 559 | isa = PBXTargetDependency; 560 | target = 0D0EA9BE1B62D9AE0041A897 /* KeychainMinderGUI */; 561 | targetProxy = 0D84C9D41B66E7B200A8F499 /* PBXContainerItemProxy */; 562 | }; 563 | C7F073081C99BDAC0049A7C7 /* PBXTargetDependency */ = { 564 | isa = PBXTargetDependency; 565 | target = C7F072FF1C99BD9C0049A7C7 /* KeychainMinderAgent */; 566 | targetProxy = C7F073071C99BDAC0049A7C7 /* PBXContainerItemProxy */; 567 | }; 568 | /* End PBXTargetDependency section */ 569 | 570 | /* Begin PBXVariantGroup section */ 571 | 0D0EA9CA1B62D9AE0041A897 /* MainMenu.xib */ = { 572 | isa = PBXVariantGroup; 573 | children = ( 574 | 0D0EA9CB1B62D9AE0041A897 /* Base */, 575 | ); 576 | name = MainMenu.xib; 577 | sourceTree = ""; 578 | }; 579 | /* End PBXVariantGroup section */ 580 | 581 | /* Begin XCBuildConfiguration section */ 582 | 0D0EA9AF1B62BFE20041A897 /* Debug */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | ALWAYS_SEARCH_USER_PATHS = NO; 586 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 587 | CLANG_CXX_LIBRARY = "libc++"; 588 | CLANG_ENABLE_MODULES = YES; 589 | CLANG_ENABLE_OBJC_ARC = YES; 590 | CLANG_WARN_BOOL_CONVERSION = YES; 591 | CLANG_WARN_CONSTANT_CONVERSION = YES; 592 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 593 | CLANG_WARN_EMPTY_BODY = YES; 594 | CLANG_WARN_ENUM_CONVERSION = YES; 595 | CLANG_WARN_INT_CONVERSION = YES; 596 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 597 | CLANG_WARN_UNREACHABLE_CODE = YES; 598 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 599 | COPY_PHASE_STRIP = NO; 600 | ENABLE_STRICT_OBJC_MSGSEND = YES; 601 | ENABLE_TESTABILITY = YES; 602 | GCC_C_LANGUAGE_STANDARD = gnu99; 603 | GCC_DYNAMIC_NO_PIC = NO; 604 | GCC_OPTIMIZATION_LEVEL = 0; 605 | GCC_PREPROCESSOR_DEFINITIONS = ( 606 | "DEBUG=1", 607 | "$(inherited)", 608 | ); 609 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 610 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 611 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 612 | GCC_WARN_UNDECLARED_SELECTOR = YES; 613 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 614 | GCC_WARN_UNUSED_FUNCTION = YES; 615 | GCC_WARN_UNUSED_VARIABLE = YES; 616 | MACOSX_DEPLOYMENT_TARGET = 10.10; 617 | MTL_ENABLE_DEBUG_INFO = YES; 618 | ONLY_ACTIVE_ARCH = YES; 619 | SDKROOT = macosx; 620 | }; 621 | name = Debug; 622 | }; 623 | 0D0EA9B01B62BFE20041A897 /* Release */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | ALWAYS_SEARCH_USER_PATHS = NO; 627 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 628 | CLANG_CXX_LIBRARY = "libc++"; 629 | CLANG_ENABLE_MODULES = YES; 630 | CLANG_ENABLE_OBJC_ARC = YES; 631 | CLANG_WARN_BOOL_CONVERSION = YES; 632 | CLANG_WARN_CONSTANT_CONVERSION = YES; 633 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 634 | CLANG_WARN_EMPTY_BODY = YES; 635 | CLANG_WARN_ENUM_CONVERSION = YES; 636 | CLANG_WARN_INT_CONVERSION = YES; 637 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 638 | CLANG_WARN_UNREACHABLE_CODE = YES; 639 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 640 | COPY_PHASE_STRIP = YES; 641 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 642 | ENABLE_NS_ASSERTIONS = NO; 643 | ENABLE_STRICT_OBJC_MSGSEND = YES; 644 | GCC_C_LANGUAGE_STANDARD = gnu99; 645 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 646 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 647 | GCC_WARN_UNDECLARED_SELECTOR = YES; 648 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 649 | GCC_WARN_UNUSED_FUNCTION = YES; 650 | GCC_WARN_UNUSED_VARIABLE = YES; 651 | MACOSX_DEPLOYMENT_TARGET = 10.10; 652 | MTL_ENABLE_DEBUG_INFO = NO; 653 | SDKROOT = macosx; 654 | }; 655 | name = Release; 656 | }; 657 | 0D0EA9B21B62BFE20041A897 /* Debug */ = { 658 | isa = XCBuildConfiguration; 659 | buildSettings = { 660 | COMBINE_HIDPI_IMAGES = YES; 661 | INFOPLIST_FILE = KeychainMinder/Info.plist; 662 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Security/SecurityAgentPlugins"; 663 | PRODUCT_BUNDLE_IDENTIFIER = "com.google.corp.$(PRODUCT_NAME:rfc1034identifier)"; 664 | PRODUCT_NAME = "$(TARGET_NAME)"; 665 | SKIP_INSTALL = YES; 666 | WRAPPER_EXTENSION = bundle; 667 | }; 668 | name = Debug; 669 | }; 670 | 0D0EA9B31B62BFE20041A897 /* Release */ = { 671 | isa = XCBuildConfiguration; 672 | buildSettings = { 673 | COMBINE_HIDPI_IMAGES = YES; 674 | INFOPLIST_FILE = KeychainMinder/Info.plist; 675 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Security/SecurityAgentPlugins"; 676 | PRODUCT_BUNDLE_IDENTIFIER = "com.google.corp.$(PRODUCT_NAME:rfc1034identifier)"; 677 | PRODUCT_NAME = "$(TARGET_NAME)"; 678 | SKIP_INSTALL = YES; 679 | WRAPPER_EXTENSION = bundle; 680 | }; 681 | name = Release; 682 | }; 683 | 0D0EA9DA1B62D9AE0041A897 /* Debug */ = { 684 | isa = XCBuildConfiguration; 685 | buildSettings = { 686 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 687 | COMBINE_HIDPI_IMAGES = YES; 688 | GCC_PREPROCESSOR_DEFINITIONS = ( 689 | "DEBUG=1", 690 | "$(inherited)", 691 | ); 692 | INFOPLIST_FILE = KeychainMinderGUI/Info.plist; 693 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 694 | MACOSX_DEPLOYMENT_TARGET = 10.8; 695 | PRODUCT_BUNDLE_IDENTIFIER = "com.google.corp.$(PRODUCT_NAME:rfc1034identifier)"; 696 | PRODUCT_NAME = "$(TARGET_NAME)"; 697 | }; 698 | name = Debug; 699 | }; 700 | 0D0EA9DB1B62D9AE0041A897 /* Release */ = { 701 | isa = XCBuildConfiguration; 702 | buildSettings = { 703 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 704 | COMBINE_HIDPI_IMAGES = YES; 705 | INFOPLIST_FILE = KeychainMinderGUI/Info.plist; 706 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 707 | MACOSX_DEPLOYMENT_TARGET = 10.8; 708 | PRODUCT_BUNDLE_IDENTIFIER = "com.google.corp.$(PRODUCT_NAME:rfc1034identifier)"; 709 | PRODUCT_NAME = "$(TARGET_NAME)"; 710 | }; 711 | name = Release; 712 | }; 713 | C739E5181CA46B9200A863A3 /* Debug */ = { 714 | isa = XCBuildConfiguration; 715 | baseConfigurationReference = 6E66D2C8B758DD6C070BA4D5 /* Pods-KeychainMinderTests.debug.xcconfig */; 716 | buildSettings = { 717 | COMBINE_HIDPI_IMAGES = YES; 718 | DEBUG_INFORMATION_FORMAT = dwarf; 719 | GCC_NO_COMMON_BLOCKS = YES; 720 | INFOPLIST_FILE = KeychainMinderTests/Info.plist; 721 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 722 | MACOSX_DEPLOYMENT_TARGET = 10.11; 723 | PRODUCT_BUNDLE_IDENTIFIER = com.google.corp.KeychainMinderTests; 724 | PRODUCT_NAME = "$(TARGET_NAME)"; 725 | }; 726 | name = Debug; 727 | }; 728 | C739E5191CA46B9200A863A3 /* Release */ = { 729 | isa = XCBuildConfiguration; 730 | baseConfigurationReference = 11F3EEBEBC8A87689D4180F9 /* Pods-KeychainMinderTests.release.xcconfig */; 731 | buildSettings = { 732 | COMBINE_HIDPI_IMAGES = YES; 733 | COPY_PHASE_STRIP = NO; 734 | GCC_NO_COMMON_BLOCKS = YES; 735 | INFOPLIST_FILE = KeychainMinderTests/Info.plist; 736 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 737 | MACOSX_DEPLOYMENT_TARGET = 10.11; 738 | PRODUCT_BUNDLE_IDENTIFIER = com.google.corp.KeychainMinderTests; 739 | PRODUCT_NAME = "$(TARGET_NAME)"; 740 | }; 741 | name = Release; 742 | }; 743 | C7F073041C99BD9C0049A7C7 /* Debug */ = { 744 | isa = XCBuildConfiguration; 745 | baseConfigurationReference = A9A7BB8BD2328D9F3836BCEA /* Pods-KeychainMinderAgent.debug.xcconfig */; 746 | buildSettings = { 747 | DEBUG_INFORMATION_FORMAT = dwarf; 748 | GCC_NO_COMMON_BLOCKS = YES; 749 | MACOSX_DEPLOYMENT_TARGET = 10.11; 750 | PRODUCT_NAME = "$(TARGET_NAME)"; 751 | }; 752 | name = Debug; 753 | }; 754 | C7F073051C99BD9C0049A7C7 /* Release */ = { 755 | isa = XCBuildConfiguration; 756 | baseConfigurationReference = FBD3F60D5C773D146528EFF2 /* Pods-KeychainMinderAgent.release.xcconfig */; 757 | buildSettings = { 758 | COPY_PHASE_STRIP = NO; 759 | GCC_NO_COMMON_BLOCKS = YES; 760 | MACOSX_DEPLOYMENT_TARGET = 10.11; 761 | PRODUCT_NAME = "$(TARGET_NAME)"; 762 | }; 763 | name = Release; 764 | }; 765 | /* End XCBuildConfiguration section */ 766 | 767 | /* Begin XCConfigurationList section */ 768 | 0D0EA9A51B62BFE20041A897 /* Build configuration list for PBXProject "KeychainMinder" */ = { 769 | isa = XCConfigurationList; 770 | buildConfigurations = ( 771 | 0D0EA9AF1B62BFE20041A897 /* Debug */, 772 | 0D0EA9B01B62BFE20041A897 /* Release */, 773 | ); 774 | defaultConfigurationIsVisible = 0; 775 | defaultConfigurationName = Release; 776 | }; 777 | 0D0EA9B11B62BFE20041A897 /* Build configuration list for PBXNativeTarget "KeychainMinder" */ = { 778 | isa = XCConfigurationList; 779 | buildConfigurations = ( 780 | 0D0EA9B21B62BFE20041A897 /* Debug */, 781 | 0D0EA9B31B62BFE20041A897 /* Release */, 782 | ); 783 | defaultConfigurationIsVisible = 0; 784 | defaultConfigurationName = Release; 785 | }; 786 | 0D0EA9D91B62D9AE0041A897 /* Build configuration list for PBXNativeTarget "KeychainMinderGUI" */ = { 787 | isa = XCConfigurationList; 788 | buildConfigurations = ( 789 | 0D0EA9DA1B62D9AE0041A897 /* Debug */, 790 | 0D0EA9DB1B62D9AE0041A897 /* Release */, 791 | ); 792 | defaultConfigurationIsVisible = 0; 793 | defaultConfigurationName = Release; 794 | }; 795 | C739E51A1CA46B9200A863A3 /* Build configuration list for PBXNativeTarget "KeychainMinderTests" */ = { 796 | isa = XCConfigurationList; 797 | buildConfigurations = ( 798 | C739E5181CA46B9200A863A3 /* Debug */, 799 | C739E5191CA46B9200A863A3 /* Release */, 800 | ); 801 | defaultConfigurationIsVisible = 0; 802 | defaultConfigurationName = Release; 803 | }; 804 | C7F073061C99BD9C0049A7C7 /* Build configuration list for PBXNativeTarget "KeychainMinderAgent" */ = { 805 | isa = XCConfigurationList; 806 | buildConfigurations = ( 807 | C7F073041C99BD9C0049A7C7 /* Debug */, 808 | C7F073051C99BD9C0049A7C7 /* Release */, 809 | ); 810 | defaultConfigurationIsVisible = 0; 811 | defaultConfigurationName = Release; 812 | }; 813 | /* End XCConfigurationList section */ 814 | }; 815 | rootObject = 0D0EA9A21B62BFE20041A897 /* Project object */; 816 | } 817 | -------------------------------------------------------------------------------- /KeychainMinder.xcodeproj/xcshareddata/xcschemes/KeychainMinder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /KeychainMinder.xcodeproj/xcshareddata/xcschemes/KeychainMinderTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /KeychainMinder.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KeychainMinder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.6 19 | CFBundleVersion 20 | 1.6 21 | NSHumanReadableCopyright 22 | Copyright © 2015 Google Inc. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /KeychainMinder/main.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #include "AuthorizationTypes.h" 16 | #include "Common.h" 17 | 18 | #import 19 | #import 20 | 21 | #include "KeychainMinderAgentProtocol.h" 22 | 23 | #pragma mark Utility Functions 24 | 25 | NSString *GetStringFromContext(MechanismRecord *mechanism, AuthorizationString key) { 26 | const AuthorizationValue *value; 27 | AuthorizationContextFlags flags; 28 | OSStatus err = mechanism->pluginRecord->callbacks->GetContextValue( 29 | mechanism->engineRef, key, &flags, &value); 30 | if (err == errSecSuccess && value->length > 0) { 31 | NSString *s = [[NSString alloc] initWithBytes:value->data 32 | length:value->length 33 | encoding:NSUTF8StringEncoding]; 34 | return [s stringByReplacingOccurrencesOfString:@"\0" withString:@""]; 35 | } 36 | return nil; 37 | } 38 | 39 | NSString *GetStringFromHint(MechanismRecord *mechanism, AuthorizationString key) { 40 | const AuthorizationValue *value; 41 | OSStatus err = mechanism->pluginRecord->callbacks->GetHintValue(mechanism->engineRef, key, 42 | &value); 43 | if (err == errSecSuccess && value->length > 0) { 44 | NSString *s = [[NSString alloc] initWithBytes:value->data 45 | length:value->length 46 | encoding:NSUTF8StringEncoding]; 47 | return [s stringByReplacingOccurrencesOfString:@"\0" withString:@""]; 48 | } 49 | return nil; 50 | } 51 | 52 | uid_t GetUIDFromContext(MechanismRecord *mechanism) { 53 | uid_t uid = -2; 54 | const AuthorizationValue *value; 55 | AuthorizationContextFlags flags; 56 | if (mechanism->pluginRecord->callbacks->GetContextValue(mechanism->engineRef, 57 | kKMAuthUID, 58 | &flags, 59 | &value) == errAuthorizationSuccess) { 60 | if ((value->length == sizeof(uid_t)) && (value->data != NULL)) { 61 | uid = *(const uid_t *)value->data; 62 | } 63 | } 64 | return uid; 65 | } 66 | 67 | gid_t GetGIDFromContext(MechanismRecord *mechanism) { 68 | gid_t gid = -2; 69 | const AuthorizationValue *value; 70 | AuthorizationContextFlags flags; 71 | if (mechanism->pluginRecord->callbacks->GetContextValue(mechanism->engineRef, 72 | kKMAuthGID, 73 | &flags, 74 | &value) == errAuthorizationSuccess) { 75 | if ((value->length == sizeof(gid_t)) && (value->data != NULL)) { 76 | gid = *(const gid_t *)value->data; 77 | } 78 | } 79 | return gid; 80 | } 81 | 82 | OSStatus AllowLogin(MechanismRecord *mechanism) { 83 | return mechanism->pluginRecord->callbacks->SetResult(mechanism->engineRef, 84 | kAuthorizationResultAllow); 85 | } 86 | 87 | #pragma mark Mechanism Functions 88 | 89 | OSStatus MechanismCreate( 90 | AuthorizationPluginRef inPlugin, 91 | AuthorizationEngineRef inEngine, 92 | AuthorizationMechanismId mechanismId, 93 | AuthorizationMechanismRef *outMechanism) { 94 | MechanismRecord *mechanism = (MechanismRecord *)malloc(sizeof(MechanismRecord)); 95 | if (mechanism == NULL) return errSecMemoryError; 96 | mechanism->magic = kMechanismMagic; 97 | mechanism->engineRef = inEngine; 98 | mechanism->pluginRecord = (PluginRecord *)inPlugin; 99 | *outMechanism = mechanism; 100 | return errSecSuccess; 101 | } 102 | 103 | OSStatus MechanismDestroy(AuthorizationMechanismRef inMechanism) { 104 | free(inMechanism); 105 | return errSecSuccess; 106 | } 107 | 108 | OSStatus MechanismInvoke(AuthorizationMechanismRef inMechanism) { 109 | MechanismRecord *mechanism = (MechanismRecord *)inMechanism; 110 | @autoreleasepool { 111 | uid_t uid = GetUIDFromContext(mechanism); 112 | gid_t gid = GetGIDFromContext(mechanism); 113 | 114 | // Make sure this is not a hidden user. 115 | if (uid < 501) { 116 | return AllowLogin(mechanism); 117 | } 118 | 119 | NSString *username = GetStringFromContext(mechanism, kAuthorizationEnvironmentUsername); 120 | NSString *password = GetStringFromContext(mechanism, kAuthorizationEnvironmentPassword); 121 | NSString *sesOwner = GetStringFromHint(mechanism, kKMAuthSuggestedUser); 122 | 123 | // Make sure we have username and password data. 124 | if (!username || !password) { 125 | return AllowLogin(mechanism); 126 | } 127 | 128 | // Make sure the auth user is the sesion owner. 129 | if (![username isEqualToString:sesOwner]) { 130 | return AllowLogin(mechanism); 131 | } 132 | 133 | BOOL keychainPasswordValid = YES; 134 | 135 | // Switch the per thread EUID/EGID to the target user so SecKeychain* knows who to affect, 136 | // validate the login keychain password, then switch back to the previous user. 137 | // Using pthread as to not disrupt all of authorizationhost. 138 | if (pthread_setugid_np(uid, gid) != 0) { 139 | return AllowLogin(mechanism); 140 | } 141 | 142 | SecKeychainSetUserInteractionAllowed(NO); 143 | keychainPasswordValid = ValidateLoginKeychainPassword(password); 144 | // Revert back to the default ids 145 | pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE); 146 | 147 | // Remove the current user, so they aren't duplicated in a second if 148 | // the password wasn't valid. 149 | NSMutableArray *users = GetUsers(); 150 | [users removeObject:username]; 151 | 152 | if (!keychainPasswordValid) { 153 | NSData *passwordData = [NSKeyedArchiver archivedDataWithRootObject:password]; 154 | 155 | NSXPCConnection *connectionToService = 156 | [[NSXPCConnection alloc] initWithMachServiceName:kKeychainMinderAgentServiceName 157 | options:NSXPCConnectionPrivileged]; 158 | connectionToService.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: 159 | @protocol(KeychainMinderAgentProtocol)]; 160 | [connectionToService resume]; 161 | 162 | id remoteObject = [connectionToService remoteObjectProxyWithErrorHandler:^(NSError *error) { 163 | NSLog(@"%@", [error debugDescription]); 164 | }]; 165 | 166 | [remoteObject setPassword:passwordData withReply:^(BOOL reply) { 167 | NSLog(@"KeychainMinderAgent %@", reply ? @"Sucess" : @"Fail"); 168 | }]; 169 | 170 | [users addObject:username]; 171 | } 172 | 173 | SetUsers(users); 174 | } 175 | 176 | return AllowLogin(mechanism); 177 | } 178 | 179 | OSStatus MechanismDeactivate(AuthorizationMechanismRef inMechanism) { 180 | MechanismRecord *mechanism = (MechanismRecord *)inMechanism; 181 | return mechanism->pluginRecord->callbacks->DidDeactivate(mechanism->engineRef); 182 | } 183 | 184 | #pragma mark Plugin Functions 185 | 186 | OSStatus PluginDestroy(AuthorizationPluginRef inPlugin) { 187 | free(inPlugin); 188 | return errSecSuccess; 189 | } 190 | 191 | OSStatus AuthorizationPluginCreate( 192 | const AuthorizationCallbacks *callbacks, 193 | AuthorizationPluginRef *outPlugin, 194 | const AuthorizationPluginInterface **outPluginInterface) { 195 | PluginRecord *plugin = (PluginRecord *)malloc(sizeof(PluginRecord)); 196 | if (plugin == NULL) return errSecMemoryError; 197 | plugin->magic = kPluginMagic; 198 | plugin->callbacks = callbacks; 199 | *outPlugin = plugin; 200 | 201 | static AuthorizationPluginInterface pluginInterface = { 202 | kAuthorizationPluginInterfaceVersion, 203 | &PluginDestroy, 204 | &MechanismCreate, 205 | &MechanismInvoke, 206 | &MechanismDeactivate, 207 | &MechanismDestroy 208 | }; 209 | *outPluginInterface = &pluginInterface; 210 | 211 | return errSecSuccess; 212 | } 213 | -------------------------------------------------------------------------------- /KeychainMinder/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | if [[ $EUID -ne 0 ]]; then 17 | echo "This script must be run as root" 1>&2 18 | exit 1 19 | fi 20 | 21 | updatearg="" 22 | for arg in $*; do 23 | if [ "$arg" == "--restore-screensaver" ]; then 24 | updatearg=$arg 25 | else 26 | echo "unknown argument: $arg" 27 | exit 1 28 | fi 29 | done 30 | 31 | /usr/bin/env python /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle/Contents/Resources/update_authdb.py remove $updatearg 32 | /bin/rm -rf /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle 33 | /bin/rm /Library/LaunchAgents/com.google.corp.keychainminder.plist 34 | /bin/rm /Library/LaunchDaemons/com.google.corp.keychainminderagent.plist 35 | /bin/rm /Library/Preferences/com.google.corp.keychainminder.plist 36 | 37 | user=$(/usr/bin/stat -f '%u' /dev/console) 38 | [[ -z "$user" ]] && exit 0 39 | /bin/launchctl asuser ${user} /bin/launchctl remove com.google.corp.keychainminder 40 | /bin/launchctl remove com.google.corp.KeychainMinderAgent 41 | -------------------------------------------------------------------------------- /KeychainMinder/update_authdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.7 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | Python module for installing/removing the KeychainMinder mechanism 18 | from the authorization database. Only designed for 10.8+. 19 | 20 | This can either be used as a standalone script or imported and used 21 | in another Python script. 22 | """ 23 | 24 | import argparse 25 | import plistlib 26 | import os 27 | import subprocess 28 | import sys 29 | 30 | 31 | KEYCHAIN_MINDER_MECHANISM = 'KeychainMinder:check,privileged' 32 | SCREENSAVER_RULE = 'authenticate-session-owner' 33 | SCREENSAVER_RULE_ALLOW_ADMIN_UNLOCK = 'authenticate-session-owner-or-admin' 34 | SCREENSAVER_PRETTY_RULE = 'use-login-window-ui' 35 | 36 | AUTHENTICATE_RIGHT = 'authenticate' 37 | LOGIN_DONE_RIGHT = 'system.login.done' 38 | SCREENSAVER_RIGHT = 'system.login.screensaver' 39 | 40 | 41 | def _GetRightData(right): 42 | """Get the current configuration for the requested right as a dict.""" 43 | output = subprocess.check_output( 44 | ['/usr/bin/security', 'authorizationdb', 'read', right], 45 | stderr=subprocess.PIPE) 46 | data = plistlib.readPlistFromString(output) 47 | return data 48 | 49 | 50 | def _SetRightData(right, data): 51 | """Update the configuration for the requested right.""" 52 | data = plistlib.writePlistToString(data) 53 | p = subprocess.Popen( 54 | ['/usr/bin/security', 'authorizationdb', 'write', right], 55 | stdin=subprocess.PIPE, 56 | stderr=subprocess.PIPE) 57 | _, stderr = p.communicate(input=data) 58 | return stderr.count('YES') == 1 59 | 60 | 61 | def InstallPlugin(allow_admin_unlock, no_screensaver): 62 | """Install the plugin to both rules and update screensaver right.""" 63 | for right in [AUTHENTICATE_RIGHT, LOGIN_DONE_RIGHT]: 64 | data = _GetRightData(right) 65 | mechanisms = data.get('mechanisms', []) 66 | if not mechanisms.count(KEYCHAIN_MINDER_MECHANISM): 67 | mechanisms.append(KEYCHAIN_MINDER_MECHANISM) 68 | data['mechanisms'] = mechanisms 69 | if _SetRightData(right, data): 70 | print '%s: Mechanism installed.' % right 71 | else: 72 | print '%s: Failed to install mechanism' % right 73 | else: 74 | print '%s: Mechanism already installed.' % right 75 | 76 | if no_screensaver: 77 | return 78 | 79 | target_rule = SCREENSAVER_RULE 80 | if allow_admin_unlock: 81 | target_rule = SCREENSAVER_RULE_ALLOW_ADMIN_UNLOCK 82 | 83 | data = _GetRightData(SCREENSAVER_RIGHT) 84 | if data.get('rule') != [target_rule]: 85 | data['rule'] = [target_rule] 86 | if _SetRightData(SCREENSAVER_RIGHT, data): 87 | print '%s: Rule updated.' % SCREENSAVER_RIGHT 88 | else: 89 | print '%s: Failed to update rule.' % SCREENSAVER_RIGHT 90 | else: 91 | print '%s: Rule already correct.' % SCREENSAVER_RIGHT 92 | 93 | 94 | def RemovePlugin(restore_screensaver): 95 | """Remove the plugin from both rules.""" 96 | for right in [AUTHENTICATE_RIGHT, LOGIN_DONE_RIGHT]: 97 | data = _GetRightData(right) 98 | mechanisms = data.get('mechanisms', []) 99 | if mechanisms.count(KEYCHAIN_MINDER_MECHANISM): 100 | mechanisms.remove(KEYCHAIN_MINDER_MECHANISM) 101 | data['mechanisms'] = mechanisms 102 | if _SetRightData(right, data): 103 | print '%s: Mechanism removed.' % right 104 | else: 105 | print '%s: Failed to remove mechanism.' % right 106 | else: 107 | print '%s: Mechanism already removed.' % right 108 | 109 | if restore_screensaver: 110 | data = _GetRightData(SCREENSAVER_RIGHT) 111 | if data.get('rule') != [SCREENSAVER_PRETTY_RULE]: 112 | data['rule'] = [SCREENSAVER_PRETTY_RULE] 113 | if _SetRightData(SCREENSAVER_RIGHT, data): 114 | print '%s: Rule updated.' % SCREENSAVER_RIGHT 115 | else: 116 | print '%s: Failed to update rule.' % SCREENSAVER_RIGHT 117 | else: 118 | print '%s: Rule already correct.' % SCREENSAVER_RIGHT 119 | 120 | 121 | def CheckForRoot(): 122 | if not os.geteuid() == 0: 123 | sys.exit('This script requires root privileges') 124 | 125 | 126 | def ParseOptions(): 127 | parser = argparse.ArgumentParser() 128 | subparsers = parser.add_subparsers(dest='subparser_name') 129 | 130 | parser_install = subparsers.add_parser('install') 131 | parser_install.add_argument( 132 | '--allow-admin-unlock', action='store_true', dest='allow_admin_unlock', 133 | help='Allow administrators to unlock any session') 134 | parser_install.add_argument('--no-screensaver', action='store_true', 135 | dest='no_screensaver', help='Don\'t update screensaver rule') 136 | 137 | parser_remove = subparsers.add_parser('remove') 138 | parser_remove.add_argument( 139 | '--restore-screensaver', action='store_true', dest='restore_screensaver', 140 | help='Restore \'new\' screensaver UI') 141 | 142 | return parser.parse_args() 143 | 144 | 145 | def main(argv): 146 | CheckForRoot() 147 | options = ParseOptions() 148 | 149 | if options.subparser_name == 'install': 150 | InstallPlugin(allow_admin_unlock=options.allow_admin_unlock, 151 | no_screensaver=options.no_screensaver) 152 | elif options.subparser_name == 'remove': 153 | RemovePlugin(restore_screensaver=options.restore_screensaver) 154 | 155 | 156 | if __name__ == '__main__': 157 | main(sys.argv) 158 | -------------------------------------------------------------------------------- /KeychainMinderAgent/KeychainMinderAgent.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "KeychainMinderAgentProtocol.h" 16 | 17 | #import 18 | 19 | @interface KeychainMinderAgent : NSObject 20 | 21 | - (void)setPassword:(NSData *)inPassword withReply:(void (^)(BOOL))reply; 22 | - (void)getPasswordWithReply:(void (^)(NSData *))reply; 23 | - (void)run; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /KeychainMinderAgent/KeychainMinderAgent.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "KeychainMinderAgent.h" 16 | 17 | #import 18 | #import 19 | 20 | @interface KeychainMinderAgent() 21 | 22 | @property (nonatomic, strong) NSXPCListener *listener; 23 | @property (nonatomic, strong) NSMutableData *passwordData; 24 | 25 | @end 26 | 27 | @implementation KeychainMinderAgent 28 | 29 | - (id)init { 30 | self = [super init]; 31 | if (self) { 32 | // Set up our XPC listener to handle requests on our Mach service. 33 | _listener = [[NSXPCListener alloc] initWithMachServiceName:kKeychainMinderAgentServiceName]; 34 | _listener.delegate = self; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)run { 40 | // Tell the XPC listener to start processing requests. 41 | [self.listener resume]; 42 | 43 | // Run the run loop forever. 44 | [[NSRunLoop currentRunLoop] run]; 45 | } 46 | 47 | - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConn { 48 | pid_t pid = newConn.processIdentifier; 49 | 50 | MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf]; 51 | MOLCodesignChecker *remoteCS = [[MOLCodesignChecker alloc] initWithPID:pid]; 52 | 53 | // Add an exemption for authorizationhost.bundle connections. This is needed so the authorization 54 | // plugin use this XPC service. 55 | NSString *const ahReqString = @"identifier \"com.apple.authorizationhost\" and anchor apple"; 56 | SecRequirementRef ahRequirements = NULL; 57 | SecRequirementCreateWithString((__bridge CFStringRef _Nonnull)ahReqString, 58 | kSecCSDefaultFlags, &ahRequirements); 59 | 60 | // Only allow connections that are signed with the same CS or match the SecRequirementRef 61 | if ([remoteCS signingInformationMatches:selfCS] || 62 | [remoteCS validateWithRequirement:ahRequirements]) { 63 | newConn.exportedInterface = [NSXPCInterface interfaceWithProtocol: 64 | @protocol(KeychainMinderAgentProtocol)]; 65 | newConn.exportedObject = self; 66 | [newConn resume]; 67 | return YES; 68 | } 69 | return NO; 70 | } 71 | 72 | - (void)setPassword:(NSData *)inPassword withReply:(void (^)(BOOL))reply { 73 | _passwordData = inPassword.mutableCopy; 74 | reply(_passwordData != nil); 75 | } 76 | 77 | - (void)getPasswordWithReply:(void (^)(NSData *))reply { 78 | reply(_passwordData); 79 | [_passwordData resetBytesInRange:NSMakeRange(0, _passwordData.length)]; 80 | // The connection has completed it's task of handing off the login password to 81 | // the KeychainMinderGUI. Kill the agent. 82 | exit(0); 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /KeychainMinderAgent/KeychainMinderAgentProtocol.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | 17 | static NSString *const kKeychainMinderAgentServiceName = @"com.google.corp.KeychainMinderAgent"; 18 | 19 | @protocol KeychainMinderAgentProtocol 20 | 21 | - (void)getPasswordWithReply:(void (^)(NSData *))reply; 22 | - (void)setPassword:(NSData *)inPassword withReply:(void (^)(BOOL))reply; 23 | 24 | @end 25 | 26 | -------------------------------------------------------------------------------- /KeychainMinderAgent/main.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | #import "KeychainMinderAgent.h" 17 | 18 | int main(int argc, const char * argv[]) { 19 | @autoreleasepool { 20 | KeychainMinderAgent *keychainMinderAgent = [[KeychainMinderAgent alloc] init]; 21 | [keychainMinderAgent run]; 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /KeychainMinderGUI/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | @import Cocoa; 16 | 17 | @interface AppDelegate : NSObject 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /KeychainMinderGUI/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "AppDelegate.h" 16 | 17 | #import "PasswordKnownView.h" 18 | #import "PasswordNotKnownView.h" 19 | 20 | #import "KeychainMinderAgentProtocol.h" 21 | 22 | @import QuartzCore; 23 | 24 | @interface AppDelegate () 25 | @property (weak) IBOutlet NSWindow *window; 26 | @property (weak) IBOutlet NSView *viewArea; 27 | 28 | @property (weak) IBOutlet NSImageView *imageView; 29 | 30 | @property (weak) IBOutlet PasswordKnownView *knownView; 31 | @property (weak) IBOutlet PasswordNotKnownView *notKnownView; 32 | 33 | @property NSXPCConnection *connectionToService; 34 | @property id remoteObject; 35 | @property (nonatomic, strong) NSString *password; 36 | 37 | @end 38 | 39 | @implementation AppDelegate 40 | 41 | - (void)applicationWillFinishLaunching:(NSNotification *)notification { 42 | [self.window setLevel:NSScreenSaverWindowLevel]; 43 | [self.window setMovable:NO]; 44 | [self.window setCanBecomeVisibleWithoutLogin:YES]; 45 | [self.window setCanHide:NO]; 46 | self.connectionToService = [[NSXPCConnection alloc] 47 | initWithMachServiceName:kKeychainMinderAgentServiceName 48 | options:NSXPCConnectionPrivileged]; 49 | self.connectionToService.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: 50 | @protocol(KeychainMinderAgentProtocol)]; 51 | [self.connectionToService resume]; 52 | self.remoteObject = [self.connectionToService remoteObjectProxyWithErrorHandler:^(NSError *err) { 53 | NSLog(@"%@", [err description]); 54 | }]; 55 | [self.window makeFirstResponder:nil]; 56 | [NSApp activateIgnoringOtherApps:YES]; 57 | 58 | NSLog(@"KeychainMinder launched for %@", NSUserName()); 59 | } 60 | 61 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { 62 | return YES; 63 | } 64 | 65 | - (void)updateIcon { 66 | [self.imageView setImage:[NSImage imageNamed:@"Keychain_Unlocked"]]; 67 | } 68 | 69 | - (IBAction)passwordKnown:(id)sender { 70 | [self updateIcon]; 71 | [[self.viewArea subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; 72 | [[self.viewArea animator] addSubview:self.knownView.view]; 73 | [self.remoteObject getPasswordWithReply:^(NSData *inPassword) { 74 | if (inPassword) { 75 | _password = (NSString *)[NSKeyedUnarchiver unarchiveObjectWithData:inPassword]; 76 | [self.knownView updatePassword:_password]; 77 | } 78 | }]; 79 | } 80 | 81 | - (IBAction)passwordUnknown:(id)sender { 82 | [self updateIcon]; 83 | [[self.viewArea subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; 84 | [[self.viewArea animator] addSubview:self.notKnownView.view]; 85 | [self.remoteObject getPasswordWithReply:^(NSData *inPassword) { 86 | if (inPassword) { 87 | _password = (NSString *)[NSKeyedUnarchiver unarchiveObjectWithData:inPassword]; 88 | [self.notKnownView updatePassword:_password]; 89 | } 90 | }]; 91 | } 92 | 93 | -(void)applicationWillTerminate:(NSNotification *)notification { 94 | [self.connectionToService invalidate]; 95 | } 96 | 97 | @end -------------------------------------------------------------------------------- /KeychainMinderGUI/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Your account password is out of sync with your login keychain password. 50 | This can cause problems with applications that use the keychain. 51 | 52 | 53 | 54 | 55 | 65 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | YnBsaXN0MDDUAQIDBAUGJCVYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCBES 83 | EwsKGR9VJG51bGzUCQoLDA0ODxBWJGNsYXNzVHR5cGVXc3VidHlwZV8QEl9fQ0FDb2RpbmdDb250ZW50 84 | c4AIgAOAAoAEWWZyb21SaWdodFRwdXNo0hQJFRhaTlMub2JqZWN0c6IWF4AFgAaAB9IaGxwdWiRjbGFz 85 | c25hbWVYJGNsYXNzZXNXTlNBcnJheaIcHlhOU09iamVjdNIaGyAhXENBVHJhbnNpdGlvbqMiIx5cQ0FU 86 | cmFuc2l0aW9uW0NBQW5pbWF0aW9uXxAPTlNLZXllZEFyY2hpdmVy0SYnVHJvb3SAAQAIABEAGgAjAC0A 87 | MgA3AEEARwBQAFcAXABkAHkAewB9AH8AgQCLAJAAlQCgAKMApQCnAKkArgC5AMIAygDNANYA2wDoAOwA 88 | +QEFARcBGgEfAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAAASE 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /KeychainMinderGUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.6 19 | CFBundleVersion 20 | 1.6 21 | LSMinimumSystemVersion 22 | $(MACOSX_DEPLOYMENT_TARGET) 23 | LSUIElement 24 | 25 | NSHumanReadableCopyright 26 | Copyright © 2015 Google Inc. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /KeychainMinderGUI/Keychain_Locked.png: -------------------------------------------------------------------------------- 1 | /Applications/Utilities/Keychain Access.app/Contents/Resources/Keychain_Locked@2x.png -------------------------------------------------------------------------------- /KeychainMinderGUI/Keychain_Unlocked.png: -------------------------------------------------------------------------------- 1 | /Applications/Utilities/Keychain Access.app/Contents/Resources/Keychain_Unlocked@2x.png -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordKnownView.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "PasswordViewController.h" 16 | 17 | @interface PasswordKnownView : PasswordViewController; 18 | - (void)updatePassword:(NSString *)inPassword; 19 | @end 20 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordKnownView.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "PasswordKnownView.h" 16 | 17 | #import "Common.h" 18 | 19 | @import QuartzCore; 20 | 21 | /// From Security.framework 22 | extern OSStatus SecKeychainChangePassword(SecKeychainRef keychainRef, 23 | UInt32 oldPasswordLength, 24 | const void* oldPassword, 25 | UInt32 newPasswordLength, 26 | const void* newPassword); 27 | 28 | @interface PasswordKnownView () 29 | @property IBOutlet NSTextField *previousPassword; 30 | @property IBOutlet NSTextField *currentPassword; 31 | @property IBOutlet NSButton *okButton; 32 | @property IBOutlet NSProgressIndicator *spinner; 33 | @end 34 | 35 | @implementation PasswordKnownView 36 | 37 | - (void)updatePassword:(NSString *)inPassword { 38 | dispatch_async(dispatch_get_main_queue(), ^{ 39 | [self.currentPassword setStringValue:inPassword]; 40 | [self.currentPassword setEnabled:NO]; 41 | }); 42 | } 43 | 44 | - (NSArray *)textFields { 45 | return @[ self.previousPassword, self.currentPassword ]; 46 | } 47 | 48 | - (void)beginProcessing { 49 | [super beginProcessing]; 50 | self.okButton.enabled = NO; 51 | [self.spinner startAnimation:self]; 52 | } 53 | 54 | - (void)endProcessing { 55 | [super endProcessing]; 56 | self.okButton.enabled = YES; 57 | [self.spinner stopAnimation:self]; 58 | } 59 | 60 | - (IBAction)readyToContinue:(id)sender { 61 | [self beginProcessing]; 62 | 63 | if (!ValidateLoginKeychainPassword(self.previousPassword.stringValue)) { 64 | [self badPasswordField:self.previousPassword]; 65 | return; 66 | } 67 | 68 | if (!ValidateLoginPassword(self.currentPassword.stringValue)) { 69 | [self badPasswordField:self.currentPassword]; 70 | return; 71 | } 72 | 73 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 74 | OSStatus ret = [self changeKeychainPasswordOldPassword:self.previousPassword.stringValue 75 | newPassword:self.currentPassword.stringValue]; 76 | NSLog(@"KeychainMinder Change: %d", ret); 77 | [NSApp terminate:sender]; 78 | }); 79 | } 80 | 81 | - (OSStatus)changeKeychainPasswordOldPassword:(NSString *)oldPw newPassword:(NSString *)newPw { 82 | return SecKeychainChangePassword( 83 | NULL, (UInt32)oldPw.length, [oldPw UTF8String], (UInt32)newPw.length, [newPw UTF8String]); 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordKnownView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | NSAllRomanInputSourcesLocaleIdentifier 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | NSAllRomanInputSourcesLocaleIdentifier 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordNotKnownView.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "PasswordViewController.h" 16 | 17 | @interface PasswordNotKnownView : PasswordViewController 18 | - (void)updatePassword:(NSString *)inPassword; 19 | @end 20 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordNotKnownView.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "PasswordNotKnownView.h" 16 | 17 | #import "Common.h" 18 | 19 | @import QuartzCore; 20 | 21 | /// From Security.framework 22 | extern OSStatus SecKeychainResetLogin(UInt32 passwordLength, 23 | const void* password, 24 | Boolean resetSearchList); 25 | 26 | @interface PasswordNotKnownView () 27 | @property IBOutlet NSTextField *password; 28 | @property IBOutlet NSButton *okButton; 29 | @property IBOutlet NSProgressIndicator *spinner; 30 | @end 31 | 32 | @implementation PasswordNotKnownView 33 | 34 | - (void)updatePassword:(NSString *)inPassword { 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | [self.password setStringValue:inPassword]; 37 | [self.password setEnabled:NO]; 38 | }); 39 | } 40 | 41 | - (NSArray *)textFields { 42 | return @[ self.password ]; 43 | } 44 | 45 | - (void)beginProcessing { 46 | [super beginProcessing]; 47 | self.okButton.enabled = NO; 48 | [self.spinner startAnimation:self]; 49 | } 50 | 51 | - (void)endProcessing { 52 | [super endProcessing]; 53 | self.okButton.enabled = YES; 54 | [self.spinner stopAnimation:self]; 55 | } 56 | 57 | - (IBAction)readyToContinue:(id)sender { 58 | [self beginProcessing]; 59 | 60 | if (!ValidateLoginPassword(self.password.stringValue)) { 61 | [self badPasswordField:self.password]; 62 | return; 63 | } 64 | 65 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 66 | OSStatus ret = [self resetKeychainWithPassword:self.password.stringValue]; 67 | NSLog(@"KeychainMinder Reset: %d", ret); 68 | [NSApp terminate:sender]; 69 | }); 70 | } 71 | 72 | - (OSStatus)resetKeychainWithPassword:(NSString *)password { 73 | return SecKeychainResetLogin((UInt32)password.length, [password UTF8String], YES); 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordNotKnownView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | NSAllRomanInputSourcesLocaleIdentifier 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | A new keychain will be created with the password matching your login password. 52 | 53 | Enter your password: 54 | 55 | 56 | 57 | 58 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordViewController.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | @import Cocoa; 16 | 17 | @interface PasswordViewController : NSViewController 18 | 19 | /// Subclasses should override and return an array of 20 | /// text fields, in the order they appear on screen. 21 | - (NSArray *)textFields; 22 | 23 | /// Call when the user has chosen to continue. Disables the focus ring around 24 | /// each of the textfields. 25 | - (void)beginProcessing; 26 | 27 | /// Called when processing is over. Re-enables the text field focus rings. 28 | - (void)endProcessing; 29 | 30 | /// Shakes the provided text field, empties it, calls endProcessing and then selects it. 31 | - (void)badPasswordField:(NSTextField *)textField; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /KeychainMinderGUI/PasswordViewController.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "PasswordViewController.h" 16 | 17 | @import QuartzCore; 18 | 19 | @implementation PasswordViewController 20 | 21 | - (instancetype)init { 22 | return [super initWithNibName:NSStringFromClass([self class]) bundle:nil]; 23 | } 24 | 25 | - (void)viewDidAppear { 26 | NSTextField *firstField = [[self textFields] firstObject]; 27 | if ([firstField isKindOfClass:[NSTextField class]]) { 28 | [self.view.window makeFirstResponder:firstField]; 29 | } 30 | } 31 | 32 | - (NSArray *)textFields { 33 | [self doesNotRecognizeSelector:_cmd]; 34 | return nil; 35 | } 36 | 37 | - (void)beginProcessing { 38 | for (NSTextField *textField in [self textFields]) { 39 | if (![textField isKindOfClass:[NSTextField class]]) continue; 40 | textField.focusRingType = NSFocusRingTypeNone; 41 | } 42 | } 43 | 44 | - (void)endProcessing { 45 | for (NSTextField *textField in [self textFields]) { 46 | if (![textField isKindOfClass:[NSTextField class]]) continue; 47 | textField.focusRingType = NSFocusRingTypeDefault; 48 | } 49 | } 50 | 51 | - (CAAnimation *)makeShakeAnimation { 52 | CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; 53 | animation.keyPath = @"position.x"; 54 | animation.values = @[ @0, @10, @-10, @10, @-10, @10, @0 ]; 55 | animation.keyTimes = @[ @0, @(1 / 6.0), @(2 / 6.0), @(3 / 6.0), @(4 / 6.0), @(5 / 6.0), @1 ]; 56 | animation.duration = 0.6; 57 | animation.additive = YES; 58 | return animation; 59 | } 60 | 61 | - (void)badPasswordField:(NSTextField *)textField { 62 | [CATransaction begin]; 63 | [CATransaction setCompletionBlock:^{ 64 | [textField setStringValue:@""]; 65 | [textField setEnabled:YES]; 66 | [self endProcessing]; 67 | [self.view.window makeFirstResponder:textField]; 68 | }]; 69 | [textField.layer addAnimation:[self makeShakeAnimation] forKey:@"shake"]; 70 | [CATransaction commit]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /KeychainMinderGUI/main.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #include "Common.h" 16 | 17 | @import Cocoa; 18 | 19 | int main(int argc, const char * argv[]) { 20 | if (![GetUsers() containsObject:NSUserName()]) exit(0); 21 | return NSApplicationMain(argc, argv); 22 | } 23 | -------------------------------------------------------------------------------- /KeychainMinderTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.6 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.6 23 | 24 | 25 | -------------------------------------------------------------------------------- /KeychainMinderTests/KMCallbackEngine.h: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | #import "AuthorizationTypes.h" 17 | 18 | @interface KMCallbackEngine : NSObject 19 | 20 | // Contex 21 | @property (strong, nonatomic) NSString *username; 22 | @property (strong, nonatomic) NSString *password; 23 | @property (strong, nonatomic) NSDictionary *authenticationAuthority; 24 | @property (nonatomic) uid_t uid; 25 | @property (nonatomic) gid_t gid; 26 | 27 | // Hint 28 | @property (strong, nonatomic) NSString *tokenName; 29 | @property (strong, nonatomic) NSString *authorizeRight; 30 | @property (strong, nonatomic) NSString *suggestedUser; 31 | @property (strong, nonatomic) NSString *clientPath; 32 | 33 | - (OSStatus)getContextValue:(AuthorizationEngineRef)inEngine 34 | forKey:(AuthorizationString)inKey 35 | outFlags:(AuthorizationContextFlags *)outContextFlags 36 | outValue:(const AuthorizationValue **)outValue; 37 | 38 | - (OSStatus)getHintValue:(AuthorizationEngineRef)inEngine 39 | forKey:(AuthorizationString)inKey 40 | outValue:(const AuthorizationValue **)outValue; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /KeychainMinderTests/KMCallbackEngine.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import "KMCallbackEngine.h" 16 | 17 | @interface KMCallbackEngine() 18 | @property (nonatomic, strong) NSMutableDictionary *context; 19 | @property (nonatomic, strong) NSMutableDictionary *hint; 20 | @end 21 | 22 | @implementation KMCallbackEngine 23 | - (id)init { 24 | if ([super init]) { 25 | _context = [[NSMutableDictionary alloc] init]; 26 | _hint = [[NSMutableDictionary alloc] init]; 27 | } 28 | return self; 29 | } 30 | 31 | - (OSStatus)getContextValue:(AuthorizationEngineRef)inEngine 32 | forKey:(AuthorizationString)inKey 33 | outFlags:(AuthorizationContextFlags *)outContextFlags 34 | outValue:(const AuthorizationValue **)outValue { 35 | NSString *key = [NSString stringWithCString:inKey encoding:NSUTF8StringEncoding]; 36 | 37 | if ([_context[key] isKindOfClass:[NSString class]]) { 38 | const AuthorizationValue value = { (UInt32)[_context[key] length], 39 | (void *)[_context[key] UTF8String] }; 40 | (*outValue) = &value; 41 | } else if ([key isEqualToString:@kKMAuthAuthenticationAuthority]) { 42 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:(NSDictionary *)_context[key] 43 | format:NSPropertyListXMLFormat_v1_0 44 | options:0 45 | error:nil]; 46 | const AuthorizationValue value = { (UInt32)[data length], (void *)[data bytes] }; 47 | (*outValue) = &value; 48 | } else if ([key isEqualToString:@kKMAuthUID] || 49 | [key isEqualToString:@kKMAuthGID]) { 50 | const AuthorizationValue value = { (UInt32)[_context[key] length], 51 | (void *)[_context[key] bytes] }; 52 | (*outValue) = &value; 53 | } else { 54 | const AuthorizationValue value = { (UInt32)0, NULL }; 55 | (*outValue) = &value; 56 | } 57 | 58 | return _context[key] ? errAuthorizationSuccess : errAuthorizationInternal; 59 | } 60 | 61 | - (OSStatus)getHintValue:(AuthorizationEngineRef)inEngine 62 | forKey:(AuthorizationString)inKey 63 | outValue:(const AuthorizationValue **)outValue { 64 | NSString *key = [NSString stringWithCString:inKey encoding:NSUTF8StringEncoding]; 65 | 66 | if ([_hint[key] isKindOfClass:[NSString class]]) { 67 | const AuthorizationValue value = { (UInt32)[_hint[key] length], 68 | (void *)[_hint[key] UTF8String] }; 69 | (*outValue) = &value; 70 | } else { 71 | const AuthorizationValue value = { (UInt32)0, NULL }; 72 | (*outValue) = &value; 73 | } 74 | 75 | return _hint[key] ? errAuthorizationSuccess : errAuthorizationInternal; 76 | } 77 | 78 | - (void)setUsername:(NSString *)username { 79 | _context[@kAuthorizationEnvironmentUsername] = username; 80 | } 81 | 82 | - (void)setPassword:(NSString *)password { 83 | _context[@kAuthorizationEnvironmentPassword] = password; 84 | } 85 | 86 | - (void)setAuthenticationAuthority:(NSDictionary *)authenticationAuthority { 87 | _context[@kKMAuthAuthenticationAuthority] = authenticationAuthority; 88 | } 89 | 90 | - (void)setUid:(uid_t)uid { 91 | NSData *data = [NSData dataWithBytes:&uid length:sizeof(uid_t)]; 92 | _context[@kKMAuthUID] = data; 93 | } 94 | 95 | - (void)setGid:(gid_t)gid { 96 | NSData *data = [NSData dataWithBytes:&gid length:sizeof(gid_t)]; 97 | _context[@kKMAuthGID] = data; 98 | } 99 | 100 | - (void)setTokenName:(NSString *)tokenName { 101 | _hint[@kKMAuthTokenName] = tokenName; 102 | } 103 | 104 | - (void)setAuthorizeRight:(NSString *)authorizeRight { 105 | _hint[@kKMAuthAuthorizeRight] = authorizeRight; 106 | } 107 | 108 | - (void)setSuggestedUser:(NSString *)suggestedUser { 109 | _hint[@kKMAuthSuggestedUser] = suggestedUser; 110 | } 111 | 112 | - (void)setClientPath:(NSString *)clientPath { 113 | _hint[@kKMAuthClientPath] = clientPath; 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /KeychainMinderTests/KeychainMinderTests.m: -------------------------------------------------------------------------------- 1 | /// Copyright 2015 Google Inc. All rights reserved. 2 | /// 3 | /// Licensed under the Apache License, Version 2.0 (the "License"); 4 | /// you may not use this file except in compliance with the License. 5 | /// You may obtain a copy of the License at 6 | /// 7 | /// http://www.apache.org/licenses/LICENSE-2.0 8 | /// 9 | /// Unless required by applicable law or agreed to in writing, software 10 | /// distributed under the License is distributed on an "AS IS" BASIS, 11 | /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | /// See the License for the specific language governing permissions and 13 | /// limitations under the License. 14 | 15 | #import 16 | 17 | #import 18 | #import 19 | 20 | #import "KMCallbackEngine.h" 21 | #import "AuthorizationTypes.h" 22 | #import "KeychainMinderAgentProtocol.h" 23 | 24 | AuthorizationResult GlobalResult; 25 | 26 | KMCallbackEngine *callbackEngine = nil; 27 | 28 | OSStatus KMSetResult(AuthorizationEngineRef inEngine, AuthorizationResult inResult) { 29 | GlobalResult = inResult; 30 | return errAuthorizationSuccess; 31 | } 32 | 33 | OSStatus KMDidDeactivate(AuthorizationEngineRef inEngine) { 34 | return errAuthorizationSuccess; 35 | } 36 | 37 | OSStatus KMGetContextValue(AuthorizationEngineRef inEngine, 38 | AuthorizationString inKey, 39 | AuthorizationContextFlags *outContextFlags, 40 | const AuthorizationValue **outValue) { 41 | return [callbackEngine getContextValue:inEngine 42 | forKey:inKey 43 | outFlags:outContextFlags 44 | outValue:outValue]; 45 | } 46 | 47 | OSStatus KMGetHintValue(AuthorizationEngineRef inEngine, 48 | AuthorizationString inKey, 49 | const AuthorizationValue **outValue) { 50 | return [callbackEngine getHintValue:inEngine 51 | forKey:inKey 52 | outValue:outValue]; 53 | } 54 | 55 | @interface KeychainMinderTests : XCTestCase 56 | 57 | @end 58 | 59 | @implementation KeychainMinderTests 60 | 61 | // 62 | // Basic test to run through the plugin's funtions to ensure AuthorizationSuccess. 63 | // 64 | - (void)testKMPluginCoreAuthorizationSuccess { 65 | callbackEngine = [[KMCallbackEngine alloc] init]; 66 | [callbackEngine setUsername:@"bur"]; 67 | [callbackEngine setPassword:@"tomtom"]; 68 | [callbackEngine setUid:501]; 69 | [callbackEngine setGid:20]; 70 | [callbackEngine setAuthorizeRight:@"authenticate"]; 71 | [callbackEngine setClientPath:@"/System/Library/CoreServices/loginwindow.app"]; 72 | [callbackEngine setSuggestedUser:@"bur"]; 73 | 74 | AuthorizationCallbacks *callbacks = 75 | (AuthorizationCallbacks *)malloc(sizeof(AuthorizationCallbacks)); 76 | callbacks->version = kAuthorizationCallbacksVersion; 77 | callbacks->SetResult = &KMSetResult; 78 | callbacks->DidDeactivate = &KMDidDeactivate; 79 | callbacks->GetContextValue = &KMGetContextValue; 80 | callbacks->GetHintValue = &KMGetHintValue; 81 | 82 | AuthorizationPluginRef plugin; 83 | const AuthorizationPluginInterface *pluginInterface; 84 | AuthorizationPluginCreate(callbacks, &plugin, &pluginInterface); 85 | PluginRecord *pluginRecord = (PluginRecord *)plugin; 86 | XCTAssertTrue(pluginRecord->magic == kPluginMagic); 87 | XCTAssertTrue(pluginRecord->callbacks != NULL); 88 | XCTAssertTrue(pluginRecord->callbacks->version >= kAuthorizationCallbacksVersion); 89 | 90 | NSArray *mechIDs = @[ @"check" ]; 91 | 92 | for (NSString *mechID in mechIDs) { 93 | GlobalResult = -1; 94 | AuthorizationEngineRef engine; 95 | AuthorizationMechanismRef mechanism; 96 | pluginInterface->MechanismCreate(plugin, engine, [mechID UTF8String], &mechanism); 97 | MechanismRecord *mechanismRecord = (MechanismRecord *)mechanism; 98 | XCTAssertTrue(mechanismRecord->magic == kMechanismMagic); 99 | XCTAssertTrue(mechanismRecord->pluginRecord != NULL); 100 | pluginInterface->MechanismInvoke(mechanism); 101 | XCTAssertTrue(GlobalResult == kAuthorizationResultAllow); 102 | pluginInterface->MechanismDeactivate(mechanism); 103 | pluginInterface->MechanismDestroy(mechanism); 104 | } 105 | 106 | pluginInterface->PluginDestroy(plugin); 107 | free(callbacks); 108 | } 109 | 110 | - (void)testRequirementsBytes { 111 | unsigned char xctestReqBytes[44] = { 112 | 0xFA, 0xDE, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 113 | 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6F, 0x6D, 0x2E, 0x61, 0x70, 114 | 0x70, 0x6C, 0x65, 0x2E, 0x78, 0x63, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03 115 | }; 116 | NSData *xctestReqData = [[NSData alloc] initWithBytes:xctestReqBytes length:44]; 117 | SecRequirementRef xctestRequirements = nil; 118 | SecRequirementCreateWithData((__bridge CFDataRef)xctestReqData, 119 | kSecCSDefaultFlags, &xctestRequirements); 120 | MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf]; 121 | XCTAssertTrue([selfCS validateWithRequirement:xctestRequirements]); 122 | 123 | } 124 | 125 | - (void)testRequirementsBase64 { 126 | NSString *xctestReqBase64 = @"+t4MAAAAACwAAAABAAAABgAAAAIAAAAQY29tLmFwcGxlLnhjdGVzdAAAAAM="; 127 | NSData *xctestReqData = [[NSData alloc] initWithBase64EncodedString:xctestReqBase64 options:0]; 128 | SecRequirementRef xctestRequirements = nil; 129 | SecRequirementCreateWithData((__bridge CFDataRef)xctestReqData, 130 | kSecCSDefaultFlags, &xctestRequirements); 131 | MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf]; 132 | XCTAssertTrue([selfCS validateWithRequirement:xctestRequirements]); 133 | } 134 | 135 | - (void)testRequirementsString { 136 | NSString *xctestReqString = @"identifier \"com.apple.xctest\" and anchor apple"; 137 | SecRequirementRef xctestRequirements = nil; 138 | SecRequirementCreateWithString((__bridge CFStringRef _Nonnull)xctestReqString, 139 | kSecCSDefaultFlags, &xctestRequirements); 140 | MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf]; 141 | XCTAssertTrue([selfCS validateWithRequirement:xctestRequirements]); 142 | } 143 | 144 | 145 | // This is not a test. It can be used to debug the XPC Connection. 146 | // To debug with XCTests you will need to edit the ahReqString in KeychainMinderAgent.m 147 | // Set the identifier to: @"\"com.apple.xctest\" and anchor apple" 148 | //- (void)testXPCConnection { 149 | // NSData *passwordData = [NSKeyedArchiver archivedDataWithRootObject:@"TOMTOM"]; 150 | // 151 | // NSXPCConnection *connectionToService = 152 | // [[NSXPCConnection alloc] initWithMachServiceName:kKeychainMinderAgentServiceName 153 | // options:NSXPCConnectionPrivileged]; 154 | // connectionToService.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: 155 | // @protocol(KeychainMinderAgentProtocol)]; 156 | // [connectionToService resume]; 157 | // 158 | // id remoteObject = [connectionToService remoteObjectProxyWithErrorHandler:^(NSError *error) { 159 | // NSLog(@"%@", [error debugDescription]); 160 | // }]; 161 | // 162 | // [remoteObject setPassword:passwordData withReply:^(BOOL reply) { 163 | // NSLog(@"%hhd", reply); 164 | // }]; 165 | // 166 | // [remoteObject getPasswordWithReply:^(NSData *data) { 167 | // NSLog(@"%@", data); 168 | // }]; 169 | //} 170 | 171 | @end -------------------------------------------------------------------------------- /Package/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Package Makefile for KeychainMinder 3 | # Requires TheLuggage (github.com/unixorn/luggage) to be installed. 4 | # 5 | 6 | LUGGAGE:=/usr/local/share/luggage/luggage.make 7 | include ${LUGGAGE} 8 | 9 | TITLE:=KeychainMinder 10 | REVERSE_DOMAIN:=com.google.corp 11 | 12 | PACKAGE_VERSION:=$(shell defaults read $$PWD/../KeychainMinder/Info.plist CFBundleVersion) 13 | 14 | PAYLOAD:=pack-SecurityAgentPlugin-KeychainMinder.bundle \ 15 | pack-Library-LaunchAgents-com.google.corp.keychainminder.plist \ 16 | pack-Library-LaunchDaemons-com.google.corp.keychainminderagent.plist \ 17 | pack-script-postinstall 18 | 19 | KeychainMinder.bundle: 20 | @pod install 21 | @cd .. && xcodebuild -workspace KeychainMinder.xcworkspace -scheme KeychainMinder -configuration Archive -derivedDataPath build 22 | @cp -R ../build/Build/Products/Release/KeychainMinder.bundle . 23 | 24 | l_Library_Security: l_Library 25 | @sudo mkdir -p ${WORK_D}/Library/Security 26 | @sudo chown root:admin ${WORK_D}/Library/Security 27 | @sudo chmod 755 ${WORK_D}/Library/Security 28 | 29 | l_Library_Security_SecurityAgentPlugins: l_Library_Security 30 | @sudo mkdir -p ${WORK_D}/Library/Security/SecurityAgentPlugins 31 | @sudo chown root:admin ${WORK_D}/Library/Security/SecurityAgentPlugins 32 | @sudo chmod 755 ${WORK_D}/Library/Security/SecurityAgentPlugins 33 | 34 | pack-SecurityAgentPlugin-%: % l_Library_Security_SecurityAgentPlugins 35 | @sudo ${DITTO} --noqtn "${<}" ${WORK_D}/Library/Security/SecurityAgentPlugins/"${<}" 36 | @sudo chown -R root:wheel ${WORK_D}/Library/Security/SecurityAgentPlugins/"${<}" 37 | @sudo chmod 755 ${WORK_D}/Library/Security/SecurityAgentPlugins/"${<}" 38 | 39 | clean: myclean 40 | 41 | myclean: 42 | rm -rf ${TITLE}-*.pkg 43 | rm -rf KeychainMinder.bundle 44 | rm -rf ../build 45 | -------------------------------------------------------------------------------- /Package/com.google.corp.keychainminder.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.google.corp.keychainminder 7 | ProgramArguments 8 | 9 | /usr/bin/open 10 | /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle/Contents/Resources/KeychainMinderGUI.app 11 | 12 | WatchPaths 13 | 14 | /Library/Preferences/com.google.corp.keychainminder.plist 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Package/com.google.corp.keychainminderagent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.google.corp.KeychainMinderAgent 7 | ProgramArguments 8 | 9 | /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle/Contents/XPCServices/KeychainMinderAgent 10 | 11 | MachServices 12 | 13 | com.google.corp.KeychainMinderAgent 14 | 15 | 16 | ProcessType 17 | Interactive 18 | 19 | 20 | -------------------------------------------------------------------------------- /Package/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [[ $3 != "/" ]] && exit 0 4 | 5 | /usr/bin/python /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle/Contents/Resources/update_authdb.py install 6 | 7 | user=$(/usr/bin/stat -f '%u' /dev/console) 8 | [[ -z "$user" ]] && exit 0 9 | /bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.corp.keychainminder.plist 10 | /bin/launchctl load /Library/LaunchDaemons/com.google.corp.keychainminderagent.plist 11 | 12 | exit 0 13 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, "10.9" 2 | 3 | target :KeychainMinderAgent do 4 | pod 'MOLCertificate' 5 | pod 'MOLCodesignChecker' 6 | end 7 | 8 | target :KeychainMinderTests do 9 | pod 'MOLCertificate' 10 | pod 'MOLCodesignChecker' 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MOLCertificate (1.3) 3 | - MOLCodesignChecker (1.4): 4 | - MOLCertificate (~> 1.3) 5 | 6 | DEPENDENCIES: 7 | - MOLCertificate 8 | - MOLCodesignChecker 9 | 10 | SPEC CHECKSUMS: 11 | MOLCertificate: a776221906b5a46dd1bd749d0682bef3ee68c1f5 12 | MOLCodesignChecker: 34e60cc6beadabfb4762b6e5087e12837774f85f 13 | 14 | COCOAPODS: 0.39.0 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keychain Minder 2 | 3 | Keychain Minder is a simple OS X SecurityAgentPlugin for monitoring keychain 4 | password synchronization in enterprise environments. 5 | 6 | Ordinarily when users change their login password, OS X will update the login 7 | keychain to match. In enterprise environments, where the password is managed 8 | centrally and synchronized with the machine (via LDAP, AD, etc.) this doesn't 9 | happen. Instead, OS X has a built-in mechanism that appears after authenticating 10 | at the login window to prompt users to update their keychain passwords but many 11 | users don't know what a keychain is, don't understand the dialog or have 12 | forgotten their password. 13 | 14 | Keychain Minder re-creates this built-in mechanism but does so for screensaver 15 | and preference pane unlock instead of login. Upon noticing the password 16 | does not work for unlocking the keychain, it will pop-up a dialog informing the 17 | user and giving them the option to either change the password (using both old 18 | and new passwords) or reset the keychain. 19 | 20 | ## Testing 21 | Keychain Minder is no longer in use at Google and while we will continue to maintain it as best as we can, it will not be as well tested on future OS releases. We will still be responding to issues and pull requests. 22 | 23 | Keychain Minder has had very little testing so far but has been known to 24 | work on 10.9.5, 10.10.5 and 10.11.5. 25 | 26 | There's no real reason it shouldn't work on 10.7 and 10.8, it just hasn't 27 | been tried. If you find it works, please let us know! 28 | 29 | ## Screenshots 30 | 31 | ![Welcome](Docs/KeychainMinderWelcome.png) 32 | ![Known Password](Docs/KeychainMinderKnownPw.png) 33 | ![Unknown Password](Docs/KeychainMinderUnknownPw.png) 34 | 35 | ## Installation 36 | 37 | Download the latest release from the [Releases](https://github.com/google/macops-keychainminder/releases) page. 38 | It's a standard Apple package, inside a disk image. 39 | 40 | If you would like to customize the package there's a Makefile in the Package 41 | folder. You'll need [The Luggage](https://github.com/unixorn/luggage) installed 42 | to build it. 43 | 44 | By default the package installs KeychainMinder so that it works at the 45 | screensaver. In order to do this, it has to: 46 | 1. Restore the screensaver login UI to an older-looking UI. SecurityAgentPlugins 47 | do not run under the newer loginwindow-like UI. 48 | 2. Set the screensaver login policy to `authenticate-session-owner`. 49 | 50 | The `update_authdb.py` script has options to change both of these behaviors. 51 | 52 | ## Uninstallation 53 | 54 | `sudo /Library/Security/SecurityAgentPlugins/KeychainMinder.bundle/Contents/Resources/uninstall.sh` 55 | 56 | ## How it works 57 | 58 | During every login the plugin is invoked. It does the following: 59 | 60 | 1. Check that the right being authenticated is either system.login.screensaver 61 | or system.preferences.\* 62 | 2. Retrieve the username and password currently being authenticated. 63 | 3. If both are true, it retrieves the logging in user's default keychain path, 64 | makes a temporary hardlink to this path, opens the 'new' keychain file 65 | and attempts to unlock it with the password from 2. It then removes this 66 | hardlink. 67 | 4. Retrieves an array (encoded as a plist) from /Library/Preferences. It either 68 | adds or removes the currently authenticating user's name from this list 69 | depending on whether unlocking the keychain in step 3 was successful. 70 | 71 | While all of this is happening, launchd is watching the plist file in step 4 72 | for changes and whenever the file is changed, it launches an app embedded in 73 | the plugin. The app does the following: 74 | 75 | 1. Checks that the currently logged-in user is in the preference file on disk. 76 | If not, it exits. 77 | 2. Displays a simple UI explaining that the keychain password is out of sync 78 | and asking the user if they remember their previous password. 79 | 3a. If the user remembers their password, it asks for both old and new password, 80 | validates them both and then updates the login keychain password using 81 | SecKeychainChangePassword. This undocumented function from the Security 82 | framework will update both the login keychain and Local Items keychain. 83 | 3b. If the user does not remember their password, it asks for the new password, 84 | validates it is the same as their login password and then resets the login 85 | keychain using this new password using SecKeychainResetLogin. This 86 | undocumented function from the Security framework will reset both the login 87 | and Local Items keychains using the provided password. 88 | 89 | The hardlink/open/unlock/unlink dance used in both the plugin and UI app 90 | are to avoid locking the Local Items keychain, as doing so can cause issues 91 | when trying to update the password or reset. 92 | 93 | The undocumented functions used to update the password or reset the keychain 94 | could stop working at any time, though the same functions *are* used by Keychain Access. 95 | 96 | ## Acknowledgements 97 | 98 | Thanks to [@tomjburgin](https://twitter.com/tomjburgin) for inspiration and 99 | help getting the plugin working at the screensaver. 100 | --------------------------------------------------------------------------------