1/*
2 * Copyright (c) 2012 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#include <asl.h>
24#include <IOKit/kext/OSKext.h>
25#include <IOKit/kext/OSKextPrivate.h>
26#include <IOKit/IOKitLib.h>
27#include <Security/Security.h>
28#include <sys/sysctl.h>
29#include <servers/bootstrap.h>
30#include <IOKit/kext/kextmanager_types.h>
31
32#include "security.h"
33#include "kext_tools_util.h"
34
35
36#define USE_OLD_EXCEPTION_LIST 1
37
38/*******************************************************************************
39 * Helper functions
40 *******************************************************************************/
41static OSStatus     checkRootCertificateIsApple(OSKextRef aKext);
42static CFStringRef  copyCDHash(SecStaticCodeRef code);
43static CFStringRef  copyIssuerCN(SecCertificateRef certificate);
44static void         copySigningInfo(CFURLRef kextURL,
45                                    CFStringRef* cdhash,
46                                    CFStringRef* teamId,
47                                    CFStringRef* subjectCN,
48                                    CFStringRef* issuerCN);
49static CFArrayRef   copySubjectCNArray(CFURLRef kextURL);
50static CFStringRef  copyTeamID(SecCertificateRef certificate);
51static CFStringRef  createArchitectureList(OSKextRef aKext, CFBooleanRef *isFat);
52static void         getAdhocSignatureHash(CFURLRef kextURL, char ** signatureBuffer);
53static void         filterKextLoadForMT(OSKextRef aKext, CFMutableArrayRef *kextList);
54static Boolean hashIsInExceptionList(CFURLRef theKextURL, CFDictionaryRef theDict);
55#if USE_OLD_EXCEPTION_LIST
56static Boolean bundleIdIsInExceptionList(OSKextRef theKext, CFDictionaryRef theDict);
57#endif
58
59/*******************************************************************************
60 * messageTraceExcludedKext() - log MessageTracer message for kexts in
61 * exclude list.
62 *  <rdar://problem/12994418> MessageTrace when we block the load of something
63 *                            on the kext exclude list
64 *******************************************************************************/
65
66void messageTraceExcludedKext(OSKextRef theKext)
67{
68    CFStringRef     versionString;
69    CFStringRef     bundleIDString;
70    CFURLRef        kextURL             = NULL;   // must release
71    CFStringRef     filename            = NULL;   // must release
72    aslmsg          amsg                = NULL;   // must free
73    char            *versionCString     = NULL;   // must free
74    char            *bundleIDCString    = NULL;   // must free
75    char            *filenameCString    = NULL;   // must free
76
77    kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext));
78    if (!kextURL) {
79        OSKextLogMemError();
80        goto finish;
81    }
82    versionString = OSKextGetValueForInfoDictionaryKey(theKext,
83                                                       kCFBundleVersionKey);
84    if (versionString) {
85        versionCString = createUTF8CStringForCFString(versionString);
86    }
87    bundleIDString = OSKextGetValueForInfoDictionaryKey(theKext,
88                                                        kCFBundleIdentifierKey);
89    if (bundleIDString) {
90        bundleIDCString = createUTF8CStringForCFString(bundleIDString);
91    }
92
93    filename = CFURLCopyLastPathComponent(kextURL);
94    if (filename) {
95        filenameCString = createUTF8CStringForCFString(filename);
96    }
97    SAFE_RELEASE(filename);
98
99    /* log the message tracer data
100     */
101    amsg = asl_new(ASL_TYPE_MSG);
102    if (!amsg) {
103        OSKextLogMemError();
104        goto finish;
105    }
106
107    asl_set(amsg, kMessageTracerDomainKey, kMTKextBlockedDomain);
108    asl_set(amsg, kMessageTracerBundleIDKey,
109            bundleIDCString ? bundleIDCString : "");
110    asl_set(amsg, kMessageTracerVersionKey,
111            versionCString ? versionCString : "");
112    asl_set(amsg, kMessageTracerKextNameKey,
113            filenameCString ? filenameCString : "");
114
115    asl_log(NULL, amsg, ASL_LEVEL_NOTICE, "");
116
117finish:
118    SAFE_FREE(versionCString);
119    SAFE_FREE(bundleIDCString);
120    SAFE_FREE(filenameCString);
121
122    SAFE_RELEASE(kextURL);
123
124    if (amsg) {
125        asl_free(amsg);
126    }
127    return;
128}
129
130/*******************************************************************************
131 * checkRootCertificateIsApple() - check if the root certificate of the kext
132 *  is issued by Apple
133 *  <rdar://problem/12435992>
134 *******************************************************************************/
135static OSStatus checkRootCertificateIsApple(OSKextRef aKext)
136{
137    OSStatus                result          = -1;
138    CFURLRef                kextURL         = NULL;   // must release
139    SecStaticCodeRef        staticCodeRef   = NULL;   // must release
140    SecRequirementRef       requirementRef  = NULL;   // must release
141    CFStringRef             myCFString;
142    CFStringRef             requirementsString;
143
144    if (aKext == NULL) {
145        return result;
146    }
147
148    kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
149    if (!kextURL) {
150        OSKextLogMemError();
151        goto finish;
152    }
153
154    if (SecStaticCodeCreateWithPath(kextURL,
155                                    kSecCSDefaultFlags,
156                                    &staticCodeRef) != errSecSuccess ||
157        (staticCodeRef == NULL)) {
158        OSKextLogMemError();
159        goto finish;
160    }
161
162    /* set up correct requirement string */
163    myCFString = OSKextGetIdentifier(aKext);
164    if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) {
165        requirementsString = CFSTR("anchor apple");
166    }
167    else {
168        requirementsString = CFSTR("anchor apple generic");
169    }
170
171    if (SecRequirementCreateWithString(requirementsString,
172                                       kSecCSDefaultFlags,
173                                       &requirementRef) != errSecSuccess ||
174        (requirementRef == NULL)) {
175        OSKextLogMemError();
176        goto finish;
177    }
178
179    // errSecCSUnsigned == -67062
180    result = SecStaticCodeCheckValidity(staticCodeRef,
181                                        kSecCSDefaultFlags,
182                                        requirementRef);
183    if (result != 0) {
184        OSKextLogCFString(NULL,
185                          kOSKextLogErrorLevel | kOSKextLogLoadFlag,
186                          CFSTR("Invalid signature %ld for kext %@"),
187                          (long)result, aKext);
188    }
189
190finish:
191    SAFE_RELEASE(kextURL);
192    SAFE_RELEASE(staticCodeRef);
193    SAFE_RELEASE(requirementRef);
194
195    return result;
196}
197
198
199/*******************************************************************************
200 * getAdhocSignatureHash() - create a hash signature for an unsigned kext
201 *******************************************************************************/
202
203static void getAdhocSignatureHash(CFURLRef kextURL, char ** signatureBuffer)
204{
205    CFMutableDictionaryRef signdict     = NULL;   // must release
206    SecCodeSignerRef    signerRef       = NULL;   // must release
207    SecStaticCodeRef    staticCodeRef   = NULL;   // must release
208    CFDataRef           signature       = NULL;   // must release
209    CFDictionaryRef     signingDict     = NULL;   // must release
210    CFDataRef           cdhash          = NULL;   // must release
211    CFMutableDictionaryRef resourceRules = NULL;  // must release
212    CFMutableDictionaryRef rules        = NULL;   // must release
213    CFMutableDictionaryRef rules2       = NULL;   // must release
214    CFMutableDictionaryRef omitPlugins  = NULL;   // must release
215    CFMutableDictionaryRef frameworksDict  = NULL;   // must release
216    CFMutableDictionaryRef topDict      = NULL;   // must release
217    char *              tempBufPtr      = NULL;   // do not free
218    CFNumberRef         myNumValue      = NULL;   // must release
219    CFNumberRef         myRealValue     = NULL;   // must release
220
221    /* Ad-hoc sign the code temporarily so we can get its hash */
222    if (SecStaticCodeCreateWithPath(kextURL,
223                                    kSecCSDefaultFlags,
224                                    &staticCodeRef) != 0 ||
225        (staticCodeRef == NULL)) {
226        OSKextLogMemError();
227        goto finish;
228    }
229
230    signature = CFDataCreateMutable(NULL, 0);
231    if (signature == NULL) {
232        OSKextLogMemError();
233        goto finish;
234    }
235
236    signdict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
237                                         &kCFTypeDictionaryKeyCallBacks,
238                                         &kCFTypeDictionaryValueCallBacks);
239    if (signdict == NULL) {
240        OSKextLogMemError();
241        goto finish;
242    }
243
244    CFDictionarySetValue(signdict, kSecCodeSignerIdentity, kCFNull);
245    CFDictionarySetValue(signdict, kSecCodeSignerDetached, signature);
246
247    resourceRules = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
248                                              &kCFTypeDictionaryKeyCallBacks,
249                                              &kCFTypeDictionaryValueCallBacks);
250    if (resourceRules == NULL) {
251        OSKextLogMemError();
252        goto finish;
253    }
254
255    rules = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
256                                      &kCFTypeDictionaryKeyCallBacks,
257                                      &kCFTypeDictionaryValueCallBacks);
258    if (rules == NULL) {
259        OSKextLogMemError();
260        goto finish;
261    }
262
263    rules2 = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
264                                      &kCFTypeDictionaryKeyCallBacks,
265                                      &kCFTypeDictionaryValueCallBacks);
266    if (rules2 == NULL) {
267        OSKextLogMemError();
268        goto finish;
269    }
270
271    omitPlugins = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
272                                            &kCFTypeDictionaryKeyCallBacks,
273                                            &kCFTypeDictionaryValueCallBacks);
274    if (omitPlugins == NULL) {
275        OSKextLogMemError();
276        goto finish;
277    }
278
279    frameworksDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
280                                               &kCFTypeDictionaryKeyCallBacks,
281                                               &kCFTypeDictionaryValueCallBacks);
282    if (frameworksDict == NULL) {
283        OSKextLogMemError();
284        goto finish;
285    }
286
287    topDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
288                                               &kCFTypeDictionaryKeyCallBacks,
289                                               &kCFTypeDictionaryValueCallBacks);
290    if (topDict == NULL) {
291        OSKextLogMemError();
292        goto finish;
293    }
294
295    /* exclude PlugIns directory (old style, see 16411212)
296     */
297    int myNum = 100;
298    myNumValue = CFNumberCreate(kCFAllocatorDefault,
299                                kCFNumberIntType, &myNum);
300    if (myNumValue == NULL) {
301        OSKextLogMemError();
302        goto finish;
303    }
304
305    CFDictionarySetValue(omitPlugins, CFSTR("omit"), kCFBooleanTrue);
306    CFDictionarySetValue(omitPlugins, CFSTR("weight"), myNumValue);
307
308    CFDictionarySetValue(rules, CFSTR("^.*"), kCFBooleanTrue);
309    CFDictionarySetValue(rules, CFSTR("^PlugIns/"), omitPlugins);
310    CFDictionarySetValue(resourceRules, CFSTR("rules"), rules);
311
312    /* exclude PlugIns directory (new style, see 16411212)
313     */
314    float myRealNum = 0.0;
315    myRealValue = CFNumberCreate(kCFAllocatorDefault,
316                                kCFNumberFloatType, &myRealNum);
317    if (myRealValue == NULL) {
318        OSKextLogMemError();
319        goto finish;
320    }
321
322    CFDictionarySetValue(frameworksDict, CFSTR("nested"), kCFBooleanTrue);
323    CFDictionarySetValue(frameworksDict, CFSTR("weight"), myRealValue);
324    CFDictionarySetValue(rules2,
325        CFSTR("^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/"),
326                         frameworksDict);
327
328    CFDictionarySetValue(rules2, CFSTR("^.*"), kCFBooleanTrue);
329    CFDictionarySetValue(rules2, CFSTR("^PlugIns/"), omitPlugins);
330
331    CFDictionarySetValue(topDict, CFSTR("top"), kCFBooleanTrue);
332    CFDictionarySetValue(topDict, CFSTR("weight"), myRealValue);
333    CFDictionarySetValue(rules2,
334                         CFSTR("^[^/]+$"),
335                         topDict);
336    CFDictionarySetValue(resourceRules, CFSTR("rules2"), rules2);
337
338    // add both rules to signdict
339    CFDictionarySetValue(signdict, kSecCodeSignerResourceRules, resourceRules);
340
341    if (SecCodeSignerCreate(signdict, kSecCSDefaultFlags, &signerRef) != 0) {
342        OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
343                  "%s - SecCodeSignerCreate failed", __func__);
344        goto finish;
345    }
346    if (SecCodeSignerAddSignature(signerRef, staticCodeRef, kSecCSDefaultFlags) != 0) {
347        OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
348                  "%s - SecCodeSignerAddSignature failed", __func__);
349        goto finish;
350    }
351    if (SecCodeSetDetachedSignature(staticCodeRef, signature, kSecCSDefaultFlags) != 0) {
352        OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
353                  "%s - SecCodeSetDetachedSignature failed", __func__);
354        goto finish;
355    }
356
357    /* get the hash info
358     */
359    if (SecCodeCopySigningInformation(staticCodeRef, kSecCSDefaultFlags, &signingDict) != 0) {
360        OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
361                  "%s - SecCodeCopySigningInformation failed", __func__);
362        goto finish;
363    }
364
365    cdhash = CFRetain(CFDictionaryGetValue(signingDict, kSecCodeInfoUnique));
366    if (cdhash) {
367        const UInt8 *   hashDataPtr     = NULL;  // don't free
368        CFIndex         hashDataLen     = 0;
369
370        hashDataPtr = CFDataGetBytePtr(cdhash);
371        hashDataLen = CFDataGetLength(cdhash);
372        tempBufPtr = (char *) malloc((hashDataLen + 1) * 2);
373        if (tempBufPtr == NULL) {
374            OSKextLogMemError();
375            goto finish;
376        }
377#if 0
378        OSKextLogCFString(/* kext */ NULL,
379                          kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
380                          CFSTR("%s - kextURL %@"),
381                          __func__,
382                          kextURL);
383        OSKextLogCFString(/* kext */ NULL,
384                          kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
385                          CFSTR("%s - cdhash %@"),
386                          __func__,
387                          cdhash);
388#endif
389
390        bzero(tempBufPtr, ((hashDataLen + 1) * 2));
391        for (int i = 0; i < hashDataLen; i++) {
392            sprintf(tempBufPtr + (i * 2), "%02.2x", *(hashDataPtr + i));
393        }
394    }
395
396finish:
397    SAFE_RELEASE(signdict);
398    SAFE_RELEASE(signerRef);
399    SAFE_RELEASE(staticCodeRef);
400    SAFE_RELEASE(signature);
401    SAFE_RELEASE(signingDict);
402    SAFE_RELEASE(cdhash);
403    SAFE_RELEASE(resourceRules);
404    SAFE_RELEASE(rules);
405    SAFE_RELEASE(rules2);
406    SAFE_RELEASE(omitPlugins);
407    SAFE_RELEASE(frameworksDict);
408    SAFE_RELEASE(topDict);
409    SAFE_RELEASE(myNumValue);
410    SAFE_RELEASE(myRealValue);
411
412    *signatureBuffer = tempBufPtr;
413}
414
415/*******************************************************************************
416 * createArchitectureList() - create the list of architectures for the kext
417 *  <rdar://13529984>
418 *  Note: the caller must release the created CFStringRef
419 *******************************************************************************/
420static CFStringRef createArchitectureList(OSKextRef aKext, CFBooleanRef *isFat)
421{
422    if (aKext == NULL || isFat == NULL) {
423        return NULL;
424    }
425
426    *isFat = kCFBooleanFalse;
427
428    const NXArchInfo **         archList            = NULL; // must free
429    CFMutableArrayRef           archNamesList       = NULL; // must release
430    CFStringRef                 archNames           = NULL; // do not release
431    const char *                archNameCString     = NULL; // do not free
432    int                         index               = 0;
433
434    archList = OSKextCopyArchitectures(aKext);
435    if (!archList) {
436        goto finish;
437    }
438
439    archNamesList = CFArrayCreateMutable(kCFAllocatorDefault,
440                                         0,
441                                         &kCFTypeArrayCallBacks);
442    if (!archNamesList) {
443        goto finish;
444    }
445
446    for (index=0; archList[index]; index++) {
447        archNameCString = archList[index]->name;
448        if (archNameCString) {
449            CFStringRef archName = NULL;
450            archName = CFStringCreateWithCString(kCFAllocatorDefault,
451                                                 archNameCString,
452                                                 kCFStringEncodingUTF8);
453            if (archName) {
454                CFArrayAppendValue(archNamesList, archName);
455                SAFE_RELEASE_NULL(archName);
456            }
457        }
458    }
459
460    if (index>1) {
461        *isFat = kCFBooleanTrue;
462    }
463    archNames = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
464                                                 archNamesList,
465                                                 CFSTR(" "));
466    if (!archNames) {
467        goto finish;
468    }
469
470finish:
471    SAFE_RELEASE(archNamesList);
472    SAFE_FREE(archList);
473
474    return archNames;
475}
476
477/*******************************************************************************
478 * copyTeamID() - copy the team id field from the given certificate
479 *  <rdar://13646260>
480 *  Note: the caller must release the created CFStringRef
481 *******************************************************************************/
482static CFStringRef copyTeamID(SecCertificateRef certificate)
483{
484    if (!certificate ||
485        CFGetTypeID(certificate) !=SecCertificateGetTypeID()) {
486        return NULL;
487    }
488
489    CFDictionaryRef     subjectDict     = NULL; // do not release
490    CFArrayRef          subjectArray    = NULL; // do not release
491    CFDictionaryRef     subjectInfo     = NULL; // do not release
492    CFStringRef         teamID          = NULL; // do not release
493    CFErrorRef          error           = NULL; // do not release
494
495    CFMutableArrayRef   certificateKeys = NULL; // must release
496    CFDictionaryRef     certificateDict = NULL; // must release
497
498    certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault,
499                                           1,
500                                           &kCFTypeArrayCallBacks);
501    if (!certificateKeys) {
502        goto finish;
503    }
504
505    CFArrayAppendValue(certificateKeys, kSecOIDX509V1SubjectName);
506
507    certificateDict = SecCertificateCopyValues(certificate,
508                                               certificateKeys,
509                                               &error);
510    if (error != errSecSuccess ||
511        !certificateDict ||
512        CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) {
513        goto finish;
514    }
515
516    subjectDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict,
517                                                         kSecOIDX509V1SubjectName);
518    if (!subjectDict ||
519        CFGetTypeID(subjectDict) != CFDictionaryGetTypeID()) {
520        goto finish;
521    }
522
523    subjectArray = (CFArrayRef) CFDictionaryGetValue(subjectDict,
524                                                     kSecPropertyKeyValue);
525    if (!subjectArray ||
526        CFGetTypeID(subjectArray) != CFArrayGetTypeID()) {
527        goto finish;
528    }
529
530    // Try to look for UserID field ("0.9.2342.19200300.100.1.1")
531    for (int index=0; index<CFArrayGetCount(subjectArray); index++) {
532        subjectInfo = (CFDictionaryRef) CFArrayGetValueAtIndex(subjectArray,
533                                                               index);
534        if (!subjectInfo ||
535            CFGetTypeID(subjectInfo) != CFDictionaryGetTypeID()) {
536            continue;
537        }
538
539        CFStringRef label = NULL; // do not release
540        label = CFDictionaryGetValue(subjectInfo,
541                                     kSecPropertyKeyLabel);
542        if (kCFCompareEqualTo == CFStringCompare(label,
543                                                 CFSTR("0.9.2342.19200300.100.1.1"),
544                                                 0)) {
545            teamID = CFDictionaryGetValue(subjectInfo,
546                                          kSecPropertyKeyValue);
547            if (teamID &&
548                CFGetTypeID(teamID) == CFStringGetTypeID()) {
549                CFRetain(teamID);
550                goto finish;
551            }
552            else {
553                teamID = NULL;
554            }
555        }
556    }
557
558    // In case of failure, look for OU field ("2.5.4.11")
559    if (!teamID) {
560        for (int index=0; index<CFArrayGetCount(subjectArray); index++) {
561            subjectInfo = (CFDictionaryRef) CFArrayGetValueAtIndex(subjectArray,
562                                                                   index);
563            if (!subjectInfo ||
564                CFGetTypeID(subjectInfo) != CFDictionaryGetTypeID()) {
565                continue;
566            }
567
568            CFStringRef label = NULL; // do not release
569            label = CFDictionaryGetValue(subjectInfo,
570                                         kSecPropertyKeyLabel);
571            if (kCFCompareEqualTo == CFStringCompare(label,
572                                                     CFSTR("2.5.4.11"),
573                                                     0)) {
574                teamID = CFDictionaryGetValue(subjectInfo,
575                                              kSecPropertyKeyValue);
576                if (teamID &&
577                    CFGetTypeID(teamID) == CFStringGetTypeID()) {
578                    CFRetain(teamID);
579                    goto finish;
580                }
581                else {
582                    teamID = NULL;
583                }
584            }
585        }
586    }
587
588finish:
589    if (!teamID && subjectArray) {
590        CFShow(subjectArray);
591    }
592    SAFE_RELEASE(certificateKeys);
593    SAFE_RELEASE(certificateDict);
594    return teamID;
595}
596
597/*******************************************************************************
598 * copyIssuerCN() - copy the issuer CN field from the given certificate
599 *  <rdar://13646260>
600 *  Note: the caller must release the created CFStringRef
601 *******************************************************************************/
602static CFStringRef copyIssuerCN(SecCertificateRef certificate)
603{
604    if (!certificate ||
605        CFGetTypeID(certificate) !=SecCertificateGetTypeID()) {
606        return NULL;
607    }
608
609    CFStringRef         issuerCN        = NULL; // do not release
610    CFDictionaryRef     issuerDict      = NULL; // do not release
611    CFArrayRef          issuerArray     = NULL; // do not release
612    CFDictionaryRef     issuerInfo      = NULL; // do not release
613    CFErrorRef          error           = NULL; // do not release
614
615    CFMutableArrayRef   certificateKeys = NULL; // must release
616    CFDictionaryRef     certificateDict = NULL; // must release
617
618    certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault,
619                                           1,
620                                           &kCFTypeArrayCallBacks);
621    if (!certificateKeys) {
622        goto finish;
623    }
624
625    CFArrayAppendValue(certificateKeys, kSecOIDX509V1IssuerName);
626
627    certificateDict = SecCertificateCopyValues(certificate,
628                                               certificateKeys,
629                                               &error);
630
631    if (error != errSecSuccess ||
632        !certificateDict ||
633        CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) {
634        goto finish;
635    }
636
637    issuerDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict,
638                                                        kSecOIDX509V1IssuerName);
639    if (!issuerDict ||
640        CFGetTypeID(issuerDict) != CFDictionaryGetTypeID()) {
641        goto finish;
642    }
643
644    issuerArray = (CFArrayRef) CFDictionaryGetValue(issuerDict,
645                                                    kSecPropertyKeyValue);
646    if (!issuerArray ||
647        CFGetTypeID(issuerArray) != CFArrayGetTypeID()) {
648        goto finish;
649    }
650
651    for (int index=0; index<CFArrayGetCount(issuerArray); index++) {
652        issuerInfo = (CFDictionaryRef) CFArrayGetValueAtIndex(issuerArray,
653                                                              index);
654        if (!issuerInfo ||
655            CFGetTypeID(issuerInfo) != CFDictionaryGetTypeID()) {
656            continue;
657        }
658
659        CFStringRef label = NULL; // do not release
660        label = CFDictionaryGetValue(issuerInfo,
661                                     kSecPropertyKeyLabel);
662        if (kCFCompareEqualTo == CFStringCompare(label,
663                                                 CFSTR("2.5.4.3"),
664                                                 0)) {
665            issuerCN = CFDictionaryGetValue(issuerInfo,
666                                            kSecPropertyKeyValue);
667            if (issuerCN &&
668                CFGetTypeID(issuerCN) == CFStringGetTypeID()) {
669                CFRetain(issuerCN);
670                goto finish;
671            }
672            else {
673                issuerCN = NULL;
674            }
675        }
676    }
677
678finish:
679    SAFE_RELEASE(certificateDict);
680    SAFE_RELEASE(certificateKeys);
681    return issuerCN;
682}
683
684/*******************************************************************************
685 * copyCDHash() - copy the SHA-1 hash of the code
686 *  <rdar://13646260>
687 *  Note: the caller must release the created CFStringRef
688 *******************************************************************************/
689static CFStringRef copyCDHash(SecStaticCodeRef code)
690{
691    CFDictionaryRef signingInfo     = NULL; // must release
692    char *          tempBufPtr      = NULL; // free
693    const UInt8 *   hashDataPtr     = NULL; // do not free
694    CFDataRef       cdhash          = NULL; // do not release
695    CFStringRef     hash            = NULL; // do not release
696    CFIndex         hashDataLen     = 0;
697
698    SecCodeCopySigningInformation(code,
699                                  kSecCSDefaultFlags,
700                                  &signingInfo);
701    if (!signingInfo) {
702        goto finish;
703    }
704
705    cdhash = CFDictionaryGetValue(signingInfo, kSecCodeInfoUnique);
706    if (!cdhash ||
707        CFGetTypeID(cdhash) != CFDataGetTypeID()) {
708        goto finish;
709    }
710
711    hashDataPtr = CFDataGetBytePtr(cdhash);
712    hashDataLen = CFDataGetLength(cdhash);
713    tempBufPtr = (char *) malloc((hashDataLen + 1) * 2);
714    if (tempBufPtr == NULL) {
715        OSKextLogMemError();
716        goto finish;
717    }
718
719    bzero(tempBufPtr, ((hashDataLen + 1) * 2));
720    for (int i = 0; i < hashDataLen; i++) {
721        sprintf(tempBufPtr + (i * 2), "%02.2x", *(hashDataPtr + i));
722    }
723
724    if (tempBufPtr) {
725        hash = CFStringCreateWithCString(kCFAllocatorDefault,
726                                         tempBufPtr,
727                                         kCFStringEncodingUTF8);
728    }
729
730finish:
731    SAFE_FREE(tempBufPtr);
732    SAFE_RELEASE(signingInfo);
733    return hash;
734}
735
736/*******************************************************************************
737 * copySigningInfo() - copy a set of signing information for the given kext
738 *  cdhash:     the SHA-1 hash
739 *  teamid:     the team id of the leaf certificate
740 *  subjectCN:  the subject common name of the leaf certificate
741 *  issuerCN:   the issuer common name of the leaf certificate
742 *  <rdar://13646260>
743 *  Note: the caller must release the created CFStringRefs
744 *******************************************************************************/
745static void copySigningInfo(CFURLRef kextURL,
746                            CFStringRef* cdhash,
747                            CFStringRef* teamId,
748                            CFStringRef* subjectCN,
749                            CFStringRef* issuerCN)
750{
751    if (!kextURL) {
752        return;
753    }
754
755    SecStaticCodeRef    code                = NULL; // must release
756    CFDictionaryRef     information         = NULL; // must release
757
758    SecCertificateRef   issuerCertificate   = NULL; // do not release
759    CFArrayRef          certificateChain    = NULL; // do not release
760    OSStatus            status;
761    CFIndex             count;
762
763    if (SecStaticCodeCreateWithPath(kextURL,
764                                    kSecCSDefaultFlags,
765                                    &code) != 0
766        || (code == NULL)) {
767        OSKextLogMemError();
768        goto finish;
769    }
770
771    if (cdhash) {
772        *cdhash = copyCDHash(code);
773    }
774
775    status = SecCodeCopySigningInformation(code,
776                                           kSecCSSigningInformation,
777                                           &information);
778    if (status != noErr) {
779        goto finish;
780    }
781
782    // a CFArrayRef array of SecCertificateRef objects
783    certificateChain = (CFArrayRef) CFDictionaryGetValue(information, kSecCodeInfoCertificates);
784    if (!certificateChain ||
785        CFGetTypeID(certificateChain) != CFArrayGetTypeID()) {
786        goto finish;
787    }
788
789    count = CFArrayGetCount(certificateChain);
790    if (count < 1) {
791        goto finish;
792    }
793
794    issuerCertificate = (SecCertificateRef) CFArrayGetValueAtIndex(certificateChain, 0);
795    if (!issuerCertificate) {
796        goto finish;
797    }
798
799    if (subjectCN) {
800        SecCertificateCopyCommonName(issuerCertificate, subjectCN);
801    }
802
803    if (teamId) {
804        *teamId = copyTeamID(issuerCertificate);
805    }
806
807    if (issuerCN) {
808        *issuerCN = copyIssuerCN(issuerCertificate);
809    }
810
811finish:
812    SAFE_RELEASE(code);
813    SAFE_RELEASE(information);
814
815    return;
816}
817
818/*******************************************************************************
819 * copySubjectCNArray() - copy the subject CN from every certificate in the kext's
820 *  certificate chain.
821 *  <rdar://13646260>
822 *  Note: the caller must release the created CFArrayRef
823 *******************************************************************************/
824static CFArrayRef copySubjectCNArray(CFURLRef kextURL)
825{
826    if (!kextURL) {
827        return NULL;
828    }
829
830    CFMutableArrayRef   subjectCNArray      = NULL; // do not release
831    CFArrayRef          certificateChain    = NULL; // do not release
832    SecCertificateRef   certificate         = NULL; // do not release
833
834    SecStaticCodeRef    code                = NULL; // must release
835    CFDictionaryRef     information         = NULL; // must release
836
837    OSStatus            status;
838    CFIndex             count;
839
840    subjectCNArray = CFArrayCreateMutable(kCFAllocatorDefault,
841                                          0,
842                                          &kCFTypeArrayCallBacks);
843    if (!subjectCNArray) {
844        goto finish;
845    }
846
847    if (SecStaticCodeCreateWithPath(kextURL,
848                                    kSecCSDefaultFlags,
849                                    &code) != 0
850        || (code == NULL)) {
851        OSKextLogMemError();
852        goto finish;
853    }
854
855    status = SecCodeCopySigningInformation(code,
856                                           kSecCSSigningInformation,
857                                           &information);
858    if (status != noErr) {
859        goto finish;
860    }
861
862    certificateChain = (CFArrayRef) CFDictionaryGetValue(information, kSecCodeInfoCertificates);
863    if (!certificateChain ||
864        CFGetTypeID(certificateChain) != CFArrayGetTypeID()) {
865        goto finish;
866    }
867
868    count = CFArrayGetCount(certificateChain);
869    if (count < 1) {
870        goto finish;
871    }
872
873    for (CFIndex i=0; i<count; i++) {
874        certificate = (SecCertificateRef) CFArrayGetValueAtIndex(certificateChain, i);
875        CFStringRef subjectCN = NULL; // must release
876        SecCertificateCopyCommonName(certificate, &subjectCN);
877        if (subjectCN) {
878            CFArrayAppendValue(subjectCNArray, subjectCN);
879            SAFE_RELEASE(subjectCN);
880        }
881    }
882
883finish:
884    SAFE_RELEASE(code);
885    SAFE_RELEASE(information);
886
887    return subjectCNArray;
888}
889
890/*******************************************************************************
891 * filterKextLoadForMT() - check that the kext is of interest, and place kext
892 * information in the kext list
893 *  <rdar://problem/12435992>
894 *******************************************************************************/
895
896static void filterKextLoadForMT(OSKextRef aKext, CFMutableArrayRef *kextList)
897{
898    if (aKext == NULL || kextList == NULL)
899        return;
900
901    CFStringRef     versionString;                // do not release
902    CFStringRef     bundleIDString;               // do not release
903    CFStringRef     kextSigningCategory = NULL;   // do not release
904    CFBooleanRef    isFat               = kCFBooleanFalse; // do not release
905    CFBooleanRef    isSigned            = kCFBooleanFalse; // do not release
906
907    CFURLRef        kextURL             = NULL;   // must release
908    CFStringRef     kextPath            = NULL;   // must release
909    CFStringRef     filename            = NULL;   // must release
910    CFStringRef     hashString          = NULL;   // must release
911    CFStringRef     archString          = NULL;   // must release
912    CFStringRef     teamId              = NULL;   // must release
913    CFStringRef     subjectCN           = NULL;   // must release
914    CFStringRef     issuerCN            = NULL;   // must release
915
916    SecStaticCodeRef        code        = NULL;   // must release
917    CFDictionaryRef         information = NULL;   // must release
918    CFMutableDictionaryRef  kextDict    = NULL;   // must release
919
920    char *          hashCString         = NULL;   // must free
921
922    OSStatus status = noErr;
923
924    /* do not message trace this if boot-args has debug set */
925    if (isDebugSetInBootargs()) {
926        return;
927    }
928
929    kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
930    if (!kextURL) {
931        OSKextLogMemError();
932        goto finish;
933    }
934    kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle);
935    if (!kextPath) {
936        OSKextLogMemError();
937        goto finish;
938    }
939
940    versionString   = OSKextGetValueForInfoDictionaryKey(aKext,
941                                                         kCFBundleVersionKey);
942    bundleIDString  = OSKextGetValueForInfoDictionaryKey(aKext,
943                                                         kCFBundleIdentifierKey);
944    filename = CFURLCopyLastPathComponent(kextURL);
945
946    archString = createArchitectureList(aKext, &isFat);
947
948    if (SecStaticCodeCreateWithPath(kextURL,
949                                    kSecCSDefaultFlags,
950                                    &code) != 0
951        || (code == NULL)) {
952        OSKextLogMemError();
953        goto finish;
954    }
955    status = SecCodeCopySigningInformation(code,
956                                           kSecCSSigningInformation,
957                                           &information);
958    if (status != noErr) {
959        goto finish;
960    }
961
962    isSigned = CFDictionaryContainsKey(information, kSecCodeInfoIdentifier);
963    if (!isSigned) {
964        /* The kext is unsigned, so there is little information we can retrieve.
965         * A hash of the kext is generated for data collection. */
966        kextSigningCategory = CFSTR(kUnsignedKext);
967
968        getAdhocSignatureHash(kextURL, &hashCString);
969        if (hashCString) {
970            hashString = CFStringCreateWithCString(kCFAllocatorDefault,
971                                                   hashCString,
972                                                   kCFStringEncodingUTF8);
973        }
974    }
975    else {
976        CFStringRef myCFString = NULL; // do not release
977        myCFString = OSKextGetIdentifier(aKext);
978        status = checkKextSignature(aKext, true, false);
979        if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) {
980            if (status == noErr) {
981                /* This is a signed Apple kext, with an Apple root certificate.
982                 * There is no need to retrieve additional signing information */
983                kextSigningCategory = CFSTR(kAppleKextWithAppleRoot);
984                copySigningInfo(kextURL,
985                                &hashString,
986                                NULL,
987                                NULL,
988                                NULL);
989            }
990            else {
991                /* This is a signed Apple kext, but the root certificate is not Apple.
992                 * This should not happen, so it is better to flag it as unsigned for
993                 * collection purpose. */
994                kextSigningCategory = CFSTR(kUnsignedKext);
995                copySigningInfo(kextURL,
996                                &hashString,
997                                &teamId,
998                                NULL,
999                                &issuerCN);
1000                CFArrayRef subjectCNArray = NULL; // must release
1001                subjectCNArray = copySubjectCNArray(kextURL);
1002                if (subjectCNArray) {
1003                    subjectCN = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
1004                                                                 subjectCNArray,
1005                                                                 CFSTR(";"));
1006                    SAFE_RELEASE(subjectCNArray);
1007                }
1008            }
1009        }
1010        else {
1011            if (status == noErr) {
1012                /* This 3rd-party kext is signed with a devid+ kext certificate */
1013                kextSigningCategory = CFSTR(k3rdPartyKextWithDevIdPlus);
1014                copySigningInfo(kextURL,
1015                                &hashString,
1016                                &teamId,
1017                                &subjectCN,
1018                                &issuerCN);
1019            }
1020            else if (status == CSSMERR_TP_CERT_REVOKED) {
1021                /* This 3rd-party kext is signed with a revoked devid+ kext certificate */
1022                kextSigningCategory = CFSTR(k3rdPartyKextWithRevokedDevIdPlus);
1023                copySigningInfo(kextURL,
1024                                &hashString,
1025                                &teamId,
1026                                &subjectCN,
1027                                &issuerCN);
1028            }
1029            else {
1030                status = checkRootCertificateIsApple(aKext);
1031                if (status == noErr) {
1032                    /* This 3rd-party kext is not signed with a devid+ certificate,
1033                     * but uses an Apple root certificate. */
1034                    kextSigningCategory = CFSTR(k3rdPartyKextWithAppleRoot);
1035                    /* The certificates may not have the expected format.
1036                     * Attempt to get the information if present, and also
1037                     * retrieve the subject cn of every certificate in the chain. */
1038                    copySigningInfo(kextURL,
1039                                    &hashString,
1040                                    &teamId,
1041                                    NULL,
1042                                    &issuerCN);
1043                    CFArrayRef subjectCNArray = NULL; // must release
1044                    subjectCNArray = copySubjectCNArray(kextURL);
1045                    if (subjectCNArray) {
1046                        subjectCN = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
1047                                                                     subjectCNArray,
1048                                                                     CFSTR(";"));
1049                        SAFE_RELEASE(subjectCNArray);
1050                    }
1051                }
1052                else {
1053                    /* This 3rd-party kext is not signed with a devid+ certificate,
1054                     * and does not use an Apple root certificate. */
1055                    kextSigningCategory = CFSTR(k3rdPartyKextWithoutAppleRoot);
1056                    /* The certificates may not have the expected format.
1057                     * The subjectcn, issuercn and teamid must not be logged. */
1058                    copySigningInfo(kextURL,
1059                                    &hashString,
1060                                    NULL,
1061                                    NULL,
1062                                    NULL);
1063                }
1064            }
1065        }
1066    }
1067
1068    kextDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
1069                                         0,
1070                                         &kCFTypeDictionaryKeyCallBacks,
1071                                         &kCFTypeDictionaryValueCallBacks);
1072    if (!kextDict) {
1073        OSKextLogMemError();
1074        goto finish;
1075    }
1076
1077    if (bundleIDString) {
1078        CFDictionaryAddValue(kextDict,
1079                             CFSTR(kMessageTracerBundleIDKey),
1080                             bundleIDString);
1081    }
1082    if (versionString) {
1083        CFDictionaryAddValue(kextDict,
1084                             CFSTR(kMessageTracerVersionKey),
1085                             versionString);
1086    }
1087    if (filename) {
1088        CFDictionaryAddValue(kextDict,
1089                             CFSTR(kMessageTracerKextNameKey),
1090                             filename);
1091    }
1092    if (isFat != NULL) {
1093        CFDictionaryAddValue(kextDict,
1094                             CFSTR(kMessageTracerFatKey),
1095                             isFat);
1096    }
1097    if (archString) {
1098        CFDictionaryAddValue(kextDict,
1099                             CFSTR(kMessageTracerArchKey),
1100                             archString);
1101    }
1102
1103    if (kextSigningCategory) {
1104        CFDictionaryAddValue(kextDict,
1105                             CFSTR(kMessageTracerSignatureTypeKey),
1106                             kextSigningCategory);
1107    }
1108
1109    if (hashString) {
1110        CFDictionaryAddValue(kextDict,
1111                             CFSTR(kMessageTracerHashKey),
1112                             hashString);
1113    }
1114
1115    if (teamId) {
1116        CFDictionaryAddValue(kextDict,
1117                             CFSTR(kMessageTracerTeamIdKey),
1118                             teamId);
1119    }
1120    if (subjectCN) {
1121        CFDictionaryAddValue(kextDict,
1122                             CFSTR(kMessageTracerSubjectCNKey),
1123                             subjectCN);
1124    }
1125    if (issuerCN) {
1126        CFDictionaryAddValue(kextDict,
1127                             CFSTR(kMessageTracerIssuerCNKey),
1128                             issuerCN);
1129    }
1130
1131    if (kextPath) {
1132        CFDictionaryAddValue(kextDict,
1133                             CFSTR(kMessageTracerPathKey),
1134                             kextPath);
1135    }
1136
1137
1138    CFArrayAppendValue(*kextList, kextDict);
1139
1140finish:
1141    SAFE_FREE(hashCString);
1142    SAFE_RELEASE(kextURL);
1143    SAFE_RELEASE(kextPath);
1144    SAFE_RELEASE(filename);
1145    SAFE_RELEASE(hashString);
1146    SAFE_RELEASE(kextDict);
1147    SAFE_RELEASE(archString);
1148    SAFE_RELEASE(teamId);
1149    SAFE_RELEASE(subjectCN);
1150    SAFE_RELEASE(issuerCN);
1151    SAFE_RELEASE(code);
1152    SAFE_RELEASE(information);
1153    return;
1154}
1155
1156/*******************************************************************************
1157 * recordKextLoadListForMT() - record the list of loaded kexts
1158 *  <rdar://problem/12435992>
1159 *******************************************************************************/
1160void
1161recordKextLoadListForMT(CFArrayRef kextList)
1162{
1163    CFIndex             count, i;
1164    CFMutableArrayRef   kextsToMessageTrace = NULL; //must release
1165    OSKextRef           aKext;                      //do not release
1166
1167    if (kextList && (count = CFArrayGetCount(kextList))) {
1168        kextsToMessageTrace = CFArrayCreateMutable(kCFAllocatorDefault,
1169                                                  CFArrayGetCount(kextList),
1170                                                  &kCFTypeArrayCallBacks);
1171        if (kextsToMessageTrace) {
1172            for (i = 0; i < count; i ++) {
1173                aKext = (OSKextRef)CFArrayGetValueAtIndex(kextList, i);
1174                filterKextLoadForMT(aKext, &kextsToMessageTrace);
1175            }
1176            if (CFArrayGetCount(kextsToMessageTrace)) {
1177                postNoteAboutKextLoadsMT(CFSTR("Loaded Kext Notification"),
1178                                         kextsToMessageTrace);
1179            }
1180            SAFE_RELEASE(kextsToMessageTrace);
1181        }
1182    }
1183}
1184
1185/*******************************************************************************
1186 * recordKextLoadForMT() - record the loaded kext
1187 *  <rdar://problem/12435992>
1188 *******************************************************************************/
1189void recordKextLoadForMT(OSKextRef aKext)
1190{
1191    CFMutableArrayRef myArray = NULL; // must release
1192
1193    if (!aKext)
1194        return;
1195
1196    myArray = CFArrayCreateMutable(kCFAllocatorDefault,
1197                                   1,
1198                                   &kCFTypeArrayCallBacks);
1199    if (myArray) {
1200        CFArrayAppendValue(myArray, aKext);
1201        recordKextLoadListForMT(myArray);
1202        SAFE_RELEASE(myArray);
1203    }
1204}
1205
1206/*******************************************************************************
1207 * checkKextSignature() - check the signature for given kext.
1208 *******************************************************************************/
1209OSStatus checkKextSignature(OSKextRef aKext,
1210                            Boolean checkExceptionList,
1211                            Boolean earlyBoot)
1212{
1213    OSStatus                result          = errSecCSSignatureFailed;
1214    CFURLRef                kextURL         = NULL;   // must release
1215    SecStaticCodeRef        staticCodeRef   = NULL;   // must release
1216    SecRequirementRef       requirementRef  = NULL;   // must release
1217    CFStringRef             myCFString;
1218    CFStringRef             requirementsString;
1219
1220    if (aKext == NULL) {
1221        return result;
1222    }
1223
1224    kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
1225    if (!kextURL) {
1226        OSKextLogMemError();
1227        goto finish;
1228    }
1229
1230    if (SecStaticCodeCreateWithPath(kextURL,
1231                                    kSecCSDefaultFlags,
1232                                    &staticCodeRef) != errSecSuccess ||
1233        (staticCodeRef == NULL)) {
1234        OSKextLogMemError();
1235        goto finish;
1236    }
1237
1238    /* set up correct requirement string.  Apple kexts are signed by B&I while
1239     * 3rd party kexts are signed through a special developer kext devid
1240     * program
1241     */
1242    myCFString = OSKextGetIdentifier(aKext);
1243    if (CFStringHasPrefix(myCFString, __kOSKextApplePrefix)) {
1244        requirementsString = CFSTR("anchor apple");
1245    }
1246    else {
1247        /* DevID for kexts cert
1248         */
1249        requirementsString =
1250            CFSTR("anchor apple generic "
1251                  "and certificate 1[field.1.2.840.113635.100.6.2.6] "
1252                  "and certificate leaf[field.1.2.840.113635.100.6.1.13] "
1253                  "and certificate leaf[field.1.2.840.113635.100.6.1.18]" );
1254    }
1255
1256    if (SecRequirementCreateWithString(requirementsString,
1257                                       kSecCSDefaultFlags,
1258                                       &requirementRef) != errSecSuccess ||
1259        (requirementRef == NULL)) {
1260        OSKextLogMemError();
1261        goto finish;
1262    }
1263
1264    // errSecCSUnsigned == -67062
1265    if (earlyBoot) {
1266        result = SecStaticCodeCheckValidity(staticCodeRef,
1267                                            kSecCSNoNetworkAccess,
1268                                            requirementRef);
1269    }
1270    else {
1271        result = SecStaticCodeCheckValidity(staticCodeRef,
1272                                            kSecCSEnforceRevocationChecks,
1273                                            requirementRef);
1274    }
1275    if ( result != 0 &&
1276        checkExceptionList &&
1277        isInExceptionList(aKext, kextURL, true) ) {
1278        result = 0;
1279    }
1280
1281finish:
1282    SAFE_RELEASE(kextURL);
1283    SAFE_RELEASE(staticCodeRef);
1284    SAFE_RELEASE(requirementRef);
1285
1286    return result;
1287}
1288
1289#define GET_CSTRING_PTR(the_cfstring, the_ptr, the_buffer, the_size) \
1290do { \
1291the_ptr = CFStringGetCStringPtr(the_cfstring, kCFStringEncodingUTF8); \
1292if (the_ptr == NULL) { \
1293the_buffer[0] = 0x00; \
1294the_ptr = the_buffer;  \
1295CFStringGetCString(the_cfstring, the_buffer, the_size, kCFStringEncodingUTF8); \
1296} \
1297} while(0)
1298
1299/*********************************************************************
1300 * isInExceptionList checks to see if the given kext is in the
1301 * kext signing exception list (in com.apple.driver.KextExcludeList).
1302 * If useCache is TRUE, we will use the cached copy of the exception list.
1303 * If useCache is FALSE, we will refresh the cache from disk.
1304 *
1305 * The kext signing exception list rarely changes but to insure you have the
1306 * most recent copy in the cache pass FALSE for the first call and TRUE for
1307 * subsequent calls (when dealing with a large list of kexts).
1308 * theKext can be NULL if you just want the invalidate the cache.
1309 *********************************************************************/
1310Boolean isInExceptionList(OSKextRef theKext,
1311                          CFURLRef  theKextURL,
1312                          Boolean   useCache)
1313{
1314    Boolean             result                      = false;
1315    CFURLRef            kextURL                     = NULL; // must release
1316    CFStringRef         kextID                      = NULL; // must release
1317    OSKextRef           excludelistKext             = NULL; // must release
1318    CFDictionaryRef     tempDict                    = NULL; // do NOT release
1319#if USE_OLD_EXCEPTION_LIST
1320    static CFDictionaryRef sExceptionListDict       = NULL; // do NOT release
1321#endif
1322    static CFDictionaryRef sExceptionHashListDict   = NULL; // do NOT release
1323
1324    /* invalidate the exception list "by hash" cache or create if not
1325     * present
1326     */
1327    if (useCache == false || sExceptionHashListDict == NULL) {
1328        if (sExceptionHashListDict) {
1329            SAFE_RELEASE_NULL(sExceptionHashListDict);
1330        }
1331        kextID = CFStringCreateWithCString(kCFAllocatorDefault,
1332                                           "com.apple.driver.KextExcludeList",
1333                                           kCFStringEncodingUTF8);
1334        if (kextID == NULL) {
1335            OSKextLogStringError(/* kext */ NULL);
1336            goto finish;
1337        }
1338
1339        excludelistKext = OSKextCreateWithIdentifier(kCFAllocatorDefault,
1340                                                     kextID);
1341        if (excludelistKext == NULL) {
1342            goto finish;
1343        }
1344
1345        /* can we trust AppleKextExcludeList.kext */
1346        if (isDevMode() == false) {
1347            if (checkKextSignature(excludelistKext, false, false) != 0) {
1348                char kextPath[PATH_MAX];
1349
1350                if (!CFURLGetFileSystemRepresentation(OSKextGetURL(excludelistKext),
1351                                                      false,
1352                                                      (UInt8 *)kextPath,
1353                                                      sizeof(kextPath))) {
1354                    strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1355                }
1356                OSKextLog(/* kext */ NULL,
1357                          kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
1358                          kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag,
1359                          "%s has invalid signature; Trust cache is disabled.",
1360                          kextPath);
1361                goto finish;
1362            }
1363        }
1364
1365        tempDict = OSKextGetValueForInfoDictionaryKey(
1366                                        excludelistKext,
1367                                        CFSTR("OSKextSigExceptionHashList") );
1368        if (tempDict) {
1369            if ((unsigned int)CFDictionaryGetCount(tempDict) > 0) {
1370                sExceptionHashListDict = CFDictionaryCreateCopy(NULL, tempDict);
1371                if (sExceptionHashListDict == NULL) {
1372                    OSKextLogMemError();
1373                }
1374            }
1375        }
1376    }
1377
1378    /* invalidate the exception list "by bundle ID" cache or create if not
1379     * present
1380     */
1381#if USE_OLD_EXCEPTION_LIST
1382    if (useCache == false || sExceptionListDict == NULL) {
1383        if (sExceptionListDict) {
1384            SAFE_RELEASE_NULL(sExceptionListDict);
1385        }
1386
1387        if (kextID == NULL) {
1388            kextID = CFStringCreateWithCString(kCFAllocatorDefault,
1389                                               "com.apple.driver.KextExcludeList",
1390                                               kCFStringEncodingUTF8);
1391            if (kextID == NULL) {
1392                OSKextLogStringError(/* kext */ NULL);
1393                goto finish;
1394            }
1395        }
1396
1397        if (excludelistKext == NULL) {
1398            excludelistKext = OSKextCreateWithIdentifier(kCFAllocatorDefault,
1399                                                         kextID);
1400            if (excludelistKext == NULL) {
1401                goto finish;
1402            }
1403        }
1404
1405        /* can we trust AppleKextExcludeList.kext */
1406        if (isDevMode() == false) {
1407            if (checkKextSignature(excludelistKext, false, false) != 0) {
1408                char kextPath[PATH_MAX];
1409
1410                if (!CFURLGetFileSystemRepresentation(OSKextGetURL(excludelistKext),
1411                                                      false,
1412                                                      (UInt8 *)kextPath,
1413                                                      sizeof(kextPath))) {
1414                    strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1415                }
1416                OSKextLog(/* kext */ NULL,
1417                          kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
1418                          kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag,
1419                          "%s has invalid signature; Trust cache is disabled.",
1420                          kextPath);
1421                goto finish;
1422            }
1423        }
1424
1425        tempDict = OSKextGetValueForInfoDictionaryKey(
1426                                            excludelistKext,
1427                                            CFSTR("OSKextSigExceptionList"));
1428        if (tempDict) {
1429            if ((unsigned int)CFDictionaryGetCount(tempDict) > 0) {
1430                sExceptionListDict = CFDictionaryCreateCopy(NULL, tempDict);
1431                if (sExceptionListDict == NULL) {
1432                    OSKextLogMemError();
1433                }
1434            }
1435        }
1436    }
1437#endif
1438
1439    if (theKext == NULL) {
1440        goto finish;
1441    }
1442
1443    if (sExceptionHashListDict) {
1444        if (theKextURL == NULL) {
1445            kextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext));
1446            if (kextURL == NULL) {
1447                OSKextLogMemError();
1448                goto finish;
1449            }
1450            theKextURL = kextURL;
1451        }
1452        if (hashIsInExceptionList(theKextURL, sExceptionHashListDict)) {
1453            result = true;
1454            goto finish;
1455        }
1456    }
1457
1458#if USE_OLD_EXCEPTION_LIST
1459    if (sExceptionListDict) {
1460        if (bundleIdIsInExceptionList(theKext, sExceptionListDict)) {
1461            result = true;
1462            goto finish;
1463        }
1464    }
1465#endif
1466
1467finish:
1468    SAFE_RELEASE(kextURL);
1469    SAFE_RELEASE(kextID);
1470    SAFE_RELEASE(excludelistKext);
1471    return result;
1472}
1473
1474#if USE_OLD_EXCEPTION_LIST
1475/*********************************************************************
1476 * theDict is a dictionary with keys / values of:
1477 *  key = bundleID string of kext we will allow to load inspite of signing
1478 *      failure.
1479 *  value = version string of kext to allow to load.
1480 *      The value is used to check equal or less than a kext with a matching
1481 *      version string.  For example if an entry in the list has key:
1482 *      com.foocompany.fookext
1483 *      and value:
1484 *      4.2.10
1485 *      Then any kext with bundle ID of com.foocompany.fookext and a version
1486 *      string of 4.2.10 or less will be allowed to load even if there is a
1487 *      a kext signing validation failure.
1488 *
1489 * NOTE - Kext versions use an extended Mac OS 'vers' format with double
1490 * the number of digits before the build stage: ####.##.##s{1-255} where 's'
1491 * is a build stage 'd', 'a', 'b', 'f' or 'fc'.  We parse this with
1492 * OSKextParseVersionString
1493 *********************************************************************/
1494
1495static Boolean bundleIdIsInExceptionList(OSKextRef          theKext,
1496                                         CFDictionaryRef    theDict)
1497{
1498    Boolean         result                  = false;
1499    CFStringRef     bundleID                = NULL;  // do NOT release
1500    CFStringRef     exceptionKextVersString = NULL;  // do NOT release
1501    OSKextVersion   kextVers                = -1;
1502    const char *    versCString             = NULL;  // do not free
1503    OSKextVersion   exceptionKextVers;
1504    char            versBuffer[256];
1505
1506    bundleID = OSKextGetIdentifier(theKext);
1507    if (!bundleID) {
1508        OSKextLog(/* kext */ NULL,
1509                  kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
1510                  "%s could not get bundleID",
1511                  __FUNCTION__);
1512        goto finish;
1513    }
1514
1515    kextVers = OSKextGetVersion(theKext);
1516    if (!kextVers) {
1517        OSKextLog(/* kext */ NULL,
1518                  kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
1519                  "%s could not get kextVers",
1520                  __FUNCTION__);
1521        goto finish;
1522    }
1523
1524    exceptionKextVersString = CFDictionaryGetValue(theDict, bundleID);
1525    if (!exceptionKextVersString) {
1526        goto finish;
1527    }
1528
1529    /* parse version strings */
1530    GET_CSTRING_PTR(exceptionKextVersString,
1531                    versCString,
1532                    versBuffer,
1533                    sizeof(versBuffer));
1534    if (strlen(versCString) < 1) {
1535        goto finish;
1536    }
1537
1538    exceptionKextVers = OSKextParseVersionString(versCString);
1539    if (kextVers <= exceptionKextVers) {
1540        OSKextLogCFString(NULL,
1541                          kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
1542                          CFSTR("kext %@  %lld is in exception list, allowing to load"),
1543                          bundleID, kextVers);
1544        result = true;
1545    }
1546
1547finish:
1548
1549    return result;
1550}
1551#endif
1552
1553/*********************************************************************
1554 * theDict is a dictionary with keys / values of:
1555 *  key = message tracing hash of kext
1556 *  value = dictionary containing the bundle ID and version of the kext
1557 *      the hash key represents
1558 *
1559 * Example OSKextSigExceptionHashList from AppleKextExcludeList.kext Info.plist:
1560 *
1561 * <key>OSKextSigExceptionHashList</key>
1562 * <dict>
1563 *      <key>3205773049fb43b4a54cafc8493aa19217fbae7a</key>
1564 *      <dict>
1565 *          <key>CFBundleIdentifier</key>
1566 *          <string>com.apple.driver.AppleMobileDevice</string>
1567 *          <key>CFBundleVersion</key>
1568 *          <string>3.3.0</string>
1569 *      </dict>
1570 * </dict>
1571 *********************************************************************/
1572
1573static Boolean hashIsInExceptionList(CFURLRef           theKextURL,
1574                                     CFDictionaryRef    theDict)
1575{
1576    Boolean         result              = false;
1577    char *          hashCString         = NULL;     // must free
1578    CFStringRef     hashString          = NULL;     // must release
1579    CFStringRef     kextInfoString      = NULL;     // do NOT release
1580
1581    if (theKextURL == NULL) {
1582        goto finish;
1583    }
1584
1585    /* generate the hash for unsigned kext to look up in exception list
1586     */
1587    getAdhocSignatureHash(theKextURL, &hashCString);
1588    if (hashCString == NULL) {
1589        goto finish;
1590    }
1591    hashString = CFStringCreateWithCString(kCFAllocatorDefault,
1592                                           hashCString,
1593                                           kCFStringEncodingUTF8);
1594    if (hashString == NULL) {
1595        OSKextLogMemError();
1596        goto finish;
1597    }
1598
1599    kextInfoString = CFDictionaryGetValue(theDict, hashString);
1600    if (kextInfoString == NULL ||
1601        CFGetTypeID(kextInfoString) != CFStringGetTypeID()) {
1602        goto finish;
1603    }
1604
1605    OSKextLogCFString(NULL,
1606                      kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
1607                      CFSTR("kext %@ is in hash exception list, allowing to load"),
1608                      theKextURL);
1609    result = true;
1610
1611finish:
1612    SAFE_RELEASE(hashString);
1613    SAFE_FREE(hashCString);
1614
1615    return result;
1616}
1617
1618/*******************************************************************************
1619 * isDevMode() - check to see if this machine is in "kext developer mode"
1620 *******************************************************************************/
1621#define KEXT_DEV_MODE_STRING "kext-dev-mode="
1622
1623Boolean isDevMode(void)
1624{
1625    Boolean     result          = false;
1626    uint64_t    kext_dev_mode;
1627    size_t      bufsize;
1628    char        bootargs_buffer[1024], *dev_mode_ptr;
1629
1630
1631    bufsize = sizeof(bootargs_buffer);
1632    if (sysctlbyname("kern.bootargs", bootargs_buffer, &bufsize, NULL, 0) < 0) {
1633        return(false);
1634    }
1635
1636    /* looking for "kext-dev-mode=1", no prefix allowed and we currently only
1637     * support a value of '1' all else are ignored.
1638     */
1639    dev_mode_ptr = strnstr(bootargs_buffer,
1640                           KEXT_DEV_MODE_STRING,
1641                           sizeof(bootargs_buffer));
1642    if (dev_mode_ptr &&
1643        (dev_mode_ptr == &bootargs_buffer[0] || *(dev_mode_ptr - 1) == ' ')) {
1644        kext_dev_mode = strtoul(dev_mode_ptr + strlen(KEXT_DEV_MODE_STRING),
1645                                NULL, 10);
1646        if (kext_dev_mode == 1) {
1647            result = true;
1648        }
1649    }
1650
1651    return(result);
1652}
1653
1654/*********************************************************************
1655 *********************************************************************/
1656Boolean isInLibraryExtensionsFolder(OSKextRef theKext)
1657{
1658    CFStringRef     myKextPath = NULL; // must release
1659    Boolean         myResult = false;
1660
1661    myKextPath = copyKextPath(theKext);
1662    if ( myKextPath ) {
1663        if ( CFStringHasPrefix(myKextPath,
1664                               CFSTR(_kOSKextLibraryExtensionsFolder)) ) {
1665            myResult = true;
1666        }
1667    }
1668    SAFE_RELEASE(myKextPath);
1669    return(myResult);
1670}
1671
1672/*********************************************************************
1673 *********************************************************************/
1674Boolean isInSystemLibraryExtensionsFolder(OSKextRef theKext)
1675{
1676    CFStringRef     myKextPath = NULL; // must release
1677    Boolean         myResult = false;
1678
1679    myKextPath = copyKextPath(theKext);
1680    if ( myKextPath ) {
1681        if ( CFStringHasPrefix(myKextPath,
1682                               CFSTR(_kOSKextSystemLibraryExtensionsFolder)) ) {
1683            myResult = true;
1684        }
1685    }
1686    SAFE_RELEASE(myKextPath);
1687    return(myResult);
1688}
1689
1690/*******************************************************************************
1691 * isInvalidSignatureAllowed() - check if kext with invalid signature is
1692 * allowed to load.  Currently we check to see if we are running with boot-args
1693 * including "kext-dev-mode".  In the future this is likely be removed or
1694 * changed to use other methods to set up machines in "developer mode".
1695 *******************************************************************************/
1696Boolean isInvalidSignatureAllowed(void)
1697{
1698    Boolean      result = false;      // default to not allowed
1699
1700    if (isDevMode()) {
1701        result = true;
1702    }
1703
1704    return(result);
1705}
1706#include <Security/SecKeychainPriv.h>
1707
1708/* If kextd isn't running, assume it's early boot and that securityd
1709 * isn't running either.  Configure Security to avoid securityd.
1710 * SecKeychainMDSInstall needs to be called once before any kext signatures
1711 * are checked.
1712 */
1713int callSecKeychainMDSInstall( void )
1714{
1715    static int  calledOnce = 0;
1716    static int  result = 0;
1717
1718    if (calledOnce)  return(result);
1719
1720    calledOnce++;
1721    if (isKextdRunning() == FALSE) {
1722        OSStatus    err;
1723
1724        err = SecKeychainMDSInstall();
1725        if (err != errSecSuccess) {
1726            OSKextLog(/* kext */ NULL,
1727                      kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1728                      "Error in security framework, error %d.", (int) err);
1729            result = err;
1730            //result = EX_SOFTWARE;
1731        }
1732    }
1733    return(result);
1734}
1735
1736/*******************************************************************************
1737 * isKextdRunning() - we use this to tell if we're running before kextd is up.
1738 *******************************************************************************/
1739Boolean isKextdRunning(void)
1740{
1741    mach_port_t     kextd_port = MACH_PORT_NULL;
1742    kern_return_t   kern_result = 0;
1743
1744    kern_result = bootstrap_look_up(bootstrap_port,
1745                                    (char *)KEXTD_SERVER_NAME,
1746                                    &kextd_port);
1747    if (kern_result == kOSReturnSuccess && kextd_port != MACH_PORT_NULL) {
1748        return( TRUE );
1749    }
1750
1751    return( FALSE );
1752}
1753