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