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