SOSAccountFullPeerInfo.m
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 #include <AssertMacros.h> 25 26 #include <stdio.h> 27 #include "SOSAccountPriv.h" 28 #include "keychain/SecureObjectSync/SOSInternal.h" 29 #include "SOSViews.h" 30 #include "SOSPeerInfoV2.h" 31 #include "SOSPeerInfoPriv.h" 32 #import "keychain/SecureObjectSync/SOSAccountPriv.h" 33 #import "keychain/SecureObjectSync/SOSAccountTrust.h" 34 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h" 35 #include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h" 36 #include "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h" 37 38 static CFStringRef kicloud_identity_name = CFSTR("Cloud Identity"); 39 40 SecKeyRef SOSAccountCopyDeviceKey(SOSAccount* account, CFErrorRef *error) { 41 SecKeyRef privateKey = NULL; 42 if(account.peerPublicKey) { 43 privateKey = SecKeyCopyMatchingPrivateKey(account.peerPublicKey, error); 44 } else { 45 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No identity to get key from")); 46 } 47 return privateKey; 48 } 49 50 SOSFullPeerInfoRef CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer, CFErrorRef *error) { 51 return SOSFullPeerInfoCreateCloudIdentity(NULL, cloudPeer, error); 52 } 53 54 55 static CF_RETURNS_RETAINED SecKeyRef GeneratePermanentFullECKey_internal(int keySize, CFStringRef name, CFTypeRef accessibility, CFBooleanRef sync, CFErrorRef* error) 56 { 57 SecKeyRef full_key = NULL; 58 59 CFNumberRef key_size_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize); 60 61 CFDictionaryRef priv_key_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 62 kSecAttrIsPermanent, kCFBooleanTrue, 63 NULL); 64 65 CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 66 kSecAttrKeyType, kSecAttrKeyTypeEC, 67 kSecAttrKeySizeInBits, key_size_num, 68 kSecPrivateKeyAttrs, priv_key_attrs, 69 kSecAttrAccessible, accessibility, 70 kSecAttrAccessGroup, kSOSInternalAccessGroup, 71 kSecAttrLabel, name, 72 kSecAttrSynchronizable, sync, 73 kSecUseTombstones, kCFBooleanTrue, 74 NULL); 75 76 CFReleaseNull(priv_key_attrs); 77 78 CFReleaseNull(key_size_num); 79 OSStatus status = SecKeyGeneratePair(keygen_parameters, NULL, &full_key); 80 CFReleaseNull(keygen_parameters); 81 82 if (status) 83 secerror("status: %ld", (long)status); 84 if (status != errSecSuccess && error != NULL && *error == NULL) { 85 *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL); 86 } 87 88 return full_key; 89 } 90 91 CF_RETURNS_RETAINED SecKeyRef GeneratePermanentFullECKey(int keySize, CFStringRef name, CFErrorRef* error) { 92 return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kCFBooleanFalse, error); 93 } 94 95 static CF_RETURNS_RETAINED SecKeyRef GeneratePermanentFullECKeyForCloudIdentity(int keySize, CFStringRef name, CFErrorRef* error) { 96 return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlocked, kCFBooleanTrue, error); 97 } 98 99 static SecKeyRef sosKeyForLabel(CFStringRef label) { 100 CFTypeRef queryResult = NULL; 101 SecKeyRef retval = NULL; 102 CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 103 kSecMatchLimit, kSecMatchLimitOne, 104 kSecClass, kSecClassKey, 105 kSecAttrKeyClass, kSecAttrKeyClassPrivate, 106 kSecAttrSynchronizable, kSecAttrSynchronizableAny, 107 kSecAttrAccessGroup, kSOSInternalAccessGroup, 108 kSecAttrLabel, label, 109 kSecReturnRef, kCFBooleanTrue, 110 NULL); 111 OSStatus stat = SecItemCopyMatching(query, &queryResult); 112 if(errSecSuccess == stat) { 113 retval = (SecKeyRef) queryResult; 114 secnotice("iCloudIdentity", "Got key for label (%@)", label); 115 } else { 116 secnotice("iCloudIdentity", "Failed query(%d) for %@", (int) stat, label); 117 } 118 CFReleaseNull(query); 119 return retval; 120 } 121 122 void SOSiCloudIdentityPrivateKeyForEach(void (^complete)(SecKeyRef privKey)) { 123 CFTypeRef queryResult = NULL; 124 CFArrayRef iCloudPrivKeys = NULL; 125 126 CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 127 kSecMatchLimit, kSecMatchLimitAll, 128 kSecClass, kSecClassKey, 129 kSecAttrKeyClass, kSecAttrKeyClassPrivate, 130 kSecAttrSynchronizable, kSecAttrSynchronizableAny, 131 kSecAttrAccessGroup, kSOSInternalAccessGroup, 132 kSecReturnAttributes, kCFBooleanTrue, 133 NULL); 134 135 if((errSecSuccess == SecItemCopyMatching(query, &queryResult)) && (iCloudPrivKeys = asArray(queryResult, NULL))) { 136 secnotice("iCloudIdentity", "Screening %ld icloud private key candidates", (long)CFArrayGetCount(iCloudPrivKeys)); 137 CFReleaseNull(query); 138 } else { 139 secnotice("iCloudIdentity", "Can't get iCloud Identity private key candidates"); 140 CFReleaseNull(query); 141 CFReleaseNull(queryResult); 142 return; 143 } 144 145 CFArrayForEach(iCloudPrivKeys, ^(const void *value) { 146 CFDictionaryRef privKeyDict = (CFDictionaryRef) value; 147 CFStringRef label = CFDictionaryGetValue(privKeyDict, kSecAttrLabel); 148 if(!label) { 149 return; 150 } 151 if(CFStringHasPrefix(label, CFSTR("Cloud Identity"))) { 152 SecKeyRef privKey = sosKeyForLabel(label); 153 if(privKey) { 154 complete(privKey); 155 CFReleaseNull(privKey); 156 } 157 } 158 }); 159 CFReleaseNull(queryResult); 160 } 161 162 bool SOSAccountHasCircle(SOSAccount* account, CFErrorRef* error) { 163 SOSAccountTrustClassic *trust = account.trust; 164 SOSCircleRef circle = trust.trustedCircle; 165 166 if (!circle) 167 SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No trusted circle")); 168 169 return circle != NULL; 170 } 171 172 bool SOSAccountHasFullPeerInfo(SOSAccount* account, CFErrorRef* error) { 173 bool hasPeer = false; 174 SOSAccountTrustClassic *trust = account.trust; 175 SOSFullPeerInfoRef identity = trust.fullPeerInfo; 176 177 require(SOSAccountHasCircle(account, error), fail); 178 179 hasPeer = identity != NULL; 180 181 if (!hasPeer) 182 SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("No peer for circle")); 183 184 fail: 185 return hasPeer; 186 } 187 188 bool SOSAccountIsAccountIdentity(SOSAccount* account, SOSPeerInfoRef peer_info, CFErrorRef *error) 189 { 190 SOSFullPeerInfoRef identity = NULL; 191 192 SOSAccountTrustClassic *trust = account.trust; 193 identity = trust.fullPeerInfo; 194 return CFEqualSafe(peer_info, SOSFullPeerInfoGetPeerInfo(identity)); 195 } 196 197 bool SOSAccountFullPeerInfoVerify(SOSAccount* account, SecKeyRef privKey, CFErrorRef *error) { 198 SOSFullPeerInfoRef identity = NULL; 199 200 SOSAccountTrustClassic *trust = account.trust; 201 identity = trust.fullPeerInfo; 202 if(!identity) return false; 203 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey); 204 bool retval = SOSPeerInfoApplicationVerify(SOSFullPeerInfoGetPeerInfo(identity), pubKey, error); 205 CFReleaseNull(pubKey); 206 return retval; 207 } 208 209 static bool UpdateKeyName(SecKeyRef key, SOSPeerInfoRef peer, CFErrorRef* error) 210 { 211 CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 212 kSecClass, kSecClassKey, 213 kSecAttrSynchronizable,kCFBooleanTrue, 214 kSecUseTombstones, kCFBooleanTrue, 215 kSecValueRef, key, 216 NULL); 217 218 CFStringRef new_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 219 CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(peer)); 220 221 CFDictionaryRef change = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 222 kSecAttrLabel, new_name, 223 NULL); 224 225 bool result = SecError(SecItemUpdate(query, change), error, CFSTR("Couldn't update name")); 226 227 CFReleaseNull(new_name); 228 CFReleaseNull(query); 229 CFReleaseNull(change); 230 return result; 231 } 232 233 SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) { 234 SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error); 235 SOSPeerInfoRef cloud_peer = NULL; 236 237 CFDictionaryRef gestalt = NULL; 238 239 require_action_quiet(cloud_key, fail, SecError(errSecAllocate, error, CFSTR("Can't generate keypair for icloud identity"))); 240 241 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 242 kPIUserDefinedDeviceNameKey, CFSTR("iCloud"), 243 NULL); 244 require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt"))); 245 246 cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error); 247 248 require(cloud_peer, fail); 249 250 UpdateKeyName(cloud_key, cloud_peer, error); 251 252 fail: 253 CFReleaseNull(gestalt); 254 CFReleaseNull(cloud_key); 255 256 return cloud_peer; 257 } 258 259 260 bool SOSAccountUpdatePeerInfo(SOSAccount* account, CFStringRef updateDescription, CFErrorRef *error, bool (^update)(SOSFullPeerInfoRef fpi, CFErrorRef *error)) { 261 262 if (!account.hasPeerInfo) 263 return true; 264 265 bool result = update(account.fullPeerInfo, error); 266 267 if (result && SOSAccountHasCircle(account, NULL)) { 268 return [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle_to_change) { 269 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription); 270 return SOSCircleUpdatePeerInfo(circle_to_change, account.peerInfo); 271 }]; 272 } 273 274 return result; 275 } 276 277 278 bool SOSAccountUpdatePeerInfoAndPush(SOSAccount* account, CFStringRef updateDescription, CFErrorRef *error, 279 bool (^update)(SOSPeerInfoRef pi, CFErrorRef *error)) { 280 return SOSAccountUpdatePeerInfo(account, updateDescription, error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *localError) { 281 return SOSFullPeerInfoUpdate(fpi, localError, ^SOSPeerInfoRef(SOSPeerInfoRef pi, SecKeyRef peerPriv, CFErrorRef *localError) { 282 SOSPeerInfoRef newPI = SOSPeerInfoCreateCopy(kCFAllocatorDefault, pi, localError); 283 if(update(newPI, error)) { 284 if(peerPriv && SOSPeerInfoSign(peerPriv, newPI, localError)) { 285 secnotice("circleOp", "Signed Peerinfo to update"); 286 return newPI; 287 } 288 } 289 secnotice("circleOp", "Failed updating PeerInfo"); 290 CFReleaseNull(newPI); 291 return NULL; 292 }); 293 }); 294 }