]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountCredentials.c
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountCredentials.c
1 /*
2 * Copyright (c) 2013-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
24
25 #include <stdio.h>
26 #include "SOSAccountPriv.h"
27 #include "SOSPeerInfoCollections.h"
28 #include "SOSTransport.h"
29
30 #define kPublicKeyNotAvailable "com.apple.security.publickeynotavailable"
31
32 //
33 // MARK: User Credential management
34 //
35
36 void SOSAccountSetPreviousPublic(SOSAccountRef account) {
37 CFReleaseNull(account->previous_public);
38 account->previous_public = account->user_public;
39 CFRetain(account->previous_public);
40 }
41
42 static void SOSAccountRemoveInvalidApplications(SOSAccountRef account, SOSCircleRef circle)
43 {
44 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
45 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
46 if (!SOSPeerInfoApplicationVerify(peer, account->user_public, NULL))
47 CFSetAddValue(peersToRemove, peer);
48 });
49
50 CFSetForEach(peersToRemove, ^(const void *value) {
51 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
52
53 SOSCircleWithdrawRequest(circle, peer, NULL);
54 });
55 }
56
57 // List of things to do
58 // Update myFullPeerInfo in circle if needed
59 // Fix iCloud Identity if needed
60 // Gen sign if private key changed
61
62 static bool sosAccountUpgradeiCloudIdentity(SOSCircleRef circle, SecKeyRef privKey) {
63 bool retval = false;
64 SOSFullPeerInfoRef cloud_fpi = SOSCircleCopyiCloudFullPeerInfoRef(circle, NULL);
65 require_quiet(cloud_fpi != NULL, errOut);
66 require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi, privKey, NULL), errOut);
67 retval = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(cloud_fpi));
68 errOut:
69 return retval;
70 }
71
72 static void SOSAccountGenerationSignatureUpdateWith(SOSAccountRef account, SecKeyRef privKey) {
73 if (account->trusted_circle && account->my_identity) {
74 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
75 SOSPeerInfoRef myPI = SOSAccountGetMyPeerInfo(account);
76 bool iAmPeer = SOSCircleHasPeer(account->trusted_circle, myPI, NULL);
77 bool change = SOSCircleUpdatePeerInfo(circle, myPI);
78 if(iAmPeer && !SOSCircleVerify(circle, account->user_public, NULL)) {
79 change |= sosAccountUpgradeiCloudIdentity(circle, privKey);
80 SOSAccountRemoveInvalidApplications(account, circle);
81 change |= SOSCircleGenerationSign(circle, privKey, account->my_identity, NULL);
82 account->departure_code = kSOSNeverLeftCircle;
83 } else if(iAmPeer) {
84 SOSFullPeerInfoRef icfpi = SOSCircleCopyiCloudFullPeerInfoRef(circle, NULL);
85 if(!icfpi) {
86 SOSAccountRemoveIncompleteiCloudIdentities(account, circle, privKey, NULL);
87 change |= SOSAccountAddiCloudIdentity(account, circle, privKey, NULL);
88 } else {
89 CFReleaseNull(icfpi);
90 }
91 }
92 secnotice("updatingGenSignature", "we changed the circle? %@", change ? CFSTR("YES") : CFSTR("NO"));
93 return change;
94 });
95 }
96 }
97
98 bool SOSAccountGenerationSignatureUpdate(SOSAccountRef account, CFErrorRef *error) {
99 bool result = false;
100 SecKeyRef priv_key = SOSAccountGetPrivateCredential(account, error);
101 require_quiet(priv_key, bail);
102
103 SOSAccountGenerationSignatureUpdateWith(account, priv_key);
104
105 result = true;
106 bail:
107 return result;
108 }
109
110 /* this one is meant to be local - not published over KVS. */
111 static bool SOSAccountPeerSignatureUpdate(SOSAccountRef account, SecKeyRef privKey, CFErrorRef *error) {
112 return account->my_identity && SOSFullPeerInfoUpgradeSignatures(account->my_identity, privKey, error);
113 }
114
115
116 void SOSAccountPurgePrivateCredential(SOSAccountRef account)
117 {
118 CFReleaseNull(account->_user_private);
119 CFReleaseNull(account->_password_tmp);
120 if (account->user_private_timer) {
121 dispatch_source_cancel(account->user_private_timer);
122 dispatch_release(account->user_private_timer);
123 account->user_private_timer = NULL;
124 xpc_transaction_end();
125 }
126 if (account->lock_notification_token != NOTIFY_TOKEN_INVALID) {
127 notify_cancel(account->lock_notification_token);
128 account->lock_notification_token = NOTIFY_TOKEN_INVALID;
129 }
130 }
131
132
133 static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account, bool public_was_trusted, SecKeyRef privKey)
134 {
135 if (!privKey) return;
136 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey);
137
138 if (account->user_public && account->user_public_trusted && CFEqual(publicKey, account->user_public)) {
139 CFReleaseNull(publicKey);
140 return;
141 }
142
143 if(public_was_trusted && account->user_public) {
144 CFReleaseNull(account->previous_public);
145 account->previous_public = account->user_public;
146 CFRetain(account->previous_public);
147 }
148
149 CFReleaseNull(account->user_public);
150 account->user_public = publicKey;
151 account->user_public_trusted = true;
152
153 if(!account->previous_public) {
154 account->previous_public = account->user_public;
155 CFRetain(account->previous_public);
156 }
157
158 secnotice("keygen", "trusting new public key: %@", account->user_public);
159 }
160
161 void SOSAccountSetUnTrustedUserPublicKey(SOSAccountRef account, SecKeyRef publicKey) {
162 if(account->user_public_trusted && account->user_public) {
163 secnotice("keygen", "Moving : %@ to previous_public", account->user_public);
164 CFRetainAssign(account->previous_public, account->user_public);
165 }
166
167 CFReleaseNull(account->user_public);
168 account->user_public = publicKey;
169 account->user_public_trusted = false;
170
171 if(!account->previous_public) {
172 CFRetainAssign(account->previous_public, account->user_public);
173 }
174
175 secnotice("keygen", "not trusting new public key: %@", account->user_public);
176 notify_post(kPublicKeyNotAvailable);
177 }
178
179
180 static void SOSAccountSetPrivateCredential(SOSAccountRef account, SecKeyRef private, CFDataRef password) {
181 if (!private)
182 return SOSAccountPurgePrivateCredential(account);
183
184 CFRetain(private);
185 CFReleaseSafe(account->_user_private);
186 account->_user_private = private;
187 CFReleaseSafe(account->_password_tmp);
188 account->_password_tmp = CFDataCreateCopy(kCFAllocatorDefault, password);
189
190 bool resume_timer = false;
191 if (!account->user_private_timer) {
192 xpc_transaction_begin();
193 resume_timer = true;
194 account->user_private_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, account->queue);
195 dispatch_source_set_event_handler(account->user_private_timer, ^{
196 SOSAccountPurgePrivateCredential(account);
197 });
198
199 notify_register_dispatch(kUserKeybagStateChangeNotification, &account->lock_notification_token, account->queue, ^(int token) {
200 bool locked = false;
201 CFErrorRef lockCheckError = NULL;
202
203 if (!SecAKSGetIsLocked(&locked, &lockCheckError)) {
204 secerror("Checking for locked after change failed: %@", lockCheckError);
205 }
206
207 if (locked) {
208 SOSAccountPurgePrivateCredential(account);
209 }
210 });
211 }
212
213 // (Re)set the timer's fire time to now + 120 seconds with a 5 second fuzz factor.
214 dispatch_time_t purgeTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * 60 * NSEC_PER_SEC));
215 dispatch_source_set_timer(account->user_private_timer, purgeTime, DISPATCH_TIME_FOREVER, (int64_t)(5 * NSEC_PER_SEC));
216 if (resume_timer)
217 dispatch_resume(account->user_private_timer);
218 }
219
220 SecKeyRef SOSAccountGetPrivateCredential(SOSAccountRef account, CFErrorRef* error)
221 {
222 if (account->_user_private == NULL) {
223 SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("Private Key not available - failed to prompt user recently"), NULL, error);
224 }
225 return account->_user_private;
226 }
227
228 CFDataRef SOSAccountGetCachedPassword(SOSAccountRef account, CFErrorRef* error)
229 {
230 if (account->_password_tmp == NULL) {
231 secnotice("keygen", "Password cache expired");
232 }
233 return account->_password_tmp;
234 }
235
236 SecKeyRef SOSAccountGetTrustedPublicCredential(SOSAccountRef account, CFErrorRef* error)
237 {
238 if (account->user_public == NULL || account->user_public_trusted == false) {
239 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key not available - failed to register before call"), NULL, error);
240 return NULL;
241 }
242 return account->user_public;
243 }
244
245
246
247 bool SOSAccountHasPublicKey(SOSAccountRef account, CFErrorRef* error)
248 {
249 return SOSAccountGetTrustedPublicCredential(account, error);
250 }
251
252 static void sosAccountSetTrustedCredentials(SOSAccountRef account, CFDataRef user_password, SecKeyRef user_private, bool public_was_trusted) {
253 if(!SOSAccountFullPeerInfoVerify(account, user_private, NULL)) {
254 (void) SOSAccountPeerSignatureUpdate(account, user_private, NULL);
255 }
256 SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private);
257 SOSAccountSetPrivateCredential(account, user_private, user_password);
258 SOSAccountCheckForAlwaysOnViews(account);
259 }
260
261 static SecKeyRef sosAccountCreateKeyIfPasswordIsCorrect(SOSAccountRef account, CFDataRef user_password, CFErrorRef *error) {
262 SecKeyRef user_private = NULL;
263 require_quiet(account->user_public && account->user_key_parameters, errOut);
264 user_private = SOSUserKeygen(user_password, account->user_key_parameters, error);
265 require_quiet(user_private, errOut);
266 SecKeyRef public_candidate = SecKeyCreatePublicFromPrivate(user_private);
267 if(!CFEqualSafe(account->user_public, public_candidate)) {
268 secnotice("keygen", "Public keys don't match: expected: %@, calculated: %@", account->user_public, public_candidate);
269 CFReleaseNull(user_private);
270 }
271 CFReleaseSafe(public_candidate);
272 errOut:
273 return user_private;
274 }
275
276 static bool sosAccountValidatePasswordOrFail(SOSAccountRef account, CFDataRef user_password, CFErrorRef *error) {
277 SecKeyRef privKey = sosAccountCreateKeyIfPasswordIsCorrect(account, user_password, error);
278 if(!privKey) {
279 if(account->user_key_parameters) debugDumpUserParameters(CFSTR("sosAccountValidatePasswordOrFail"), account->user_key_parameters);
280 SOSCreateError(kSOSErrorWrongPassword, CFSTR("Could not create correct key with password."), NULL, error);
281 return false;
282 }
283 sosAccountSetTrustedCredentials(account, user_password, privKey, account->user_public_trusted);
284 CFReleaseNull(privKey);
285 return true;
286 }
287
288 void SOSAccountSetParameters(SOSAccountRef account, CFDataRef parameters) {
289 CFRetainAssign(account->user_key_parameters, parameters);
290 }
291
292 bool SOSAccountAssertUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error)
293 {
294 bool public_was_trusted = account->user_public_trusted;
295 account->user_public_trusted = false;
296 SecKeyRef user_private = NULL;
297 CFDataRef parameters = NULL;
298
299 // if this succeeds, skip to the end. Success will update account->user_public_trusted by side-effect.
300 require_quiet(!sosAccountValidatePasswordOrFail(account, user_password, error), errOut);
301
302 // We may or may not have parameters here.
303 // In any case we tried using them and they didn't match
304 // So forget all that and start again, assume we're the first to push anything useful.
305
306 if (CFDataGetLength(user_password) > 20) {
307 secwarning("Long password (>20 byte utf8) being used to derive account key – this may be a PET by mistake!!");
308 }
309
310 parameters = SOSUserKeyCreateGenerateParameters(error);
311 require_quiet(user_private = SOSUserKeygen(user_password, parameters, error), errOut);
312 SOSAccountSetParameters(account, parameters);
313 sosAccountSetTrustedCredentials(account, user_password, user_private, public_was_trusted);
314
315 CFErrorRef publishError = NULL;
316 if (!SOSAccountPublishCloudParameters(account, &publishError)) {
317 secerror("Failed to publish new cloud parameters: %@", publishError);
318 }
319
320 CFReleaseSafe(publishError);
321
322 errOut:
323 CFReleaseSafe(parameters);
324 CFReleaseSafe(user_private);
325 account->key_interests_need_updating = true;
326 return account->user_public_trusted;
327 }
328
329
330 bool SOSAccountTryUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error) {
331 bool success = sosAccountValidatePasswordOrFail(account, user_password, error);
332 account->key_interests_need_updating = true;
333 return success;
334 }
335
336 bool SOSAccountRetryUserCredentials(SOSAccountRef account) {
337 CFDataRef cachedPassword = SOSAccountGetCachedPassword(account, NULL);
338 if (cachedPassword == NULL)
339 return false;
340 /*
341 * SOSAccountTryUserCredentials reset the cached password internally,
342 * so we must have a retain of the password over SOSAccountTryUserCredentials().
343 */
344 CFRetain(cachedPassword);
345 bool res = SOSAccountTryUserCredentials(account, NULL, cachedPassword, NULL);
346 CFRelease(cachedPassword);
347 return res;
348 }
349
350
351