2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "SOSAccountPriv.h"
27 #include "SOSPeerInfoCollections.h"
28 #include "SOSTransport.h"
31 // MARK: User Credential management
34 void SOSAccountSetPreviousPublic(SOSAccountRef account
) {
35 CFReleaseNull(account
->previous_public
);
36 account
->previous_public
= account
->user_public
;
37 CFRetain(account
->previous_public
);
40 static void SOSAccountRemoveInvalidApplications(SOSAccountRef account
, SOSCircleRef circle
)
42 CFMutableSetRef peersToRemove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
43 SOSCircleForEachApplicant(circle
, ^(SOSPeerInfoRef peer
) {
44 if (!SOSPeerInfoApplicationVerify(peer
, account
->user_public
, NULL
))
45 CFSetAddValue(peersToRemove
, peer
);
48 CFSetForEach(peersToRemove
, ^(const void *value
) {
49 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
51 SOSCircleWithdrawRequest(circle
, peer
, NULL
);
55 static void SOSAccountGenerationSignatureUpdate(SOSAccountRef account
, SecKeyRef privKey
) {
57 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
58 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInCircle(account
, circle
, NULL
);
59 if(SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
) &&
60 !SOSCircleVerify(circle
, account
->user_public
, NULL
)) {
61 SOSAccountModifyCircle(account
, SOSCircleGetName(circle
), NULL
, ^(SOSCircleRef circle
) {
62 SOSAccountRemoveInvalidApplications(account
, circle
); // We might be updating our signatures so remove, but don't reject applicants
64 SOSFullPeerInfoRef cloud_fpi
= SOSCircleGetiCloudFullPeerInfoRef(circle
);
65 require_quiet(cloud_fpi
!= NULL
, gen_sign
);
66 require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi
, privKey
, NULL
), gen_sign
);
67 if(!SOSCircleUpdatePeerInfo(circle
, SOSFullPeerInfoGetPeerInfo(cloud_fpi
))) {
69 gen_sign
: // finally generation sign this.
70 SOSCircleGenerationUpdate(circle
, privKey
, fpi
, NULL
);
71 account
->departure_code
= kSOSNeverLeftCircle
;
79 /* this one is meant to be local - not published over KVS. */
80 static void SOSAccountPeerSignatureUpdate(SOSAccountRef account
, SecKeyRef privKey
) {
81 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
82 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInCircle(account
, circle
, NULL
);
84 SOSFullPeerInfoUpgradeSignatures(fpi
, privKey
, NULL
);
89 void SOSAccountPurgePrivateCredential(SOSAccountRef account
)
91 CFReleaseNull(account
->_user_private
);
92 if (account
->user_private_timer
) {
93 dispatch_source_cancel(account
->user_private_timer
);
94 dispatch_release(account
->user_private_timer
);
95 account
->user_private_timer
= NULL
;
96 xpc_transaction_end();
98 if (account
->lock_notification_token
) {
99 notify_cancel(account
->lock_notification_token
);
100 account
->lock_notification_token
= 0;
105 static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account
, bool public_was_trusted
, SecKeyRef privKey
)
107 if (!privKey
) return;
108 SecKeyRef publicKey
= SecKeyCreatePublicFromPrivate(privKey
);
110 if (account
->user_public
&& account
->user_public_trusted
&& CFEqual(publicKey
, account
->user_public
)) return;
112 if(public_was_trusted
&& account
->user_public
) {
113 CFReleaseNull(account
->previous_public
);
114 account
->previous_public
= account
->user_public
;
115 CFRetain(account
->previous_public
);
118 CFReleaseNull(account
->user_public
);
119 account
->user_public
= publicKey
;
120 account
->user_public_trusted
= true;
122 if(!account
->previous_public
) {
123 account
->previous_public
= account
->user_public
;
124 CFRetain(account
->previous_public
);
127 secnotice("trust", "trusting new public key: %@", account
->user_public
);
131 static void SOSAccountSetPrivateCredential(SOSAccountRef account
, SecKeyRef
private) {
133 return SOSAccountPurgePrivateCredential(account
);
136 CFReleaseSafe(account
->_user_private
);
137 account
->_user_private
= private;
139 bool resume_timer
= false;
140 if (!account
->user_private_timer
) {
141 xpc_transaction_begin();
143 account
->user_private_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, account
->queue
);
144 dispatch_source_set_event_handler(account
->user_private_timer
, ^{
145 SOSAccountPurgePrivateCredential(account
);
148 notify_register_dispatch(kUserKeybagStateChangeNotification
, &account
->lock_notification_token
, account
->queue
, ^(int token
) {
150 CFErrorRef lockCheckError
= NULL
;
152 if (!SecAKSGetIsLocked(&locked
, &lockCheckError
)) {
153 secerror("Checking for locked after change failed: %@", lockCheckError
);
157 SOSAccountPurgePrivateCredential(account
);
162 // (Re)set the timer's fire time to now + 120 seconds with a 5 second fuzz factor.
163 dispatch_time_t purgeTime
= dispatch_time(DISPATCH_TIME_NOW
, (int64_t)(10 * 60 * NSEC_PER_SEC
));
164 dispatch_source_set_timer(account
->user_private_timer
, purgeTime
, DISPATCH_TIME_FOREVER
, (int64_t)(5 * NSEC_PER_SEC
));
166 dispatch_resume(account
->user_private_timer
);
169 SecKeyRef
SOSAccountGetPrivateCredential(SOSAccountRef account
, CFErrorRef
* error
)
171 if (account
->_user_private
== NULL
) {
172 SOSCreateError(kSOSErrorPrivateKeyAbsent
, CFSTR("Private Key not available - failed to prompt user recently"), NULL
, error
);
174 return account
->_user_private
;
177 bool SOSAccountHasPublicKey(SOSAccountRef account
, CFErrorRef
* error
)
179 if (account
->user_public
== NULL
|| account
->user_public_trusted
== false) {
180 SOSCreateError(kSOSErrorPublicKeyAbsent
, CFSTR("Public Key not available - failed to register before call"), NULL
, error
);
187 bool SOSAccountAssertUserCredentials(SOSAccountRef account
, CFStringRef user_account __unused
, CFDataRef user_password
, CFErrorRef
*error
)
189 bool public_was_trusted
= account
->user_public_trusted
;
190 account
->user_public_trusted
= false;
191 SecKeyRef user_private
= NULL
;
193 if (account
->user_public
&& account
->user_key_parameters
) {
194 // We have an untrusted public key – see if our generation makes the same key:
195 // if so we trust it and we have the private key.
196 // if not we still don't trust it.
197 require_quiet(user_private
= SOSUserKeygen(user_password
, account
->user_key_parameters
, error
), exit
);
198 SecKeyRef public_candidate
= SecKeyCreatePublicFromPrivate(user_private
);
199 if (!CFEqualSafe(account
->user_public
, public_candidate
)) {
200 secnotice("trust", "Public keys don't match: calculated: %@, expected: %@",
201 account
->user_public
, public_candidate
);
202 debugDumpUserParameters(CFSTR("params"), account
->user_key_parameters
);
203 CFReleaseNull(user_private
);
205 SOSAccountPeerSignatureUpdate(account
, user_private
);
206 SOSAccountSetTrustedUserPublicKey(account
, public_was_trusted
, user_private
);
208 CFReleaseSafe(public_candidate
);
211 if (!account
->user_public_trusted
) {
212 // We may or may not have parameters here.
213 // In any case we tried using them and they didn't match
214 // So forget all that and start again, assume we're the first to push anything useful.
216 CFReleaseNull(account
->user_key_parameters
);
217 account
->user_key_parameters
= SOSUserKeyCreateGenerateParameters(error
);
218 require_quiet(user_private
= SOSUserKeygen(user_password
, account
->user_key_parameters
, error
), exit
);
220 SOSAccountPeerSignatureUpdate(account
, user_private
);
221 SOSAccountSetTrustedUserPublicKey(account
, public_was_trusted
, user_private
);
223 CFErrorRef publishError
= NULL
;
224 if (!SOSAccountPublishCloudParameters(account
, &publishError
))
225 secerror("Failed to publish new cloud parameters: %@", publishError
);
226 CFReleaseSafe(publishError
);
229 SOSAccountGenerationSignatureUpdate(account
, user_private
);
230 SOSAccountSetPrivateCredential(account
, user_private
);
232 SOSUpdateKeyInterest();
234 CFReleaseSafe(user_private
);
236 return account
->user_public_trusted
;
240 bool SOSAccountTryUserCredentials(SOSAccountRef account
, CFStringRef user_account __unused
, CFDataRef user_password
, CFErrorRef
*error
)
242 bool success
= false;
244 if (!SOSAccountHasPublicKey(account
, error
))
247 if (account
->user_key_parameters
) {
248 SecKeyRef new_key
= SOSUserKeygen(user_password
, account
->user_key_parameters
, error
);
250 SecKeyRef new_public_key
= SecKeyCreatePublicFromPrivate(new_key
);
252 if (CFEqualSafe(new_public_key
, account
->user_public
)) {
253 SOSAccountSetPrivateCredential(account
, new_key
);
256 SOSCreateError(kSOSErrorWrongPassword
, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL
, error
);
258 CFReleaseSafe(new_public_key
);
259 CFReleaseSafe(new_key
);
262 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("Have public key but no parameters??"), NULL
, error
);