diff --git a/Installer/Configure.m b/Installer/Configure.m index 5e19586..63a9935 100644 --- a/Installer/Configure.m +++ b/Installer/Configure.m @@ -34,7 +34,11 @@ if(YES == [self isInstalled]) { //dbg msg - logMsg(LOG_DEBUG, @"already installed, so uninstalling..."); + logMsg(LOG_DEBUG, @"already installed, so stopping/uninstalling..."); + + //stop + // ->kill login item/XPC service + [self stop]; //uninstall if(YES != [self uninstall]) @@ -43,10 +47,6 @@ goto bail; } - //and stop - //TODO: erorr checking - [self stop]; - //dbg msg logMsg(LOG_DEBUG, @"uninstalled"); } @@ -81,16 +81,9 @@ //dbg msg logMsg(LOG_DEBUG, @"stopping login item"); - //stop login item/XPC service - if(YES != [self stop]) - { - //err msg - logMsg(LOG_ERR, @"stopping failed"); - - //bail - goto bail; - } - + //stop + // ->kill login item/XPC service + [self stop]; //dbg msg logMsg(LOG_DEBUG, @"uninstalling..."); @@ -228,22 +221,13 @@ bail: } //stop --(BOOL)stop +-(void)stop { - //flag - BOOL bStopped = NO; - //kill it // pkill doesn't provide error info, so... execTask(PKILL, @[APP_HELPER_NAME]); - - //happy - bStopped = YES; - -//bail -bail: - - return bStopped; + + return; } //uninstall diff --git a/LoginItem/AVMonitor.m b/LoginItem/AVMonitor.m index 82e593f..3ede48f 100644 --- a/LoginItem/AVMonitor.m +++ b/LoginItem/AVMonitor.m @@ -14,67 +14,6 @@ #import "../Shared/XPCProtocol.h" -//TODO: make instance methods?! - -//grab first apple camera -AVCaptureDevice* findAppleCamera() -{ - //apple camera - // ->likely FaceTime camera - AVCaptureDevice* appleCamera = nil; - - //list of cameras - NSArray *cameras = nil; - - //get cameras - cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - for(AVCaptureDevice* camera in cameras) - { - //check if apple - if(YES == [camera.manufacturer isEqualToString:@"Apple Inc."]) - { - //save - appleCamera = camera; - - //exit loop - break; - } - } - - return appleCamera; -} - -//grab built-in mic -AVCaptureDevice* findAppleMic() -{ - //built-in mic - AVCaptureDevice* appleMic = nil; - - //list of mics - NSArray *mics = nil; - - //get mics - mics = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]; - for(AVCaptureDevice* mic in mics) - { - //check if apple - // ->also check input source - if( (YES == [mic.manufacturer isEqualToString:@"Apple Inc."]) && - (YES == [[[mic activeInputSource] inputSourceID] isEqualToString:@"imic"]) ) - { - //save - appleMic = mic; - - //exit loop - break; - } - - } - - return appleMic; -} - - @implementation AVMonitor @synthesize mic; @@ -96,6 +35,54 @@ AVCaptureDevice* findAppleMic() return self; } +//grab first apple camera +// ->saves into iVar 'camera' +// note: could maybe use defaultDeviceWithDeviceType method() to default device... +-(void)findAppleCamera +{ + //get cameras + // ->look for one that belongs to apple + for(AVCaptureDevice* currentCamera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) + { + //check if apple + if(YES == [currentCamera.manufacturer isEqualToString:@"Apple Inc."]) + { + //save + self.camera = currentCamera; + + //exit loop + break; + } + } + + return; +} + +//grab first apple mic +// ->saves into iVar 'mic' +-(void)findAppleMic +{ + //get mics + // ->loof for one that belongs to app + for(AVCaptureDevice* currentMic in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]) + { + //check if apple + // ->also check input source + if( (YES == [currentMic.manufacturer isEqualToString:@"Apple Inc."]) && + (YES == [[[currentMic activeInputSource] inputSourceID] isEqualToString:@"imic"]) ) + { + //save + self.mic = currentMic; + + //exit loop + break; + } + + } + + return; +} + //initialiaze AV notifcations/callbacks -(BOOL)monitor { @@ -155,13 +142,12 @@ AVCaptureDevice* findAppleMic() methodSelector = NSSelectorFromString(@"connectionID"); //find (first) apple camera - self.camera = findAppleCamera(); + // ->saves camera into iVar, 'camera' + [self findAppleCamera]; - //find built in mic - self.mic = findAppleMic(); - - //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found mic: %@", self.mic]); + //find (first) apple mic + // ->saves mic into iVar, 'mic' + [self findAppleMic]; //got camera // ->grab connection ID and invoke helper functions @@ -522,7 +508,7 @@ bail: if(noErr != status) { //err msg - //TODO: add + logMsg(LOG_ERR, [NSString stringWithFormat:@"CMIOObjectAddPropertyListenerBlock() failed with %d", status]); //bail goto bail; @@ -737,23 +723,22 @@ bail: // ->for activatated video; allow/block else { + //get process name + processName = getProcessName([event[EVENT_PROCESS_ID] intValue]); + //set other button title notification.otherButtonTitle = @"allow"; //set action title notification.actionButtonTitle = @"block"; - //get process name - // TODO: see 'determineName' in BB (to get name from bundle, etc) - processName = [getProcessPath([event[EVENT_PROCESS_ID] intValue]) lastPathComponent]; - //set pid in user info // ->allows code to try kill proc (later) if user clicks 'block' notification.userInfo = @{EVENT_PROCESS_ID:event[EVENT_PROCESS_ID]}; //set details // ->name of process using it / icon too? - [notification setInformativeText:[NSString stringWithFormat:@"%@ (%@)", processName, event[EVENT_PROCESS_ID]]]; + [notification setInformativeText:[NSString stringWithFormat:@"process: %@ (%@)", processName, event[EVENT_PROCESS_ID]]]; } //log event? @@ -772,11 +757,11 @@ bail: } //process - // ->add title / details / process + // ->add title / details / process path else { //add - [logMsg appendFormat:@"%@ (%@, %@)", title, details, processName]; + [logMsg appendFormat:@"%@ (process: %@, %@)", title, details, getProcessPath([event[EVENT_PROCESS_ID] intValue])]; } //write it out to syslog diff --git a/MainApp/Info.plist b/MainApp/Info.plist index 79b6bf7..42e9bed 100644 --- a/MainApp/Info.plist +++ b/MainApp/Info.plist @@ -6,8 +6,6 @@ en CFBundleExecutable $(EXECUTABLE_NAME) - CFBundleIconFile - CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -24,12 +22,12 @@ 1.0.0 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + NSHumanReadableCopyright Copyright (c) 2016 Objective-See. All rights reserved. NSMainNibFile MainMenu - LSUIElement - NSPrincipalClass NSApplication diff --git a/OverSight.xcodeproj/project.pbxproj b/OverSight.xcodeproj/project.pbxproj index 6fe27c1..27dec0d 100644 --- a/OverSight.xcodeproj/project.pbxproj +++ b/OverSight.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ 7D9A7DE81D893E4F0091C1AF /* InfoWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D62458C1D87D38400870565 /* InfoWindow.xib */; }; 7D9A7DEC1D8BE1E00091C1AF /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5311D659E580066232A /* Exception.m */; }; 7D9A7DEE1D8CACE30091C1AF /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D9A7DED1D8CACE30091C1AF /* libbsm.tbd */; }; + 7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D6245841D87C43900870565 /* Images.xcassets */; }; 7DAF4B7F1D657192000DA31A /* StatusBarMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */; }; 7DC9C8171D641A350017D143 /* OverSightXPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8161D641A350017D143 /* OverSightXPC.m */; }; 7DC9C8191D641A350017D143 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8181D641A350017D143 /* main.m */; }; @@ -166,7 +167,7 @@ 7D17C5131D658FE20066232A /* Images */ = { isa = PBXGroup; children = ( - 7D17CFE21D81121E0017B475 /* AVMonitor.h */, + 8B5755C819DA3F9300799E6B /* AppDelegate.h */, 7D17C5141D658FEB0066232A /* statusIcon.png */, 7D17C5151D658FEB0066232A /* statusIcon@2x.png */, ); @@ -276,11 +277,11 @@ isa = PBXGroup; children = ( 7D17C5131D658FE20066232A /* Images */, + 8B5755C919DA3F9300799E6B /* AppDelegate.m */, + 7D17CFE21D81121E0017B475 /* AVMonitor.h */, + 7D17CFE11D81121E0017B475 /* AVMonitor.m */, 7DAF4B7C1D656FD3000DA31A /* StatusBarMenu.h */, 7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */, - 8B5755C819DA3F9300799E6B /* AppDelegate.h */, - 8B5755C919DA3F9300799E6B /* AppDelegate.m */, - 7D17CFE11D81121E0017B475 /* AVMonitor.m */, 8B5755CD19DA3F9300799E6B /* MainMenu.xib */, 8B5755C419DA3F9300799E6B /* Supporting Files */, ); @@ -419,6 +420,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */, 7D6245921D87D46800870565 /* InfoWindow.xib in Resources */, 7D17C53C1D659E580066232A /* icon.png in Resources */, 8B5755A919DA3E9500799E6B /* MainMenu.xib in Resources */, @@ -629,6 +631,7 @@ 8B5755B919DA3E9500799E6B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Developer ID Application"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; @@ -644,6 +647,7 @@ 8B5755BA19DA3E9500799E6B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Developer ID Application"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; diff --git a/OverSightXPC/Enumerator.m b/OverSightXPC/Enumerator.m index b0d1189..a9e7f39 100644 --- a/OverSightXPC/Enumerator.m +++ b/OverSightXPC/Enumerator.m @@ -309,7 +309,6 @@ bail: } //parse on '()' - // TODO: improve this! subStrings = [line componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"()"]]; if(subStrings.count < 3) { @@ -371,10 +370,10 @@ bail: videoProcs = [NSMutableArray array]; //invoke 'sample' on each - // TODO: delete tmp file? 'Sample analysis of process 37370 written to file /tmp/FaceTime_2016-09-10_081703_TAwB.sample.txt' (written 2 std err?) for(NSNumber* processID in currentSenders) { //exec 'sample' to get threads/dylibs + // ->uses 1.0 seconds for sampling time results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding]; if( (nil == results) || (0 == results.length) ) @@ -382,7 +381,11 @@ bail: //skip continue; } - + + //sampling a process creates a temp file + //->delete it! + [self deleteSampleFile:getProcessPath(processID.intValue)]; + //for now, just check for 'CMIOGraph::DoWork' // ->TODO: could look for dylibs, other calls, etc if(YES != [results containsString:@"CMIOGraph::DoWork"]) @@ -398,6 +401,66 @@ bail: return videoProcs; } +//'sample' binary creates a file +// ->this looks for that file and deletes it +-(void)deleteSampleFile:(NSString*)processPath +{ + //error + NSError* error = nil; + + //files + NSArray* files = nil; + + //grab all files in /tmp + files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/tmp/" error:&error]; + if(nil != error) + { + //err msg + logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to enumerate files in /tmp, %@", error]); + + //bail + goto bail; + } + + //find/delete file + for(NSString* file in files) + { + //skip non-sample files + if(YES != [file hasSuffix:@".sample.txt"]) + { + //skip + continue; + } + + //ignore files that don't contain process name + if(YES != [file containsString:[processPath lastPathComponent]]) + { + //skip + continue; + } + + //dbg msg + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"deleting sample file: %@", file]); + + //delete + if(YES != [[NSFileManager defaultManager] removeItemAtPath:[@"/tmp" stringByAppendingPathComponent:file] error:&error]) + { + //err msg + logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete %@ (%@)", file, error]); + + //bail + goto bail; + } + + }//all files + +//bail +bail: + + + return; +} + //set status of video -(void)updateVideoStatus:(BOOL)isEnabled { diff --git a/Shared/Exception.m b/Shared/Exception.m index 140bcb1..7f7d035 100755 --- a/Shared/Exception.m +++ b/Shared/Exception.m @@ -11,12 +11,10 @@ #import "Exception.h" #import "Utilities.h" -//TODO: renenable -/* #ifdef IS_INSTALLER_APP #import "AppDelegate.h" #endif -*/ + //global // ->only report an fatal exception once @@ -98,9 +96,8 @@ void exceptionHandler(NSException *exception) // ->agent should exit errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES]; - //TODO: renable //display error msg - //[((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo]; + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo]; //need to sleep, otherwise returning from this function will cause OS to kill agent // ->instead, we want error popup to be displayed (which will exit agent when closed) @@ -178,9 +175,8 @@ void signalHandler(int signal, siginfo_t *info, void *context) // ->agent should exit errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES]; - //TODO: renable //display error msg - //[((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo]; + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo]; //end app-specific code #endif diff --git a/Shared/Logging.m b/Shared/Logging.m index fb79fbb..d48badb 100644 --- a/Shared/Logging.m +++ b/Shared/Logging.m @@ -42,8 +42,5 @@ void logMsg(int level, NSString* msg) //log to syslog syslog(level, "%s: %s\n", [logPrefix UTF8String], [msg UTF8String]); - //TODO: remove - NSLog(@"%s: %s", [logPrefix UTF8String], [msg UTF8String]); - return; } diff --git a/Shared/Utilities.h b/Shared/Utilities.h index 3c73874..4f17046 100644 --- a/Shared/Utilities.h +++ b/Shared/Utilities.h @@ -14,8 +14,6 @@ /* FUNCTIONS */ -//TODO: cleanup/remove un-needed - //get app's version // ->extracted from Info.plist NSString* getAppVersion(); @@ -36,13 +34,6 @@ SInt32 getVersion(OSType selector); // parse it back up to find app's bundle NSBundle* findAppBundle(NSString* binaryPath); -//given a directory and a filter predicate -// ->return all matches -NSArray* directoryContents(NSString* directory, NSString* predicate); - -//hash (sha1/md5) a file -NSDictionary* hashFile(NSString* filePath); - //get app's version // ->extracted from Info.plist NSString* getAppVersion(); @@ -54,15 +45,15 @@ NSString* getLatestVersion(); // -1, YES or NO NSInteger isNewVersion(NSMutableString* versionString); -//exec a process and grab it's output -NSData* execTask(NSString* binaryPath, NSArray* arguments); - //get process's path NSString* getProcessPath(pid_t pid); +//given a pid +// ->get the name of the process +NSString* getProcessName(pid_t pid); + //wait until a window is non nil // ->then make it modal void makeModal(NSWindowController* windowController); - #endif diff --git a/Shared/Utilities.m b/Shared/Utilities.m index cc7d138..4edb730 100644 --- a/Shared/Utilities.m +++ b/Shared/Utilities.m @@ -254,7 +254,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments) //launch [task launch]; } - @catch(NSException *exception) + @catch(NSException* exception) { //bail goto bail; @@ -412,6 +412,53 @@ bail: return taskPath; } +//given a pid +// ->get the name of the process +NSString* getProcessName(pid_t pid) +{ + //task path + NSString* processName = nil; + + //process path + NSString* processPath = nil; + + //app's bundle + NSBundle* appBundle = nil; + + //get process path + processPath = getProcessPath(pid); + if( (nil == processPath) || + (0 == processPath.length) ) + { + //default to 'unknown' + processName = @""; + + //bail + goto bail; + } + + //try find an app bundle + appBundle = findAppBundle(processPath); + if(nil != appBundle) + { + //grab name from app's bundle + processName = [appBundle infoDictionary][@"CFBundleName"]; + } + + //still nil? + // ->just grab from path + if(nil == processName) + { + //from path + processName = [processPath lastPathComponent]; + } + +//bail +bail: + + return processName; +} + //determine if there is a new version // -1, YES or NO NSInteger isNewVersion(NSMutableString* versionString)