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