/ OSX / sec / Security / SecSharedCredential.c
SecSharedCredential.c
  1  /*
  2   * Copyright (c) 2014-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.c - CoreFoundation-based functions to store and retrieve shared credentials.
 24   *
 25   */
 26  
 27  
 28  #include <Security/SecSharedCredential.h>
 29  #include <Security/SecBasePriv.h>
 30  #include <utilities/SecCFError.h>
 31  #include <utilities/SecCFWrappers.h>
 32  #include "SecItemInternal.h"
 33  #include <ipc/securityd_client.h>
 34  #include "SecPasswordGenerate.h"
 35  
 36  /* forward declarations */
 37  OSStatus SecAddSharedWebCredentialSync(CFStringRef fqdn, CFStringRef account, CFStringRef password, CFErrorRef *error);
 38  OSStatus SecCopySharedWebCredentialSync(CFStringRef fqdn, CFStringRef account, CFArrayRef *credentials, CFErrorRef *error);
 39  #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
 40  OSStatus SecCopySharedWebCredentialSyncUsingAuthSvcs(CFStringRef fqdn, CFStringRef account, CFArrayRef *credentials, CFErrorRef *error);
 41  #endif
 42  
 43  #if SHAREDWEBCREDENTIALS
 44  
 45  // OSX now has SWC enabled, but cannot link SharedWebCredentials framework: rdar://59958701
 46  #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
 47  
 48  OSStatus SecAddSharedWebCredentialSync(CFStringRef fqdn,
 49      CFStringRef account,
 50      CFStringRef password,
 51      CFErrorRef *error)
 52  {
 53      OSStatus status = errSecUnimplemented;
 54      if (error) {
 55          SecError(status, error, CFSTR("SecAddSharedWebCredentialSync not supported on this platform"));
 56      }
 57      return status;
 58  }
 59  
 60  #else
 61  
 62  OSStatus SecAddSharedWebCredentialSync(CFStringRef fqdn,
 63      CFStringRef account,
 64      CFStringRef password,
 65      CFErrorRef *error)
 66  {
 67      OSStatus status;
 68      __block CFErrorRef* outError = error;
 69      __block CFMutableDictionaryRef args = CFDictionaryCreateMutable(kCFAllocatorDefault,
 70          0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 71      if (fqdn) {
 72          CFDictionaryAddValue(args, kSecAttrServer, fqdn);
 73      }
 74      if (account) {
 75          CFDictionaryAddValue(args, kSecAttrAccount, account);
 76      }
 77      if (password) {
 78          CFDictionaryAddValue(args, kSecSharedPassword, password);
 79      }
 80      status = SecOSStatusWith(^bool (CFErrorRef *error) {
 81          CFTypeRef raw_result = NULL;
 82          bool xpc_result = false;
 83          bool internal_spi = false; // TODO: support this for SecurityDevTests
 84          if(internal_spi && gSecurityd && gSecurityd->sec_add_shared_web_credential) {
 85              xpc_result = gSecurityd->sec_add_shared_web_credential(args, NULL, NULL, NULL, SecAccessGroupsGetCurrent(), &raw_result, error);
 86          } else {
 87              xpc_result = cftype_client_to_bool_cftype_error_request(sec_add_shared_web_credential_id, args, SecSecurityClientGet(), &raw_result, error);
 88          }
 89          CFReleaseSafe(args);
 90          if (!xpc_result) {
 91              if (NULL == *error) {
 92                  SecError(errSecInternal, error, CFSTR("Internal error (XPC failure)"));
 93              }
 94          }
 95          if (outError) {
 96              *outError = (error) ? *error : NULL;
 97              CFRetainSafe(*outError);
 98          } else {
 99              CFReleaseNull(*error);
100          }
101          CFReleaseNull(raw_result);
102          return xpc_result;
103      });
104  
105      return status;
106  }
107  #endif /* !TARGET_OS_OSX || !TARGET_OS_MACCATALYST */
108  #endif /* SHAREDWEBCREDENTIALS */
109  
110  void SecAddSharedWebCredential(CFStringRef fqdn,
111      CFStringRef account,
112      CFStringRef password,
113      void (^completionHandler)(CFErrorRef error))
114  {
115  	__block CFErrorRef error = NULL;
116  	__block dispatch_queue_t dst_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
117  #if SHAREDWEBCREDENTIALS
118  
119      /* sanity check input arguments */
120  	CFStringRef errStr = NULL;
121  	if (!fqdn || CFGetTypeID(fqdn) != CFStringGetTypeID() || !CFStringGetLength(fqdn) ||
122  		!account || CFGetTypeID(account) != CFStringGetTypeID() || !CFStringGetLength(account) ) {
123  		errStr = CFSTR("fqdn or account was not of type CFString, or not provided");
124  	}
125  	else if (password && CFGetTypeID(password) != CFStringGetTypeID()) {
126  		errStr = CFSTR("non-nil password was not of type CFString");
127  	}
128  	if (errStr) {
129  		SecError(errSecParam, &error, CFSTR("%@"), errStr);
130  		dispatch_async(dst_queue, ^{
131  			if (completionHandler) {
132  				completionHandler(error);
133  			}
134  			CFReleaseSafe(error);
135  		});
136  		return;
137  	}
138  
139  	__block CFStringRef serverStr = CFRetainSafe(fqdn);
140  	__block CFStringRef accountStr = CFRetainSafe(account);
141  	__block CFStringRef passwordStr = CFRetainSafe(password);
142  
143  	dispatch_async(dst_queue, ^{
144  		OSStatus status = SecAddSharedWebCredentialSync(serverStr, accountStr, passwordStr, &error);
145  		CFReleaseSafe(serverStr);
146  		CFReleaseSafe(accountStr);
147  		CFReleaseSafe(passwordStr);
148  
149  		if (status && !error) {
150  			SecError(status, &error, CFSTR("Error adding shared password"));
151  		}
152  		dispatch_async(dst_queue, ^{
153  			if (completionHandler) {
154  				completionHandler(error);
155  			}
156  			CFReleaseSafe(error);
157  		});
158  	});
159  #else
160      SecError(errSecParam, &error, CFSTR("SharedWebCredentials not supported on this platform"));
161      dispatch_async(dst_queue, ^{
162          if (completionHandler) {
163              completionHandler(error);
164          }
165          CFReleaseSafe(error);
166      });
167  #endif
168  }
169  
170  #if SHAREDWEBCREDENTIALS
171  
172  #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
173  
174  OSStatus SecCopySharedWebCredentialSync(CFStringRef fqdn,
175      CFStringRef account,
176      CFArrayRef *credentials,
177      CFErrorRef *error)
178  {
179      OSStatus status = errSecUnimplemented;
180      if (error) {
181          SecError(status, error, CFSTR("SecCopySharedWebCredentialSync not supported on this platform"));
182      }
183      return status;
184  }
185  
186  #else
187  
188  OSStatus SecCopySharedWebCredentialSync(CFStringRef fqdn,
189      CFStringRef account,
190      CFArrayRef *credentials,
191      CFErrorRef *error)
192  {
193      OSStatus status;
194      __block CFErrorRef* outError = error;
195      __block CFMutableDictionaryRef args = CFDictionaryCreateMutable(kCFAllocatorDefault,
196          0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
197      if (fqdn) {
198          CFDictionaryAddValue(args, kSecAttrServer, fqdn);
199      }
200      if (account) {
201          CFDictionaryAddValue(args, kSecAttrAccount, account);
202      }
203      status = SecOSStatusWith(^bool (CFErrorRef *error) {
204          CFTypeRef raw_result = NULL;
205          bool xpc_result = false;
206          bool internal_spi = false; // TODO: support this for SecurityDevTests
207          if(internal_spi && gSecurityd && gSecurityd->sec_copy_shared_web_credential) {
208              xpc_result = gSecurityd->sec_copy_shared_web_credential(args, NULL, NULL, NULL, SecAccessGroupsGetCurrent(), &raw_result, error);
209          } else {
210              xpc_result = cftype_client_to_bool_cftype_error_request(sec_copy_shared_web_credential_id, args, SecSecurityClientGet(), &raw_result, error);
211          }
212          CFReleaseSafe(args);
213          if (!xpc_result) {
214              if (NULL == *error) {
215                  SecError(errSecInternal, error, CFSTR("Internal error (XPC failure)"));
216              }
217          }
218          if (outError) {
219              *outError = (error) ? *error : NULL;
220              CFRetainSafe(*outError);
221          } else {
222              CFReleaseNull(*error);
223          }
224          if (!raw_result) {
225              raw_result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
226          }
227          *credentials = raw_result;
228          return xpc_result;
229      });
230  
231      return status;
232  }
233  #endif /* !TARGET_OS_OSX || !TARGET_OS_MACCATALYST */
234  #endif /* SHAREDWEBCREDENTIALS */
235  
236  void SecRequestSharedWebCredential(CFStringRef fqdn,
237      CFStringRef account,
238      void (^completionHandler)(CFArrayRef credentials, CFErrorRef error))
239  {
240  	__block CFErrorRef error = NULL;
241  	__block dispatch_queue_t dst_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
242  #if SHAREDWEBCREDENTIALS
243      __block CFArrayRef result = NULL;
244  
245      /* sanity check input arguments, if provided */
246  	CFStringRef errStr = NULL;
247  	if (fqdn && (CFGetTypeID(fqdn) != CFStringGetTypeID() || !CFStringGetLength(fqdn))) {
248  		errStr = CFSTR("fqdn was empty or not a CFString");
249      }
250      else if (account && (CFGetTypeID(account) != CFStringGetTypeID() || !CFStringGetLength(account))) {
251  		errStr = CFSTR("account was empty or not a CFString");
252  	}
253  	if (errStr) {
254  		SecError(errSecParam, &error, CFSTR("%@"), errStr);
255  		dispatch_async(dst_queue, ^{
256  			if (completionHandler) {
257  				completionHandler(result, error);
258  			}
259  			CFReleaseSafe(error);
260              CFReleaseSafe(result);
261  		});
262  		return;
263  	}
264  
265      __block CFStringRef serverStr = CFRetainSafe(fqdn);
266  	__block CFStringRef accountStr = CFRetainSafe(account);
267  
268      dispatch_async(dst_queue, ^{
269  #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
270  		OSStatus status = SecCopySharedWebCredentialSyncUsingAuthSvcs(serverStr, accountStr, &result, &error);
271  #else
272  		OSStatus status = SecCopySharedWebCredentialSync(serverStr, accountStr, &result, &error);
273  #endif
274  		CFReleaseSafe(serverStr);
275  		CFReleaseSafe(accountStr);
276  
277  		if (status && !error) {
278  			SecError(status, &error, CFSTR("Error copying shared password"));
279  		}
280  		dispatch_async(dst_queue, ^{
281  			if (completionHandler) {
282  				completionHandler(result, error);
283  			}
284  			CFReleaseSafe(error);
285  			CFReleaseSafe(result);
286  		});
287  	});
288  #else
289      SecError(errSecParam, &error, CFSTR("SharedWebCredentials not supported on this platform"));
290      dispatch_async(dst_queue, ^{
291          if (completionHandler) {
292              completionHandler(NULL, error);
293          }
294          CFReleaseSafe(error);
295      });
296  #endif /* SHAREDWEBCREDENTIALS */
297  
298  }
299  
300  CFStringRef SecCreateSharedWebCredentialPassword(void)
301  {
302  
303      CFStringRef password = NULL;
304      CFErrorRef error = NULL;
305      CFMutableDictionaryRef passwordRequirements = NULL;
306  
307      CFStringRef allowedCharacters = CFSTR("abcdefghkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789");
308      CFCharacterSetRef requiredCharactersLower = CFCharacterSetCreateWithCharactersInString(NULL, CFSTR("abcdefghkmnopqrstuvwxyz"));
309      CFCharacterSetRef requiredCharactersUppder = CFCharacterSetCreateWithCharactersInString(NULL, CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ"));
310      CFCharacterSetRef requiredCharactersNumbers = CFCharacterSetCreateWithCharactersInString(NULL, CFSTR("3456789"));
311  
312      int groupSize = 3;
313      int groupCount = 4;
314      int totalLength = (groupSize * groupCount);
315      CFNumberRef groupSizeRef = CFNumberCreate(NULL, kCFNumberIntType, &groupSize);
316      CFNumberRef groupCountRef = CFNumberCreate(NULL, kCFNumberIntType, &groupCount);
317      CFNumberRef totalLengthRef = CFNumberCreate(NULL, kCFNumberIntType, &totalLength);
318      CFStringRef separator = CFSTR("-");
319  
320      CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
321      CFArrayAppendValue(requiredCharacterSets, requiredCharactersLower);
322      CFArrayAppendValue(requiredCharacterSets, requiredCharactersUppder);
323      CFArrayAppendValue(requiredCharacterSets, requiredCharactersNumbers);
324  
325      passwordRequirements = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
326      CFDictionaryAddValue(passwordRequirements, kSecPasswordAllowedCharactersKey, allowedCharacters);
327      CFDictionaryAddValue(passwordRequirements, kSecPasswordRequiredCharactersKey, requiredCharacterSets);
328      CFDictionaryAddValue(passwordRequirements, kSecPasswordGroupSize, groupSizeRef );
329      CFDictionaryAddValue(passwordRequirements, kSecPasswordNumberOfGroups, groupCountRef);
330      CFDictionaryAddValue(passwordRequirements, kSecPasswordSeparator, separator);
331      CFDictionaryAddValue(passwordRequirements, kSecPasswordMaxLengthKey, totalLengthRef);
332      CFDictionaryAddValue(passwordRequirements, kSecPasswordMinLengthKey, totalLengthRef);
333      CFDictionaryAddValue(passwordRequirements, kSecPasswordDefaultForType, CFSTR("false"));
334      CFRelease(requiredCharactersLower);
335      CFRelease(requiredCharactersUppder);
336      CFRelease(requiredCharactersNumbers);
337      CFRelease(groupSizeRef);
338      CFRelease(groupCountRef);
339      CFRelease(totalLengthRef);
340  
341      password = SecPasswordGenerate(kSecPasswordTypeSafari, &error, passwordRequirements);
342  
343      CFRelease(requiredCharacterSets);
344      CFRelease(passwordRequirements);
345      if ((error && error != errSecSuccess) || !password)
346      {
347          if (password) CFRelease(password);
348          secwarning("SecPasswordGenerate failed to generate a password for SecCreateSharedWebCredentialPassword.");
349          return NULL;
350      } else {
351          return password;
352      }
353  
354  }