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