/* * Copyright (c) 2012 Apple Computer, 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@ */ #include #include #include #include #include #include "security.h" #include "kext_tools_util.h" /******************************************************************************* * Helper functions *******************************************************************************/ static OSStatus checkRootCertificateIsApple(OSKextRef aKext); static CFStringRef copyCDHash(SecStaticCodeRef code); static CFStringRef copyIssuerCN(SecCertificateRef certificate); static void copySigningInfo(CFURLRef kextURL, CFStringRef* cdhash, CFStringRef* teamId, CFStringRef* subjectCN, CFStringRef* issuerCN); static CFArrayRef copySubjectCNArray(CFURLRef kextURL); static CFStringRef copyTeamID(SecCertificateRef certificate); static CFStringRef createArchitectureList(OSKextRef aKext, CFBooleanRef *isFat); static void createHashForMT(CFURLRef kextURL, char ** signatureBuffer); static void filterKextLoadForMT(OSKextRef aKext, CFMutableArrayRef *kextList); /******************************************************************************* * messageTraceExcludedKext() - log MessageTracer message for kexts in * exclude list. * MessageTrace when we block the load of something * on the kext exclude list *******************************************************************************/ void messageTraceExcludedKext(OSKextRef theKext) { CFStringRef versionString; CFStringRef bundleIDString; CFURLRef kextURL = NULL; // must release CFStringRef filename = NULL; // must release aslmsg amsg = NULL; // must free char *versionCString = NULL; // must free char *bundleIDCString = NULL; // must free char *filenameCString = NULL; // must free kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext)); if (!kextURL) { OSKextLogMemError(); goto finish; } versionString = OSKextGetValueForInfoDictionaryKey(theKext, kCFBundleVersionKey); if (versionString) { versionCString = createUTF8CStringForCFString(versionString); } bundleIDString = OSKextGetValueForInfoDictionaryKey(theKext, kCFBundleIdentifierKey); if (bundleIDString) { bundleIDCString = createUTF8CStringForCFString(bundleIDString); } filename = CFURLCopyLastPathComponent(kextURL); if (filename) { filenameCString = createUTF8CStringForCFString(filename); } SAFE_RELEASE(filename); /* log the message tracer data */ amsg = asl_new(ASL_TYPE_MSG); if (!amsg) { OSKextLogMemError(); goto finish; } asl_set(amsg, kMessageTracerDomainKey, kMTKextBlockedDomain); asl_set(amsg, kMessageTracerBundleIDKey, bundleIDCString ? bundleIDCString : ""); asl_set(amsg, kMessageTracerVersionKey, versionCString ? versionCString : ""); asl_set(amsg, kMessageTracerKextNameKey, filenameCString ? filenameCString : ""); asl_log(NULL, amsg, ASL_LEVEL_NOTICE, ""); finish: SAFE_FREE(versionCString); SAFE_FREE(bundleIDCString); SAFE_FREE(filenameCString); SAFE_RELEASE(kextURL); if (amsg) { asl_free(amsg); } return; } /******************************************************************************* * checkRootCertificateIsApple() - check if the root certificate of the kext * is issued by Apple * *******************************************************************************/ static OSStatus checkRootCertificateIsApple(OSKextRef aKext) { OSStatus result = -1; CFURLRef kextURL = NULL; // must release SecStaticCodeRef staticCodeRef = NULL; // must release SecRequirementRef requirementRef = NULL; // must release CFStringRef myCFString; CFStringRef requirementsString; if (aKext == NULL) { return result; } kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext)); if (!kextURL) { OSKextLogMemError(); goto finish; } if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &staticCodeRef) != errSecSuccess || (staticCodeRef == NULL)) { OSKextLogMemError(); goto finish; } /* set up correct requirement string */ myCFString = OSKextGetIdentifier(aKext); if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) { requirementsString = CFSTR("anchor apple"); } else { requirementsString = CFSTR("anchor apple generic"); } if (SecRequirementCreateWithString(requirementsString, kSecCSDefaultFlags, &requirementRef) != errSecSuccess || (requirementRef == NULL)) { OSKextLogMemError(); goto finish; } // errSecCSUnsigned == -67062 result = SecStaticCodeCheckValidity(staticCodeRef, kSecCSDefaultFlags, requirementRef); if (result != 0) { OSKextLogCFString(NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag, CFSTR("Invalid signature %ld for kext %@"), (long)result, aKext); } finish: SAFE_RELEASE(kextURL); SAFE_RELEASE(staticCodeRef); SAFE_RELEASE(requirementRef); return result; } /******************************************************************************* * createHashForMT() - create a hash signature for the kext * *******************************************************************************/ static void createHashForMT(CFURLRef kextURL, char ** signatureBuffer) { CFMutableDictionaryRef signdict = NULL; // must release SecCodeSignerRef signerRef = NULL; // must release SecStaticCodeRef staticCodeRef = NULL; // must release CFDataRef signature = NULL; // must release CFDictionaryRef signingDict = NULL; // must release CFDataRef cdhash = NULL; // must release CFMutableDictionaryRef resourceRules = NULL; // must release CFMutableDictionaryRef rules = NULL; // must release CFMutableDictionaryRef omitPlugins = NULL; // must release char * tempBufPtr = NULL; // do not free if (!kextURL || !signatureBuffer) { return; } /* Ad-hoc sign the code temporarily so we can get its hash */ if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &staticCodeRef) != 0 || (staticCodeRef == NULL)) { OSKextLogMemError(); goto finish; } signature = CFDataCreateMutable(NULL, 0); if (signature == NULL) { OSKextLogMemError(); goto finish; } signdict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (signdict == NULL) { OSKextLogMemError(); goto finish; } CFDictionarySetValue(signdict, kSecCodeSignerIdentity, kCFNull); CFDictionarySetValue(signdict, kSecCodeSignerDetached, signature); resourceRules = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (resourceRules == NULL) { OSKextLogMemError(); goto finish; } rules = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (rules == NULL) { OSKextLogMemError(); goto finish; } omitPlugins = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (omitPlugins == NULL) { OSKextLogMemError(); goto finish; } /* exclude PlugIns directory */ CFNumberRef myNumValue; int myNum = 100; myNumValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &myNum); if (myNumValue == NULL) { OSKextLogMemError(); goto finish; } CFDictionarySetValue(omitPlugins, CFSTR("omit"), kCFBooleanTrue); CFDictionarySetValue(omitPlugins, CFSTR("weight"), myNumValue); CFRelease( myNumValue ); CFDictionarySetValue(rules, CFSTR("^.*"), kCFBooleanTrue); CFDictionarySetValue(rules, CFSTR("^PlugIns/"), omitPlugins); CFDictionarySetValue(resourceRules, CFSTR("rules"), rules); CFDictionarySetValue(signdict, kSecCodeSignerResourceRules, resourceRules); if (SecCodeSignerCreate(signdict, kSecCSDefaultFlags, &signerRef) != 0) { OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s - SecCodeSignerCreate failed", __func__); goto finish; } if (SecCodeSignerAddSignature(signerRef, staticCodeRef, kSecCSDefaultFlags) != 0) { OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s - SecCodeSignerAddSignature failed", __func__); goto finish; } if (SecCodeSetDetachedSignature(staticCodeRef, signature, kSecCSDefaultFlags) != 0) { OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s - SecCodeSetDetachedSignature failed", __func__); goto finish; } /* get the hash info */ if (SecCodeCopySigningInformation(staticCodeRef, kSecCSDefaultFlags, &signingDict) != 0) { OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s - SecCodeCopySigningInformation failed", __func__); goto finish; } cdhash = CFRetain(CFDictionaryGetValue(signingDict, kSecCodeInfoUnique)); if (cdhash) { const UInt8 * hashDataPtr = NULL; // don't free CFIndex hashDataLen = 0; hashDataPtr = CFDataGetBytePtr(cdhash); hashDataLen = CFDataGetLength(cdhash); tempBufPtr = (char *) malloc((hashDataLen + 1) * 2); if (tempBufPtr == NULL) { OSKextLogMemError(); goto finish; } #if 0 OSKextLogCFString(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, CFSTR("%s - cdhash %@"), __func__, cdhash); #endif bzero(tempBufPtr, ((hashDataLen + 1) * 2)); for (int i = 0; i < hashDataLen; i++) { sprintf(tempBufPtr + (i * 2), "%02.2x", *(hashDataPtr + i)); } } finish: SAFE_RELEASE(signdict); SAFE_RELEASE(signerRef); SAFE_RELEASE(staticCodeRef); SAFE_RELEASE(signature); SAFE_RELEASE(signingDict); SAFE_RELEASE(cdhash); SAFE_RELEASE(resourceRules); SAFE_RELEASE(rules); SAFE_RELEASE(omitPlugins); *signatureBuffer = tempBufPtr; } /******************************************************************************* * createArchitectureList() - create the list of architectures for the kext * * Note: the caller must release the created CFStringRef *******************************************************************************/ static CFStringRef createArchitectureList(OSKextRef aKext, CFBooleanRef *isFat) { if (aKext == NULL || isFat == NULL) { return NULL; } *isFat = kCFBooleanFalse; const NXArchInfo ** archList = NULL; // must free CFMutableArrayRef archNamesList = NULL; // must release CFStringRef archNames = NULL; // do not release const char * archNameCString = NULL; // do not free int index = 0; archList = OSKextCopyArchitectures(aKext); if (!archList) { goto finish; } archNamesList = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!archNamesList) { goto finish; } for (index=0; archList[index]; index++) { archNameCString = archList[index]->name; if (archNameCString) { CFStringRef archName = NULL; archName = CFStringCreateWithCString(kCFAllocatorDefault, archNameCString, kCFStringEncodingUTF8); if (archName) { CFArrayAppendValue(archNamesList, archName); SAFE_RELEASE_NULL(archName); } } } if (index>1) { *isFat = kCFBooleanTrue; } archNames = CFStringCreateByCombiningStrings(kCFAllocatorDefault, archNamesList, CFSTR(" ")); if (!archNames) { goto finish; } finish: SAFE_RELEASE(archNamesList); SAFE_FREE(archList); return archNames; } /******************************************************************************* * copyTeamID() - copy the team id field from the given certificate * * Note: the caller must release the created CFStringRef *******************************************************************************/ static CFStringRef copyTeamID(SecCertificateRef certificate) { if (!certificate || CFGetTypeID(certificate) !=SecCertificateGetTypeID()) { return NULL; } CFDictionaryRef subjectDict = NULL; // do not release CFArrayRef subjectArray = NULL; // do not release CFDictionaryRef subjectInfo = NULL; // do not release CFStringRef teamID = NULL; // do not release CFErrorRef error = NULL; // do not release CFMutableArrayRef certificateKeys = NULL; // must release CFDictionaryRef certificateDict = NULL; // must release certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks); if (!certificateKeys) { goto finish; } CFArrayAppendValue(certificateKeys, kSecOIDX509V1SubjectName); certificateDict = SecCertificateCopyValues(certificate, certificateKeys, &error); if (error != errSecSuccess || !certificateDict || CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) { goto finish; } subjectDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict, kSecOIDX509V1SubjectName); if (!subjectDict || CFGetTypeID(subjectDict) != CFDictionaryGetTypeID()) { goto finish; } subjectArray = (CFArrayRef) CFDictionaryGetValue(subjectDict, kSecPropertyKeyValue); if (!subjectArray || CFGetTypeID(subjectArray) != CFArrayGetTypeID()) { goto finish; } // Try to look for UserID field ("0.9.2342.19200300.100.1.1") for (int index=0; index * Note: the caller must release the created CFStringRef *******************************************************************************/ static CFStringRef copyIssuerCN(SecCertificateRef certificate) { if (!certificate || CFGetTypeID(certificate) !=SecCertificateGetTypeID()) { return NULL; } CFStringRef issuerCN = NULL; // do not release CFDictionaryRef issuerDict = NULL; // do not release CFArrayRef issuerArray = NULL; // do not release CFDictionaryRef issuerInfo = NULL; // do not release CFErrorRef error = NULL; // do not release CFMutableArrayRef certificateKeys = NULL; // must release CFDictionaryRef certificateDict = NULL; // must release certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks); if (!certificateKeys) { goto finish; } CFArrayAppendValue(certificateKeys, kSecOIDX509V1IssuerName); certificateDict = SecCertificateCopyValues(certificate, certificateKeys, &error); if (error != errSecSuccess || !certificateDict || CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) { goto finish; } issuerDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict, kSecOIDX509V1IssuerName); if (!issuerDict || CFGetTypeID(issuerDict) != CFDictionaryGetTypeID()) { goto finish; } issuerArray = (CFArrayRef) CFDictionaryGetValue(issuerDict, kSecPropertyKeyValue); if (!issuerArray || CFGetTypeID(issuerArray) != CFArrayGetTypeID()) { goto finish; } for (int index=0; index * Note: the caller must release the created CFStringRef *******************************************************************************/ static CFStringRef copyCDHash(SecStaticCodeRef code) { CFDictionaryRef signingInfo = NULL; // must release char * tempBufPtr = NULL; // free const UInt8 * hashDataPtr = NULL; // do not free CFDataRef cdhash = NULL; // do not release CFStringRef hash = NULL; // do not release CFIndex hashDataLen = 0; SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &signingInfo); if (!signingInfo) { goto finish; } cdhash = CFDictionaryGetValue(signingInfo, kSecCodeInfoUnique); if (!cdhash || CFGetTypeID(cdhash) != CFDataGetTypeID()) { goto finish; } hashDataPtr = CFDataGetBytePtr(cdhash); hashDataLen = CFDataGetLength(cdhash); tempBufPtr = (char *) malloc((hashDataLen + 1) * 2); if (tempBufPtr == NULL) { OSKextLogMemError(); goto finish; } bzero(tempBufPtr, ((hashDataLen + 1) * 2)); for (int i = 0; i < hashDataLen; i++) { sprintf(tempBufPtr + (i * 2), "%02.2x", *(hashDataPtr + i)); } if (tempBufPtr) { hash = CFStringCreateWithCString(kCFAllocatorDefault, tempBufPtr, kCFStringEncodingUTF8); } finish: SAFE_FREE(tempBufPtr); SAFE_RELEASE(signingInfo); return hash; } /******************************************************************************* * copySigningInfo() - copy a set of signing information for the given kext * cdhash: the SHA-1 hash * teamid: the team id of the leaf certificate * subjectCN: the subject common name of the leaf certificate * issuerCN: the issuer common name of the leaf certificate * * Note: the caller must release the created CFStringRefs *******************************************************************************/ static void copySigningInfo(CFURLRef kextURL, CFStringRef* cdhash, CFStringRef* teamId, CFStringRef* subjectCN, CFStringRef* issuerCN) { if (!kextURL) { return; } SecStaticCodeRef code = NULL; // must release CFDictionaryRef information = NULL; // must release SecCertificateRef issuerCertificate = NULL; // do not release CFArrayRef certificateChain = NULL; // do not release OSStatus status; CFIndex count; if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &code) != 0 || (code == NULL)) { OSKextLogMemError(); goto finish; } if (cdhash) { *cdhash = copyCDHash(code); } status = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &information); if (status != noErr) { goto finish; } // a CFArrayRef array of SecCertificateRef objects certificateChain = (CFArrayRef) CFDictionaryGetValue(information, kSecCodeInfoCertificates); if (!certificateChain || CFGetTypeID(certificateChain) != CFArrayGetTypeID()) { goto finish; } count = CFArrayGetCount(certificateChain); if (count < 1) { goto finish; } issuerCertificate = (SecCertificateRef) CFArrayGetValueAtIndex(certificateChain, 0); if (!issuerCertificate) { goto finish; } if (subjectCN) { SecCertificateCopyCommonName(issuerCertificate, subjectCN); } if (teamId) { *teamId = copyTeamID(issuerCertificate); } if (issuerCN) { *issuerCN = copyIssuerCN(issuerCertificate); } finish: SAFE_RELEASE(code); SAFE_RELEASE(information); return; } /******************************************************************************* * copySubjectCNArray() - copy the subject CN from every certificate in the kext's * certificate chain. * * Note: the caller must release the created CFArrayRef *******************************************************************************/ static CFArrayRef copySubjectCNArray(CFURLRef kextURL) { if (!kextURL) { return NULL; } CFMutableArrayRef subjectCNArray = NULL; // do not release CFArrayRef certificateChain = NULL; // do not release SecCertificateRef certificate = NULL; // do not release SecStaticCodeRef code = NULL; // must release CFDictionaryRef information = NULL; // must release OSStatus status; CFIndex count; subjectCNArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!subjectCNArray) { goto finish; } if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &code) != 0 || (code == NULL)) { OSKextLogMemError(); goto finish; } status = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &information); if (status != noErr) { goto finish; } certificateChain = (CFArrayRef) CFDictionaryGetValue(information, kSecCodeInfoCertificates); if (!certificateChain || CFGetTypeID(certificateChain) != CFArrayGetTypeID()) { goto finish; } count = CFArrayGetCount(certificateChain); if (count < 1) { goto finish; } for (CFIndex i=0; i *******************************************************************************/ static void filterKextLoadForMT(OSKextRef aKext, CFMutableArrayRef *kextList) { if (aKext == NULL || kextList == NULL) return; CFStringRef versionString; // do not release CFStringRef bundleIDString; // do not release CFStringRef kextSigningCategory = NULL; // do not release CFBooleanRef isFat = kCFBooleanFalse; // do not release CFBooleanRef isSigned = kCFBooleanFalse; // do not release CFURLRef kextURL = NULL; // must release CFStringRef kextPath = NULL; // must release CFStringRef filename = NULL; // must release CFStringRef hashString = NULL; // must release CFStringRef archString = NULL; // must release CFStringRef teamId = NULL; // must release CFStringRef subjectCN = NULL; // must release CFStringRef issuerCN = NULL; // must release SecStaticCodeRef code = NULL; // must release CFDictionaryRef information = NULL; // must release CFMutableDictionaryRef kextDict = NULL; // must release char * hashCString = NULL; // must free OSStatus status = noErr; /* do not message trace this if boot-args has debug set */ if (isDebugSetInBootargs()) { return; } kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext)); if (!kextURL) { OSKextLogMemError(); goto finish; } kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle); if (!kextPath) { OSKextLogMemError(); goto finish; } versionString = OSKextGetValueForInfoDictionaryKey(aKext, kCFBundleVersionKey); bundleIDString = OSKextGetValueForInfoDictionaryKey(aKext, kCFBundleIdentifierKey); filename = CFURLCopyLastPathComponent(kextURL); archString = createArchitectureList(aKext, &isFat); if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &code) != 0 || (code == NULL)) { OSKextLogMemError(); goto finish; } status = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &information); if (status != noErr) { goto finish; } isSigned = CFDictionaryContainsKey(information, kSecCodeInfoIdentifier); if (!isSigned) { /* The kext is unsigned, so there is little information we can retrieve. * A hash of the kext is generated for data collection. */ kextSigningCategory = CFSTR(kUnsignedKext); createHashForMT(kextURL, &hashCString); if (hashCString) { hashString = CFStringCreateWithCString(kCFAllocatorDefault, hashCString, kCFStringEncodingUTF8); } } else { CFStringRef myCFString = NULL; // do not release myCFString = OSKextGetIdentifier(aKext); status = checkKextSignature(aKext, true); if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) { if (status == noErr) { /* This is a signed Apple kext, with an Apple root certificate. * There is no need to retrieve additional signing information */ kextSigningCategory = CFSTR(kAppleKextWithAppleRoot); copySigningInfo(kextURL, &hashString, NULL, NULL, NULL); } else { /* This is a signed Apple kext, but the root certificate is not Apple. * This should not happen, so it is better to flag it as unsigned for * collection purpose. */ kextSigningCategory = CFSTR(kUnsignedKext); copySigningInfo(kextURL, &hashString, &teamId, NULL, &issuerCN); CFArrayRef subjectCNArray = NULL; // must release subjectCNArray = copySubjectCNArray(kextURL); if (subjectCNArray) { subjectCN = CFStringCreateByCombiningStrings(kCFAllocatorDefault, subjectCNArray, CFSTR(";")); SAFE_RELEASE(subjectCNArray); } } } else { if (status == noErr) { /* This 3rd-party kext is signed with a devid+ kext certificate */ kextSigningCategory = CFSTR(k3rdPartyKextWithDevIdPlus); copySigningInfo(kextURL, &hashString, &teamId, &subjectCN, &issuerCN); } else if (status == CSSMERR_TP_CERT_REVOKED) { /* This 3rd-party kext is signed with a revoked devid+ kext certificate */ kextSigningCategory = CFSTR(k3rdPartyKextWithRevokedDevIdPlus); copySigningInfo(kextURL, &hashString, &teamId, &subjectCN, &issuerCN); } else { status = checkRootCertificateIsApple(aKext); if (status == noErr) { /* This 3rd-party kext is not signed with a devid+ certificate, * but uses an Apple root certificate. */ kextSigningCategory = CFSTR(k3rdPartyKextWithAppleRoot); /* The certificates may not have the expected format. * Attempt to get the information if present, and also * retrieve the subject cn of every certificate in the chain. */ copySigningInfo(kextURL, &hashString, &teamId, NULL, &issuerCN); CFArrayRef subjectCNArray = NULL; // must release subjectCNArray = copySubjectCNArray(kextURL); if (subjectCNArray) { subjectCN = CFStringCreateByCombiningStrings(kCFAllocatorDefault, subjectCNArray, CFSTR(";")); SAFE_RELEASE(subjectCNArray); } } else { /* This 3rd-party kext is not signed with a devid+ certificate, * and does not use an Apple root certificate. */ kextSigningCategory = CFSTR(k3rdPartyKextWithoutAppleRoot); /* The certificates may not have the expected format. * The subjectcn, issuercn and teamid must not be logged. */ copySigningInfo(kextURL, &hashString, NULL, NULL, NULL); } } } } kextDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!kextDict) { OSKextLogMemError(); goto finish; } if (bundleIDString) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerBundleIDKey), bundleIDString); } if (versionString) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerVersionKey), versionString); } if (filename) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerKextNameKey), filename); } if (isFat != NULL) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerFatKey), isFat); } if (archString) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerArchKey), archString); } if (kextSigningCategory) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerSignatureTypeKey), kextSigningCategory); } if (hashString) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerHashKey), hashString); } if (teamId) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerTeamIdKey), teamId); } if (subjectCN) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerSubjectCNKey), subjectCN); } if (issuerCN) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerIssuerCNKey), issuerCN); } if (kextPath) { CFDictionaryAddValue(kextDict, CFSTR(kMessageTracerPathKey), kextPath); } CFArrayAppendValue(*kextList, kextDict); finish: SAFE_FREE(hashCString); SAFE_RELEASE(kextURL); SAFE_RELEASE(kextPath); SAFE_RELEASE(filename); SAFE_RELEASE(hashString); SAFE_RELEASE(kextDict); SAFE_RELEASE(archString); SAFE_RELEASE(teamId); SAFE_RELEASE(subjectCN); SAFE_RELEASE(issuerCN); SAFE_RELEASE(code); SAFE_RELEASE(information); return; } /******************************************************************************* * recordKextLoadListForMT() - record the list of loaded kexts * *******************************************************************************/ void recordKextLoadListForMT(CFArrayRef kextList) { CFIndex count, i; CFMutableArrayRef kextsToMessageTrace = NULL; //must release OSKextRef aKext; //do not release if (kextList && (count = CFArrayGetCount(kextList))) { kextsToMessageTrace = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(kextList), &kCFTypeArrayCallBacks); if (kextsToMessageTrace) { for (i = 0; i < count; i ++) { aKext = (OSKextRef)CFArrayGetValueAtIndex(kextList, i); filterKextLoadForMT(aKext, &kextsToMessageTrace); } if (CFArrayGetCount(kextsToMessageTrace)) { postNoteAboutKextLoadsMT(CFSTR("Loaded Kext Notification"), kextsToMessageTrace); } SAFE_RELEASE(kextsToMessageTrace); } } } /******************************************************************************* * recordKextLoadForMT() - record the loaded kext * *******************************************************************************/ void recordKextLoadForMT(OSKextRef aKext) { CFMutableArrayRef myArray = NULL; // must release if (!aKext) return; myArray = CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks); if (myArray) { CFArrayAppendValue(myArray, aKext); recordKextLoadListForMT(myArray); SAFE_RELEASE(myArray); } } /******************************************************************************* * checkKextSignature() - check the signature for given kext. *******************************************************************************/ OSStatus checkKextSignature(OSKextRef aKext, Boolean checkExceptionList) { OSStatus result = -1; CFURLRef kextURL = NULL; // must release SecStaticCodeRef staticCodeRef = NULL; // must release SecRequirementRef requirementRef = NULL; // must release CFStringRef myCFString; CFStringRef requirementsString; if (aKext == NULL) { return result; } kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext)); if (!kextURL) { OSKextLogMemError(); goto finish; } if (SecStaticCodeCreateWithPath(kextURL, kSecCSDefaultFlags, &staticCodeRef) != errSecSuccess || (staticCodeRef == NULL)) { OSKextLogMemError(); goto finish; } /* set up correct requirement string. Apple kexts are signed by B&I while * 3rd party kexts are signed through a special developer kext devid * program */ myCFString = OSKextGetIdentifier(aKext); if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) { requirementsString = CFSTR("anchor apple"); } else { /* DevID for kexts cert */ requirementsString = CFSTR("anchor apple generic " "and certificate 1[field.1.2.840.113635.100.6.2.6] " "and certificate leaf[field.1.2.840.113635.100.6.1.13] " "and certificate leaf[field.1.2.840.113635.100.6.1.18]" ); } if (SecRequirementCreateWithString(requirementsString, kSecCSDefaultFlags, &requirementRef) != errSecSuccess || (requirementRef == NULL)) { OSKextLogMemError(); goto finish; } // errSecCSUnsigned == -67062 result = SecStaticCodeCheckValidity(staticCodeRef, kSecCSEnforceRevocationChecks, requirementRef); if ( result != 0 && checkExceptionList && isInExceptionList(aKext, true) ) { result = 0; } finish: SAFE_RELEASE(kextURL); SAFE_RELEASE(staticCodeRef); SAFE_RELEASE(requirementRef); return result; } #define GET_CSTRING_PTR(the_cfstring, the_ptr, the_buffer, the_size) \ do { \ the_ptr = CFStringGetCStringPtr(the_cfstring, kCFStringEncodingUTF8); \ if (the_ptr == NULL) { \ the_buffer[0] = 0x00; \ the_ptr = the_buffer; \ CFStringGetCString(the_cfstring, the_buffer, the_size, kCFStringEncodingUTF8); \ } \ } while(0) /********************************************************************* * isInExceptionList checks to see if the given kext is in the * kext signing exception list (in com.apple.driver.KextExcludeList). * If useCache is TRUE, we will use the cached copy of the exception list. * If useCache is FALSE, we will refresh the cache from disk. * * The kext signing exception list rarely changes but to insure you have the * most recent copy in the cache pass FALSE for the first call and TRUE for * subsequent calls (when dealing with a large list of kexts). * theKext can be NULL if you just want the invalidate the cache. *********************************************************************/ Boolean isInExceptionList(OSKextRef theKext, Boolean useCache) { Boolean result = false; CFStringRef kextID = NULL; // must release OSKextRef excludelistKext = NULL; // must release CFDictionaryRef exceptionlistDict = NULL; // do NOT release static CFDictionaryRef myDictionary = NULL; // do NOT release /* invalidate the cache or create it if not present */ if (useCache == false || myDictionary == NULL) { if (myDictionary != NULL) { SAFE_RELEASE_NULL(myDictionary); } kextID = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.driver.KextExcludeList", kCFStringEncodingUTF8); if (!kextID) { OSKextLogStringError(/* kext */ NULL); goto finish; } excludelistKext = OSKextCreateWithIdentifier(kCFAllocatorDefault, kextID); if (!excludelistKext) { OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "Warning: %s could not find com.apple.driver.KextExcludeList", __FUNCTION__); goto finish; } exceptionlistDict = OSKextGetValueForInfoDictionaryKey( excludelistKext, CFSTR("OSKextSigExceptionList")); if (!exceptionlistDict) { goto finish; } if ((unsigned int)CFDictionaryGetCount(exceptionlistDict) > 0) { myDictionary = CFDictionaryCreateCopy(NULL, exceptionlistDict); } if (myDictionary == NULL) { OSKextLogMemError(); goto finish; } } if (theKext == NULL) { goto finish; } /********************************************************************* * myDictionary is a dictionary with keys / values of: * key = bundleID string of kext we will allow to load inspite of signing * failure. * value = version string of kext to allow to load. * The value is used to check equal or less than a kext with a matching * version string. For example if an entry in the list has key: * com.foocompany.fookext * and value: * 4.2.10 * Then any kext with bundle ID of com.foocompany.fookext and a version * string of 4.2.10 or less will be allowed to load even if there is a * a kext signing validation failure. * * NOTE - Kext versions use an extended Mac OS 'vers' format with double * the number of digits before the build stage: ####.##.##s{1-255} where 's' * is a build stage 'd', 'a', 'b', 'f' or 'fc'. We parse this with * OSKextParseVersionString *********************************************************************/ CFStringRef bundleID = NULL; // do NOT release CFStringRef exceptionKextVersString = NULL; // do NOT release OSKextVersion kextVers = -1; const char * versCString = NULL; // do not free OSKextVersion exceptionKextVers; char versBuffer[256]; bundleID = OSKextGetIdentifier(theKext); if (!bundleID) { OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s could not get bundleID", __FUNCTION__); goto finish; } kextVers = OSKextGetVersion(theKext); if (!kextVers) { OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag, "%s could not get kextVers", __FUNCTION__); goto finish; } exceptionKextVersString = CFDictionaryGetValue(myDictionary, bundleID); if (!exceptionKextVersString) { goto finish; } /* parse version strings */ GET_CSTRING_PTR(exceptionKextVersString, versCString, versBuffer, sizeof(versBuffer)); if (strlen(versCString) < 1) { goto finish; } exceptionKextVers = OSKextParseVersionString(versCString); if (kextVers <= exceptionKextVers) { OSKextLogCFString(NULL, kOSKextLogGeneralFlag | kOSKextLogErrorLevel, CFSTR("kext %@ %lld is in exception list, allowing to load"), bundleID, kextVers); result = true; } finish: SAFE_RELEASE(kextID); SAFE_RELEASE(excludelistKext); return result; } /******************************************************************************* * isDebugSetInBootargs() - check to see if boot-args has debug set. We cache * the result since boot-args / debug will not change until reboot. *******************************************************************************/ Boolean isDebugSetInBootargs(void) { static int didOnce = 0; static Boolean result = false; io_registry_entry_t optionsNode = MACH_PORT_NULL; // must release CFStringRef bootargsEntry = NULL; // must release if (didOnce) { return(result); } optionsNode = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options"); if (optionsNode) { bootargsEntry = (CFStringRef) IORegistryEntryCreateCFProperty(optionsNode, CFSTR("boot-args"), kCFAllocatorDefault, 0); if (bootargsEntry && (CFGetTypeID(bootargsEntry) == CFStringGetTypeID())) { CFRange findRange; findRange = CFStringFind(bootargsEntry, CFSTR("debug"), 0); if (findRange.length != 0) { result = true; } } } didOnce++; if (optionsNode) IOObjectRelease(optionsNode); SAFE_RELEASE(bootargsEntry); return(result); } /********************************************************************* *********************************************************************/ Boolean isInLibraryExtensionsFolder(OSKextRef theKext) { CFStringRef myKextPath = NULL; // must release Boolean myResult = false; myKextPath = copyKextPath(theKext); if ( myKextPath ) { if ( CFStringHasPrefix(myKextPath, CFSTR(_kOSKextLibraryExtensionsFolder)) ) { myResult = true; } } SAFE_RELEASE(myKextPath); return(myResult); }