]>
Commit | Line | Data |
---|---|---|
d64be36e A |
1 | /* |
2 | * Copyright (c) 2020 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | * | |
23 | * SecSharedCredential.m - Retrieve shared credentials with AuthenticationServices. | |
24 | * | |
25 | */ | |
26 | ||
27 | #include <Security/SecSharedCredential.h> | |
28 | #include <Security/SecBasePriv.h> | |
29 | #include <utilities/SecCFError.h> | |
30 | #include <utilities/SecCFWrappers.h> | |
31 | #include "SecItemInternal.h" | |
32 | #include <dlfcn.h> | |
33 | ||
34 | #import <Foundation/Foundation.h> | |
35 | #import <AuthenticationServices/AuthenticationServices.h> | |
36 | ||
37 | // Forward declaration of the primary function implemented in this file | |
38 | OSStatus SecCopySharedWebCredentialSyncUsingAuthSvcs(CFStringRef fqdn, CFStringRef account, CFArrayRef *credentials, CFErrorRef *error); | |
39 | ||
40 | // Classes we will load dynamically | |
41 | static Class kASAuthorizationClass = NULL; | |
42 | static Class kASAuthorizationControllerClass = NULL; | |
43 | static Class kASAuthorizationPasswordProviderClass = NULL; | |
44 | static Class kASPasswordCredentialClass = NULL; | |
45 | static Class kUIApplicationClass = NULL; | |
46 | static Class kNSApplicationClass = NULL; | |
47 | ||
48 | static void loadAuthenticationServices(void) { | |
49 | static dispatch_once_t onceToken; | |
50 | dispatch_once(&onceToken, ^{ | |
51 | const char *path = "/System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices"; | |
52 | if ( [NSProcessInfo processInfo].macCatalystApp == YES ) { | |
53 | path = "/System/iOSSupport/System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices"; | |
54 | } | |
55 | void* lib_handle = dlopen(path, RTLD_LAZY); | |
56 | if (lib_handle != NULL) { | |
57 | kASAuthorizationClass = NSClassFromString(@"ASAuthorization"); | |
58 | kASAuthorizationControllerClass = NSClassFromString(@"ASAuthorizationController"); | |
59 | kASAuthorizationPasswordProviderClass = NSClassFromString(@"ASAuthorizationPasswordProvider"); | |
60 | kASPasswordCredentialClass = NSClassFromString(@"ASPasswordCredential"); | |
61 | } | |
62 | }); | |
63 | } | |
64 | ||
65 | static void loadUIKit(void) { | |
66 | static dispatch_once_t onceToken; | |
67 | dispatch_once(&onceToken, ^{ | |
68 | const char *path = "/System/Library/Frameworks/UIKit.framework/UIKit"; | |
69 | if ( [NSProcessInfo processInfo].macCatalystApp == YES ) { | |
70 | path = "/System/Library/iOSSupport/System/Library/Frameworks/UIKit.framework/UIKit"; | |
71 | } | |
72 | void* lib_handle = dlopen(path, RTLD_LAZY); | |
73 | if (lib_handle != NULL) { | |
74 | kUIApplicationClass = NSClassFromString(@"UIApplication"); | |
75 | } | |
76 | }); | |
77 | } | |
78 | ||
79 | static void loadAppKit(void) { | |
80 | static dispatch_once_t onceToken; | |
81 | dispatch_once(&onceToken, ^{ | |
82 | const char *path = "/System/Library/Frameworks/AppKit.framework/AppKit"; | |
83 | void* lib_handle = dlopen(path, RTLD_LAZY); | |
84 | if (lib_handle != NULL) { | |
85 | kNSApplicationClass = NSClassFromString(@"NSApplication"); | |
86 | } | |
87 | }); | |
88 | } | |
89 | ||
90 | static Class ASAuthorizationClass() { | |
91 | loadAuthenticationServices(); | |
92 | return kASAuthorizationClass; | |
93 | } | |
94 | ||
95 | static Class ASAuthorizationControllerClass() { | |
96 | loadAuthenticationServices(); | |
97 | return kASAuthorizationControllerClass; | |
98 | } | |
99 | ||
100 | static Class ASAuthorizationPasswordProviderClass() { | |
101 | loadAuthenticationServices(); | |
102 | return kASAuthorizationPasswordProviderClass; | |
103 | } | |
104 | ||
105 | static Class ASPasswordCredentialClass() { | |
106 | loadAuthenticationServices(); | |
107 | return kASPasswordCredentialClass; | |
108 | } | |
109 | ||
110 | static Class UIApplicationClass() { | |
111 | loadUIKit(); | |
112 | return kUIApplicationClass; | |
113 | } | |
114 | ||
115 | static Class NSApplicationClass() { | |
116 | loadAppKit(); | |
117 | return kNSApplicationClass; | |
118 | } | |
119 | ||
120 | @interface SharedCredentialController : NSObject | |
121 | <ASAuthorizationControllerDelegate, | |
122 | ASAuthorizationControllerPresentationContextProviding> | |
123 | ||
124 | -(ASPasswordCredential *)passwordCredential; | |
125 | ||
126 | @end | |
127 | ||
128 | @implementation SharedCredentialController { | |
129 | ASAuthorizationPasswordProvider *_provider; | |
130 | ASAuthorizationController *_controller; | |
131 | ASPasswordCredential *_passwordCredential; | |
132 | dispatch_semaphore_t _semaphore; | |
133 | NSError *_error; | |
134 | OSStatus _result; | |
135 | } | |
136 | ||
137 | - (void)dealloc { | |
138 | // Don't want any further callbacks since we are going away | |
139 | _controller.delegate = nil; | |
140 | _controller.presentationContextProvider = nil; | |
141 | } | |
142 | ||
143 | - (void)_requestCredential { | |
144 | if (!_provider) { | |
145 | _provider = [[ASAuthorizationPasswordProviderClass() alloc] init]; | |
146 | } | |
147 | if (!_controller) { | |
148 | _controller = [[ASAuthorizationControllerClass() alloc] initWithAuthorizationRequests:@[ [_provider createRequest] ]]; | |
149 | } | |
150 | _controller.delegate = self; | |
151 | _controller.presentationContextProvider = self; | |
152 | _semaphore = dispatch_semaphore_create(0); | |
153 | _result = errSecItemNotFound; | |
154 | _error = nil; | |
155 | ||
156 | [_controller performRequests]; | |
157 | } | |
158 | ||
159 | - (ASPasswordCredential *)passwordCredential { | |
160 | if (_passwordCredential) { | |
161 | return _passwordCredential; | |
162 | } | |
163 | BOOL shouldRequest = YES; // ( [NSProcessInfo processInfo].macCatalystApp == YES ); | |
164 | if (shouldRequest) { | |
165 | [self _requestCredential]; | |
166 | // wait synchronously until user picks a credential or cancels | |
167 | dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); | |
168 | } else { | |
169 | // unable to return a shared credential: <rdar://problem/59958701> | |
170 | _result = errSecItemNotFound; | |
171 | _error = [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:_result userInfo:NULL]; | |
172 | } | |
173 | return _passwordCredential; | |
174 | } | |
175 | ||
176 | - (NSError *)error { | |
177 | return _error; | |
178 | } | |
179 | ||
180 | - (OSStatus)result { | |
181 | return _result; | |
182 | } | |
183 | ||
184 | - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization { | |
185 | secinfo("swcagent", "SWC received didCompleteWithAuthorization"); | |
186 | ASPasswordCredential *passwordCredential = authorization.credential; | |
187 | if (![passwordCredential isKindOfClass:[ASPasswordCredentialClass() class]]) { | |
188 | _passwordCredential = nil; | |
189 | _result = errSecItemNotFound; | |
190 | } else { | |
191 | _passwordCredential = passwordCredential; | |
192 | _result = errSecSuccess; | |
193 | } | |
194 | dispatch_semaphore_signal(_semaphore); | |
195 | } | |
196 | ||
197 | - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error { | |
198 | secinfo("swcagent", "SWC received didCompleteWithError"); | |
199 | _passwordCredential = nil; | |
200 | _error = error; | |
201 | _result = errSecItemNotFound; | |
202 | dispatch_semaphore_signal(_semaphore); | |
203 | } | |
204 | ||
205 | - (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller | |
206 | { | |
207 | ASPresentationAnchor anchorWindow = nil; | |
208 | #if TARGET_OS_OSX | |
209 | if ( [NSProcessInfo processInfo].macCatalystApp == NO ) { | |
210 | anchorWindow = [[NSApplicationClass() sharedApplication] keyWindow]; | |
211 | } | |
212 | #endif | |
213 | if (!anchorWindow) { | |
214 | anchorWindow = [[UIApplicationClass() sharedApplication] keyWindow]; | |
215 | } | |
216 | return anchorWindow; | |
217 | } | |
218 | ||
219 | @end | |
220 | ||
221 | OSStatus SecCopySharedWebCredentialSyncUsingAuthSvcs(CFStringRef fqdn, CFStringRef account, CFArrayRef *credentials, CFErrorRef *error) { | |
222 | SharedCredentialController *controller = [[SharedCredentialController alloc] init]; | |
223 | ASPasswordCredential *passwordCredential = [controller passwordCredential]; | |
224 | OSStatus status = [controller result]; | |
225 | NSArray *returnedCredentials = @[]; | |
226 | if (status != errSecSuccess) { | |
227 | secinfo("swcagent", "SecCopySharedWebCredentialSyncUsingAuthSvcs received result %d", (int)status); | |
228 | if (error) { | |
229 | *error = (CFErrorRef)CFBridgingRetain([controller error]); | |
230 | } | |
231 | } else if (passwordCredential) { | |
232 | // Use the .user and .password of the passwordCredential to satisfy the SWC interface. | |
233 | NSDictionary *credential = @{ | |
234 | (id)kSecAttrServer : (__bridge NSString*)fqdn, | |
235 | (id)kSecAttrAccount : passwordCredential.user, | |
236 | (id)kSecSharedPassword : passwordCredential.password, | |
237 | }; | |
238 | returnedCredentials = @[ credential ]; | |
239 | } else { | |
240 | secinfo("swcagent", "SecCopySharedWebCredentialSyncUsingAuthSvcs found no credential"); | |
241 | status = errSecItemNotFound; | |
242 | } | |
243 | if (credentials) { | |
244 | *credentials = (CFArrayRef)CFBridgingRetain(returnedCredentials); | |
245 | } | |
246 | return status; | |
247 | } |