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 }