]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecSharedCredential.m
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / sec / Security / SecSharedCredential.m
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 }