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 34void SOSAccountSetPreviousPublic(SOSAccountRef account) { 35 CFReleaseNull(account->previous_public); 36 account->previous_public = account->user_public; 37 CFRetain(account->previous_public); 38} 39 40static 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 55static void SOSAccountGenerationSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) { 56 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 63 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))) { 68 } 69 gen_sign: // finally generation sign this. 70 SOSCircleGenerationUpdate(circle, privKey, fpi, NULL); 71 account->departure_code = kSOSNeverLeftCircle; 72 return (bool) true; 73 }); 74 } 75 }); 76} 77 78 79/* this one is meant to be local - not published over KVS. */ 80static void SOSAccountPeerSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) { 81 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { 82 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL); 83 if (fpi) 84 SOSFullPeerInfoUpgradeSignatures(fpi, privKey, NULL); 85 }); 86} 87 88 89void SOSAccountPurgePrivateCredential(SOSAccountRef account) 90{ 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(); 97 } 98 if (account->lock_notification_token) { 99 notify_cancel(account->lock_notification_token); 100 account->lock_notification_token = 0; 101 } 102} 103 104 105static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account, bool public_was_trusted, SecKeyRef privKey) 106{ 107 if (!privKey) return; 108 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey); 109 110 if (account->user_public && account->user_public_trusted && CFEqual(publicKey, account->user_public)) return; 111 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); 116 } 117 118 CFReleaseNull(account->user_public); 119 account->user_public = publicKey; 120 account->user_public_trusted = true; 121 122 if(!account->previous_public) { 123 account->previous_public = account->user_public; 124 CFRetain(account->previous_public); 125 } 126 127 secnotice("trust", "trusting new public key: %@", account->user_public); 128} 129 130 131static void SOSAccountSetPrivateCredential(SOSAccountRef account, SecKeyRef private) { 132 if (!private) 133 return SOSAccountPurgePrivateCredential(account); 134 135 CFRetain(private); 136 CFReleaseSafe(account->_user_private); 137 account->_user_private = private; 138 139 bool resume_timer = false; 140 if (!account->user_private_timer) { 141 xpc_transaction_begin(); 142 resume_timer = true; 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); 146 }); 147 148 notify_register_dispatch(kUserKeybagStateChangeNotification, &account->lock_notification_token, account->queue, ^(int token) { 149 bool locked = false; 150 CFErrorRef lockCheckError = NULL; 151 152 if (!SecAKSGetIsLocked(&locked, &lockCheckError)) { 153 secerror("Checking for locked after change failed: %@", lockCheckError); 154 } 155 156 if (locked) { 157 SOSAccountPurgePrivateCredential(account); 158 } 159 }); 160 } 161 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)); 165 if (resume_timer) 166 dispatch_resume(account->user_private_timer); 167} 168 169SecKeyRef SOSAccountGetPrivateCredential(SOSAccountRef account, CFErrorRef* error) 170{ 171 if (account->_user_private == NULL) { 172 SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("Private Key not available - failed to prompt user recently"), NULL, error); 173 } 174 return account->_user_private; 175} 176 177bool SOSAccountHasPublicKey(SOSAccountRef account, CFErrorRef* error) 178{ 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); 181 return false; 182 } 183 184 return true; 185} 186 187bool SOSAccountAssertUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error) 188{ 189 bool public_was_trusted = account->user_public_trusted; 190 account->user_public_trusted = false; 191 SecKeyRef user_private = NULL; 192 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); 204 } else { 205 SOSAccountPeerSignatureUpdate(account, user_private); 206 SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private); 207 } 208 CFReleaseSafe(public_candidate); 209 } 210 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. 215 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); 219 220 SOSAccountPeerSignatureUpdate(account, user_private); 221 SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private); 222 223 CFErrorRef publishError = NULL; 224 if (!SOSAccountPublishCloudParameters(account, &publishError)) 225 secerror("Failed to publish new cloud parameters: %@", publishError); 226 CFReleaseSafe(publishError); 227 } 228 229 SOSAccountGenerationSignatureUpdate(account, user_private); 230 SOSAccountSetPrivateCredential(account, user_private); 231exit: 232 SOSUpdateKeyInterest(); 233 234 CFReleaseSafe(user_private); 235 236 return account->user_public_trusted; 237} 238 239 240bool SOSAccountTryUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error) 241{ 242 bool success = false; 243 244 if (!SOSAccountHasPublicKey(account, error)) 245 return false; 246 247 if (account->user_key_parameters) { 248 SecKeyRef new_key = SOSUserKeygen(user_password, account->user_key_parameters, error); 249 if (new_key) { 250 SecKeyRef new_public_key = SecKeyCreatePublicFromPrivate(new_key); 251 252 if (CFEqualSafe(new_public_key, account->user_public)) { 253 SOSAccountSetPrivateCredential(account, new_key); 254 success = true; 255 } else { 256 SOSCreateError(kSOSErrorWrongPassword, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL, error); 257 } 258 CFReleaseSafe(new_public_key); 259 CFReleaseSafe(new_key); 260 } 261 } else { 262 SOSCreateError(kSOSErrorProcessingFailure, CFSTR("Have public key but no parameters??"), NULL, error); 263 } 264 265 return success; 266} 267