kextd_usernotification.c [plain text]
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "kextd_usernotification.h"
#ifdef NO_CFUserNotification
void kextd_raise_notification(
CFStringRef alertHeader,
CFArrayRef alertMessageArray)
{
return;
}
#else
SCDynamicStoreRef sSysConfigDynamicStore = NULL;
uid_t sConsoleUser = (uid_t)-1;
CFRunLoopSourceRef sNotificationQueueRunLoopSource = NULL; CFUserNotificationRef sCurrentNotification = NULL; CFRunLoopSourceRef sCurrentNotificationRunLoopSource = NULL; CFMutableArrayRef sPendedNonsecureKextPaths = NULL; CFMutableDictionaryRef sNotifiedNonsecureKextPaths = NULL;
static void _sessionDidChange(
SCDynamicStoreRef store,
CFArrayRef changedKeys,
void * info);
void _checkNotificationQueue(void * info);
void _notificationDismissed(
CFUserNotificationRef userNotification,
CFOptionFlags responseFlags);
ExitStatus startMonitoringConsoleUser(
KextdArgs * toolArgs,
unsigned int * sourcePriority)
{
ExitStatus result = EX_OSERR;
CFStringRef consoleUserName = NULL; CFStringRef consoleUserKey = NULL; CFMutableArrayRef keys = NULL; CFRunLoopSourceRef sysConfigRunLoopSource = NULL; CFRunLoopSourceContext sourceContext;
sSysConfigDynamicStore = SCDynamicStoreCreate(
kCFAllocatorDefault, CFSTR(KEXTD_SERVER_NAME),
_sessionDidChange, NULL);
if (!sSysConfigDynamicStore) {
OSKextLogMemError();
goto finish;
}
consoleUserName = SCDynamicStoreCopyConsoleUser(sSysConfigDynamicStore,
&sConsoleUser, NULL);
if (!consoleUserName) {
sConsoleUser = (uid_t)-1;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"No user logged in at kextd startup.");
} else {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"User %d logged in at kextd startup.", sConsoleUser);
}
consoleUserKey = SCDynamicStoreKeyCreateConsoleUser(kCFAllocatorDefault);
if (!consoleUserKey) {
OSKextLogMemError();
goto finish;
}
keys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!keys) {
OSKextLogMemError();
goto finish;
}
CFArrayAppendValue(keys, consoleUserKey);
SCDynamicStoreSetNotificationKeys(sSysConfigDynamicStore, keys,
NULL);
sysConfigRunLoopSource = SCDynamicStoreCreateRunLoopSource(
kCFAllocatorDefault, sSysConfigDynamicStore, 0);
if (!sysConfigRunLoopSource) {
OSKextLogMemError();
goto finish;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), sysConfigRunLoopSource,
kCFRunLoopCommonModes);
bzero(&sourceContext, sizeof(CFRunLoopSourceContext));
sourceContext.version = 0;
sourceContext.perform = _checkNotificationQueue;
sNotificationQueueRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault,
(*sourcePriority)++, &sourceContext);
if (!sNotificationQueueRunLoopSource) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Failed to create alert run loop source.");
goto finish;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), sNotificationQueueRunLoopSource,
kCFRunLoopDefaultMode);
if (!createCFMutableArray(&sPendedNonsecureKextPaths,
&kCFTypeArrayCallBacks)) {
OSKextLogMemError();
goto finish;
}
if (!createCFMutableDictionary(&sNotifiedNonsecureKextPaths)) {
OSKextLogMemError();
goto finish;
}
result = EX_OK;
finish:
SAFE_RELEASE(consoleUserName);
SAFE_RELEASE(consoleUserKey);
SAFE_RELEASE(keys);
SAFE_RELEASE(sysConfigRunLoopSource);
return result;
}
void stopMonitoringConsoleUser(void)
{
SAFE_RELEASE(sSysConfigDynamicStore);
SAFE_RELEASE(sNotificationQueueRunLoopSource);
SAFE_RELEASE(sPendedNonsecureKextPaths);
SAFE_RELEASE(sNotifiedNonsecureKextPaths);
return;
}
static void _sessionDidChange(
SCDynamicStoreRef store,
CFArrayRef changedKeys,
void * info)
{
CFStringRef consoleUserName = NULL; uid_t oldUser = sConsoleUser;
consoleUserName = SCDynamicStoreCopyConsoleUser(sSysConfigDynamicStore,
&sConsoleUser, NULL);
if (!consoleUserName) {
if (oldUser != (uid_t)-1) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"User %d logged out.", oldUser);
}
sConsoleUser = (uid_t)-1;
resetUserNotifications( true);
goto finish;
}
if (sConsoleUser != (uid_t)-1 && oldUser != sConsoleUser) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"User %d logged in.", sConsoleUser);
CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}
finish:
SAFE_RELEASE(consoleUserName);
return;
}
void resetUserNotifications(Boolean dismissAlert)
{
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"Resetting user notifications.");
if (dismissAlert) {
if (sCurrentNotification) {
CFUserNotificationCancel(sCurrentNotification);
CFRelease(sCurrentNotification);
sCurrentNotification = NULL;
}
if (sCurrentNotificationRunLoopSource) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
sCurrentNotificationRunLoopSource,
kCFRunLoopDefaultMode);
CFRelease(sCurrentNotificationRunLoopSource);
sCurrentNotificationRunLoopSource = NULL;
}
}
CFArrayRemoveAllValues(sPendedNonsecureKextPaths);
CFDictionaryRemoveAllValues(sNotifiedNonsecureKextPaths);
return;
}
void _checkNotificationQueue(void * info __unused)
{
CFStringRef kextPath = NULL; CFMutableArrayRef alertMessageArray = NULL;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"Checking user notification queue.");
if (sConsoleUser == (uid_t)-1 || sCurrentNotificationRunLoopSource) {
goto finish;
}
if (CFArrayGetCount(sPendedNonsecureKextPaths)) {
kextPath = (CFStringRef)CFArrayGetValueAtIndex(
sPendedNonsecureKextPaths, 0);
alertMessageArray = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!kextPath || !alertMessageArray) {
goto finish;
}
CFArrayAppendValue(alertMessageArray,
CFSTR("The system extension \""));
CFArrayAppendValue(alertMessageArray, kextPath);
CFArrayAppendValue(alertMessageArray,
CFSTR("\" was installed improperly and cannot be used. "
"Please try reinstalling it, or contact the product's vendor "
"for an update."));
kextd_raise_notification(CFSTR("System extension cannot be used"),
alertMessageArray);
}
finish:
SAFE_RELEASE(alertMessageArray);
if (kextPath) {
CFArrayRemoveValueAtIndex(sPendedNonsecureKextPaths, 0);
}
return;
}
Boolean recordNonsecureKexts(CFArrayRef kextList)
{
Boolean result = false;
CFStringRef nonsecureKextPath = NULL; CFIndex count, i;
if (kextList && (count = CFArrayGetCount(kextList))) {
for (i = 0; i < count; i ++) {
OSKextRef checkKext = (OSKextRef)CFArrayGetValueAtIndex(kextList, i);
SAFE_RELEASE_NULL(nonsecureKextPath);
if (OSKextIsAuthentic(checkKext)) {
continue;
}
nonsecureKextPath = copyKextPath(checkKext);
if (!nonsecureKextPath) {
OSKextLogMemError();
goto finish;
}
if (!CFDictionaryGetValue(sNotifiedNonsecureKextPaths,
nonsecureKextPath)) {
CFArrayAppendValue(sPendedNonsecureKextPaths,
nonsecureKextPath);
CFDictionarySetValue(sNotifiedNonsecureKextPaths,
nonsecureKextPath, kCFBooleanTrue);
result = true;
}
}
}
finish:
SAFE_RELEASE(nonsecureKextPath);
if (result) {
CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}
return result;
}
void kextd_raise_notification(
CFStringRef alertHeader,
CFArrayRef alertMessageArray)
{
CFMutableDictionaryRef alertDict = NULL; CFURLRef iokitFrameworkBundleURL = NULL; SInt32 userNotificationError = 0;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"Raising user notification.");
if (sConsoleUser == (uid_t)-1) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"No logged in user.");
goto finish;
}
alertDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!alertDict) {
goto finish;
}
iokitFrameworkBundleURL = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
CFSTR("/System/Library/Frameworks/IOKit.framework"),
kCFURLPOSIXPathStyle, true);
if (!iokitFrameworkBundleURL) {
goto finish;
}
CFDictionarySetValue(alertDict, kCFUserNotificationLocalizationURLKey,
iokitFrameworkBundleURL);
CFDictionarySetValue(alertDict, kCFUserNotificationAlertHeaderKey,
alertHeader);
CFDictionarySetValue(alertDict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("OK"));
CFDictionarySetValue(alertDict, kCFUserNotificationAlertMessageKey,
alertMessageArray);
sCurrentNotification = CFUserNotificationCreate(kCFAllocatorDefault,
0 , kCFUserNotificationCautionAlertLevel,
&userNotificationError, alertDict);
if (!sCurrentNotification) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't create user notification - %d",
(int)userNotificationError);
goto finish;
}
sCurrentNotificationRunLoopSource = CFUserNotificationCreateRunLoopSource(
kCFAllocatorDefault, sCurrentNotification,
&_notificationDismissed, 5 );
if (!sCurrentNotificationRunLoopSource) {
CFRelease(sCurrentNotification);
sCurrentNotification = NULL;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), sCurrentNotificationRunLoopSource,
kCFRunLoopDefaultMode);
finish:
SAFE_RELEASE(alertDict);
SAFE_RELEASE(iokitFrameworkBundleURL);
return;
}
void _notificationDismissed(
CFUserNotificationRef userNotification,
CFOptionFlags responseFlags)
{
if (sCurrentNotification) {
CFRelease(sCurrentNotification);
sCurrentNotification = NULL;
}
if (sCurrentNotificationRunLoopSource) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), sCurrentNotificationRunLoopSource,
kCFRunLoopDefaultMode);
CFRelease(sCurrentNotificationRunLoopSource);
sCurrentNotificationRunLoopSource = NULL;
}
CFRunLoopSourceSignal(sNotificationQueueRunLoopSource);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
return;
}
#endif