]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSAccountFullPeerInfo.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / SecureObjectSync / 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 }