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
28static CFStringRef kicloud_identity_name = CFSTR("Cloud Identity");
29
30
31SOSFullPeerInfoRef CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer, CFErrorRef *error) {
32    return SOSFullPeerInfoCreateCloudIdentity(NULL, cloudPeer, error);
33}
34
35
36SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamedIfPresent(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
37    if (CFDictionaryGetValue(account->circles, name) == NULL) {
38        SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
39        return NULL;
40    }
41
42    return (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
43}
44
45
46
47static SecKeyRef GeneratePermanentFullECKey_internal(int keySize, CFStringRef name, CFTypeRef accessibility, CFBooleanRef sync,  CFErrorRef* error)
48{
49    SecKeyRef full_key = NULL;
50
51    CFNumberRef key_size_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
52
53    CFDictionaryRef priv_key_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
54                                                                  kSecAttrIsPermanent,    kCFBooleanTrue,
55                                                                  NULL);
56
57    CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
58                                                                     kSecAttrKeyType,        kSecAttrKeyTypeEC,
59                                                                     kSecAttrKeySizeInBits,  key_size_num,
60                                                                     kSecPrivateKeyAttrs,    priv_key_attrs,
61                                                                     kSecAttrAccessible,     accessibility,
62                                                                     kSecAttrAccessGroup,    kSOSInternalAccessGroup,
63                                                                     kSecAttrLabel,          name,
64                                                                     kSecAttrSynchronizable, sync,
65                                                                     kSecUseTombstones,      kCFBooleanTrue,
66                                                                     NULL);
67
68    CFReleaseNull(priv_key_attrs);
69
70    CFReleaseNull(key_size_num);
71    OSStatus status = SecKeyGeneratePair(keygen_parameters, NULL, &full_key);
72    CFReleaseNull(keygen_parameters);
73
74    if (status)
75        secerror("status: %ld", (long)status);
76    if (status != errSecSuccess && error != NULL && *error == NULL) {
77        *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
78    }
79
80    return full_key;
81}
82
83static SecKeyRef GeneratePermanentFullECKey(int keySize, CFStringRef name, CFErrorRef* error) {
84    return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kCFBooleanFalse, error);
85}
86
87static SecKeyRef GeneratePermanentFullECKeyForCloudIdentity(int keySize, CFStringRef name, CFErrorRef* error) {
88    return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlocked, kCFBooleanTrue, error);
89}
90
91
92bool SOSAccountIsAccountIdentity(SOSAccountRef account, SOSPeerInfoRef peer_info, CFErrorRef *error)
93{
94    __block bool matches = false;
95    CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) {
96        if (!matches) {
97            matches = CFEqual(peer_info, SOSFullPeerInfoGetPeerInfo((SOSFullPeerInfoRef) value));
98        }
99    });
100
101    return matches;
102}
103
104
105
106SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
107    if (CFDictionaryGetValue(account->circles, name) == NULL) {
108        SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
109        return NULL;
110    }
111    SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
112
113
114    if (circle_full_peer_info == NULL) {
115        CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(account->gestalt), name);
116        SecKeyRef full_key = GeneratePermanentFullECKey(256, keyName, error);
117        CFReleaseNull(keyName);
118
119        if (full_key) {
120            circle_full_peer_info = SOSFullPeerInfoCreate(kCFAllocatorDefault, account->gestalt, full_key, error);
121
122            CFReleaseNull(full_key);
123
124            if (!circle_full_peer_info) {
125                secerror("Can't make FullPeerInfo for %@-%@ (%@) - is AKS ok?", SOSPeerGestaltGetName(account->gestalt), name, error ? (void*)*error : (void*)CFSTR("-"));
126                return circle_full_peer_info;
127            }
128
129            CFDictionarySetValue(account->circle_identities, name, circle_full_peer_info);
130            CFReleaseNull(circle_full_peer_info);
131            circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
132        }
133        else
134            secerror("No full_key: %@:", error ? *error : NULL);
135    }
136
137    return circle_full_peer_info;
138}
139
140
141SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
142    return SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
143}
144
145
146SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
147    SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error);
148    SOSPeerInfoRef cloud_peer = NULL;
149    CFDictionaryRef query = NULL;
150    CFDictionaryRef change = NULL;
151    CFStringRef new_name = NULL;
152
153    CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
154                                                           kPIUserDefinedDeviceName, CFSTR("iCloud"),
155                                                           NULL);
156    require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt")));
157
158    cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error);
159
160    require(cloud_peer, fail);
161
162    query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
163                                         kSecClass,             kSecClassKey,
164                                         kSecAttrSynchronizable,kCFBooleanTrue,
165                                         kSecUseTombstones,     kCFBooleanTrue,
166                                         kSecValueRef,          cloud_key,
167                                         NULL);
168
169    new_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
170                                        CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(cloud_peer));
171
172    change = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
173                                          kSecAttrLabel,        new_name,
174                                          NULL);
175
176    SecError(SecItemUpdate(query, change), error, CFSTR("Couldn't update name"));
177
178fail:
179    CFReleaseNull(new_name);
180    CFReleaseNull(query);
181    CFReleaseNull(change);
182    CFReleaseNull(gestalt);
183    CFReleaseNull(cloud_key);
184
185    return cloud_peer;
186}
187