1/* 2 * Copyright (c) 2003-2007 Apple Computer, 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 * TokenIDHelper.cpp 24 */ 25 26#include "TokenIDHelper.h" 27 28#include <Security/SecKeychain.h> 29#include <Security/SecKeychainPriv.h> 30#include <Security/SecCertificate.h> 31#include <Security/SecKey.h> 32#include <security_utilities/cfutilities.h> 33#include <security_utilities/errors.h> 34#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 35 36static void extract_certificate_from_identity(const void *value, void *context); 37static bool encryptionEnabled(SecKeyRef privateKeyRef); 38static OSStatus findCertificatePublicKeyHash(SecCertificateRef certificate, CFDataRef *label); 39 40int findFirstEncryptionPublicKeyOnToken(SecKeyRef *publicKey, SecKeychainRef *keychainRef, CFDataRef *label) 41{ 42 if (!publicKey || !keychainRef) 43 return paramErr; 44 45 OSStatus status = noErr; 46 CFArrayRef identityArray = NULL; 47 SecKeyRef tmpKeyRef = NULL; 48 SecCertificateRef certificate = NULL; 49 SecKeychainRef tmpKeychainRef = NULL; 50 51 try 52 { 53 status = findEncryptionIdentities((CFTypeRef *)&identityArray); 54 if (status) 55 MacOSError::throwMe(status); 56 57 if (!identityArray || 58 (CFGetTypeID(identityArray)!=CFArrayGetTypeID()) || 59 (CFArrayGetCount(identityArray)==0)) 60 MacOSError::throwMe(paramErr); 61 62 CFTypeRef tmpref = CFArrayGetValueAtIndex(identityArray, 0); 63 if (CFGetTypeID(tmpref)!=SecIdentityGetTypeID()) 64 MacOSError::throwMe(paramErr); 65 66 status = SecIdentityCopyCertificate(SecIdentityRef(tmpref), &certificate); 67 if (status) 68 MacOSError::throwMe(status); 69 70 if (!certificate) 71 MacOSError::throwMe(errKCItemNotFound); 72 73 status = findCertificatePublicKeyHash(certificate, label); 74 if (status) 75 MacOSError::throwMe(status); 76 77 status = SecKeychainItemCopyKeychain(SecKeychainItemRef(certificate), &tmpKeychainRef); 78 if (status) 79 MacOSError::throwMe(status); 80 81 status = SecCertificateCopyPublicKey(certificate, &tmpKeyRef); 82 if (status) 83 MacOSError::throwMe(status); 84 85 // Found an encryption key 86 *publicKey = tmpKeyRef; 87 *keychainRef = tmpKeychainRef; 88 } 89 catch (const MacOSError &err) 90 { 91 status = err.osStatus(); 92 cssmPerror("findFirstEncryptionPublicKeyOnToken", status); 93 } 94 catch (...) 95 { 96 fprintf(stderr, "findFirstEncryptionPublicKeyOnToken: unknown exception\n"); 97 status = errKCItemNotFound; 98 } 99 100 if (status) 101 { 102 if (identityArray) 103 CFRelease(identityArray); 104 if (certificate) 105 CFRelease(certificate); 106 } 107 108 if (identityArray) 109 CFRelease(identityArray); 110 if (certificate) 111 CFRelease(certificate); 112 113 return status; 114} 115 116OSStatus findCertificatePublicKeyHash(SecCertificateRef certificate, CFDataRef *label) 117{ 118 UInt32 tag[1] = { kSecPublicKeyHashItemAttr }; // kSecKeyLabel == hash public key [kSecPublicKeyHashItemAttr ??kSecKeyLabel] 119 UInt32 format[1] = { CSSM_DB_ATTRIBUTE_FORMAT_BLOB }; 120 SecKeychainAttributeInfo info = { 1, tag, format }; // attrs to retrieve 121 122 SecKeychainAttributeList *attrList = NULL; 123 124 OSStatus status = SecKeychainItemCopyAttributesAndData(SecKeychainItemRef(certificate), &info, NULL, &attrList, 0, NULL); 125 if (status || !attrList || !attrList->count) 126 return status; 127 128 const uint32_t index = 0; 129 if (attrList->attr[index].tag == kSecPublicKeyHashItemAttr) 130 *label = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)attrList->attr[index].data, attrList->attr[index].length); 131 132 SecKeychainItemFreeAttributesAndData(attrList, NULL); 133 return noErr; 134} 135 136int findEncryptionIdentities(CFTypeRef *identityOrArray) 137{ 138 /* 139 Similar code is available in Leopard9A311 and later as "DIHLFVCopyEncryptionIdentities". 140 See <rdar://problem/4816811> FV: Add SecTokenBasedEncryptionIdentities call 141 We reproduce it here for two reasons: 142 1) The semantics of DIHLFVCopyEncryptionIdentities are different, 143 returning either a CFData or CFArray 144 2) We don' have to introduce a dependence on DiskImages.framework here 145 146 147 Since CSSM searching for attributes is an AND, not an OR, we need to get all 148 identities then check each one for a good key usage. If we built up a search 149 using an OR predicate, we would want to specify this for key usage: 150 151 uint32_t keyuse = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP; 152 */ 153 OSStatus status = noErr; 154 CFArrayRef searchList = NULL; 155 CFMutableArrayRef idArray = NULL; // holds all SecIdentityRefs found 156 157 status = SecKeychainCopyDomainSearchList(kSecPreferencesDomainDynamic, &searchList); 158 if (status) 159 return status; 160 161 CFIndex count = searchList ? CFArrayGetCount(searchList) : 0; 162 if (!count) 163 return errSecNoSuchKeychain; 164 165 // Search for all identities 166 uint32_t keyuse = 0; 167 SecIdentitySearchRef srchRef = NULL; 168 status = SecIdentitySearchCreate(searchList, keyuse, &srchRef); 169 if (status) 170 return status; 171 172 while (!status) 173 { 174 SecIdentityRef identity = NULL; 175 status = SecIdentitySearchCopyNext(srchRef, &identity); 176 if (status == errSecItemNotFound) // done 177 break; 178 if (status) 179 return status; 180 181 SecKeyRef privateKeyRef = NULL; 182 status = SecIdentityCopyPrivateKey(identity, &privateKeyRef); 183 if (status) 184 continue; 185 bool canEncrypt = encryptionEnabled(privateKeyRef); 186 CFRelease(privateKeyRef); 187 if (!canEncrypt) 188 continue; 189 190 // add the identity to the array 191 if (!idArray) 192 idArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 193 CFArrayAppendValue(idArray, identity); 194 } 195 196 if ((status == noErr || status == errSecItemNotFound) && idArray && CFArrayGetCount(idArray)) 197 { 198 if (idArray) 199 { 200 *identityOrArray = idArray; 201 ::CFRetain(*identityOrArray); 202 } 203 status = noErr; 204 } 205 else 206 if (idArray) 207 CFRelease(idArray); 208 209 return status; 210} 211 212int unlockToken(const char *password) 213{ 214 OSStatus status = noErr; 215 if (!password) 216 return paramErr; 217 218 CFArrayRef searchList = NULL; 219 220 status = SecKeychainCopyDomainSearchList(kSecPreferencesDomainDynamic, &searchList); 221 if (status) 222 return status; 223 224 CFIndex count = searchList ? CFArrayGetCount(searchList) : 0; 225 if (count) 226 { 227 SecKeychainRef keychainRef = (SecKeychainRef)CFArrayGetValueAtIndex(searchList, 0); // only first dynamic keychain! 228 status = SecKeychainUnlock(keychainRef, (UInt32)strlen(password), password, 1); 229 if (keychainRef) 230 CFRelease(keychainRef); 231 } 232 else 233 status = errSecNoSuchKeychain; 234 if (searchList) 235 CFRelease(searchList); 236 return status; 237} 238 239void extractCertificatesFromIdentities(CFTypeRef identityOrArray, CFArrayRef *certificateArrayOut) 240{ 241 if (!identityOrArray || !certificateArrayOut) 242 return; 243 244 CFIndex cnt = (CFGetTypeID(identityOrArray)==CFArrayGetTypeID())?CFArrayGetCount((CFArrayRef)identityOrArray):1; 245 CFMutableArrayRef certificateArray = CFArrayCreateMutable(kCFAllocatorDefault, cnt, &kCFTypeArrayCallBacks); 246 247 if (CFGetTypeID(identityOrArray)==CFArrayGetTypeID()) 248 CFArrayApplyFunction((CFArrayRef)identityOrArray, CFRangeMake(0, cnt), 249 extract_certificate_from_identity, 250 certificateArray); 251 else 252 extract_certificate_from_identity(identityOrArray, certificateArray); 253 *certificateArrayOut = certificateArray; 254} 255 256void extract_certificate_from_identity(const void *value, void *context) 257{ 258 if (!context || !value) 259 return; 260 261 CSSM_DATA certData = {0,}; 262 SecCertificateRef certificateRef; 263 OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)value, &certificateRef); 264 if (!status) 265 { 266 status = SecCertificateGetData(certificateRef, &certData); 267 CFRelease(certificateRef); 268 269 if (!status) 270 { 271 CFDataRef cert = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certData.Data, certData.Length); 272 CFArrayAppendValue((CFMutableArrayRef)context, cert); 273 CFRelease(cert); 274 if (certData.Data) 275 free(certData.Data); 276 } 277 } 278} 279 280bool encryptionEnabled(SecKeyRef privateKeyRef) 281{ 282 /* 283 Since CSSM searching for attributes is an AND, not an OR, we need to get all 284 identities then check each one for a good key usage. Note that for the CAC 285 card, the "Email Encryption Private Key" only has the unwrap bit set (0x1A). 286 Return true if this identity supports appropriate encryption. 287 */ 288 289 UInt32 tag[] = { kSecKeyEncrypt, kSecKeyDecrypt, kSecKeyDerive, kSecKeyWrap, kSecKeyUnwrap }; 290 UInt32 format[] = { CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32, 291 CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32}; 292 SecKeychainAttributeInfo info = { 5, tag, format }; // attrs to retrieve 293 294 SecKeychainAttributeList *attrList = NULL; 295 OSStatus status = SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)privateKeyRef, &info, NULL, &attrList, 0, NULL); 296 if (status || !attrList) 297 return false; 298 299 bool canEncrypt = false; 300 for (uint32_t index = 0; index < attrList->count; ++index) 301 { 302 if (attrList->attr[index].length != sizeof(uint32_t) || !attrList->attr[index].data || 303 0 == *(uint32_t*)attrList->attr[index].data) 304 continue; 305 canEncrypt = true; 306 break; 307 } 308 309 status = SecKeychainItemFreeAttributesAndData(attrList, NULL); 310 return canEncrypt; 311} 312 313