1/*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <IOKit/kext/kextmanager_types.h>
30#include <IOKit/kext/OSKextPrivate.h>
31
32#include "kextd_usernotification.h"
33#include "security.h"
34
35//#include <ApplicationServices/ApplicationServices.h>
36
37// xxx - do we want a new log activity flag?
38
39#ifdef NO_CFUserNotification
40
41void kextd_raise_notification(
42    CFStringRef alertHeader,
43    CFArrayRef  alertMessageArray)
44{
45    return;
46}
47
48#else
49
50SCDynamicStoreRef       sSysConfigDynamicStore            = NULL;
51uid_t                   sConsoleUser                      = (uid_t)-1;
52CFRunLoopSourceRef      sNotificationQueueRunLoopSource   = NULL;  // must release
53
54CFMutableArrayRef       sPendedNonsecureKextPaths         = NULL;  // must release
55CFUserNotificationRef   sNonSecureNotification            = NULL;  // must release
56CFRunLoopSourceRef      sNonSecureNotificationRunLoopSource = NULL;  // must release
57CFMutableDictionaryRef  sNotifiedNonsecureKextPaths       = NULL;  // must release
58
59CFMutableArrayRef       sPendedRevokedCertKextPaths       = NULL;  // must release
60CFUserNotificationRef   sRevokedCertNotification          = NULL;  // must release
61CFRunLoopSourceRef      sRevokedCertNotificationRunLoopSource = NULL;  // must release
62
63#if 0 // not yet
64CFMutableArrayRef       sPendedUnsignedKextPaths           = NULL;  // must release
65CFUserNotificationRef   sUnsignedKextNotification          = NULL;  // must release
66CFRunLoopSourceRef      sUnsignedKextNotificationRunLoopSource = NULL;  // must release
67#endif
68
69CFMutableArrayRef       sPendedNoLoadKextPaths            = NULL;  // must release
70CFUserNotificationRef   sNoLoadKextNotification           = NULL;  // must release
71CFRunLoopSourceRef      sNoLoadKextNotificationRunLoopSource = NULL;  // must release
72
73CFMutableArrayRef       sPendedInvalidSignedKextPaths     = NULL;  // must release
74CFUserNotificationRef   sInvalidSigNotification           = NULL;  // must release
75CFRunLoopSourceRef      sInvalidSigNotificationRunLoopSource = NULL;  // must release
76
77
78CFMutableArrayRef       sPendedExcludedKextPaths          = NULL;  // must release
79CFUserNotificationRef   sExcludedKextNotification         = NULL;  // must release
80CFRunLoopSourceRef      sExcludedKextNotificationRunLoopSource = NULL;  // must release
81
82CFDictionaryRef         sKextTranslationsPlist            = NULL;
83
84static void _sessionDidChange(
85    SCDynamicStoreRef store,
86    CFArrayRef        changedKeys,
87    void            * info);
88
89void _checkNotificationQueue(void * info);
90void _notificationDismissed(
91    CFUserNotificationRef userNotification,
92    CFOptionFlags         responseFlags);
93
94static CFStringRef createBundleMappingKey( CFStringRef theBundleID );
95static Boolean isInAlertsSentArray(
96                                   CFArrayRef          theSentArray,
97                                   CFDictionaryRef     theDict,
98                                   CFStringRef         theMappingKey );
99static CFStringRef getKextAlertMessage(
100                                       CFDictionaryRef  theDict,
101                                       CFStringRef      theMappingKey );
102static Boolean sendKextAlertNotifications(
103                                    CFMutableArrayRef * theSentAlertsArrayPtr,
104                                    CFArrayRef theKextsArray,
105                                    int theAlertType );
106
107static CFStringRef createPathFromAlertType(
108                                           CFStringRef theVolRoot,
109                                           int theAlertType );
110static Boolean doingSystemInstall(void);
111static void kextd_raise_nonsecure_notification(
112                                               CFStringRef alertHeader,
113                                               CFArrayRef  alertMessageArray );
114static void kextd_raise_noload_notification(
115                                            CFStringRef alertHeader,
116                                            CFArrayRef  alertMessageArray );
117static void kextd_raise_invalidsig_notification(
118                                                CFStringRef alertHeader,
119                                                CFArrayRef  alertMessageArray );
120static void kextd_raise_revokedcert_notification(
121                                                 CFStringRef alertHeader,
122                                                 CFArrayRef  alertMessageArray );
123static void revealInFinder( CFArrayRef theArray );
124#if 0 // not yet
125static void kextd_raise_unsignedkext_notification(
126                                                  CFStringRef alertHeader,
127                                                  CFArrayRef  alertMessageArray );
128#endif
129
130static void kextd_raise_excludedkext_notification(
131                                                  CFStringRef alertHeader,
132                                                  CFArrayRef  alertMessageArray );
133
134
135/*******************************************************************************
136*******************************************************************************/
137ExitStatus startMonitoringConsoleUser(
138    KextdArgs    * toolArgs,
139    unsigned int * sourcePriority)
140{
141    ExitStatus         result                 = EX_OSERR;
142    CFStringRef        consoleUserName        = NULL;  // must release
143    CFStringRef        consoleUserKey         = NULL;  // must release
144    CFMutableArrayRef  keys                   = NULL;  // must release
145    CFRunLoopSourceRef sysConfigRunLoopSource = NULL;  // must release
146    CFRunLoopSourceContext sourceContext;
147
148    sSysConfigDynamicStore = SCDynamicStoreCreate(
149        kCFAllocatorDefault, CFSTR(KEXTD_SERVER_NAME),
150        _sessionDidChange, /* context */ NULL);
151    if (!sSysConfigDynamicStore) {
152        OSKextLogMemError();
153        goto finish;
154
155    }
156
157    consoleUserName = SCDynamicStoreCopyConsoleUser(sSysConfigDynamicStore,
158        &sConsoleUser, NULL);
159    if (!consoleUserName) {
160        sConsoleUser = (uid_t)-1;
161        OSKextLog(/* kext */ NULL,
162            kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
163            "No user logged in at kextd startup.");
164    } else {
165    OSKextLog(/* kext */ NULL,
166        kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
167        "User %d logged in at kextd startup.", sConsoleUser);
168    }
169
170    consoleUserKey = SCDynamicStoreKeyCreateConsoleUser(kCFAllocatorDefault);
171    if (!consoleUserKey) {
172        OSKextLogMemError();
173        goto finish;
174    }
175    keys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
176    if (!keys) {
177        OSKextLogMemError();
178        goto finish;
179    }
180
181    CFArrayAppendValue(keys, consoleUserKey);
182    SCDynamicStoreSetNotificationKeys(sSysConfigDynamicStore, keys,
183        /* patterns */ NULL);
184
185    sysConfigRunLoopSource = SCDynamicStoreCreateRunLoopSource(
186        kCFAllocatorDefault, sSysConfigDynamicStore, 0);
187    if (!sysConfigRunLoopSource) {
188        OSKextLogMemError();
189        goto finish;
190    }
191    CFRunLoopAddSource(CFRunLoopGetCurrent(), sysConfigRunLoopSource,
192        kCFRunLoopCommonModes);
193
194    bzero(&sourceContext, sizeof(CFRunLoopSourceContext));
195    sourceContext.version = 0;
196    sourceContext.perform = _checkNotificationQueue;
197    sNotificationQueueRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault,
198        (*sourcePriority)++, &sourceContext);
199    if (!sNotificationQueueRunLoopSource) {
200       OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
201           "Failed to create alert run loop source.");
202        goto finish;
203    }
204    CFRunLoopAddSource(CFRunLoopGetCurrent(), sNotificationQueueRunLoopSource,
205        kCFRunLoopDefaultMode);
206
207    if (!createCFMutableArray(&sPendedNonsecureKextPaths,
208                              &kCFTypeArrayCallBacks)) {
209        OSKextLogMemError();
210        goto finish;
211    }
212
213    if (!createCFMutableArray(&sPendedNoLoadKextPaths,
214                              &kCFTypeArrayCallBacks)) {
215        OSKextLogMemError();
216        goto finish;
217    }
218
219    if (!createCFMutableArray(&sPendedRevokedCertKextPaths,
220                              &kCFTypeArrayCallBacks)) {
221        OSKextLogMemError();
222        goto finish;
223    }
224
225#if 0 // not yet
226    if (!createCFMutableArray(&sPendedUnsignedKextPaths,
227                              &kCFTypeArrayCallBacks)) {
228        OSKextLogMemError();
229        goto finish;
230    }
231#endif
232
233    if (!createCFMutableArray(&sPendedInvalidSignedKextPaths,
234                              &kCFTypeArrayCallBacks)) {
235        OSKextLogMemError();
236        goto finish;
237    }
238
239    if (!createCFMutableArray(&sPendedExcludedKextPaths,
240                              &kCFTypeArrayCallBacks)) {
241        OSKextLogMemError();
242        goto finish;
243    }
244
245    if (!createCFMutableDictionary(&sNotifiedNonsecureKextPaths)) {
246        OSKextLogMemError();
247        goto finish;
248    }
249
250    result = EX_OK;
251
252finish:
253    SAFE_RELEASE(consoleUserName);
254    SAFE_RELEASE(consoleUserKey);
255    SAFE_RELEASE(keys);
256    SAFE_RELEASE(sysConfigRunLoopSource);
257
258    return result;
259}
260
261/*******************************************************************************
262*******************************************************************************/
263void stopMonitoringConsoleUser(void)
264{
265    SAFE_RELEASE(sSysConfigDynamicStore);
266
267    SAFE_RELEASE(sNotificationQueueRunLoopSource);
268    SAFE_RELEASE(sPendedNonsecureKextPaths);
269    SAFE_RELEASE(sPendedNoLoadKextPaths);
270    SAFE_RELEASE(sPendedRevokedCertKextPaths);
271    SAFE_RELEASE(sPendedInvalidSignedKextPaths);
272    //    SAFE_RELEASE(sPendedUnsignedKextPaths);
273    SAFE_RELEASE(sPendedExcludedKextPaths);
274    SAFE_RELEASE(sNotifiedNonsecureKextPaths);
275
276    return;
277}
278
279/*******************************************************************************
280*******************************************************************************/
281static void _sessionDidChange(
282    SCDynamicStoreRef store,
283    CFArrayRef        changedKeys,
284    void            * info)
285{
286    CFStringRef consoleUserName = NULL;  // must release
287    uid_t       oldUser         = sConsoleUser;
288
289   /* If any users are logged on via fast user switching, logging out to
290    * loginwindow sets the console user to 0 (root). We can't do a reset
291    * until all users have fully logged out, in which case
292    * SCDynamicStoreCopyConsoleUser() returns NULL.
293    */
294    consoleUserName = SCDynamicStoreCopyConsoleUser(sSysConfigDynamicStore,
295        &sConsoleUser, NULL);
296    if (!consoleUserName) {
297        if (oldUser != (uid_t)-1) {
298            OSKextLog(/* kext */ NULL,
299                kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
300                "User %d logged out.", oldUser);
301        }
302        sConsoleUser = (uid_t)-1;
303        resetUserNotifications(/* dismissAlert */ true);
304        goto finish;
305    }
306
307   /* Sometimes we'll get >1 notification on a user login, so make sure
308    * the old & new uid are different.
309    */
310    if (sConsoleUser != (uid_t)-1 && oldUser != sConsoleUser) {
311        OSKextLog(/* kext */ NULL,
312            kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
313            "User %d logged in.", sConsoleUser);
314        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
315        CFRunLoopWakeUp(CFRunLoopGetCurrent());
316    }
317
318finish:
319    SAFE_RELEASE(consoleUserName);
320
321    return;
322}
323
324/*******************************************************************************
325*******************************************************************************/
326void resetUserNotifications(Boolean dismissAlert)
327{
328    OSKextLog(/* kext */ NULL,
329        kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
330        "Resetting user notifications.");
331
332    if (dismissAlert) {
333
334        /* Release any reference to any pending user notifications.
335         */
336        if (sNonSecureNotification) {
337            CFUserNotificationCancel(sNonSecureNotification);
338            CFRelease(sNonSecureNotification);
339            sNonSecureNotification = NULL;
340        }
341        if (sNonSecureNotificationRunLoopSource) {
342            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
343                                  sNonSecureNotificationRunLoopSource,
344                                  kCFRunLoopDefaultMode);
345            CFRelease(sNonSecureNotificationRunLoopSource);
346            sNonSecureNotificationRunLoopSource = NULL;
347        }
348
349        if (sNoLoadKextNotification) {
350            CFUserNotificationCancel(sNoLoadKextNotification);
351            CFRelease(sNoLoadKextNotification);
352            sNoLoadKextNotification = NULL;
353        }
354        if (sNoLoadKextNotificationRunLoopSource) {
355            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
356                                  sNoLoadKextNotificationRunLoopSource,
357                                  kCFRunLoopDefaultMode);
358            CFRelease(sNoLoadKextNotificationRunLoopSource);
359            sNoLoadKextNotificationRunLoopSource = NULL;
360        }
361
362        if (sRevokedCertNotification) {
363            CFUserNotificationCancel(sRevokedCertNotification);
364            CFRelease(sRevokedCertNotification);
365            sRevokedCertNotification = NULL;
366        }
367        if (sRevokedCertNotificationRunLoopSource) {
368            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
369                                  sRevokedCertNotificationRunLoopSource,
370                                  kCFRunLoopDefaultMode);
371            CFRelease(sRevokedCertNotificationRunLoopSource);
372            sRevokedCertNotificationRunLoopSource = NULL;
373        }
374
375#if 0 // not yet
376        if (sUnsignedKextNotification) {
377            CFUserNotificationCancel(sUnsignedKextNotification);
378            CFRelease(sUnsignedKextNotification);
379            sUnsignedKextNotification = NULL;
380        }
381        if (sUnsignedKextNotificationRunLoopSource) {
382            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
383                                  sUnsignedKextNotificationRunLoopSource,
384                                  kCFRunLoopDefaultMode);
385            CFRelease(sUnsignedKextNotificationRunLoopSource);
386            sUnsignedKextNotificationRunLoopSource = NULL;
387        }
388#endif
389
390        if (sInvalidSigNotification) {
391            CFUserNotificationCancel(sInvalidSigNotification);
392            CFRelease(sInvalidSigNotification);
393            sInvalidSigNotification = NULL;
394        }
395        if (sInvalidSigNotificationRunLoopSource) {
396            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
397                                  sInvalidSigNotificationRunLoopSource,
398                                  kCFRunLoopDefaultMode);
399            CFRelease(sInvalidSigNotificationRunLoopSource);
400            sInvalidSigNotificationRunLoopSource = NULL;
401        }
402
403
404        if (sExcludedKextNotification) {
405            CFUserNotificationCancel(sExcludedKextNotification);
406            CFRelease(sExcludedKextNotification);
407            sExcludedKextNotification = NULL;
408        }
409        if (sExcludedKextNotificationRunLoopSource) {
410            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
411                                  sExcludedKextNotificationRunLoopSource,
412                                  kCFRunLoopDefaultMode);
413            CFRelease(sExcludedKextNotificationRunLoopSource);
414            sExcludedKextNotificationRunLoopSource = NULL;
415        }
416    }
417
418    /* Clear the record of which kexts the user has been told are insecure.
419     * If extensions folders have been modified, who knows which kexts are changed?
420     * If user is logging out, logging back in will get the same alerts.
421     */
422    CFArrayRemoveAllValues(sPendedNonsecureKextPaths);
423    CFDictionaryRemoveAllValues(sNotifiedNonsecureKextPaths);
424
425    /* clean up pending kext alerts too */
426    CFArrayRemoveAllValues(sPendedRevokedCertKextPaths);
427    CFArrayRemoveAllValues(sPendedNoLoadKextPaths);
428    CFArrayRemoveAllValues(sPendedInvalidSignedKextPaths);
429    //    CFArrayRemoveAllValues(sPendedUnsignedKextPaths);
430    CFArrayRemoveAllValues(sPendedExcludedKextPaths);
431
432    return;
433}
434
435/*******************************************************************************
436*******************************************************************************/
437void _checkNotificationQueue(void * info __unused)
438{
439    CFStringRef       kextPath                      = NULL;  // do not release
440    CFMutableArrayRef nonsecureAlertMessageArray    = NULL;  // must release
441    CFMutableArrayRef noLoadAlertMessageArray       = NULL;  // must release
442    CFMutableArrayRef revokedCertAlertMessageArray  = NULL;  // must release
443    CFMutableArrayRef invalidSigAlertMessageArray   = NULL;  // must release
444    CFMutableArrayRef unsignedKextAlertMessageArray = NULL;  // must release
445    CFMutableArrayRef excludedAlertMessageArray     = NULL;  // must release
446    CFMutableStringRef excludedAlertHeader          = NULL;  // must release
447    CFIndex     count, i;
448
449    if (sConsoleUser == (uid_t)-1) {
450        goto finish;
451    }
452
453    /* handle alerts for kexts that do not have the proper privs set
454     */
455    if (CFArrayGetCount(sPendedNonsecureKextPaths)  &&
456        sNonSecureNotificationRunLoopSource == NULL) {
457        nonsecureAlertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault,
458                                                   0,
459                                                   &kCFTypeArrayCallBacks);
460        if (nonsecureAlertMessageArray == NULL) {
461            goto finish;
462        }
463
464        kextPath = (CFStringRef) CFArrayGetValueAtIndex(
465                                        sPendedNonsecureKextPaths, 0);
466        if (!kextPath) {
467            goto finish;
468        }
469
470       /* This is the localized format string for the alert message.
471        */
472        CFArrayAppendValue(nonsecureAlertMessageArray,
473            CFSTR("The system extension \""));
474        CFArrayAppendValue(nonsecureAlertMessageArray, kextPath);
475        CFArrayAppendValue(nonsecureAlertMessageArray,
476            CFSTR("\" was installed improperly and cannot be used. "
477                  "Please try reinstalling it, or contact the product's vendor "
478                  "for an update."));
479
480        CFArrayRemoveValueAtIndex(sPendedNonsecureKextPaths, 0);
481        kextd_raise_nonsecure_notification(CFSTR("System extension cannot be used"),
482                                           nonsecureAlertMessageArray);
483    }
484    /* handle alerts for kext signature verification errors that result
485     * in a kext not loading (currently only kexts in /Library/Extensions/
486     */
487    count = CFArrayGetCount(sPendedNoLoadKextPaths);
488    if (count > 0 && sNoLoadKextNotificationRunLoopSource == NULL) {
489        noLoadAlertMessageArray = CFArrayCreateMutable(
490                                                       kCFAllocatorDefault,
491                                                       0,
492                                                       &kCFTypeArrayCallBacks );
493        if (noLoadAlertMessageArray == NULL) {
494            goto finish;
495        }
496        CFArrayAppendValue(
497                           noLoadAlertMessageArray,
498                           (count > 1 ? CFSTR("The following kernel extensions can't "
499                                              "be loaded because they are from "
500                                              "unidentified developers.  Extensions "
501                                              "loaded from /Library/Extensions must be "
502                                              "signed by identified developers. \r")
503                            : CFSTR("The kernel extension at \"")) );
504        for (i = 0; i < count; i ++) {
505            kextPath = (CFStringRef)
506            CFArrayGetValueAtIndex( sPendedNoLoadKextPaths, i );
507            if (kextPath) {
508                if (count > 1) {
509                    CFArrayAppendValue(noLoadAlertMessageArray, CFSTR("\r"));
510                }
511                CFArrayAppendValue(noLoadAlertMessageArray, kextPath);
512            }
513        }
514        if (count == 1) {
515            CFArrayAppendValue(noLoadAlertMessageArray,
516                               CFSTR("\" can't be loaded because it is from an "
517                                     "unidentified developer.  Extensions "
518                                     "loaded from /Library/Extensions must be "
519                                     "signed by identified developers."));
520        }
521        CFArrayAppendValue(noLoadAlertMessageArray,
522                           CFSTR("\r\rPlease contact the kernel extension "
523                                 "vendor for updated software."));
524
525        CFArrayRemoveAllValues(sPendedNoLoadKextPaths);
526        kextd_raise_noload_notification(
527                                        (count > 1  ? CFSTR("Kernel extensions could not be loaded")
528                                         : CFSTR("Kernel extension could not be loaded")),
529                                        noLoadAlertMessageArray);
530    }
531
532    /* handle alerts for kexts with a revoked cert
533     */
534    count = CFArrayGetCount(sPendedRevokedCertKextPaths);
535    if (count > 0  && sRevokedCertNotificationRunLoopSource == NULL) {
536        revokedCertAlertMessageArray = CFArrayCreateMutable(
537                                                            kCFAllocatorDefault,
538                                                            0,
539                                                            &kCFTypeArrayCallBacks );
540        if (revokedCertAlertMessageArray == NULL) {
541            goto finish;
542        }
543        CFArrayAppendValue(
544                           revokedCertAlertMessageArray,
545                           (count > 1 ? CFSTR("The following kernel extensions "
546                                              "are damaged and can't be "
547                                              "loaded. \r")
548                            : CFSTR("The kernel extension at \"")) );
549        for (i = 0; i < count; i ++) {
550            kextPath = (CFStringRef) CFArrayGetValueAtIndex(
551                                            sPendedRevokedCertKextPaths, i );
552            if (kextPath) {
553                if (count > 1) {
554                    CFArrayAppendValue(revokedCertAlertMessageArray, CFSTR("\r"));
555                }
556                CFArrayAppendValue(revokedCertAlertMessageArray,
557                                   kextPath);
558            }
559        }
560        if (count == 1) {
561            CFArrayAppendValue(revokedCertAlertMessageArray,
562                               CFSTR("\" is damaged and can't be loaded."));
563        }
564        CFArrayAppendValue(revokedCertAlertMessageArray,
565                           (count > 1
566                            ? CFSTR("\r\rYou should move them to the Trash.")
567                            : CFSTR("\r\rYou should move it to the Trash."))
568                           );
569
570        // CFArrayRemoveAllValues(sPendedRevokedCertKextPaths); do not remove here, need to use these to reveal in Finder
571        kextd_raise_revokedcert_notification(
572                                        (count > 1  ? CFSTR("Kernel extensions could not be loaded")
573                                         : CFSTR("Kernel extension could not be loaded")),
574                                        revokedCertAlertMessageArray);
575    }
576
577#if 0 // not yet
578    /* handle alerts for unsigned kexts
579     */
580    count = CFArrayGetCount(sPendedUnsignedKextPaths);
581    if (count > 0 && sUnsignedKextNotificationRunLoopSource == NULL) {
582        unsignedKextAlertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault,
583                                                             0,
584                                                             &kCFTypeArrayCallBacks);
585        if (unsignedKextAlertMessageArray == NULL) {
586            goto finish;
587        }
588        CFArrayAppendValue(unsignedKextAlertMessageArray,
589                           CFSTR("The following kernel extensions are not "
590                                 "signed.\r"));
591        for (i = 0; i < count; i ++) {
592            kextPath = (CFStringRef) CFArrayGetValueAtIndex(
593                                                            sPendedUnsignedKextPaths, i);
594            if (kextPath) {
595                CFArrayAppendValue(unsignedKextAlertMessageArray,
596                                   CFSTR("\r"));
597                CFArrayAppendValue(unsignedKextAlertMessageArray,
598                                   kextPath);
599            }
600        }
601        CFArrayAppendValue(unsignedKextAlertMessageArray,
602                           CFSTR("\r\rPlease contact the vendor for each "
603                                 "kernel extension for updated software."));
604
605        CFArrayRemoveAllValues(sPendedUnsignedKextPaths);
606        kextd_raise_unsignedkext_notification(CFSTR("Kernel extensions are not signed"),
607                                              unsignedKextAlertMessageArray);
608    }
609#endif
610
611    /* handle alerts for kext signature verification errors
612     */
613    count = CFArrayGetCount(sPendedInvalidSignedKextPaths);
614    if (count > 0 && sInvalidSigNotificationRunLoopSource == NULL) {
615        invalidSigAlertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault,
616                                                           0,
617                                                           &kCFTypeArrayCallBacks);
618        if (invalidSigAlertMessageArray == NULL) {
619            goto finish;
620        }
621        CFArrayAppendValue(invalidSigAlertMessageArray,
622                           (count > 1 ? CFSTR("The following kernel extensions are not "
623                                              "from identified developers but will "
624                                              "still be loaded. \r")
625                            : CFSTR("The kernel extension at \"")) );
626        for (i = 0; i < count; i ++) {
627            kextPath = (CFStringRef) CFArrayGetValueAtIndex(
628                                                            sPendedInvalidSignedKextPaths, i);
629            if (kextPath) {
630                if (count > 1) {
631                    CFArrayAppendValue(invalidSigAlertMessageArray, CFSTR("\r"));
632                }
633                CFArrayAppendValue(invalidSigAlertMessageArray, kextPath);
634            }
635        }
636        if (count == 1) {
637            CFArrayAppendValue(invalidSigAlertMessageArray,
638                               CFSTR("\" is not from an identified developer "
639                                     "but will still be loaded."));
640        }
641        CFArrayAppendValue(invalidSigAlertMessageArray,
642                           CFSTR("\r\rPlease contact the kernel extension "
643                                 "vendor for updated software."));
644
645        CFArrayRemoveAllValues(sPendedInvalidSignedKextPaths);
646        kextd_raise_invalidsig_notification(
647                                            (count > 1  ? CFSTR("Kernel extensions are not from identified "
648                                                                "developers")
649                                             : CFSTR("Kernel extension is not from an identified "
650                                                     "developer")),
651                                            invalidSigAlertMessageArray);
652    }
653
654#if 1 // <rdar://problem/12811081> warn user about excluded kexts
655    count = CFArrayGetCount(sPendedExcludedKextPaths);
656    if (count > 0 && sExcludedKextNotificationRunLoopSource == NULL) {
657        excludedAlertMessageArray =
658        CFArrayCreateMutable(kCFAllocatorDefault,
659                             0,
660                             &kCFTypeArrayCallBacks);
661        if (excludedAlertMessageArray == NULL) {
662            goto finish;
663        }
664        excludedAlertHeader = CFStringCreateMutable(kCFAllocatorDefault, 0);
665        if (excludedAlertHeader == NULL) {
666            goto finish;
667        }
668        if (count > 1) {
669            CFStringAppend(excludedAlertHeader,
670                           CFSTR("Some system extensions are not compatible with this version of OS X and can’t be used:"));
671            for (i = 0; i < count; i ++) {
672                kextPath = (CFStringRef) CFArrayGetValueAtIndex(
673                                                                sPendedExcludedKextPaths, i);
674                if (kextPath) {
675                    CFRange             myRange;
676                    myRange = CFStringFind(kextPath, CFSTR("/"), kCFCompareBackwards);
677                    // just get kext name if possible, not the full path
678                    if (myRange.length != 0 && myRange.location++ < CFStringGetLength(kextPath)) {
679                        CFStringRef     myString;
680                        myRange.length = CFStringGetLength(kextPath) - myRange.location;
681
682                        myString = CFStringCreateWithSubstring(kCFAllocatorDefault,
683                                                               kextPath,
684                                                               myRange);
685                        if (myString) {
686                            CFArrayAppendValue(excludedAlertMessageArray,
687                                               myString);
688                            SAFE_RELEASE(myString);
689                        }
690                        else {
691                            // fall back to full path
692                            CFArrayAppendValue(excludedAlertMessageArray,
693                                               kextPath);
694                        }
695                    }
696                    else {
697                        // fall back to full path
698                        CFArrayAppendValue(excludedAlertMessageArray,
699                                           kextPath);
700                    }
701                    CFArrayAppendValue(excludedAlertMessageArray,
702                                       CFSTR("\r"));
703                }
704            }
705            // one extra for "Please contact the developer..." to look good
706            CFArrayAppendValue(excludedAlertMessageArray,
707                               CFSTR("\r"));
708        }
709        else {
710            kextPath = (CFStringRef) CFArrayGetValueAtIndex(
711                                                            sPendedExcludedKextPaths, 0 );
712            if (kextPath == NULL) {
713                goto finish;
714            }
715            CFStringAppend(excludedAlertHeader,
716                           CFSTR("The system extension \""));
717            CFRange             myRange;
718            myRange = CFStringFind(kextPath, CFSTR("/"), kCFCompareBackwards);
719            // just get kext name if possible, not the full path
720            if (myRange.length != 0 && myRange.location++ < CFStringGetLength(kextPath)) {
721                CFStringRef     myString;
722                myRange.length = CFStringGetLength(kextPath) - myRange.location;
723
724                myString = CFStringCreateWithSubstring(kCFAllocatorDefault,
725                                                       kextPath,
726                                                       myRange);
727                if (myString) {
728                    CFStringAppend(excludedAlertHeader,
729                                   myString);
730                   SAFE_RELEASE(myString);
731                }
732                else {
733                    // fall back to full path
734                    CFStringAppend(excludedAlertHeader,
735                                   kextPath);
736                }
737            }
738            else {
739                // fall back to full path
740                CFStringAppend(excludedAlertHeader,
741                               kextPath);
742            }
743            CFStringAppend(excludedAlertHeader,
744                           CFSTR("\" is not compatible with this version of OS X and can’t be used."));
745        }
746        CFArrayAppendValue(excludedAlertMessageArray,
747                           CFSTR("Please contact the developer for updated software."));
748
749        CFArrayRemoveAllValues(sPendedExcludedKextPaths);
750        kextd_raise_excludedkext_notification(excludedAlertHeader,
751                                              excludedAlertMessageArray);
752    }
753#endif // <rdar://problem/12811081>
754
755finish:
756    SAFE_RELEASE(nonsecureAlertMessageArray);
757    SAFE_RELEASE(noLoadAlertMessageArray);
758    SAFE_RELEASE(excludedAlertMessageArray);
759    SAFE_RELEASE(revokedCertAlertMessageArray);
760    SAFE_RELEASE(invalidSigAlertMessageArray);
761    SAFE_RELEASE(unsignedKextAlertMessageArray);
762    SAFE_RELEASE(excludedAlertHeader);
763    return;
764}
765
766/******************************************************************************
767 ******************************************************************************/
768Boolean recordNonsecureKexts(CFArrayRef kextList)
769{
770    Boolean     result             = false;
771    CFStringRef nonsecureKextPath  = NULL;  // must release
772    CFIndex     count, i;
773
774    if (kextList && (count = CFArrayGetCount(kextList))) {
775        for (i = 0; i < count; i ++) {
776            OSKextRef checkKext = (OSKextRef)CFArrayGetValueAtIndex(kextList, i);
777
778            SAFE_RELEASE_NULL(nonsecureKextPath);
779
780            if (OSKextIsAuthentic(checkKext)) {
781                continue;
782            }
783            nonsecureKextPath = copyKextPath(checkKext);
784            if (!nonsecureKextPath) {
785                OSKextLogMemError();
786                goto finish;
787            }
788            if (!CFDictionaryGetValue(sNotifiedNonsecureKextPaths,
789                nonsecureKextPath)) {
790
791                CFArrayAppendValue(sPendedNonsecureKextPaths,
792                    nonsecureKextPath);
793                CFDictionarySetValue(sNotifiedNonsecureKextPaths,
794                    nonsecureKextPath, kCFBooleanTrue);
795
796                result = true;
797            }
798        }
799    }
800
801finish:
802    SAFE_RELEASE(nonsecureKextPath);
803
804    if (result) {
805        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
806        CFRunLoopWakeUp(CFRunLoopGetCurrent());
807    }
808    return result;
809}
810
811/*******************************************************************************
812 *******************************************************************************/
813Boolean recordNoLoadKextPath(CFStringRef theKextPath)
814{
815    Boolean     result = false;
816
817    if (theKextPath == NULL) {
818        goto finish;
819    }
820    CFArrayAppendValue(sPendedNoLoadKextPaths, theKextPath);
821    result = true;
822
823finish:
824    return result;
825}
826
827/*******************************************************************************
828 *******************************************************************************/
829void sendNoLoadKextNotification(void)
830{
831    if (CFArrayGetCount(sPendedNoLoadKextPaths)) {
832        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
833        CFRunLoopWakeUp(CFRunLoopGetCurrent());
834    }
835    return;
836}
837
838/*******************************************************************************
839 *******************************************************************************/
840void recordRevokedCertKextPath(CFStringRef theKextPath)
841{
842    if (theKextPath == NULL) {
843        goto finish;
844    }
845    CFArrayAppendValue(sPendedRevokedCertKextPaths, theKextPath);
846
847finish:
848    return;
849}
850
851/*******************************************************************************
852 *******************************************************************************/
853void sendRevokedCertKextPath(void)
854{
855    if (CFArrayGetCount(sPendedRevokedCertKextPaths)) {
856        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
857        CFRunLoopWakeUp(CFRunLoopGetCurrent());
858    }
859    return;
860}
861
862/*******************************************************************************
863 *******************************************************************************/
864Boolean recordInvalidSignedKextPath(CFStringRef theKextPath)
865{
866    Boolean     result              = false;
867
868    if (theKextPath == NULL) {
869        goto finish;
870    }
871    CFArrayAppendValue(sPendedInvalidSignedKextPaths, theKextPath);
872    result = true;
873
874finish:
875    return result;
876}
877
878/*******************************************************************************
879 *******************************************************************************/
880void sendInvalidSignedKextNotification(void)
881{
882    if (CFArrayGetCount(sPendedInvalidSignedKextPaths)) {
883        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
884        CFRunLoopWakeUp(CFRunLoopGetCurrent());
885    }
886    return;
887}
888
889#if 0 // not yet
890/*******************************************************************************
891 *******************************************************************************/
892Boolean recordUnsignedKextPath(CFStringRef theKextPath)
893{
894    Boolean     result              = false;
895
896    if (theKextPath == NULL) {
897        goto finish;
898    }
899    CFArrayAppendValue(sPendedUnsignedKextPaths, theKextPath);
900    result = true;
901
902finish:
903    return result;
904}
905
906/*******************************************************************************
907 *******************************************************************************/
908void sendUnsignedKextNotification(void)
909{
910    if (CFArrayGetCount(sPendedUnsignedKextPaths)) {
911        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
912        CFRunLoopWakeUp(CFRunLoopGetCurrent());
913    }
914    return;
915}
916#endif
917
918/*******************************************************************************
919 *******************************************************************************/
920Boolean recordExcludedKextPath(CFStringRef theKextPath)
921{
922    Boolean     result              = false;
923
924    if (theKextPath == NULL) {
925        goto finish;
926    }
927    CFArrayAppendValue(sPendedExcludedKextPaths, theKextPath);
928    result = true;
929
930finish:
931    return result;
932}
933
934/*******************************************************************************
935 *******************************************************************************/
936void sendExcludedKextNotification(void)
937{
938    if (CFArrayGetCount(sPendedExcludedKextPaths)) {
939        CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
940        CFRunLoopWakeUp(CFRunLoopGetCurrent());
941    }
942    return;
943}
944
945/*******************************************************************************
946 *******************************************************************************/
947static CFMutableDictionaryRef createAlertDict(
948                                              CFStringRef alertHeader,
949                                              CFArrayRef  alertMessageArray )
950{
951    CFMutableDictionaryRef alertDict               = NULL;  // do not release
952    CFURLRef               iokitFrameworkBundleURL = NULL;  // must release
953
954    /* Do not alert if we're doing a system install */
955    if ( doingSystemInstall() ) {
956        goto finish;
957    }
958
959    OSKextLog(/* kext */ NULL,
960              kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
961              "Raising user notification.");
962
963    if (sConsoleUser == (uid_t)-1) {
964        OSKextLog(/* kext */ NULL,
965                  kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
966                  "No logged in user.");
967        goto finish;
968    }
969
970    iokitFrameworkBundleURL = CFURLCreateWithFileSystemPath(
971                                                            kCFAllocatorDefault,
972                                                            CFSTR("/System/Library/Frameworks/IOKit.framework"),
973                                                            kCFURLPOSIXPathStyle, true);
974    if (!iokitFrameworkBundleURL) {
975        goto finish;
976    }
977
978    alertDict = CFDictionaryCreateMutable(
979                                          kCFAllocatorDefault, 0,
980                                          &kCFTypeDictionaryKeyCallBacks,
981                                          &kCFTypeDictionaryValueCallBacks);
982    if (!alertDict) {
983        goto finish;
984    }
985
986    CFDictionarySetValue(alertDict, kCFUserNotificationLocalizationURLKey,
987                         iokitFrameworkBundleURL);
988    CFDictionarySetValue(alertDict, kCFUserNotificationAlertHeaderKey,
989                         alertHeader);
990    CFDictionarySetValue(alertDict, kCFUserNotificationDefaultButtonTitleKey,
991                         CFSTR("OK"));
992    CFDictionarySetValue(alertDict, kCFUserNotificationAlertMessageKey,
993                         alertMessageArray);
994finish:
995    SAFE_RELEASE(iokitFrameworkBundleURL);
996
997    return(alertDict);
998}
999
1000/*******************************************************************************
1001 *******************************************************************************/
1002static void kextd_raise_nonsecure_notification(
1003                                               CFStringRef alertHeader,
1004                                               CFArrayRef  alertMessageArray )
1005{
1006    CFMutableDictionaryRef alertDict               = NULL;  // must release
1007    SInt32                 userNotificationError   = 0;
1008
1009    alertDict = createAlertDict(alertHeader, alertMessageArray);
1010    if (alertDict == NULL) {
1011        goto finish;
1012    }
1013
1014    sNonSecureNotification = CFUserNotificationCreate(kCFAllocatorDefault,
1015                                                      0 /* time interval */, kCFUserNotificationCautionAlertLevel,
1016                                                      &userNotificationError, alertDict);
1017    if (!sNonSecureNotification) {
1018        OSKextLog(/* kext */ NULL,
1019                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1020                  "Can't create user notification - %d",
1021                  (int)userNotificationError);
1022        goto finish;
1023    }
1024
1025    sNonSecureNotificationRunLoopSource = CFUserNotificationCreateRunLoopSource(
1026                                                                                kCFAllocatorDefault,
1027                                                                                sNonSecureNotification,
1028                                                                                &_notificationDismissed,
1029                                                                                /* order */ 5 /* xxx - cheesy! */ );
1030    if (!sNonSecureNotificationRunLoopSource) {
1031        CFRelease(sNonSecureNotification);
1032        sNonSecureNotification = NULL;
1033    }
1034    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1035                       sNonSecureNotificationRunLoopSource,
1036                       kCFRunLoopDefaultMode);
1037finish:
1038    SAFE_RELEASE(alertDict);
1039
1040    return;
1041}
1042
1043/*******************************************************************************
1044 *******************************************************************************/
1045static void kextd_raise_noload_notification(
1046                                            CFStringRef alertHeader,
1047                                            CFArrayRef  alertMessageArray )
1048{
1049    CFMutableDictionaryRef alertDict               = NULL;  // must release
1050    SInt32                 userNotificationError   = 0;
1051
1052    alertDict = createAlertDict(alertHeader, alertMessageArray);
1053    if (alertDict == NULL) {
1054        goto finish;
1055    }
1056
1057    sNoLoadKextNotification =
1058    CFUserNotificationCreate(
1059                             kCFAllocatorDefault,
1060                             0 /* time interval */,
1061                             kCFUserNotificationCautionAlertLevel,
1062                             &userNotificationError,
1063                             alertDict );
1064    if (!sNoLoadKextNotification) {
1065        OSKextLog(/* kext */ NULL,
1066                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1067                  "Can't create user notification - %d",
1068                  (int)userNotificationError);
1069        goto finish;
1070    }
1071
1072    sNoLoadKextNotificationRunLoopSource =
1073    CFUserNotificationCreateRunLoopSource(
1074                                          kCFAllocatorDefault,
1075                                          sNoLoadKextNotification,
1076                                          &_notificationDismissed,
1077                                          /* order */ 5 /* xxx - cheesy! */ );
1078    if (!sNoLoadKextNotificationRunLoopSource) {
1079        CFRelease(sNoLoadKextNotification);
1080        sNoLoadKextNotification = NULL;
1081    }
1082    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1083                       sNoLoadKextNotificationRunLoopSource,
1084                       kCFRunLoopDefaultMode);
1085finish:
1086    SAFE_RELEASE(alertDict);
1087
1088    return;
1089}
1090
1091/*******************************************************************************
1092 *******************************************************************************/
1093static void kextd_raise_revokedcert_notification(
1094                                                 CFStringRef alertHeader,
1095                                                 CFArrayRef  alertMessageArray )
1096{
1097    CFMutableDictionaryRef alertDict               = NULL;  // must release
1098    SInt32                 userNotificationError   = 0;
1099
1100    alertDict = createAlertDict(alertHeader, alertMessageArray);
1101    if (alertDict == NULL) {
1102        goto finish;
1103    }
1104
1105    /* retitle default button */
1106    CFDictionarySetValue(alertDict, kCFUserNotificationDefaultButtonTitleKey,
1107                         CFSTR("Reveal in Finder"));
1108
1109    sRevokedCertNotification =
1110    CFUserNotificationCreate(
1111                             kCFAllocatorDefault,
1112                             0 /* time interval */,
1113                             kCFUserNotificationCautionAlertLevel,
1114                             &userNotificationError,
1115                             alertDict );
1116    if (!sRevokedCertNotification) {
1117        OSKextLog(/* kext */ NULL,
1118                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1119                  "Can't create user notification - %d",
1120                  (int)userNotificationError);
1121        goto finish;
1122    }
1123
1124    sRevokedCertNotificationRunLoopSource =
1125    CFUserNotificationCreateRunLoopSource(
1126                                          kCFAllocatorDefault,
1127                                          sRevokedCertNotification,
1128                                          &_notificationDismissed,
1129                                          /* order */ 5 /* xxx - cheesy! */ );
1130    if (!sRevokedCertNotificationRunLoopSource) {
1131        CFRelease(sRevokedCertNotification);
1132        sRevokedCertNotification = NULL;
1133    }
1134    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1135                       sRevokedCertNotificationRunLoopSource,
1136                       kCFRunLoopDefaultMode);
1137finish:
1138    SAFE_RELEASE(alertDict);
1139
1140    return;
1141}
1142
1143/*******************************************************************************
1144 *******************************************************************************/
1145static void kextd_raise_invalidsig_notification(
1146                                                CFStringRef alertHeader,
1147                                                CFArrayRef  alertMessageArray )
1148{
1149    CFMutableDictionaryRef alertDict               = NULL;  // must release
1150    SInt32                 userNotificationError   = 0;
1151
1152    alertDict = createAlertDict(alertHeader, alertMessageArray);
1153    if (alertDict == NULL) {
1154        goto finish;
1155    }
1156
1157    sInvalidSigNotification =
1158    CFUserNotificationCreate(
1159                             kCFAllocatorDefault,
1160                             0 /* time interval */,
1161                             kCFUserNotificationCautionAlertLevel,
1162                             &userNotificationError,
1163                             alertDict );
1164    if (!sInvalidSigNotification) {
1165        OSKextLog(/* kext */ NULL,
1166                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1167                  "Can't create user notification - %d",
1168                  (int)userNotificationError);
1169        goto finish;
1170    }
1171
1172    sInvalidSigNotificationRunLoopSource =
1173    CFUserNotificationCreateRunLoopSource(
1174                                          kCFAllocatorDefault,
1175                                          sInvalidSigNotification,
1176                                          &_notificationDismissed,
1177                                          /* order */ 5 /* xxx - cheesy! */ );
1178    if (!sInvalidSigNotificationRunLoopSource) {
1179        CFRelease(sInvalidSigNotification);
1180        sInvalidSigNotification = NULL;
1181    }
1182    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1183                       sInvalidSigNotificationRunLoopSource,
1184                       kCFRunLoopDefaultMode);
1185finish:
1186    SAFE_RELEASE(alertDict);
1187
1188    return;
1189}
1190
1191#if 0 // not yet
1192/*******************************************************************************
1193 *******************************************************************************/
1194static void kextd_raise_unsignedkext_notification(
1195                                                  CFStringRef alertHeader,
1196                                                  CFArrayRef  alertMessageArray )
1197{
1198    CFMutableDictionaryRef alertDict               = NULL;  // must release
1199    SInt32                 userNotificationError   = 0;
1200
1201    alertDict = createAlertDict(alertHeader, alertMessageArray);
1202    if (alertDict == NULL) {
1203        goto finish;
1204    }
1205
1206    sUnsignedKextNotification =
1207    CFUserNotificationCreate(
1208                             kCFAllocatorDefault,
1209                             0 /* time interval */,
1210                             kCFUserNotificationCautionAlertLevel,
1211                             &userNotificationError,
1212                             alertDict );
1213    if (!sUnsignedKextNotification) {
1214        OSKextLog(/* kext */ NULL,
1215                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1216                  "Can't create user notification - %d",
1217                  (int)userNotificationError);
1218        goto finish;
1219    }
1220
1221    sUnsignedKextNotificationRunLoopSource =
1222    CFUserNotificationCreateRunLoopSource(
1223                                          kCFAllocatorDefault,
1224                                          sUnsignedKextNotification,
1225                                          &_notificationDismissed,
1226                                          /* order */ 5 /* xxx - cheesy! */ );
1227    if (!sUnsignedKextNotificationRunLoopSource) {
1228        CFRelease(sUnsignedKextNotification);
1229        sUnsignedKextNotification = NULL;
1230    }
1231    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1232                       sUnsignedKextNotificationRunLoopSource,
1233                       kCFRunLoopDefaultMode);
1234finish:
1235    SAFE_RELEASE(alertDict);
1236
1237    return;
1238}
1239#endif
1240
1241/*******************************************************************************
1242 *******************************************************************************/
1243static void kextd_raise_excludedkext_notification(
1244                                                  CFStringRef alertHeader,
1245                                                  CFArrayRef  alertMessageArray )
1246{
1247    CFMutableDictionaryRef alertDict               = NULL;  // must release
1248    SInt32                 userNotificationError   = 0;
1249
1250    alertDict = createAlertDict(alertHeader, alertMessageArray);
1251    if (alertDict == NULL) {
1252        goto finish;
1253    }
1254
1255    sExcludedKextNotification =
1256    CFUserNotificationCreate(
1257                             kCFAllocatorDefault,
1258                             0 /* time interval */,
1259                             kCFUserNotificationCautionAlertLevel,
1260                             &userNotificationError,
1261                             alertDict );
1262    if (!sExcludedKextNotification) {
1263        OSKextLog(/* kext */ NULL,
1264                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1265                  "Can't create user notification - %d",
1266                  (int)userNotificationError);
1267        goto finish;
1268    }
1269
1270    sExcludedKextNotificationRunLoopSource =
1271    CFUserNotificationCreateRunLoopSource(
1272                                          kCFAllocatorDefault,
1273                                          sExcludedKextNotification,
1274                                          &_notificationDismissed,
1275                                          /* order */ 5 /* xxx - cheesy! */ );
1276    if (!sExcludedKextNotificationRunLoopSource) {
1277        CFRelease(sExcludedKextNotification);
1278        sExcludedKextNotification = NULL;
1279    }
1280    CFRunLoopAddSource(CFRunLoopGetCurrent(),
1281                       sExcludedKextNotificationRunLoopSource,
1282                       kCFRunLoopDefaultMode);
1283finish:
1284    SAFE_RELEASE(alertDict);
1285
1286    return;
1287}
1288
1289/*******************************************************************************
1290 *******************************************************************************/
1291void _notificationDismissed(
1292                            CFUserNotificationRef userNotification,
1293                            CFOptionFlags         responseFlags)
1294{
1295    if (sNonSecureNotification && sNonSecureNotification == userNotification) {
1296        CFRelease(sNonSecureNotification);
1297        sNonSecureNotification = NULL;
1298        if (sNonSecureNotificationRunLoopSource) {
1299            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1300                                  sNonSecureNotificationRunLoopSource,
1301                                  kCFRunLoopDefaultMode);
1302            CFRelease(sNonSecureNotificationRunLoopSource);
1303            sNonSecureNotificationRunLoopSource = NULL;
1304        }
1305    }
1306    else if (sNoLoadKextNotification && sNoLoadKextNotification == userNotification) {
1307        CFRelease(sNoLoadKextNotification);
1308        sNoLoadKextNotification = NULL;
1309        if (sNoLoadKextNotificationRunLoopSource) {
1310            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1311                                  sNoLoadKextNotificationRunLoopSource,
1312                                  kCFRunLoopDefaultMode);
1313            CFRelease(sNoLoadKextNotificationRunLoopSource);
1314            sNoLoadKextNotificationRunLoopSource = NULL;
1315        }
1316    }
1317    else if (sRevokedCertNotification && sRevokedCertNotification == userNotification) {
1318        CFRelease(sRevokedCertNotification);
1319        sRevokedCertNotification = NULL;
1320        if (sRevokedCertNotificationRunLoopSource) {
1321            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1322                                  sRevokedCertNotificationRunLoopSource,
1323                                  kCFRunLoopDefaultMode);
1324            CFRelease(sRevokedCertNotificationRunLoopSource);
1325            sRevokedCertNotificationRunLoopSource = NULL;
1326        }
1327
1328        // CFArrayRef CFArrayCreateCopy
1329        if (sPendedRevokedCertKextPaths) {
1330            revealInFinder(sPendedRevokedCertKextPaths);
1331            CFArrayRemoveAllValues(sPendedRevokedCertKextPaths);
1332        } // sPendedRevokedCertKextPaths
1333    }
1334    else if (sInvalidSigNotification && sInvalidSigNotification == userNotification) {
1335        CFRelease(sInvalidSigNotification);
1336        sInvalidSigNotification = NULL;
1337        if (sInvalidSigNotificationRunLoopSource) {
1338            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1339                                  sInvalidSigNotificationRunLoopSource,
1340                                  kCFRunLoopDefaultMode);
1341            CFRelease(sInvalidSigNotificationRunLoopSource);
1342            sInvalidSigNotificationRunLoopSource = NULL;
1343        }
1344    }
1345#if 0 // not yet
1346    else if (sUnsignedKextNotification && sUnsignedKextNotification == userNotification) {
1347        CFRelease(sUnsignedKextNotification);
1348        sUnsignedKextNotification = NULL;
1349        if (sUnsignedKextNotificationRunLoopSource) {
1350            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1351                                  sUnsignedKextNotificationRunLoopSource,
1352                                  kCFRunLoopDefaultMode);
1353            CFRelease(sUnsignedKextNotificationRunLoopSource);
1354            sUnsignedKextNotificationRunLoopSource = NULL;
1355        }
1356    }
1357#endif
1358    else if (sExcludedKextNotification && sExcludedKextNotification == userNotification) {
1359        CFRelease(sExcludedKextNotification);
1360        sExcludedKextNotification = NULL;
1361        if (sExcludedKextNotificationRunLoopSource) {
1362            CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
1363                                  sExcludedKextNotificationRunLoopSource,
1364                                  kCFRunLoopDefaultMode);
1365            CFRelease(sExcludedKextNotificationRunLoopSource);
1366            sExcludedKextNotificationRunLoopSource = NULL;
1367        }
1368    }
1369
1370    /* check to see if there are any other alerts pending */
1371    CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
1372    CFRunLoopWakeUp(CFRunLoopGetCurrent());
1373
1374    return;
1375}
1376
1377#include <ApplicationServices/ApplicationServices.h>
1378static const char kFinderBundleID[] = { "com.apple.finder" };
1379
1380static void revealInFinder(CFArrayRef theArray)
1381{
1382    CFIndex     myCount, i;
1383
1384    if (theArray == NULL)       return;
1385
1386    myCount = CFArrayGetCount(theArray);
1387
1388    for (i = 0; i < myCount; i ++) {
1389        CFStringRef         myKextPath          = NULL;  // do not release
1390        CFURLRef            myURL               = NULL;  // must release
1391        OSErr               myResult;
1392        AEDesc              myTargetDesc        = { typeNull, NULL };
1393        AEDesc              myFileDesc          = { typeNull, NULL };
1394        AEDescList          myParmList          = { typeNull, NULL };
1395        AppleEvent          myRevealEvent       = { typeNull, NULL };
1396        AppleEvent          myActivateEvent     = { typeNull, NULL };
1397        char                myPathString[2 * PATH_MAX];
1398
1399        myKextPath = (CFStringRef) CFArrayGetValueAtIndex(theArray, i);
1400        if (myKextPath == NULL)   continue;
1401
1402        /* NOTE - we create the URL from the path we are given then
1403         * extract the c string path from the URL in order to get the
1404         * correct URL prefix on the path.  The AppleEvent system requires
1405         * this.  Passing the full UNIX path does not work for AppleEvents
1406         */
1407        myURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
1408                                              myKextPath,
1409                                              kCFURLPOSIXPathStyle,
1410                                              true );
1411        if (myURL == NULL)  continue;
1412        myKextPath = CFURLGetString(myURL);
1413
1414        if (myKextPath == NULL ||
1415            CFStringGetCString(myKextPath,
1416                               myPathString,
1417                               sizeof(myPathString),
1418                               kCFStringEncodingUTF8) == false) {
1419            SAFE_RELEASE_NULL(myURL);
1420            continue;
1421        }
1422        SAFE_RELEASE_NULL(myURL);
1423
1424        myResult = AECreateDesc(typeApplicationBundleID,
1425                                &kFinderBundleID,
1426                                sizeof(kFinderBundleID),
1427                                &myTargetDesc);
1428
1429        if (myResult == noErr) {
1430            myResult = AECreateDesc(typeFileURL,
1431                                    myPathString,
1432                                    strlen(myPathString),
1433                                    &myFileDesc);
1434        }
1435        if (myResult == noErr) {
1436            myResult = AECreateList(NULL, 0, false, &myParmList);
1437        }
1438        if (myResult == noErr) {
1439            AEPutDesc(&myParmList, 1, &myFileDesc);
1440        }
1441        if (myResult == noErr) {
1442            myResult = AECreateAppleEvent(kAEMiscStandards,
1443                                          kAESelect,
1444                                          &myTargetDesc,
1445                                          kAutoGenerateReturnID,
1446                                          kAnyTransactionID,
1447                                          &myRevealEvent);
1448        }
1449        if (myResult == noErr) {
1450            myResult = AEPutParamDesc(&myRevealEvent,
1451                                      keyDirectObject,
1452                                      &myParmList);
1453        }
1454        if (myResult == noErr) {
1455            myResult = AECreateAppleEvent(kAEMiscStandards,
1456                                          kAEActivate,
1457                                          &myTargetDesc,
1458                                          kAutoGenerateReturnID,
1459                                          kAnyTransactionID,
1460                                          &myActivateEvent);
1461        }
1462        if (myResult == noErr) {
1463            AESendMessage(&myActivateEvent, NULL, kAENoReply, 0);
1464            AESendMessage(&myRevealEvent, NULL, kAENoReply, 0);
1465        }
1466        AEDisposeDesc(&myTargetDesc);
1467        AEDisposeDesc(&myFileDesc);
1468        AEDisposeDesc(&myParmList);
1469        AEDisposeDesc(&myRevealEvent);
1470        AEDisposeDesc(&myActivateEvent);
1471    } // for loop...
1472
1473    return;
1474}
1475
1476/*******************************************************************************
1477 * writeKextAlertPlist() - update or create one of our alert plist files:
1478 *     invalidsignedkextalert.plist
1479 *     noloadkextalert.plist
1480 *     excludedkextalert.plist
1481 * with the given array of kext paths (CFStrings).
1482 * The key for the array in the plist dictionary is "Alerts sent".
1483 *
1484 * We use these plist files to control which kexts we have displayed an alert
1485 * dialog about.
1486 * Marketing only wanted us to alert once.  Accees to this routine needs to be
1487 * synchronized (all callers use a dispatch queue).
1488 *
1489 * The plist files are located at:
1490 * /System/Library/Caches/com.apple.kext.caches/Startup/
1491 *
1492 * The plist looks something like:
1493 <plist version="1.0">
1494 <dict>
1495    <key>Alerts sent</key>
1496    <array>
1497        <dict>
1498            <key>CFBundleIdentifier</key>
1499            <string>com.foocompany.driver.foo</string>
1500            <key>CFBundleVersion</key>
1501            <string>2.1</string>
1502            <key>KextPathKey</key>
1503            <string>/System/Library/Extensions/foo.kext</string>
1504        </dict>
1505    </array>
1506 </dict>
1507 </plist>
1508 *
1509 * see addKextToAlertDict() for layout of theDict.
1510 * NOTE - this routine must drop reference to theDict.
1511 *******************************************************************************/
1512
1513void writeKextAlertPlist( CFDictionaryRef theDict, int theAlertType )
1514{
1515    CFStringRef             myVolRoot       = NULL;  // do NOT release
1516    CFArrayRef              myKextArray;             // do NOT release
1517    CFURLRef                myURL           = NULL;  // must release
1518    CFStringRef             myPath          = NULL;  // must release
1519    CFReadStreamRef         readStream      = NULL;  // must release
1520    CFWriteStreamRef        writeStream     = NULL;  // must release
1521    CFDictionaryRef         alertPlist      = NULL;  // must release
1522    CFMutableDictionaryRef  alertDict       = NULL;  // must release
1523    Boolean                 fileExists;
1524    Boolean                 closeReadStream     = false;
1525    Boolean                 closeWriteStream    = false;
1526
1527    if (theDict == NULL) {
1528        goto finish;
1529    }
1530    myKextArray = (CFArrayRef)
1531        CFDictionaryGetValue(theDict, CFSTR("KextInfoArrayKey"));
1532    if (myKextArray == NULL || CFArrayGetCount(myKextArray) < 1) {
1533        goto finish;
1534    }
1535
1536    /* note for the kextcache case we could target a volume other than the boot
1537     * volume.  In that case we need to add the "/Volumes/XXXvol/"
1538     * to the path.
1539     */
1540    myVolRoot = (CFStringRef) CFDictionaryGetValue( theDict,
1541                                                    CFSTR("VolRootKey"));
1542    myPath = createPathFromAlertType(myVolRoot, theAlertType);
1543    if (myPath == NULL) {
1544        OSKextLogMemError();
1545        goto finish;
1546    }
1547
1548    myURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
1549                                           myPath,
1550                                           kCFURLPOSIXPathStyle,
1551                                           false );
1552    if (myURL == NULL) {
1553        OSKextLogMemError();
1554        goto finish;
1555    }
1556    fileExists = CFURLResourceIsReachable(myURL, NULL);
1557
1558    /* grab existing data and append to it */
1559    if (fileExists) {
1560        readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL);
1561        if (readStream == NULL) {
1562            OSKextLogMemError();
1563            goto finish;
1564        }
1565        closeReadStream = CFReadStreamOpen(readStream);
1566        if (closeReadStream == false) {
1567            OSKextLogMemError();
1568            goto finish;
1569        }
1570
1571        /* read in the existing contents of alert plist file */
1572        alertPlist = CFPropertyListCreateWithStream(
1573                                                    kCFAllocatorDefault,
1574                                                    readStream,
1575                                                    0,
1576                                                    kCFPropertyListMutableContainersAndLeaves,
1577                                                    NULL, NULL);
1578        if (alertPlist == NULL) {
1579            OSKextLogMemError();
1580            goto finish;
1581        }
1582
1583        CFMutableArrayRef sentArray = NULL;  // do not release
1584        sentArray = (CFMutableArrayRef)
1585                CFDictionaryGetValue(alertPlist, CFSTR("Alerts sent"));
1586
1587        if (sentArray == NULL) {
1588            OSKextLogMemError();
1589            goto finish;
1590        }
1591
1592        /* add any kext paths that are not already known */
1593        Boolean     didAppend = false;
1594
1595        didAppend = sendKextAlertNotifications(&sentArray, myKextArray, theAlertType);
1596
1597        /* now replace previous plist with our updated one */
1598        if (didAppend) {
1599            writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, myURL);
1600            if (writeStream == NULL) {
1601                OSKextLogMemError();
1602                goto finish;
1603            }
1604            closeWriteStream = CFWriteStreamOpen(writeStream);
1605            if (closeWriteStream == false) {
1606                OSKextLogMemError();
1607                goto finish;
1608            }
1609
1610            CFPropertyListWrite(alertPlist,
1611                                writeStream,
1612                                kCFPropertyListXMLFormat_v1_0,
1613                                0,
1614                                NULL);
1615        }
1616    }
1617    else {
1618        /* plist does not exist, create one */
1619        alertDict = CFDictionaryCreateMutable(
1620                                              kCFAllocatorDefault, 0,
1621                                              &kCFTypeDictionaryKeyCallBacks,
1622                                              &kCFTypeDictionaryValueCallBacks);
1623        if (alertDict == NULL) {
1624            OSKextLogMemError();
1625            goto finish;
1626        }
1627
1628        /* add our array to the dictionary */
1629        CFDictionarySetValue(alertDict, CFSTR("Alerts sent"), myKextArray);
1630
1631        alertPlist = CFPropertyListCreateDeepCopy(
1632                                                  kCFAllocatorDefault,
1633                                                  alertDict,
1634                                                  kCFPropertyListMutableContainersAndLeaves );
1635        if (alertPlist == NULL) {
1636            OSKextLogMemError();
1637            goto finish;
1638        }
1639
1640        writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, myURL);
1641        if (writeStream == NULL) {
1642            OSKextLogMemError();
1643            goto finish;
1644        }
1645
1646        closeWriteStream = CFWriteStreamOpen(writeStream);
1647        if (closeWriteStream == false) {
1648            OSKextLogMemError();
1649            goto finish;
1650        }
1651
1652        CFPropertyListWrite(alertPlist,
1653                            writeStream,
1654                            kCFPropertyListXMLFormat_v1_0,
1655                            0,
1656                            NULL);
1657
1658        sendKextAlertNotifications(NULL, myKextArray, theAlertType);
1659    }
1660
1661finish:
1662#if 0
1663    if (alertPlist) {
1664        OSKextLogCFString(NULL,
1665                          kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
1666                          CFSTR("%s: alertPlist %@"),
1667                          __func__, alertPlist);
1668    }
1669#endif
1670    if (closeReadStream)    CFReadStreamClose(readStream);
1671    if (closeWriteStream)   CFWriteStreamClose(writeStream);
1672    SAFE_RELEASE(myURL);
1673    SAFE_RELEASE(readStream);
1674    SAFE_RELEASE(writeStream);
1675    SAFE_RELEASE(alertPlist);
1676    SAFE_RELEASE(alertDict);
1677    SAFE_RELEASE(myPath);
1678    SAFE_RELEASE(theDict);
1679
1680    return;
1681}
1682
1683/*******************************************************************************
1684 * sendRevokedCertAlert() - build an array of kexts with revoked certs then
1685 * send to put up an alert.
1686 * NOTE - this routine must drop reference to theDict.
1687 *******************************************************************************/
1688void sendRevokedCertAlert( CFDictionaryRef theDict )
1689{
1690    CFArrayRef  myKextArray;             // do NOT release
1691    CFIndex     count, i;
1692
1693    if (theDict == NULL) {
1694        goto finish;
1695    }
1696    myKextArray = (CFArrayRef)
1697    CFDictionaryGetValue(theDict, CFSTR("KextInfoArrayKey"));
1698    if (myKextArray == NULL ||
1699        CFGetTypeID(myKextArray) != CFArrayGetTypeID() ) {
1700        goto finish;
1701    }
1702
1703    count = CFArrayGetCount(myKextArray);
1704    for (i = 0; i < count; i++) {
1705        CFDictionaryRef         myDict;                 // do NOT release
1706        CFStringRef             myKextPath;             // do NOT release
1707
1708        myDict = (CFDictionaryRef)
1709        CFArrayGetValueAtIndex(myKextArray, i);
1710        if (myDict == NULL ||
1711            CFGetTypeID(myDict) != CFDictionaryGetTypeID()) {
1712            continue;
1713        }
1714
1715        myKextPath = CFDictionaryGetValue(myDict, CFSTR("KextPathKey"));
1716        recordRevokedCertKextPath(myKextPath);
1717    } // for loop...
1718
1719    sendRevokedCertKextPath();
1720
1721finish:
1722    SAFE_RELEASE(theDict);
1723    return;
1724
1725}
1726
1727/* theKextsArray is an array of dictionaries of kext info for kexts
1728 * we may want to send an alert about.  Each kext info dictionary
1729 * contains bundle ID, bundle version and kext path
1730 * (all are CFStrings)
1731 * If we have already alerted before there will be a list of kexts we
1732 * brought to the attention of the user.  We do not want to alert more than
1733 * once about the same kext or class of kexts when we have bundle ID to
1734 * mappings to a vendor or product.  theSentAlertsArray is an array of the
1735 * kexts we have alerted.
1736 */
1737// NOTE - we have decided to not use bundle mappings for the alert messages
1738// at this point.  So for now myMappingKey will always be NULL.
1739static Boolean sendKextAlertNotifications(CFMutableArrayRef *theSentAlertsArrayPtr,
1740                                          CFArrayRef theKextsArray,
1741                                          int theAlertType)
1742{
1743    Boolean     didAppend = false;
1744    CFIndex     count, i;
1745
1746    count = CFArrayGetCount(theKextsArray);
1747    for (i = 0; i < count; i++) {
1748        CFDictionaryRef         myDict;                 // do NOT release
1749        CFStringRef             myKextMessage;          // do NOT release
1750        CFStringRef             myBundleID;             // do NOT release
1751        CFStringRef             myMappingKey = NULL;    // must release
1752
1753        myDict = (CFDictionaryRef)
1754        CFArrayGetValueAtIndex(theKextsArray, i);
1755        if (myDict == NULL)   continue;
1756
1757        myBundleID = (CFStringRef)
1758        CFDictionaryGetValue(myDict, kCFBundleIdentifierKey);
1759
1760        myMappingKey = createBundleMappingKey(myBundleID);
1761
1762        if (theSentAlertsArrayPtr) {
1763            /* skip this one if we already have alerted for it */
1764            if ( isInAlertsSentArray(*theSentAlertsArrayPtr, myDict, myMappingKey) ) {
1765                SAFE_RELEASE_NULL(myMappingKey);
1766                continue;
1767            }
1768            didAppend = true;
1769            CFArrayAppendValue(*theSentAlertsArrayPtr, myDict);
1770        }
1771
1772        /* nag user about this kext */
1773        myKextMessage = getKextAlertMessage(myDict, myMappingKey);
1774        if (theAlertType == INVALID_SIGNATURE_KEXT_ALERT) {
1775            recordInvalidSignedKextPath(myKextMessage);
1776        }
1777        else if (theAlertType == NO_LOAD_KEXT_ALERT) {
1778            recordNoLoadKextPath(myKextMessage);
1779        }
1780        else if (theAlertType == EXCLUDED_KEXT_ALERT) {
1781            recordExcludedKextPath(myKextMessage);
1782        }
1783#if 0 // not yet
1784        else if (theAlertType == UNSIGNED_KEXT_ALERT) {
1785            recordUnsignedKextPath(myKextMessage);
1786        }
1787#endif
1788        SAFE_RELEASE_NULL(myMappingKey);
1789    } // for loop...
1790
1791    if (theAlertType == INVALID_SIGNATURE_KEXT_ALERT) {
1792        sendInvalidSignedKextNotification();
1793    }
1794    else if (theAlertType == NO_LOAD_KEXT_ALERT) {
1795        sendNoLoadKextNotification();
1796    }
1797    else if (theAlertType == EXCLUDED_KEXT_ALERT) {
1798        sendExcludedKextNotification();
1799    }
1800#if 0 // not yet
1801    else if (theAlertType == UNSIGNED_KEXT_ALERT) {
1802        sendUnsignedKextNotification();
1803    }
1804#endif
1805    return(didAppend);
1806}
1807
1808
1809/* The alert message is either going to be the kext path or a product
1810 * or vendor name mapped from the bundle ID.
1811 */
1812// NOTE - we have decided to not use bundle mappings for the alert messages
1813// at this point.  So for now theMappingKey will always be NULL and this routine
1814// will always return the kext path.
1815static CFStringRef getKextAlertMessage(
1816                                       CFDictionaryRef theDict,
1817                                       CFStringRef theMappingKey )
1818{
1819    CFStringRef     myKextMessage = NULL;
1820
1821    if (theMappingKey && sKextTranslationsPlist) {
1822        CFDictionaryRef     myMappingDict = NULL;       // do NOT release
1823
1824        myMappingDict = (CFDictionaryRef)
1825            CFDictionaryGetValue(sKextTranslationsPlist,
1826                                 CFSTR("BundleMappings"));
1827
1828        if (myMappingDict) {
1829            myKextMessage = (CFStringRef)
1830            CFDictionaryGetValue(myMappingDict,
1831                                 theMappingKey);
1832        }
1833    }
1834
1835    if (myKextMessage == NULL) {
1836        /* fall back to kext path */
1837        myKextMessage = CFDictionaryGetValue(theDict,
1838                                             CFSTR("KextPathKey"));
1839    }
1840
1841    return(myKextMessage);
1842}
1843
1844/*******************************************************************************
1845 * writeKextLoadPlist() - update or create the plist file tracking kexts we
1846 * have loaded and message traced.
1847 * The key for the array in the plist dictionary is "Alerts sent".  >> todo change this key name?
1848 *
1849 * The plist files is located at:
1850 * /System/Library/Caches/com.apple.kext.caches/Startup/loadedkextmt.plist
1851 *
1852 * The plist looks something like:
1853cat loadedkextmt.plist
1854 <dict>
1855    <key>Alerts sent</key>
1856    <array>
1857        <dict>
1858            <key>com.apple.message.bundleID</key>
1859            <string>com.softraid.driver.SoftRAID</string>
1860            <key>com.apple.message.hash</key>
1861            <string>4567bcd500cf46ec773fc895902cfc33ebbaab00</string>
1862            <key>com.apple.message.kextname</key>
1863            <string>SoftRAID.kext</string>
1864            <key>com.apple.message.version</key>
1865            <string>4.4</string>
1866            </dict>
1867        <dict>
1868            <key>com.apple.message.bundleID</key>
1869            <string>com.promise.driver.stex</string>
1870            <key>com.apple.message.hash</key>
1871            <string>8d70fb592ec9e6581fbe7dc78b15e915fcbc0db8</string>
1872            <key>com.apple.message.kextname</key>
1873            <string>PromiseSTEX.kext</string>
1874            <key>com.apple.message.version</key>
1875            <string>5.1.62</string>
1876        </dict>
1877    </array>
1878 </dict>
1879 *
1880 */
1881#define LOADED_KEXT_MT_ALERT_FULL_PATH \
1882_kOSKextCachesRootFolder "/" \
1883_kOSKextStartupCachesSubfolder "/" \
1884"loadedkextmt.plist"
1885
1886void writeKextLoadPlist( CFArrayRef theArray )
1887{
1888    CFURLRef                myURL           = NULL;  // must release
1889    CFReadStreamRef         readStream      = NULL;  // must release
1890    CFWriteStreamRef        writeStream     = NULL;  // must release
1891    CFDictionaryRef         alertPlist      = NULL;  // must release
1892    CFMutableDictionaryRef  alertDict       = NULL;  // must release
1893    Boolean                 fileExists;
1894    Boolean                 closeReadStream     = false;
1895    Boolean                 closeWriteStream    = false;
1896
1897    if (theArray == NULL || CFArrayGetCount(theArray) < 1) {
1898        goto finish;
1899    }
1900
1901    myURL = CFURLCreateFromFileSystemRepresentation(
1902                                    kCFAllocatorDefault,
1903                                    (UInt8 *) LOADED_KEXT_MT_ALERT_FULL_PATH,
1904                                    strlen(LOADED_KEXT_MT_ALERT_FULL_PATH),
1905                                    false );
1906    if (myURL == NULL) {
1907        OSKextLogMemError();
1908        goto finish;
1909    }
1910    fileExists = CFURLResourceIsReachable(myURL, NULL);
1911
1912    /* grab existing data and append to it */
1913    if (fileExists) {
1914        readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL);
1915        if (readStream == NULL) {
1916            OSKextLogMemError();
1917            goto finish;
1918        }
1919
1920        closeReadStream = CFReadStreamOpen(readStream);
1921        if (closeReadStream == false) {
1922            OSKextLogMemError();
1923            goto finish;
1924        }
1925
1926        /* read in the existing contents of plist */
1927        alertPlist = CFPropertyListCreateWithStream(
1928                                                    kCFAllocatorDefault,
1929                                                    readStream,
1930                                                    0,
1931                                                    kCFPropertyListMutableContainersAndLeaves,
1932                                                    NULL, NULL);
1933        if (alertPlist == NULL) {
1934            OSKextLogMemError();
1935            goto finish;
1936        }
1937
1938        CFMutableArrayRef sentArray = NULL;  // do not release
1939        sentArray = (CFMutableArrayRef)
1940            CFDictionaryGetValue(alertPlist, CFSTR("Alerts sent"));
1941
1942        if (sentArray == NULL) {
1943            OSKextLogMemError();
1944            goto finish;
1945        }
1946
1947        /* add any kext paths that are not already known */
1948        CFIndex     count, i;
1949        Boolean     didAppend = false;
1950
1951        count = CFArrayGetCount(theArray);
1952        for (i = 0; i < count; i++) {
1953            // the information for each kext is stored as a dictionary
1954            CFDictionaryRef kextDict = CFArrayGetValueAtIndex(theArray, i);
1955
1956            if (!CFArrayContainsValue(sentArray, RANGE_ALL(sentArray), kextDict)) {
1957                didAppend = true;
1958                CFArrayAppendValue(sentArray, kextDict);
1959            }
1960        }
1961
1962        /* now replace previous plist with our updated one */
1963        if (didAppend) {
1964            writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, myURL);
1965            if (writeStream == NULL) {
1966                OSKextLogMemError();
1967                goto finish;
1968            }
1969            closeWriteStream = CFWriteStreamOpen(writeStream);
1970            if (closeWriteStream == false) {
1971                OSKextLogMemError();
1972                goto finish;
1973            }
1974
1975            CFPropertyListWrite(alertPlist,
1976                                writeStream,
1977                                kCFPropertyListXMLFormat_v1_0,
1978                                0,
1979                                NULL);
1980        }
1981    }
1982    else {
1983        /* plist does not exist, create one */
1984        alertDict = CFDictionaryCreateMutable(
1985                                              kCFAllocatorDefault, 0,
1986                                              &kCFTypeDictionaryKeyCallBacks,
1987                                              &kCFTypeDictionaryValueCallBacks);
1988        if (alertDict == NULL) {
1989            OSKextLogMemError();
1990            goto finish;
1991        }
1992
1993        /* add our array to the dictionary */
1994        CFDictionarySetValue(alertDict, CFSTR("Alerts sent"), theArray);
1995
1996        alertPlist = CFPropertyListCreateDeepCopy(
1997                                                  kCFAllocatorDefault,
1998                                                  alertDict,
1999                                                  kCFPropertyListMutableContainersAndLeaves );
2000        if (alertPlist == NULL) {
2001            OSKextLogMemError();
2002            goto finish;
2003        }
2004
2005        writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, myURL);
2006        if (writeStream == NULL) {
2007            OSKextLogMemError();
2008            goto finish;
2009        }
2010
2011        closeWriteStream = CFWriteStreamOpen(writeStream);
2012        if (closeWriteStream == false) {
2013            OSKextLogMemError();
2014            goto finish;
2015        }
2016
2017        CFPropertyListWrite(alertPlist,
2018                            writeStream,
2019                            kCFPropertyListXMLFormat_v1_0,
2020                            0,
2021                            NULL);
2022    }
2023
2024finish:
2025    if (closeReadStream)    CFReadStreamClose(readStream);
2026    if (closeWriteStream)   CFWriteStreamClose(writeStream);
2027    SAFE_RELEASE(myURL);
2028    SAFE_RELEASE(readStream);
2029    SAFE_RELEASE(writeStream);
2030    SAFE_RELEASE(alertPlist);
2031    SAFE_RELEASE(alertDict);
2032    SAFE_RELEASE(theArray);
2033
2034    return;
2035}
2036
2037/* the BundleMappings dictionary contains key / values that map
2038 * bundle IDs to product / vendor names.  The key is either a full
2039 * bundle ID or some partial bundle ID.  The more of the bundle ID
2040 * for a key that matches means a more specific match.  For example,
2041 * key com.foo.driver is a better match than a key with com.foo
2042 * Matching starts with a full bundle ID and works backwards to the
2043 * next '.'.  So if we had bundle ID com.foo.driver.bar we look for
2044 * matches in this order:  com.foo.driver.bar, com.foo.driver,
2045 * com.foo
2046 */
2047// NOTE - we have decided to not use bundle mappings for the alert messages
2048// at this point.  So for now this routine just bails out quickly because
2049// sKextTranslationsPlist will always be NULL.
2050static CFStringRef createBundleMappingKey( CFStringRef theBundleID )
2051{
2052    CFDictionaryRef     myMappingDict = NULL;       // do NOT release
2053    CFMutableStringRef  myMatchKey = NULL;          // do NOT release
2054
2055    if (theBundleID == NULL || sKextTranslationsPlist == NULL) {
2056        goto finish;
2057    };
2058
2059    /* see if there are any BundleMappings for this bundle ID */
2060    myMappingDict = (CFDictionaryRef)
2061        CFDictionaryGetValue(sKextTranslationsPlist,
2062                             CFSTR("BundleMappings"));
2063
2064    if (myMappingDict == NULL) {
2065        goto finish;
2066    }
2067
2068    myMatchKey = CFStringCreateMutableCopy(kCFAllocatorDefault,
2069                                           0, theBundleID);
2070    while (myMatchKey) {
2071        CFStringRef         myProductString = NULL; // do not release
2072        CFRange             myRange;
2073
2074        myProductString = CFDictionaryGetValue(myMappingDict,
2075                                               myMatchKey);
2076        if (myProductString) {
2077            /* found a product string with this key so we are done! */
2078           break;
2079        }
2080
2081        /* trim off anything from the last '.' including the '.' */
2082        myRange = CFStringFind(myMatchKey, CFSTR("."), kCFCompareBackwards);
2083        if (myRange.length != 0) {
2084            myRange.length = CFStringGetLength(myMatchKey) - myRange.location;
2085            CFStringDelete(myMatchKey, myRange);
2086            myRange = CFStringFind(myMatchKey, CFSTR("."), 0);
2087            if (myRange.length == 0) {
2088                /* we are done if no more '.'s left.  Clean up
2089                 * myMatchKey so we know there was no match.
2090                 */
2091                SAFE_RELEASE_NULL(myMatchKey);
2092            }
2093        }
2094    }
2095
2096finish:
2097    return(myMatchKey);
2098}
2099
2100
2101/* This is the routine that controls our "alert only once" policy.
2102 * theSentArray is an array of kext info dictionaries for kexts we have
2103 * displayed an alert for.  We use kext bundle ID and version to determine
2104 * if a kext has been alerted.
2105 */
2106static Boolean isInAlertsSentArray(CFArrayRef theSentArray,
2107                                   CFDictionaryRef theDict,
2108                                   CFStringRef theMappingKey)
2109{
2110    Boolean             myResult = false;
2111    CFIndex             myCount, i;
2112
2113    if (theSentArray == NULL || theDict == NULL)    return(false);
2114
2115    if (CFArrayContainsValue(theSentArray, RANGE_ALL(theSentArray), theDict)) {
2116        return(true);
2117    }
2118
2119    myCount = CFArrayGetCount(theSentArray);
2120    if (theMappingKey && myCount > 0) {
2121        for (i = 0; i < myCount; i++) {
2122            CFDictionaryRef     myKextAlertedDict;
2123            CFStringRef         myAlertedBundleID;
2124
2125            myKextAlertedDict = (CFDictionaryRef)
2126                CFArrayGetValueAtIndex(theSentArray, i);
2127            if (myKextAlertedDict == NULL ||
2128                CFGetTypeID(myKextAlertedDict) != CFDictionaryGetTypeID()) {
2129                continue;
2130            }
2131
2132            myAlertedBundleID = (CFStringRef)
2133            CFDictionaryGetValue(myKextAlertedDict, kCFBundleIdentifierKey);
2134
2135            /* if our bundle mapping key matches any part of a bundle ID in
2136             * the sent alerts array then we have alreay messaged about
2137             * this class of kexts and should not alert again.
2138             */
2139            if (myAlertedBundleID &&
2140                CFGetTypeID(myAlertedBundleID) == CFStringGetTypeID()) {
2141                CFRange     myRange;
2142                myRange = CFStringFind(myAlertedBundleID, theMappingKey, 0);
2143                if (myRange.length > 0) {
2144                    myResult = true;
2145                    break;
2146                }
2147            }
2148        } // for loop...
2149    }
2150
2151    return( myResult);
2152}
2153
2154
2155/* The full paths to each of the alert plist files currently supported
2156 */
2157#define INVALIDSIGNED_KEXT_ALERT_FULL_PATH   \
2158_kOSKextCachesRootFolder "/" \
2159_kOSKextStartupCachesSubfolder "/" \
2160"invalidsignedkextalert.plist"
2161
2162#if 0 // not yet
2163#define UNSIGNED_KEXT_ALERT_FULL_PATH   \
2164_kOSKextCachesRootFolder "/" \
2165_kOSKextStartupCachesSubfolder "/" \
2166"unsignedkextalert.plist"
2167#endif
2168
2169#define NO_LOAD_KEXT_ALERT_FULL_PATH   \
2170_kOSKextCachesRootFolder "/" \
2171_kOSKextStartupCachesSubfolder "/" \
2172"noloadkextalert.plist"
2173
2174#define EXCLUDED_KEXT_ALERT_FULL_PATH   \
2175_kOSKextCachesRootFolder "/" \
2176_kOSKextStartupCachesSubfolder "/" \
2177"excludedkextalert.plist"
2178
2179#define TRANSLATIONS_FULL_PATH   \
2180_kOSKextCachesRootFolder "/" \
2181_kOSKextStartupCachesSubfolder "/" \
2182"kexttranslation.plist"
2183
2184
2185static CFStringRef createPathFromAlertType( CFStringRef theVolRoot,
2186                                            int theAlertType )
2187{
2188    CFStringRef myPath              = NULL; // do NOT release
2189
2190    if (theVolRoot) {
2191        if (theAlertType == INVALID_SIGNATURE_KEXT_ALERT) {
2192            myPath = CFStringCreateWithFormat(
2193                                              kCFAllocatorDefault,
2194                                              /* formatOptions */ NULL,
2195                                              CFSTR("%@%s"),
2196                                              theVolRoot,
2197                                              INVALIDSIGNED_KEXT_ALERT_FULL_PATH);
2198        }
2199#if 0 // not yet
2200        else if (theAlertType == UNSIGNED_KEXT_ALERT) {
2201            myPath = CFStringCreateWithFormat(
2202                                              kCFAllocatorDefault,
2203                                              /* formatOptions */ NULL,
2204                                              CFSTR("%@%s"),
2205                                              theVolRoot,
2206                                              UNSIGNED_KEXT_ALERT_FULL_PATH);
2207        }
2208#endif
2209        else if (theAlertType == NO_LOAD_KEXT_ALERT) {
2210            myPath = CFStringCreateWithFormat(
2211                                              kCFAllocatorDefault,
2212                                              /* formatOptions */ NULL,
2213                                              CFSTR("%@%s"),
2214                                              theVolRoot,
2215                                              NO_LOAD_KEXT_ALERT_FULL_PATH);
2216        }
2217        else if (theAlertType == EXCLUDED_KEXT_ALERT) {
2218            myPath = CFStringCreateWithFormat(
2219                                              kCFAllocatorDefault,
2220                                              /* formatOptions */ NULL,
2221                                              CFSTR("%@%s"),
2222                                              theVolRoot,
2223                                              EXCLUDED_KEXT_ALERT_FULL_PATH);
2224        }
2225        else {
2226            goto finish;
2227        }
2228#if 0 // disable this for now.  We will go with kext file name or path.
2229        // enable this if we decide to translate a kext bundle ID to a
2230        // product of vendor name.
2231        if (sKextTranslationsPlist == NULL) {
2232            myTranslatePath = CFStringCreateWithFormat(
2233                                                       kCFAllocatorDefault,
2234                                                       /* formatOptions */ NULL,
2235                                                       CFSTR("%@%s"),
2236                                                       theVolRoot,
2237                                                       TRANSLATIONS_FULL_PATH);
2238        }
2239#endif
2240    }
2241    else {
2242        if (theAlertType == INVALID_SIGNATURE_KEXT_ALERT) {
2243            myPath = CFStringCreateWithCString(
2244                                               kCFAllocatorDefault,
2245                                               INVALIDSIGNED_KEXT_ALERT_FULL_PATH,
2246                                               kCFStringEncodingUTF8 );
2247        }
2248#if 0 // not yet
2249        else if (theAlertType == UNSIGNED_KEXT_ALERT) {
2250            myPath = CFStringCreateWithCString(
2251                                               kCFAllocatorDefault,
2252                                               UNSIGNED_KEXT_ALERT_FULL_PATH,
2253                                               kCFStringEncodingUTF8 );
2254        }
2255#endif
2256        else if (theAlertType == NO_LOAD_KEXT_ALERT) {
2257            myPath = CFStringCreateWithCString(
2258                                               kCFAllocatorDefault,
2259                                               NO_LOAD_KEXT_ALERT_FULL_PATH,
2260                                               kCFStringEncodingUTF8 );
2261        }
2262        else if (theAlertType == EXCLUDED_KEXT_ALERT) {
2263            myPath = CFStringCreateWithCString(
2264                                               kCFAllocatorDefault,
2265                                               EXCLUDED_KEXT_ALERT_FULL_PATH,
2266                                               kCFStringEncodingUTF8 );
2267        }
2268        else {
2269            goto finish;
2270        }
2271#if 0 // disable this for now.  We will go with kext file name or path.
2272        // enable this if we decide to translate a kext bundle ID to a
2273        // product of vendor name.
2274        if (sKextTranslationsPlist == NULL) {
2275            myTranslatePath = CFStringCreateWithCString(
2276                                                        kCFAllocatorDefault,
2277                                                        TRANSLATIONS_FULL_PATH,
2278                                                        kCFStringEncodingUTF8 );
2279        }
2280#endif
2281    }
2282#if 0 // disable this for now.  We will go with kext file name or path.
2283    if (myTranslatePath) {
2284        CFURLRef    myURL           = NULL;  // must release
2285
2286        myURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
2287                                              myTranslatePath,
2288                                              kCFURLPOSIXPathStyle,
2289                                              false );
2290        if (myURL && CFURLResourceIsReachable(myURL, NULL)) {
2291            CFReadStreamRef         readStream      = NULL;  // must release
2292
2293            readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL);
2294            if (readStream) {
2295                if (CFReadStreamOpen(readStream)) {
2296                    /* read in contents of kexttranslation.plist */
2297                    sKextTranslationsPlist = CFPropertyListCreateWithStream(
2298                                                                            kCFAllocatorDefault,
2299                                                                            readStream,
2300                                                                            0,
2301                                                                            kCFPropertyListMutableContainersAndLeaves,
2302                                                                            NULL, NULL);
2303                    CFReadStreamClose(readStream);
2304                }
2305                SAFE_RELEASE(readStream);
2306            }
2307        }
2308        SAFE_RELEASE(myURL);
2309    } /* myTranslatePath */
2310#endif
2311
2312finish:
2313    //SAFE_RELEASE(myTranslatePath);
2314
2315    return( myPath );
2316}
2317
2318
2319/*******************************************************************************
2320 * Installer folks tell us the best way to determine if we are doing a system
2321 * install is to look for "/private/etc/rc.cdrom".
2322 *******************************************************************************/
2323Boolean doingSystemInstall(void)
2324{
2325    CFURLRef    myURL           = NULL;  // must release
2326    Boolean     result          = false;
2327
2328    myURL = CFURLCreateWithString(NULL,
2329                                  CFSTR("file://localhost/private/etc/rc.cdrom"),
2330                                  NULL);
2331    if (myURL) {
2332        result = CFURLResourceIsReachable(myURL, NULL);
2333    }
2334    SAFE_RELEASE(myURL);
2335    return result;
2336}
2337
2338
2339#endif /* ifndef NO_CFUserNotification */
2340