From 5a1f7049312d1e7c8157ed8502f0111aa300fc7d Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Fri, 7 Apr 2017 21:37:23 -1000 Subject: [PATCH] white-listing granularity is now down to the device level (mic/camera) ui: stuff is resizable / fixed warnings --- LoginItem/AVMonitor.m | 35 +++++++++------ LoginItem/RemeberWindowController.h | 4 ++ LoginItem/RemeberWindowController.m | 23 ++++------ LoginItem/RememberPopup.xib | 67 +++++++++++++++++++---------- LoginItem/main.h | 2 +- LoginItem/main.m | 34 +++++++-------- MainApp/AppDelegate.m | 5 +++ MainApp/Rules.xib | 51 ++++++++++++---------- MainApp/RulesWindowController.m | 59 +++++++++++++++---------- OverSightXPC/Enumerator.h | 3 ++ OverSightXPC/Enumerator.m | 50 ++++++++++++++++++--- OverSightXPC/OverSightXPC.m | 55 ++++++++++++++++------- Shared/AboutWindow.xib | 4 +- Shared/XPCProtocol.h | 4 +- 14 files changed, 257 insertions(+), 139 deletions(-) diff --git a/LoginItem/AVMonitor.m b/LoginItem/AVMonitor.m index af1c7ec..c70ddba 100644 --- a/LoginItem/AVMonitor.m +++ b/LoginItem/AVMonitor.m @@ -56,9 +56,9 @@ #ifdef DEBUG logMsg(LOG_DEBUG, [NSString stringWithFormat:@"loading whitelist %@", path]); #endif - + //since file is created by priv'd XPC, it shouldn't be writeable - // ...unless somebody maliciously creates it, so we check if that here + // ...unless somebody maliciously creates it, so we check that here if(YES == [[NSFileManager defaultManager] isWritableFileAtPath:path]) { //err msg @@ -67,7 +67,7 @@ //bail goto bail; } - + //load self.whiteList = [NSMutableArray arrayWithContentsOfFile:path]; @@ -834,7 +834,7 @@ bail: //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]); - #endif + #endif //generate notification for each process for(NSNumber* processID in audioProcesses) @@ -1018,16 +1018,25 @@ bail: //ignore whitelisted processes // ->for activation events, can check process path - if( (YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) && - (YES == [self.whiteList containsObject:processPath]) ) + if(YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) { - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"activation alert for process %@ is whitelisted, so ignoring", processPath]); - #endif - - //bail - goto bail; + //check each + // ->need match on path and device (camera || mic) + for(NSDictionary* item in self.whiteList) + { + //check path & device + if( (YES == [item[EVENT_PROCESS_PATH] isEqualToString:processPath]) && + ([item[EVENT_DEVICE] intValue] == deviceType.intValue) ) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"activation alert for process %@ is whitelisted, so ignoring", item]); + #endif + + //bail + goto bail; + } + } } //ignore whitelisted processes // ->for deactivation, ignore when no activation alert was shown (cuz process will have likely died, so no pid/path, etc) diff --git a/LoginItem/RemeberWindowController.h b/LoginItem/RemeberWindowController.h index b16fb78..caf1f51 100644 --- a/LoginItem/RemeberWindowController.h +++ b/LoginItem/RemeberWindowController.h @@ -21,6 +21,10 @@ // ->used for whitelisting @property (nonatomic, retain)NSString* processPath; +//device +// ->used for whitelisting +@property (nonatomic, retain)NSNumber* device; + //instance of av monitor @property (nonatomic, retain)AVMonitor* avMonitor; diff --git a/LoginItem/RemeberWindowController.m b/LoginItem/RemeberWindowController.m index aaa9136..ae66d8f 100644 --- a/LoginItem/RemeberWindowController.m +++ b/LoginItem/RemeberWindowController.m @@ -16,6 +16,7 @@ @implementation RememberWindowController +@synthesize device; @synthesize avMonitor; @synthesize processPath; @@ -45,18 +46,6 @@ return; } -/* -//automatically invoked when window is closing -// ->make ourselves unmodal --(void)windowWillClose:(NSNotification *)notification -{ - //make un-modal - [[NSApplication sharedApplication] stopModal]; - - return; -} -*/ - //save stuff into iVars // ->configure window w/ dynamic text -(void)configure:(NSUserNotification*)notification avMonitor:(AVMonitor*)monitor; @@ -83,14 +72,18 @@ // ->saved into iVar for whitelisting self.processPath = notification.userInfo[EVENT_PROCESS_PATH]; + //grab device + // ->saved into iVar for whitelisting + self.device = notification.userInfo[EVENT_DEVICE]; + //set device type for audio - if(SOURCE_AUDIO.intValue == [notification.userInfo[EVENT_DEVICE] intValue]) + if(SOURCE_AUDIO.intValue == [self.device intValue]) { //set deviceType = @"mic"; } //set device type for mic - else if(SOURCE_VIDEO.intValue == [notification.userInfo[EVENT_DEVICE] intValue]) + else if(SOURCE_VIDEO.intValue == [self.device intValue]) { //set deviceType = @"camera"; @@ -131,7 +124,7 @@ #endif //invoke XPC method 'whitelistProcess' to add process to white list - [[xpcConnection remoteObjectProxy] whitelistProcess:self.processPath reply:^(BOOL wasWhitelisted) + [[xpcConnection remoteObjectProxy] whitelistProcess:self.processPath device:self.device reply:^(BOOL wasWhitelisted) { //dbg msg #ifdef DEBUG diff --git a/LoginItem/RememberPopup.xib b/LoginItem/RememberPopup.xib index ea493f4..13b1e12 100644 --- a/LoginItem/RememberPopup.xib +++ b/LoginItem/RememberPopup.xib @@ -1,5 +1,5 @@ - + @@ -14,47 +14,45 @@ - + + + - + - + + + + - + - + + + + - + - + + + - - + + + + + + + + + + + + + + diff --git a/LoginItem/main.h b/LoginItem/main.h index ad56f2c..b99f7c4 100644 --- a/LoginItem/main.h +++ b/LoginItem/main.h @@ -16,6 +16,6 @@ /* FUNCTION DEFINITIONS */ //send XPC message to remove process from whitelist file -void unWhiteList(NSString* process); +void unWhiteList(NSString* process, NSNumber* deviceType); #endif /* main_h */ diff --git a/LoginItem/main.m b/LoginItem/main.m index e594475..b9b05bb 100644 --- a/LoginItem/main.m +++ b/LoginItem/main.m @@ -86,21 +86,21 @@ int main(int argc, const char * argv[]) //bail goto bail; } + } + + //unwhitelist path/device + else if(3 == argc) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"running 'un-whitelist me' logic"); + #endif + + //remove from whitelist file + unWhiteList([NSString stringWithUTF8String:argv[1]], [NSNumber numberWithInt:atoi(argv[2])]); - //assume its a path to a process to remove from whitelist - else - { - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, @"running 'un-whitelist me' logic"); - #endif - - //remove from whitelist file - unWhiteList([NSString stringWithUTF8String:argv[1]]); - - //don't bail - // ->let it start (as it was killed) - } + //don't bail + // ->let it start (as it was killed) } //launch app normally @@ -113,7 +113,7 @@ bail: } //send XPC message to remove process from whitelist file -void unWhiteList(NSString* process) +void unWhiteList(NSString* process, NSNumber* device) { //xpc connection __block NSXPCConnection* xpcConnection = nil; @@ -129,11 +129,11 @@ void unWhiteList(NSString* process) //dbg msg #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sending XPC message to remove %@ from whitelist file", process]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sending XPC message to remove %@/%@ from whitelist file", process, device]); #endif //invoke XPC method 'whitelistProcess' to add process to white list - [[xpcConnection remoteObjectProxy] unWhitelistProcess:process reply:^(BOOL wasRemoved) + [[xpcConnection remoteObjectProxy] unWhitelistProcess:process device:device reply:^(BOOL wasRemoved) { //dbg msg #ifdef DEBUG diff --git a/MainApp/AppDelegate.m b/MainApp/AppDelegate.m index af1284e..6dc778a 100644 --- a/MainApp/AppDelegate.m +++ b/MainApp/AppDelegate.m @@ -455,6 +455,11 @@ bail: configuration = @{NSWorkspaceLaunchConfigurationArguments:args}; } + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item with: %@/%@", configuration, args]); + #endif + //launch it [[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:loginItem] options:NSWorkspaceLaunchWithoutActivation configuration:configuration error:&error]; diff --git a/MainApp/Rules.xib b/MainApp/Rules.xib index 7a288d7..3c7fbf8 100644 --- a/MainApp/Rules.xib +++ b/MainApp/Rules.xib @@ -1,5 +1,5 @@ - + @@ -14,11 +14,12 @@ - + + @@ -36,7 +37,7 @@ - + @@ -55,10 +56,14 @@ + + + + - + @@ -66,10 +71,7 @@ - - - - + @@ -77,7 +79,11 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/MainApp/RulesWindowController.m b/MainApp/RulesWindowController.m index 07d357b..aee42eb 100644 --- a/MainApp/RulesWindowController.m +++ b/MainApp/RulesWindowController.m @@ -34,20 +34,6 @@ } - -/* --(void)windowDidLoad -{ - //super - [super windowDidLoad]; - - //load whitelisted items - self.items = [NSMutableArray arrayWithContentsOfFile:[[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST]]; - - return; -} -*/ - //table delegate // ->return number of rows -(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView @@ -75,6 +61,9 @@ //app bundle NSBundle* appBundle = nil; + //device type + NSString* device = nil; + //sanity check if(row >= self.items.count) { @@ -83,7 +72,7 @@ } //grab process path - processPath = [self.items objectAtIndex:row]; + processPath = [[self.items objectAtIndex:row] objectForKey:EVENT_PROCESS_PATH]; //try find an app bundle appBundle = findAppBundle(processPath); @@ -104,6 +93,19 @@ //grab icon processIcon = getIconForProcess(processPath); + //set device type for audio + if(SOURCE_AUDIO.intValue == [[[self.items objectAtIndex:row] objectForKey:EVENT_DEVICE] intValue]) + { + //set + device = @"mic"; + } + //set device type for mic + else if(SOURCE_VIDEO.intValue == [[[self.items objectAtIndex:row] objectForKey:EVENT_DEVICE] intValue]) + { + //set + device = @"camera"; + } + //init table cell tableCell = [tableView makeViewWithIdentifier:@"itemCell" owner:self]; if(nil == tableCell) @@ -116,7 +118,8 @@ tableCell.imageView.image = processIcon; //set (main) text - tableCell.textField.stringValue = processName; + // process name (device) + tableCell.textField.stringValue = [NSString stringWithFormat:@"%@ (access: %@)", processName, device]; //set sub text [[tableCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG] setStringValue:processPath]; @@ -166,27 +169,39 @@ bail: //index of selected row NSInteger selectedRow = 0; + //item + NSDictionary* item = nil; + //rule NSString* processPath = nil; + //device + NSNumber* device = nil; + //grab selected row selectedRow = [self.tableView rowForView:sender]; - - //extract selected item - processPath = self.items[selectedRow]; + + //grab item + item = self.items[selectedRow]; + + //extract path + processPath = item[EVENT_PROCESS_PATH]; + + //extract device + device = item[EVENT_DEVICE]; //remove from items - [self.items removeObject:processPath]; + [self.items removeObject:item]; //reload table [self.tableView reloadData]; //restart login item in background - // ->pass in process name so it can un-whitelist via XPC + // ->pass in process path/device so it can un-whitelist via XPC dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //restart - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) startLoginItem:YES args:@[processPath]]; + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) startLoginItem:YES args:@[processPath, [device stringValue]]]; }); return; diff --git a/OverSightXPC/Enumerator.h b/OverSightXPC/Enumerator.h index db53083..b0d6150 100644 --- a/OverSightXPC/Enumerator.h +++ b/OverSightXPC/Enumerator.h @@ -27,6 +27,9 @@ //sample binary #define SAMPLE @"/usr/bin/sample" +//path to siri +#define SIRI @"/System/Library/PrivateFrameworks/AssistantServices.framework/assistantd" + /* PROPERTIES */ diff --git a/OverSightXPC/Enumerator.m b/OverSightXPC/Enumerator.m index b96a315..f8983a1 100644 --- a/OverSightXPC/Enumerator.m +++ b/OverSightXPC/Enumerator.m @@ -388,13 +388,50 @@ bail: //assign candidateAudioProcs = [[intersection allObjects] mutableCopy]; - //if there aren't any new i/o registy clients and only one new mach sender - // ->use that! (e.g. Siri, reactivated) - if( (0 == candidateAudioProcs.count) && - (1 == newSenders.count) ) + //if there aren't any new i/o registy clients might just be siri + if(0 == candidateAudioProcs.count) { - //assign as candidate - [candidateAudioProcs addObject:newSenders.firstObject]; + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"no new user clients"); + #endif + + //1 new mach msg sender + // just use that as candidate + if(1 == newSenders.count) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"but only found one new mach sender, so using that!"); + #endif + + //assign as candidate + [candidateAudioProcs addObject:newSenders.firstObject]; + } + + //more than + // ->check if any are siri ('assisantd')? + else + { + //check each new ones + for(NSNumber* newSender in newSenders) + { + //check each + if(YES == [SIRI isEqualToString:getProcessPath([newSender intValue])]) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"found a mach sender that's 'siri' so using that!"); + #endif + + //assign as candidate + [candidateAudioProcs addObject:newSenders.firstObject]; + + //all set + break; + } + } + } } //dbg msg @@ -413,6 +450,7 @@ bail: goto bail; } + //got more than one candidate // ->invoke 'sample' to determine which candidate is using CMIO/video inputs // note: will skip FaceTime.app on macOS Sierra, as it doesn't do CMIO stuff directly audioProcs = [self sampleCandidates:candidateAudioProcs]; diff --git a/OverSightXPC/OverSightXPC.m b/OverSightXPC/OverSightXPC.m index c57d0de..5ab54c2 100644 --- a/OverSightXPC/OverSightXPC.m +++ b/OverSightXPC/OverSightXPC.m @@ -77,7 +77,7 @@ } //whitelist a process --(void)whitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply +-(void)whitelistProcess:(NSString*)processPath device:(NSNumber*)device reply:(void (^)(BOOL))reply { //flag BOOL wasAdded = NO; @@ -106,7 +106,7 @@ } //add - [whiteList addObject:processPath]; + [whiteList addObject:@{EVENT_PROCESS_PATH:processPath, EVENT_DEVICE:device}]; //check if intermediate dirs exist // ->create them if they aren't there yet @@ -151,7 +151,7 @@ bail: } //remove a process from the whitelist file --(void)unWhitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply +-(void)unWhitelistProcess:(NSString*)processPath device:(NSNumber*)device reply:(void (^)(BOOL))reply { //flag BOOL wasRemoved = NO; @@ -162,6 +162,11 @@ bail: //whitelist NSMutableArray* whiteList = nil; + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got request to unwhitelist %@/%@", processPath, device]); + #endif + //init path to whitelist path = [[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST]; @@ -176,22 +181,40 @@ bail: goto bail; } - //remove item - [whiteList removeObject:processPath]; - - //save to disk - if(YES != [whiteList writeToFile:path atomically:YES]) + //find/remove item from whitelist + for(NSDictionary* item in whiteList) { - //err msg - logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to save updated whitelist to %@", path]); - - //bail - goto bail; + //match path and device? + if( (YES == [item[EVENT_PROCESS_PATH] isEqualToString:processPath]) && + ([item[EVENT_DEVICE] intValue] == device.intValue) ) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"found match in whitelist, will remove!"); + #endif + + //remove + // ->ok, since we aren't going to iterate any more + [whiteList removeObject:item]; + + //save to disk + if(YES != [whiteList writeToFile:path atomically:YES]) + { + //err msg + logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to save updated whitelist to %@", path]); + + //bail + goto bail; + } + + //happy + wasRemoved = YES; + + //done + goto bail; + } } - //happy - wasRemoved = YES; - //bail bail: diff --git a/Shared/AboutWindow.xib b/Shared/AboutWindow.xib index ac3c7c8..74d5950 100644 --- a/Shared/AboutWindow.xib +++ b/Shared/AboutWindow.xib @@ -43,7 +43,7 @@