/* * Copyright (c) 2001-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * EAPCertificateUtil.c * - certificate utility functions */ /* * Modification History * * April 2, 2004 Dieter Siegmund (dieter@apple.com) * - created */ #include #include #include #include #if TARGET_OS_EMBEDDED #include #else /* TARGET_OS_EMBEDDED */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* TARGET_OS_EMBEDDED */ #include #include #include #include #include #include #include #include #include #include "EAPTLSUtil.h" #include "EAPCertificateUtil.h" #include "EAPSecurity.h" #include "myCFUtil.h" #define kEAPSecIdentityHandleType CFSTR("IdentityHandleType") #define kEAPSecIdentityHandleTypeCertificateData CFSTR("CertificateData") #define kEAPSecIdentityHandleData CFSTR("IdentityHandleData") #if TARGET_OS_EMBEDDED Boolean EAPSecCertificateEqual(SecCertificateRef cert1, SecCertificateRef cert2) { CFDataRef cert1_data = NULL; CFDataRef cert2_data = NULL; Boolean equal = FALSE; cert1_data = SecCertificateCopyData(cert1); if (cert1_data == NULL) { goto done; } cert2_data = SecCertificateCopyData(cert2); if (cert2_data == NULL) { goto done; } equal = CFEqual(cert1_data, cert2_data); done: my_CFRelease(&cert1_data); my_CFRelease(&cert2_data); return (equal); } static __inline__ SecCertificateRef _EAPCFDataCreateSecCertificate(CFDataRef data_cf) { return (SecCertificateCreateWithData(NULL, data_cf)); } static __inline__ CFDataRef _EAPSecCertificateCreateCFData(SecCertificateRef cert) { return (SecCertificateCopyData(cert)); } OSStatus EAPSecIdentityListCreate(CFArrayRef * ret_array) { const void * keys[] = { kSecClass, kSecReturnRef, kSecMatchLimit }; CFDictionaryRef query; CFTypeRef results = NULL; OSStatus status = noErr; const void * values[] = { kSecClassIdentity, kCFBooleanTrue, kSecMatchLimitAll }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &results); CFRelease(query); if (status == noErr) { *ret_array = results; } return (status); } #else /* TARGET_OS_EMBEDDED */ Boolean EAPSecCertificateEqual(SecCertificateRef cert1, SecCertificateRef cert2) { CSSM_DATA cert1_data; CSSM_DATA cert2_data; OSStatus status; status = SecCertificateGetData(cert1, &cert1_data); if (status != noErr) { return (FALSE); } status = SecCertificateGetData(cert2, &cert2_data); if (status != noErr) { return (FALSE); } if (cert1_data.Length != cert2_data.Length) { return (FALSE); } return (!bcmp(cert1_data.Data, cert2_data.Data, cert1_data.Length)); } /* * Function: _EAPCFDataCreateSecCertificate * Purpose: * Creates a SecCertificateRef from a CFDataRef. */ static SecCertificateRef _EAPCFDataCreateSecCertificate(CFDataRef data_cf) { SecCertificateRef cert = NULL; CSSM_DATA data; OSStatus status; if (data_cf == NULL) { goto done; } data.Length = CFDataGetLength(data_cf); data.Data = (uint8 *)CFDataGetBytePtr(data_cf); status = SecCertificateCreateFromData(&data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert); if (status != noErr) { fprintf(stderr, "SecCertificateCreateFromData failed, %d", (int)status); } done: return (cert); } /* * Function: _EAPSecCertificateCreateCFData * Purpose: * Creates a CFDataRef from a SecCertificateRef. */ static CFDataRef _EAPSecCertificateCreateCFData(SecCertificateRef cert) { CSSM_DATA cert_data; CFDataRef data = NULL; OSStatus status; status = SecCertificateGetData(cert, &cert_data); if (status != noErr) { goto done; } data = CFDataCreate(NULL, cert_data.Data, cert_data.Length); done: return (data); } OSStatus EAPSecIdentityListCreate(CFArrayRef * ret_array) { CFMutableArrayRef array; SecIdentityRef identity = NULL; SecIdentitySearchRef search = NULL; OSStatus status = noErr; *ret_array = NULL; status = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); if (status != noErr) { fprintf(stderr, "SecIdentitySearchCreate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); return (status); } array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); while (TRUE) { status = SecIdentitySearchCopyNext(search, &identity); if (status != noErr) { if (status != errSecItemNotFound) { fprintf(stderr, "SecIdentitySearchCopyNext failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); } break; /* out of while */ } CFArrayAppendValue(array, identity); my_CFRelease(&identity); } my_CFRelease(&search); if (CFArrayGetCount(array) == 0) { my_CFRelease(&array); } else { status = noErr; } *ret_array = array; return (status); } #endif /* TARGET_OS_EMBEDDED */ /* * Function: IdentityCreateFromDictionary * * Purpose: * This function locates a SecIdentityRef matching the passed in * EAPSecIdentityHandle, in the form of a dictionary. It also handles the * NULL case i.e. find the first identity. * * Old EAPSecIdentityHandle's used a dictionary with two key/value * pairs, one for the type, the second for the data corresponding to the * entire certificate. * * This function grabs all of the identities, then finds a match, either * the first identity (dict == NULL), or one that matches the given * certificate. * Returns: * noErr and a non-NULL SecIdentityRef in *ret_identity if an identity * was found, non-noErr otherwise. */ static OSStatus IdentityCreateFromDictionary(CFDictionaryRef dict, SecIdentityRef * ret_identity) { SecCertificateRef cert_to_match = NULL; int count; int i; CFArrayRef identity_list; OSStatus status; *ret_identity = NULL; if (dict != NULL) { CFStringRef certid_type; CFDataRef certid_data; status = paramErr; certid_type = CFDictionaryGetValue(dict, kEAPSecIdentityHandleType); if (isA_CFString(certid_type) == NULL) { goto done; } if (!CFEqual(certid_type, kEAPSecIdentityHandleTypeCertificateData)) { goto done; } certid_data = CFDictionaryGetValue(dict, kEAPSecIdentityHandleData); if (isA_CFData(certid_data) == NULL) { goto done; } cert_to_match = _EAPCFDataCreateSecCertificate(certid_data); if (cert_to_match == NULL) { goto done; } } status = EAPSecIdentityListCreate(&identity_list); if (status != noErr) { goto done; } count = CFArrayGetCount(identity_list); for (i = 0; *ret_identity == NULL && i < count; i++) { SecIdentityRef identity; SecCertificateRef this_cert; identity = (SecIdentityRef)CFArrayGetValueAtIndex(identity_list, i); if (cert_to_match == NULL) { /* just return the first one */ CFRetain(identity); *ret_identity = identity; break; } status = SecIdentityCopyCertificate(identity, &this_cert); if (this_cert == NULL) { fprintf(stderr, "IdentityCreateFromDictionary:" "SecIdentityCopyCertificate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); break; } if (EAPSecCertificateEqual(cert_to_match, this_cert)) { /* found a match */ CFRetain(identity); *ret_identity = identity; } CFRelease(this_cert); } CFRelease(identity_list); done: my_CFRelease(&cert_to_match); return (status); } #if TARGET_OS_EMBEDDED static OSStatus IdentityCreateFromData(CFDataRef data, SecIdentityRef * ret_identity) { const void * keys[] = { kSecClass, kSecReturnRef, kSecValuePersistentRef }; CFDictionaryRef query; CFTypeRef results = NULL; OSStatus status = noErr; const void * values[] = { kSecClassIdentity, kCFBooleanTrue, data }; *ret_identity = NULL; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &results); CFRelease(query); if (status == noErr) { *ret_identity = (SecIdentityRef)results; } return (status); } #else /* TARGET_OS_EMBEDDED */ static OSStatus IdentityCreateFromData(CFDataRef data, SecIdentityRef * ret_identity) { SecKeychainItemRef cert; OSStatus status; status = SecKeychainItemCopyFromPersistentReference(data, &cert); if (status != noErr) { return (status); } status = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef) cert, ret_identity); CFRelease(cert); return (status); } #endif /* TARGET_OS_EMBEDDED */ /* * Function: EAPSecIdentityHandleCreateSecIdentity * Purpose: * Creates a SecIdentityRef for the given EAPSecIdentityHandle. * * The handle 'cert_id' is NULL, a non-NULL dictionary, or a non-NULL data. * Any other input is invalid. * * Returns: * noErr and !NULL *ret_identity on success, non-noErr otherwise. */ OSStatus EAPSecIdentityHandleCreateSecIdentity(EAPSecIdentityHandleRef cert_id, SecIdentityRef * ret_identity) { *ret_identity = NULL; if (cert_id == NULL || isA_CFDictionary(cert_id) != NULL) { return (IdentityCreateFromDictionary(cert_id, ret_identity)); } if (isA_CFData(cert_id) != NULL) { return (IdentityCreateFromData((CFDataRef)cert_id, ret_identity)); } return (paramErr); } static OSStatus _EAPSecIdentityCreateCertificateTrustChain(SecIdentityRef identity, CFArrayRef * ret_chain) { SecCertificateRef cert; CFArrayRef certs; SecPolicyRef policy = NULL; OSStatus status; SecTrustRef trust = NULL; SecTrustResultType trust_result; *ret_chain = NULL; status = EAPSecPolicyCopy(&policy); if (status != noErr) { fprintf(stderr, "EAPSecPolicyCopy failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); goto done; } status = SecIdentityCopyCertificate(identity, &cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); goto done; } certs = CFArrayCreate(NULL, (const void **)&cert, 1, &kCFTypeArrayCallBacks); my_CFRelease(&cert); status = SecTrustCreateWithCertificates(certs, policy, &trust); my_CFRelease(&certs); if (status != noErr) { fprintf(stderr, "SecTrustCreateWithCertificates failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); goto done; } status = SecTrustEvaluate(trust, &trust_result); if (status != noErr) { fprintf(stderr, "SecTrustEvaluate returned %s (%d)\n", EAPSecurityErrorString(status), (int)status); } #if TARGET_OS_EMBEDDED { CFMutableArrayRef array; int count = SecTrustGetCertificateCount(trust); int i; if (count == 0) { fprintf(stderr, "SecTrustGetCertificateCount returned 0)\n"); goto done; } array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); for (i = 0; i < count; i++) { SecCertificateRef s; s = SecTrustGetCertificateAtIndex(trust, i); CFArrayAppendValue(array, s); } *ret_chain = array; } #else /* TARGET_OS_EMBEDDED */ { CSSM_TP_APPLE_EVIDENCE_INFO * ignored; CFArrayRef status_chain = NULL; status = SecTrustGetResult(trust, &trust_result, &status_chain, &ignored); if (status != noErr) { fprintf(stderr, "SecTrustGetResult failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); my_CFRelease(&status_chain); /* just in case */ goto done; } else { *ret_chain = status_chain; } } #endif /* TARGET_OS_EMBEDDED */ done: my_CFRelease(&trust); my_CFRelease(&policy); return (status); } /* * Function: EAPSecIdentityHandleCreateSecIdentityTrustChain * * Purpose: * Turns an EAPSecIdentityHandle into the array required by * SSLSetCertificates(). See the for more * information. * * Returns: * noErr and *ret_array != NULL on success, non-noErr otherwise. */ OSStatus EAPSecIdentityHandleCreateSecIdentityTrustChain(EAPSecIdentityHandleRef cert_id, CFArrayRef * ret_array) { CFMutableArrayRef array = NULL; int count; SecIdentityRef identity = NULL; OSStatus status; CFArrayRef trust_chain = NULL; *ret_array = NULL; status = EAPSecIdentityHandleCreateSecIdentity(cert_id, &identity); if (status != noErr) { goto done; } status = _EAPSecIdentityCreateCertificateTrustChain(identity, &trust_chain); if (status != noErr) { fprintf(stderr, "_EAPSecIdentityCreateCertificateTrustChain failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); goto done; } count = CFArrayGetCount(trust_chain); array = CFArrayCreateMutableCopy(NULL, count, trust_chain); /* array[0] contains the identity's cert, replace it with the identity */ CFArraySetValueAtIndex(array, 0, identity); *ret_array = array; done: my_CFRelease(&trust_chain); my_CFRelease(&identity); return (status); } /* * Function: EAPSecIdentityHandleCreate * Purpose: * Return the persistent reference for a given SecIdentityRef. * Returns: * !NULL SecIdentityRef on success, NULL otherwise. */ #if TARGET_OS_EMBEDDED EAPSecIdentityHandleRef EAPSecIdentityHandleCreate(SecIdentityRef identity) { const void * keys[] = { kSecReturnPersistentRef, kSecValueRef }; CFDictionaryRef query; CFTypeRef results = NULL; OSStatus status = noErr; const void * values[] = { kCFBooleanTrue, identity }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &results); if (status != noErr) { results = NULL; fprintf(stderr, "EAPSecIdentityHandleCreate() failed, %d\n", (int)status); } CFRelease(query); return (results); } #else /* TARGET_OS_EMBEDDED */ EAPSecIdentityHandleRef EAPSecIdentityHandleCreate(SecIdentityRef identity) { SecCertificateRef cert; CFDataRef data; OSStatus status; status = SecIdentityCopyCertificate(identity, &cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); return (NULL); } status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert, &data); CFRelease(cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); return (NULL); } return (data); } #endif /* TARGET_OS_EMBEDDED */ /* * Function: EAPSecCertificateArrayCreateCFDataArray * Purpose: * Convert a CFArray[SecCertificate] to CFArray[CFData]. */ CFArrayRef EAPSecCertificateArrayCreateCFDataArray(CFArrayRef certs) { CFMutableArrayRef array = NULL; int count = CFArrayGetCount(certs); int i; array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); for (i = 0; i < count; i++) { SecCertificateRef cert; CFDataRef data; cert = (SecCertificateRef) isA_SecCertificate(CFArrayGetValueAtIndex(certs, i)); if (cert == NULL) { continue; } data = _EAPSecCertificateCreateCFData(cert); if (data == NULL) { continue; } CFArrayAppendValue(array, data); my_CFRelease(&data); } return (array); } /* * Function: EAPCFDataArrayCreateSecCertificateArray * Purpose: * Convert a CFArray[CFData] to CFArray[SecCertificate]. */ CFArrayRef EAPCFDataArrayCreateSecCertificateArray(CFArrayRef certs) { CFMutableArrayRef array = NULL; int count = CFArrayGetCount(certs); int i; array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); for (i = 0; i < count; i++) { SecCertificateRef cert; CFDataRef data; data = isA_CFData((CFDataRef)CFArrayGetValueAtIndex(certs, i)); if (data == NULL) { goto failed; } cert = _EAPCFDataCreateSecCertificate(data); if (cert == NULL) { goto failed; } CFArrayAppendValue(array, cert); my_CFRelease(&cert); } return (array); failed: my_CFRelease(&array); return (NULL); } CFTypeRef isA_SecCertificate(CFTypeRef obj) { return (isA_CFType(obj, SecCertificateGetTypeID())); } Boolean EAPSecCertificateListEqual(CFArrayRef list1, CFArrayRef list2) { int count1 = 0; int count2 = 0; int i; count1 = CFArrayGetCount(list1); count2 = CFArrayGetCount(list2); if (count1 != count2) { return (FALSE); } for (i = 0; i < count1; i++) { SecCertificateRef cert1; SecCertificateRef cert2; cert1 = (SecCertificateRef)CFArrayGetValueAtIndex(list1, i); cert2 = (SecCertificateRef)CFArrayGetValueAtIndex(list2, i); if (EAPSecCertificateEqual(cert1, cert2) == FALSE) { return (FALSE); } } return (TRUE); } #if TARGET_OS_EMBEDDED typedef CFArrayRef (*cert_names_func_t)(SecCertificateRef cert); static void dict_insert_cert_name_attr(CFMutableDictionaryRef dict, CFStringRef key, cert_names_func_t func, SecCertificateRef cert) { CFArrayRef names; names = (*func)(cert); if (names != NULL) { CFDictionarySetValue(dict, key, CFArrayGetValueAtIndex(names, 0)); CFRelease(names); } return; } typedef struct { cert_names_func_t func; CFStringRef key; } cert_names_func_info_t; CFDictionaryRef EAPSecCertificateCopyAttributesDictionary(const SecCertificateRef cert) { cert_names_func_info_t cert_names_info[] = { { SecCertificateCopyRFC822Names, kEAPSecCertificateAttributeRFC822Name }, { SecCertificateCopyNTPrincipalNames, kEAPSecCertificateAttributeNTPrincipalName }, { SecCertificateCopyCommonNames, kEAPSecCertificateAttributeCommonName }, { NULL, NULL } }; CFMutableDictionaryRef dict = NULL; bool is_root = false; cert_names_func_info_t * scan; dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); for (scan = cert_names_info; scan->func != NULL; scan++) { dict_insert_cert_name_attr(dict, scan->key, scan->func, cert); } if (is_root) { CFDictionarySetValue(dict, kEAPSecCertificateAttributeIsRoot, kCFBooleanTrue); } if (CFDictionaryGetCount(dict) == 0) { CFRelease(dict); dict = NULL; } return (dict); } CFStringRef EAPSecCertificateCopySHA1DigestString(SecCertificateRef cert) { const UInt8 * bytes; CFIndex count; CFIndex i; CFDataRef hash; CFMutableStringRef str; hash = SecCertificateGetSHA1Digest(cert); count = CFDataGetLength(hash); bytes = CFDataGetBytePtr(hash); str = CFStringCreateMutable(NULL, 0); for (i = 0; i < count; i++) { CFStringAppendFormat(str, NULL, CFSTR("%02x"), bytes[i]); } return (str); } #else /* TARGET_OS_EMBEDDED */ /** ** Certificate attributes: ** The following code is transcribed from SFCertificateData.m. Once ** SecCertificateRef's give us what we need, we can remove this code. **/ #define MS_OID OID_DOD, 0x01, 0x04, 0x01, 0x82, 0x37 #define MS_OID_LEN 2 + 5 #define MS_ENROLLMENT_OID MS_OID, 0x14 #define MS_ENROLLMENT_LEN MS_OID_LEN + 1 #define MS_PRINCIPAL_NAME MS_ENROLLMENT_OID, 0x02, 0x03 #define MS_PRINCIPAL_NAME_LEN MS_ENROLLMENT_LEN + 2 static const uint8 OID_MS_NTPrincipalName[] = {MS_PRINCIPAL_NAME}; const CSSM_OID CSSMOID_MS_NTPrincipalName = {MS_PRINCIPAL_NAME_LEN, (uint8 *)OID_MS_NTPrincipalName}; typedef struct { uint32 ID; /* Identifier */ uint32 tag; /* Tag */ uint32 length; /* Data length */ bool indefinite; /* Item has indefinite length */ uint32 headerSize; /* Size of tag+length */ uint32 header[ 16 ]; /* Tag+length data */ } ASN1Item; // ASN.1 stuff #define TAG_MASK 0x1F /* Bits 5 - 1 */ #define EOC 0x00 /* 0: End-of-contents octets */ #define LEN_XTND 0x80 /* Indefinite or long form */ #define LEN_MASK 0x7F /* Bits 7 - 1 */ // --------------------------------------------------------------------------- // getASN1ItemInfo // --------------------------------------------------------------------------- // Given a pointer to an ASN.1-encoded object and a pointer to an ASN1Item // struct, fill out that struct with tag and length information. // inObjectLen prevents reading past the end of the object being parsed. // Returns TRUE if successful, FALSE if not. static bool getASN1ItemInfo (uint8 *inObject, int inObjectLen, ASN1Item *item) { uint32 tag, length, index = 0; uint8 *p = inObject; uint8 *q = inObject+inObjectLen; if (!p || !item || inObjectLen < 2) return(false); memset( item, 0, sizeof( ASN1Item ) ); item->indefinite = FALSE; tag = item->header[ index++ ] = *p++; item->ID = tag & ~TAG_MASK; tag &= TAG_MASK; if( tag == TAG_MASK ) /* long tag encoded as sequence of 7-bit values */ { uint32 value; tag = 0; do { value = *p++; tag = ( tag << 7 ) | ( value & 0x7F ); item->header[ index++ ] = value; } while ((value & LEN_XTND) && (p < q) && (*p != EOC)); } item->tag = tag; if ((!(p < q)) || (*p == EOC)) return(false); length = item->header[ index++ ] = *p++; if (!(p < q)) return(false); item->headerSize = index; if ( length & LEN_XTND ) { uint32 i; length &= LEN_MASK; if( length > 4 ) { /* Object length field is greater than 4 bytes?! */ return(false); } item->headerSize += length; item->length = 0; if ( !length ) item->indefinite = true; for ( i = 0; i < length; i++ ) { uint32 ch; if (!(p < q)) return(false); ch = *p++; item->length = ( item->length << 8 ) | ch; item->header[ i + index ] = ch; } } else /* standard 1-byte length field */ { item->length = length; if ((q-p) < length) return(false); } return(true); } static CSSM_BOOL compareCssmData( const CSSM_DATA *d1, const CSSM_DATA *d2) { if (d1->Length != d2->Length) { return CSSM_FALSE; } if(memcmp(d1->Data, d2->Data, d1->Length)) { return CSSM_FALSE; } return CSSM_TRUE; } static CSSM_BOOL compareOids( const CSSM_OID *oid1, const CSSM_OID *oid2) { if((oid1 == NULL) || (oid2 == NULL)) { return CSSM_FALSE; } if(oid1->Length != oid2->Length) { return CSSM_FALSE; } if(memcmp(oid1->Data, oid2->Data, oid1->Length)) { return CSSM_FALSE; } else { return CSSM_TRUE; } } static bool extension_common_valid(const CSSM_DATA * value, bool expect_parsed) { CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value->Data; if (value->Length != sizeof(CSSM_X509_EXTENSION)) { return (false); } switch (cssmExt->format) { case CSSM_X509_DATAFORMAT_ENCODED: if (expect_parsed) { return (false); } /* we don't use the value.tagAndValue field yet */ if (cssmExt->BERvalue.Data == NULL || 0 /* cssmExt->value.tagAndValue == NULL */) { return (false); } break; case CSSM_X509_DATAFORMAT_PARSED: if (!expect_parsed) { return (false); } if ((cssmExt->BERvalue.Data == NULL) || (cssmExt->value.parsedValue == NULL)) { return (false); } break; case CSSM_X509_DATAFORMAT_PAIR: /* we don't use the value.valuePair field yet */ if (cssmExt->BERvalue.Data == NULL || 0 /* cssmExt->value.valuePair == NULL */) { return (false); } break; default: return (false); break; } return (true); } static CFStringRef myCFStringCreateWithDerData(CFAllocatorRef alloc, CSSM_BER_TAG tag_type, const CSSM_DATA * value) { CFStringEncoding encoding = kCFStringEncodingInvalidId; CFStringRef str = NULL; switch (tag_type) { case BER_TAG_PRINTABLE_STRING: case BER_TAG_IA5_STRING: encoding = kCFStringEncodingASCII; break; case BER_TAG_PKIX_UTF8_STRING: case BER_TAG_GENERAL_STRING: case BER_TAG_PKIX_UNIVERSAL_STRING: encoding = kCFStringEncodingUTF8; break; case BER_TAG_T61_STRING: case BER_TAG_VIDEOTEX_STRING: case BER_TAG_ISO646_STRING: encoding = kCFStringEncodingISOLatin1; break; case BER_TAG_PKIX_BMP_STRING: encoding = kCFStringEncodingUnicode; default: break; } if (encoding != kCFStringEncodingInvalidId) { str = CFStringCreateWithBytes(alloc, value->Data, (CFIndex)value->Length, encoding, true); } return (str); } static CFStringRef myCFStringCreateFromPrintableBERSequence(CFAllocatorRef alloc, const CSSM_DATA * value) { char * eos; ASN1Item item; CSSM_DATA item_data = *value; CFStringRef ret_str = NULL; /* determine end-of-sequence based on initial tag */ if (!getASN1ItemInfo(item_data.Data, item_data.Length, &item)) { return (NULL); } eos = (char *)(item_data.Data + (UInt32)item.headerSize + item.length); while (getASN1ItemInfo(item_data.Data, item_data.Length, &item)) { if (((char *)item_data.Data + (UInt32)item.headerSize + item.length) > eos) { break; } item_data.Data += item.headerSize; item_data.Length = item.length; switch (item.tag) { case BER_TAG_UNKNOWN: case BER_TAG_SEQUENCE: case BER_TAG_SET: /* skip constructed object lengths */ break; case BER_TAG_PKIX_UTF8_STRING: case BER_TAG_NUMERIC_STRING: case BER_TAG_PRINTABLE_STRING: case BER_TAG_T61_STRING: case BER_TAG_VIDEOTEX_STRING: case BER_TAG_IA5_STRING: case BER_TAG_GRAPHIC_STRING: case BER_TAG_ISO646_STRING: case BER_TAG_GENERAL_STRING: case BER_TAG_PKIX_UNIVERSAL_STRING: ret_str = myCFStringCreateWithDerData(alloc, item.tag, &item_data); goto done; default: item_data.Data += item_data.Length; break; } } done: return (ret_str); } static void parse_subject_struct(CFMutableDictionaryRef dict, const CSSM_DATA * d) { CSSM_X509_RDN_PTR rdnp; int r; CSSM_X509_NAME_PTR name = (CSSM_X509_NAME_PTR)d->Data; if ((name == NULL) || (d->Length != sizeof(CSSM_X509_NAME))) { return; } for (r = 0; r < name->numberOfRDNs; r++) { int p; rdnp = &name->RelativeDistinguishedName[r]; for (p = 0; p < rdnp->numberOfPairs; p++) { CFStringRef key = NULL; CSSM_X509_TYPE_VALUE_PAIR * ptvp; CFStringRef value; ptvp = &rdnp->AttributeTypeAndValue[p]; if (compareOids(&ptvp->type, &CSSMOID_CommonName)) { key = kEAPSecCertificateAttributeCommonName; } else if (compareOids(&ptvp->type, &CSSMOID_EmailAddress)) { key = kEAPSecCertificateAttributeEmailAddress; } else { continue; } value = myCFStringCreateWithDerData(NULL, ptvp->valueType, &ptvp->value); if (value != NULL) { CFStringRef current; current = CFDictionaryGetValue(dict, key); if (current == NULL) { CFDictionarySetValue(dict, key, value); } CFRelease(value); } } } return; } static void parse_subject_alt_name(CFMutableDictionaryRef dict, const CSSM_DATA * d) { CSSM_X509_EXTENSION * cssmExt = (CSSM_X509_EXTENSION *)d->Data; int i; CE_GeneralName * name; CE_OtherName * other; CE_GeneralNames * san; if (extension_common_valid(d, true) == false) { return; } san = (CE_GeneralNames *)cssmExt->value.parsedValue; for (i = 0; i < san->numNames; i++) { CFStringRef key = NULL; CFStringRef value = NULL; CSSM_OID tmp_oid = { 0, nil }; CSSM_DATA tmp_value = { 0, nil }; name = &san->generalName[i]; switch (name->nameType) { case GNT_RFC822Name: key = kEAPSecCertificateAttributeRFC822Name; value = CFStringCreateWithBytes(NULL, name->name.Data, name->name.Length, kCFStringEncodingASCII, 0); break; case GNT_OtherName: other = (CE_OtherName *)(name->name.Data); /* work-around for 3722123 */ if (other != NULL) { tmp_oid = other->typeId; tmp_value = other->value; } if (tmp_oid.Data && tmp_value.Data == NULL) { uint32 unparsed_len = tmp_oid.Length; uint8 header_len = 2; if (unparsed_len > header_len) { uint8 oid_len = tmp_oid.Data[1]; if (unparsed_len > (oid_len + header_len)) { tmp_oid.Length = oid_len; tmp_oid.Data = (uint8*)(tmp_oid.Data + (uint32)header_len); tmp_value.Length = unparsed_len - (oid_len + header_len); tmp_value.Data = (uint8*)(tmp_oid.Data + (uint32)tmp_oid.Length); } } } if (compareOids(&tmp_oid, &CSSMOID_MS_NTPrincipalName)) { key = kEAPSecCertificateAttributeNTPrincipalName; value = myCFStringCreateFromPrintableBERSequence(NULL, &tmp_value); break; } break; default: break; } if (key != NULL && value != NULL) { CFStringRef current; current = CFDictionaryGetValue(dict, key); if (current == NULL) { CFDictionarySetValue(dict, key, value); } } if (value != NULL) { CFRelease(value); } } return; } CFDictionaryRef EAPSecCertificateCopyAttributesDictionary(const SecCertificateRef cert) { CSSM_DATA cert_data; CSSM_CL_HANDLE clh; CSSM_RETURN crtn; CFMutableDictionaryRef dict = NULL; CSSM_DATA_PTR issuer = NULL; CSSM_FIELD_PTR fields; int i; bool is_root = false; uint32 n_fields; CSSM_DATA_PTR subject = NULL; CSSM_DATA_PTR subject_alt = NULL; CSSM_DATA_PTR subject_struct = NULL; (void)SecCertificateGetCLHandle(cert, &clh); (void)SecCertificateGetData(cert, &cert_data); crtn = CSSM_CL_CertGetAllFields(clh, &cert_data, &n_fields, &fields); if (crtn) { fprintf(stderr, "CSSM_CL_CertGetAllFields failed %d\n", (int)crtn); goto done; } dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); for (i = 0; i < n_fields; i++) { if (compareOids(&fields[i].FieldOid, &CSSMOID_X509V1IssuerName)) { issuer = &fields[i].FieldValue; } else if (compareOids(&fields[i].FieldOid, &CSSMOID_X509V1SubjectName)) { subject = &fields[i].FieldValue; } else if (compareOids(&fields[i].FieldOid, &CSSMOID_SubjectAltName)) { subject_alt = &fields[i].FieldValue; } else if (compareOids(&fields[i].FieldOid, &CSSMOID_X509V1SubjectNameCStruct)) { subject_struct = &fields[i].FieldValue; } } is_root = (issuer != NULL) && (subject != NULL) && (issuer->Data != NULL) && (subject->Data != NULL) && (issuer->Data != subject->Data) && compareCssmData(subject, issuer); if (is_root) { CSSM_RETURN crtn; /* verify that the cert is self-signed [3949265] */ crtn = CSSM_CL_CertVerify(clh, CSSM_INVALID_HANDLE, &cert_data, &cert_data, NULL, 0); if (crtn != CSSM_OK) { is_root = false; } } if (is_root) { CFDictionarySetValue(dict, kEAPSecCertificateAttributeIsRoot, kCFBooleanTrue); } if (subject_alt != NULL) { parse_subject_alt_name(dict, subject_alt); } if (subject_struct != NULL) { parse_subject_struct(dict, subject_struct); } /* free memory */ if (fields != NULL) { crtn = CSSM_CL_FreeFields(clh, n_fields, &fields); } if (CFDictionaryGetCount(dict) == 0) { CFRelease(dict); dict = NULL; } done: return (dict); } #endif /* TARGET_OS_EMBEDDED */ CFStringRef EAPSecCertificateCopyUserNameString(SecCertificateRef cert) { CFStringRef attrs[] = { kEAPSecCertificateAttributeNTPrincipalName, kEAPSecCertificateAttributeRFC822Name, kEAPSecCertificateAttributeCommonName, NULL }; CFDictionaryRef dict = NULL; int i; CFStringRef user_name = NULL; dict = EAPSecCertificateCopyAttributesDictionary(cert); if (dict == NULL) { goto done; } for (i = 0; attrs[i] != NULL; i++) { user_name = CFDictionaryGetValue(dict, attrs[i]); if (user_name != NULL) { break; } } done: if (user_name != NULL) { CFRetain(user_name); } my_CFRelease(&dict); return (user_name); } #ifdef TEST_EAPSecCertificateCopyAttributesDictionary #if TARGET_OS_EMBEDDED static CFArrayRef copyAllCerts(void) { const void * keys[] = { kSecClass, kSecReturnRef, kSecMatchLimit }; CFDictionaryRef query; CFTypeRef results; OSStatus status; const void * values[] = { kSecClassCertificate, kCFBooleanTrue, kSecMatchLimitAll }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &results); CFRelease(query); if (status == noErr) { return (results); } return (NULL); } #else /* TARGET_OS_EMBEDDED */ static CFArrayRef copyAllCerts(void) { CFMutableArrayRef array = NULL; SecKeychainAttributeList attr_list; SecCertificateRef cert = NULL; CSSM_DATA data; SecKeychainItemRef item = NULL; SecKeychainSearchRef search = NULL; OSStatus status; status = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &search); if (status != noErr) { fprintf(stderr, "SecKeychainSearchCreateFromAttributes failed, %d", (int)status); goto failed; } array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); do { status = SecKeychainSearchCopyNext(search, &item); if (status != noErr) { break; } attr_list.count = 0; attr_list.attr = NULL; status = SecKeychainItemCopyContent(item, NULL, /* item class */ &attr_list, &data.Length, (void * *)(&data.Data)); if (status != noErr) { fprintf(stderr, "SecKeychainItemCopyContent failed, %d", (int)status); break; } status = SecCertificateCreateFromData(&data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &cert); SecKeychainItemFreeContent(&attr_list, data.Data); if (status != noErr) { fprintf(stderr, "SecCertificateCreateFromData failed, %d", (int)status); break; } CFArrayAppendValue(array, cert); if (item != NULL) { CFRelease(item); } if (cert != NULL) { CFRelease(cert); } } while (1); failed: my_CFRelease(&search); if (array != NULL && CFArrayGetCount(array) == 0) { CFRelease(array); array = NULL; } return (array); } #endif /* TARGET_OS_EMBEDDED */ static void showAllCerts(void) { CFArrayRef certs; int count; int i; certs = copyAllCerts(); if (certs == NULL) { return; } count = CFArrayGetCount(certs); for (i = 0; i < count; i++) { CFDictionaryRef dict; SecCertificateRef cert; cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); dict = EAPSecCertificateCopyAttributesDictionary(cert); if (dict != NULL) { (void)CFShow(dict); printf("\n"); CFRelease(dict); } } CFRelease(certs); return; } int main(int argc, const char * argv[]) { showAllCerts(); if (argc > 1) { sleep(120); } exit(0); return (0); } #endif /* TEST_EAPSecCertificateCopyAttributesDictionary */ #ifdef TEST_EAPSecIdentity static void dump_as_xml(CFPropertyListRef p) { CFDataRef xml; xml = CFPropertyListCreateXMLData(NULL, p); if (xml != NULL) { fwrite(CFDataGetBytePtr(xml), CFDataGetLength(xml), 1, stdout); CFRelease(xml); } return; } static void dump_cert(SecCertificateRef cert) { CFDictionaryRef attrs = NULL; attrs = EAPSecCertificateCopyAttributesDictionary(cert); if (attrs != NULL) { printf("Attributes:\n"); dump_as_xml(attrs); CFRelease(attrs); } #if TARGET_OS_EMBEDDED else { CFStringRef summary; summary = SecCertificateCopySubjectSummary(cert); if (summary != NULL) { printf("Summary:\n"); dump_as_xml(summary); CFRelease(summary); } } #endif /* TARGET_OS_EMBEDDED */ } static void show_all_identities(void) { int count; int i; CFArrayRef list = NULL; OSStatus status; status = EAPSecIdentityListCreate(&list); if (status != noErr) { fprintf(stderr, "EAPSecIdentityListCreate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(1); } count = CFArrayGetCount(list); for (i = 0; i < count; i++) { SecCertificateRef cert = NULL; EAPSecIdentityHandleRef handle = NULL; SecIdentityRef identity; SecIdentityRef new_id = NULL; identity = (SecIdentityRef)CFArrayGetValueAtIndex(list, i); handle = EAPSecIdentityHandleCreate(identity); if (handle == NULL) { fprintf(stderr, "EAPSecIdentityHandleCreate failed\n"); exit(1); } status = EAPSecIdentityHandleCreateSecIdentity(handle, &new_id); if (status != noErr) { fprintf(stderr, "EAPSecIdentityHandleCreateSecIdentity failed %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(1); } status = SecIdentityCopyCertificate(new_id, &cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed %d\n", (int)status); exit(1); } printf("\nCertificate[%d]:\n", i); dump_cert(cert); printf("Handle:\n"); dump_as_xml(handle); my_CFRelease(&cert); my_CFRelease(&new_id); my_CFRelease(&handle); } CFRelease(list); return; } static void get_identity(const char * filename) { SecCertificateRef cert; CFTypeRef handle; SecIdentityRef identity; OSStatus status; handle = my_CFPropertyListCreateFromFile(filename); if (handle == NULL) { fprintf(stderr, "could not read '%s'\n", filename); exit(1); } status = EAPSecIdentityHandleCreateSecIdentity(handle, &identity); if (status != noErr) { fprintf(stderr, "could not turn handle into identity, %d\n", (int)status); exit(1); } status = SecIdentityCopyCertificate(identity, &cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed %d\n", (int)status); exit(1); } dump_cert(cert); CFRelease(cert); CFRelease(handle); CFRelease(identity); return; } #if TARGET_OS_EMBEDDED static OSStatus remove_identity(SecIdentityRef identity) { const void * keys[] = { kSecValueRef }; CFDictionaryRef query; OSStatus status = noErr; const void * values[] = { identity }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemDelete(query); if (status != noErr) { fprintf(stderr, "SecItemDelete() failed, %d\n", (int)status); } CFRelease(query); return (status); } static void remove_all_identities(void) { int count; int i; CFArrayRef list = NULL; OSStatus status; status = EAPSecIdentityListCreate(&list); if (status != noErr) { fprintf(stderr, "EAPSecIdentityListCreate failed, %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(1); } count = CFArrayGetCount(list); for (i = 0; i < count; i++) { SecCertificateRef cert = NULL; SecIdentityRef identity; identity = (SecIdentityRef)CFArrayGetValueAtIndex(list, i); status = SecIdentityCopyCertificate(identity, &cert); if (status != noErr) { fprintf(stderr, "SecIdentityCopyCertificate failed %d\n", (int)status); exit(1); } printf("Removing:\n"); dump_cert(cert); my_CFRelease(&cert); remove_identity(identity); } CFRelease(list); return; } #endif /* TARGET_OS_EMBEDDED */ static void usage(const char * progname) { fprintf(stderr, "%s: list\n", progname); fprintf(stderr, "%s: get \n", progname); #if TARGET_OS_EMBEDDED fprintf(stderr, "%s: remove\n", progname); #endif /* TARGET_OS_EMBEDDED */ exit(1); return; } enum { kCommandList, kCommandGet, kCommandRemove }; int main(int argc, char * argv[]) { int command = kCommandList; if (argc > 1) { if (strcmp(argv[1], "list") == 0) { ; } else if (strcmp(argv[1], "get") == 0) { if (argc < 3) { usage(argv[0]); } command = kCommandGet; } #if TARGET_OS_EMBEDDED else if (strcmp(argv[1], "remove") == 0) { command = kCommandRemove; } #endif /* TARGET_OS_EMBEDDED */ else { usage(argv[0]); } } switch (command) { case kCommandList: show_all_identities(); break; case kCommandGet: get_identity(argv[2]); break; #if TARGET_OS_EMBEDDED case kCommandRemove: remove_all_identities(); break; #endif /* TARGET_OS_EMBEDDED */ } exit(0); } #endif /* TEST_EAPSecIdentity */ #ifdef TEST_EAPSecIdentityHandleCreateSecIdentityTrustChain int main() { int count; int i; CFArrayRef list = NULL; CFArrayRef trust_chain; OSStatus status; status = EAPSecIdentityHandleCreateSecIdentityTrustChain(NULL, &trust_chain); if (status != noErr) { fprintf(stderr, "EAPSecIdentityHandleCreateSecIdentityTrustChain" " failed %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(2); } CFShow(trust_chain); CFRelease(trust_chain); status = EAPSecIdentityListCreate(&list); if (status != noErr) { fprintf(stderr, "EAPSecIdentityListCreate" " failed %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(2); } count = CFArrayGetCount(list); for (i = 0; i < count; i++) { EAPSecIdentityHandleRef h; SecIdentityRef ident = CFArrayGetValueAtIndex(list, i); h = EAPSecIdentityHandleCreate(ident); status = EAPSecIdentityHandleCreateSecIdentityTrustChain(h, &trust_chain); if (status != noErr) { fprintf(stderr, "EAPSecIdentityHandleCreateSecIdentityTrustChain" " failed %s (%d)\n", EAPSecurityErrorString(status), (int)status); exit(2); } CFRelease(h); fprintf(stderr, "[%d]:\n", i); CFShow(trust_chain); CFRelease(trust_chain); } exit(0); return (0); } #endif /* TEST_EAPSecIdentityHandleCreateSecIdentityTrustChain */