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