high sierra compatibility
 improved nstask execution
 alleviated need for 'sudo'
 refactored code to find camera/mic process
 pushed various XPC intensive stuff into background
This commit is contained in:
Patrick Wardle 2017-09-24 20:45:02 -10:00
parent 027d0840ed
commit d7b4648a90
33 changed files with 822 additions and 409 deletions

View File

@ -172,7 +172,7 @@
7D24C85D1D2CDEA7009932EE /* Project object */ = { 7D24C85D1D2CDEA7009932EE /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0800; LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Objective-See"; ORGANIZATIONNAME = "Objective-See";
TargetAttributes = { TargetAttributes = {
7D24C8641D2CDEA7009932EE = { 7D24C8641D2CDEA7009932EE = {
@ -229,7 +229,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "#for normal builds\n# ->just copy in app\n#cp -R -f $BUILT_PRODUCTS_DIR/OverSight.app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/\n\n#for achieving\n# ->first build OverSight in release mode (don't archive it)\n# then archive (this) installer app\ncp -R -f ~/objective-see/OverSight/DerivedData/OverSight/Build/Products/Release/OverSight.app ~/objective-see/OverSight/DerivedData/OverSight/Build/Intermediates/ArchiveIntermediates/Installer/BuildProductsPath/Release/OverSight_Installer.app/Contents/Resources/\n"; shellScript = "#for normal builds\n# ->just copy in app\n#cp -R -f $BUILT_PRODUCTS_DIR/OverSight.app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/\n\n#for achieving\n# ->first build OverSight in release mode (don't archive it)\n# then archive (this) installer app\n#cp -R -f ~/objective-see/OverSight/DerivedData/OverSight/Build/Products/Release/OverSight.app ~/objective-see/OverSight/DerivedData/OverSight/Build/Intermediates/ArchiveIntermediates/Installer/BuildProductsPath/Release/OverSight_Installer.app/Contents/Resources/\n";
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -273,14 +273,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -319,14 +325,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

View File

@ -72,6 +72,9 @@ bail:
//alloc/init //alloc/init
configureWindowController = [[ConfigureWindowController alloc] initWithWindowNibName:@"ConfigureWindowController"]; configureWindowController = [[ConfigureWindowController alloc] initWithWindowNibName:@"ConfigureWindowController"];
//indicated title bar is tranparent (too)
self.configureWindowController.window.titlebarAppearsTransparent = YES;
//display it //display it
// ->call this first to so that outlets are connected // ->call this first to so that outlets are connected
[self.configureWindowController display]; [self.configureWindowController display];

View File

@ -117,7 +117,6 @@
//no errors //no errors
wasConfigured = YES; wasConfigured = YES;
//bail
bail: bail:
return wasConfigured; return wasConfigured;
@ -154,8 +153,8 @@ bail:
//path to login item //path to login item
NSString* loginItem = nil; NSString* loginItem = nil;
//logged in user //logged in user info
NSString* user = nil; NSMutableDictionary* userInfo = nil;
//white list //white list
NSString* whiteList = nil; NSString* whiteList = nil;
@ -184,7 +183,7 @@ bail:
//remove xattrs //remove xattrs
// ->otherwise app translocation causes issues // ->otherwise app translocation causes issues
execTask(XATTR, @[@"-cr", appPathDest], YES); execTask(XATTR, @[@"-cr", appPathDest], NO);
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -194,9 +193,9 @@ bail:
//init path to login item //init path to login item
loginItem = [appPathDest stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"]; loginItem = [appPathDest stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
//get user //get user info
user = loggedinUser(); userInfo = loggedinUser();
if(nil == user) if(nil == userInfo[@"user"])
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to determine logged-in user"); logMsg(LOG_ERR, @"failed to determine logged-in user");
@ -206,7 +205,7 @@ bail:
} }
//create app support directory //create app support directory
if(YES != [self createAppSupport:user]) if(YES != [self createAppSupport:userInfo[@"user"]])
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to create app support directory for current user"); logMsg(LOG_ERR, @"failed to create app support directory for current user");
@ -221,7 +220,7 @@ bail:
#endif #endif
//init path to whitelist //init path to whitelist
whiteList = [[NSString pathWithComponents:@[@"/Users/", user, APP_SUPPORT_DIRECTORY]] stringByAppendingPathComponent:FILE_WHITELIST]; whiteList = [[NSString pathWithComponents:@[@"/Users/", userInfo[@"user"], APP_SUPPORT_DIRECTORY]] stringByAppendingPathComponent:FILE_WHITELIST];
//if whitelist exists //if whitelist exists
// ->make sure it's owned by root // ->make sure it's owned by root
@ -232,8 +231,7 @@ bail:
} }
//call into login item to install itself //call into login item to install itself
// ->runs as logged in user, so can access user's login items, etc execTask(loginItem, @[[NSString stringWithUTF8String:CMD_INSTALL]], NO);
execTask(SUDO, @[@"-u", user, loginItem, [NSString stringWithUTF8String:CMD_INSTALL]], YES);
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -266,7 +264,6 @@ bail:
//no error //no error
wasInstalled = YES; wasInstalled = YES;
//bail
bail: bail:
return wasInstalled; return wasInstalled;
@ -278,35 +275,20 @@ bail:
{ {
//flag //flag
BOOL bStarted = NO; BOOL bStarted = NO;
//logged in user
NSString* user = nil;
//path to login item //path to login item
NSString* loginItem = nil; NSString* loginItem = nil;
//get user
user = loggedinUser();
if(nil == user)
{
//err msg
logMsg(LOG_ERR, @"failed to determine logged-in user");
//bail
goto bail;
}
//init path //init path
loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"]; loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
//start it! //start it!
// ->don't wait, as it won't exit // ->don't wait, as it won't exit
execTask(SUDO, @[@"-u", user, loginItem], NO); execTask(loginItem, nil, NO);
//happy //happy
bStarted = YES; bStarted = YES;
//bail
bail: bail:
return bStarted; return bStarted;
@ -350,8 +332,8 @@ bail:
//error //error
NSError* error = nil; NSError* error = nil;
//logged in user //logged in user info
NSString* user = nil; NSMutableDictionary* userInfo = nil;
//uninstall command //uninstall command
// ->changed between v1.0 and 1.1+ // ->changed between v1.0 and 1.1+
@ -385,8 +367,8 @@ bail:
#endif #endif
//get user //get user
user = loggedinUser(); userInfo = loggedinUser();
if(nil == user) if(nil == userInfo[@"user"])
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to determine logged-in user"); logMsg(LOG_ERR, @"failed to determine logged-in user");
@ -401,8 +383,7 @@ bail:
#endif #endif
//call into login item to uninstall itself //call into login item to uninstall itself
// ->runs as logged in user, so can access user's login items, etc execTask(loginItem, @[uninstallCmd], YES);
execTask(SUDO, @[@"-u", user, loginItem, uninstallCmd], YES);
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -436,13 +417,13 @@ bail:
#endif #endif
//delete app's app support folder //delete app's app support folder
if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[self appSupportPath:user]]) if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[self appSupportPath:userInfo[@"user"]]])
{ {
//delete //delete
if(YES != [self removeAppSupport:user]) if(YES != [self removeAppSupport:userInfo[@"user"]])
{ {
//err msg //err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", [self appSupportPath:user]]); logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", [self appSupportPath:userInfo[@"user"]]]);
//set flag //set flag
bAnyErrors = YES; bAnyErrors = YES;
@ -455,7 +436,7 @@ bail:
else else
{ {
//dbg msg //dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed app support directory %@", [self appSupportPath:user]]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed app support directory %@", [self appSupportPath:userInfo[@"user"]]]);
} }
#endif #endif
@ -469,7 +450,6 @@ bail:
wasUninstalled = YES; wasUninstalled = YES;
} }
//bail
bail: bail:
return wasUninstalled; return wasUninstalled;
@ -531,7 +511,6 @@ bail:
//happy //happy
createdDirectory = YES; createdDirectory = YES;
//bail
bail: bail:
return createdDirectory; return createdDirectory;
@ -580,12 +559,9 @@ bail:
//happy //happy
removedDirectory = YES; removedDirectory = YES;
//bail
bail: bail:
return removedDirectory; return removedDirectory;
} }
@end @end

View File

@ -81,6 +81,9 @@
// ->center, make front, set bg to white, etc // ->center, make front, set bg to white, etc
-(void)display -(void)display
{ {
//indicated title bar is tranparent (too)
self.window.titlebarAppearsTransparent = YES;
//center window //center window
[[self window] center]; [[self window] center];

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

View File

@ -21,9 +21,9 @@
BOOL spawnAsRoot(const char* path2Self); BOOL spawnAsRoot(const char* path2Self);
//install //install
BOOL cmdlineInstall(); BOOL cmdlineInstall(void);
//uninstall //uninstall
BOOL cmdlineUninstall(); BOOL cmdlineUninstall(void);
#endif /* main_h */ #endif /* main_h */

View File

@ -17,100 +17,101 @@ int main(int argc, const char * argv[])
//return var //return var
int retVar = -1; int retVar = -1;
@autoreleasepool //pool
@autoreleasepool {
//handle '-install' / '-uninstall'
// ->this performs non-UI logic for easier automated deployment
if( (argc >= 2) &&
( (0 == strcmp(argv[1], CMD_INSTALL)) || (0 == strcmp(argv[1], CMD_UNINSTALL)) ) )
{ {
//handle '-install' / '-uninstall' //first check rooot
// ->this performs non-UI logic for easier automated deployment
if( (argc >= 2) &&
( (0 == strcmp(argv[1], CMD_INSTALL)) || (0 == strcmp(argv[1], CMD_UNINSTALL)) ) )
{
//first check rooot
if(0 != geteuid())
{
//err msg
printf("\nERROR: '%s' option, requires root\n\n", argv[1]);
//bail
goto bail;
}
//handle install
if(0 == strcmp(argv[1], CMD_INSTALL))
{
//install
if(YES != cmdlineInstall())
{
//err msg
printf("\nERROR: install failed\n\n");
//bail
goto bail;
}
//dbg msg
printf("OVERSIGHT: install ok!\n");
//happy
retVar = 0;
}
//handle uninstall
else if(0 == strcmp(argv[1], CMD_UNINSTALL))
{
//uninstall
if(YES != cmdlineUninstall())
{
//err msg
printf("\nERROR: install failed\n\n");
}
//dbg msg
printf("OVERSIGHT: uninstall ok!\n");
//happy
retVar = 0;
}
//bail
goto bail;
}//args
//check for r00t
// ->then spawn self via auth exec
if(0 != geteuid()) if(0 != geteuid())
{ {
//dbg msg //err msg
#ifdef DEBUG printf("\nERROR: '%s' option, requires root\n\n", argv[1]);
logMsg(LOG_DEBUG, @"non-root installer instance");
#endif
//spawn as root //bail
if(YES != spawnAsRoot(argv[0])) goto bail;
}
//handle install
if(0 == strcmp(argv[1], CMD_INSTALL))
{
//install
if(YES != cmdlineInstall())
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to spawn self as r00t"); printf("\nERROR: install failed\n\n");
//bail //bail
goto bail; goto bail;
} }
//dbg msg
printf("OVERSIGHT: install ok!\n");
//happy //happy
retVar = 0; retVar = 0;
} }
//otherwise //handle uninstall
// ->just kick off app, as we're root now else if(0 == strcmp(argv[1], CMD_UNINSTALL))
else
{ {
//dbg msg //uninstall
#ifdef DEBUG if(YES != cmdlineUninstall())
logMsg(LOG_DEBUG, @"root installer instance"); {
#endif //err msg
printf("\nERROR: install failed\n\n");
}
//app away //dbg msg
retVar = NSApplicationMain(argc, (const char **)argv); printf("OVERSIGHT: uninstall ok!\n");
//happy
retVar = 0;
} }
//bail
goto bail;
}//args
//check for r00t
// ->then spawn self via auth exec
if(0 != geteuid())
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"non-root installer instance");
#endif
//spawn as root
if(YES != spawnAsRoot(argv[0]))
{
//err msg
logMsg(LOG_ERR, @"failed to spawn self as r00t");
//bail
goto bail;
}
//happy
retVar = 0;
}
//otherwise
// ->just kick off app, as we're root now
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"root installer instance");
#endif
//app away
retVar = NSApplicationMain(argc, (const char **)argv);
}
}//pool }//pool

View File

@ -239,7 +239,7 @@ bail:
{ {
//signal sema //signal sema
dispatch_semaphore_signal(waitSema); dispatch_semaphore_signal(waitSema);
}]; }];
//wait until XPC is done //wait until XPC is done
@ -414,9 +414,7 @@ bail:
{ {
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
} }
return bRet; return bRet;
@ -474,7 +472,7 @@ bail:
} }
//helper function //helper function
// ->determines if video went active/inactive then invokes notification generator method // determines if video went active/inactive then invokes notification generator method
-(void)handleVideoNotification:(CMIOObjectID)deviceID addresses:(const CMIOObjectPropertyAddress[]) addresses -(void)handleVideoNotification:(CMIOObjectID)deviceID addresses:(const CMIOObjectPropertyAddress[]) addresses
{ {
//event dictionary //event dictionary
@ -517,9 +515,14 @@ bail:
[devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}]; [devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}];
} }
//send msg to status menu //update status menu
// ->update menu to show (all) devices & their status // run on main thread, since its a UI update
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices]; dispatch_async(dispatch_get_main_queue(), ^{
//update status menu
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
});
//add timestamp //add timestamp
event[EVENT_TIMESTAMP] = [NSDate date]; event[EVENT_TIMESTAMP] = [NSDate date];
@ -579,9 +582,6 @@ bail:
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]);
@ -594,7 +594,14 @@ bail:
event[EVENT_PROCESS_ID] = processID; event[EVENT_PROCESS_ID] = processID;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
//if no consumer process was found //if no consumer process was found
@ -605,7 +612,13 @@ bail:
event[EVENT_PROCESS_ID] = @0; event[EVENT_PROCESS_ID] = @0;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
//signal sema //signal sema
@ -625,11 +638,15 @@ bail:
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
//poll for new video procs //poll for new video procs
@ -694,8 +711,27 @@ bail:
// ->invoked when video changes & just calls helper function // ->invoked when video changes & just calls helper function
CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[]) CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[])
{ {
//invoke helper function //on main thread?
[self handleVideoNotification:deviceID addresses:addresses]; // handle on background thread
if(YES == [NSThread isMainThread])
{
//invoke in background
// XPC stuff might be slow!
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//handle notification
[self handleVideoNotification:deviceID addresses:addresses];
});
}
//on background thread
// just can invoke as it
else
{
//handle notification
[self handleVideoNotification:deviceID addresses:addresses];
}
}; };
//register (add) property block listener //register (add) property block listener
@ -764,6 +800,9 @@ bail:
//event dictionary //event dictionary
NSMutableDictionary* event = nil; NSMutableDictionary* event = nil;
//devices
NSMutableArray* devices = nil;
//xpc connection //xpc connection
__block NSXPCConnection* xpcConnection = nil; __block NSXPCConnection* xpcConnection = nil;
@ -772,6 +811,9 @@ bail:
//init dictionary //init dictionary
event = [NSMutableDictionary dictionary]; event = [NSMutableDictionary dictionary];
//init array for devices
devices = [NSMutableArray array];
//sync //sync
@synchronized (self) @synchronized (self)
@ -781,10 +823,29 @@ bail:
// ->updates 'audioActive' iVar // ->updates 'audioActive' iVar
[self setAudioDevStatus:deviceID]; [self setAudioDevStatus:deviceID];
//send msg to status menu //add camera
// ->update menu to show (all) devices & their status if(nil != self.camera)
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)},@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}]]; {
//add
[devices addObject:@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}];
}
//add mic
if(nil != self.mic)
{
//add
[devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}];
}
//update status menu
// run on main thread, since its a UI update
dispatch_async(dispatch_get_main_queue(), ^{
//update status menu
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
});
//add timestamp //add timestamp
event[EVENT_TIMESTAMP] = [NSDate date]; event[EVENT_TIMESTAMP] = [NSDate date];
@ -804,28 +865,28 @@ bail:
//set remote object interface //set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)]; xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume //resume
[xpcConnection resume]; [xpcConnection resume];
//init wait semaphore //init wait semaphore
waitSema = dispatch_semaphore_create(0); waitSema = dispatch_semaphore_create(0);
//tell XPC about audio status //tell XPC about audio status
// ->for example, when audio is active, will stop baselining // ->for example, when audio is active, will stop baselining
[[xpcConnection remoteObjectProxy] updateAudioStatus:self.audioActive reply:^{ [[xpcConnection remoteObjectProxy] updateAudioStatus:self.audioActive reply:^{
//signal sema //signal sema
dispatch_semaphore_signal(waitSema); dispatch_semaphore_signal(waitSema);
}]; }];
//wait until XPC is done //wait until XPC is done
// ->XPC reply block will signal semaphore // ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//if video just started //if audio just started
// ->ask for video procs from XPC // ->ask for audio procs from XPC
if(YES == self.audioActive) if(YES == self.audioActive)
{ {
//dbg msg //dbg msg
@ -843,9 +904,6 @@ bail:
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]);
@ -858,7 +916,13 @@ bail:
event[EVENT_PROCESS_ID] = processID; event[EVENT_PROCESS_ID] = processID;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
//if no consumer process was found //if no consumer process was found
@ -869,7 +933,13 @@ bail:
event[EVENT_PROCESS_ID] = @0; event[EVENT_PROCESS_ID] = @0;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
//signal sema //signal sema
@ -889,11 +959,14 @@ bail:
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
//generate notification //generate notification
[self generateNotification:event]; // do on main thread since its a UI event
dispatch_async(dispatch_get_main_queue(), ^{
//generate notification
[self generateNotification:event];
});
} }
}//sync }//sync
@ -930,7 +1003,27 @@ bail:
// ->invoked when audio changes & just calls helper function // ->invoked when audio changes & just calls helper function
AudioObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses) AudioObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses)
{ {
[self handleAudioNotification:deviceID]; //on main thread?
// handle on background thread
if(YES == [NSThread isMainThread])
{
//invoke in background
// XPC stuff might be slow!
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//handle notification
[self handleAudioNotification:deviceID];
});
}
//on background thread
// just can invoke as it
else
{
//handle notification
[self handleAudioNotification:deviceID];
}
}; };
//add property listener for audio changes //add property listener for audio changes
@ -957,6 +1050,9 @@ bail:
// ->handles extra logic like ignore whitelisted apps, disable alerts (if user has turned that off), etc // ->handles extra logic like ignore whitelisted apps, disable alerts (if user has turned that off), etc
-(void)generateNotification:(NSMutableDictionary*)event -(void)generateNotification:(NSMutableDictionary*)event
{ {
//pool
@autoreleasepool {
//notification //notification
NSUserNotification* notification = nil; NSUserNotification* notification = nil;
@ -1291,6 +1387,8 @@ bail:
} }
} }
}//pool
//bail //bail
bail: bail:
@ -1307,6 +1405,9 @@ bail:
// ->handle rule creation, blocking/killing proc, etc // ->handle rule creation, blocking/killing proc, etc
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification -(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{ {
//pool
@autoreleasepool {
//xpc connection //xpc connection
__block NSXPCConnection* xpcConnection = nil; __block NSXPCConnection* xpcConnection = nil;
@ -1524,11 +1625,11 @@ bail:
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
}]; }];
}//user clicked 'block' }//user clicked 'block'
}//pool
//bail //bail
bail: bail:
@ -1607,9 +1708,13 @@ bail:
} }
//monitor for new procs (video only at the moment) //monitor for new procs (video only at the moment)
// ->runs until video is no longer in use (set elsewhere) // runs until video is no longer in use (set elsewhere)
-(void)monitor4Procs -(void)monitor4Procs
{ {
//pool
@autoreleasepool {
//xpc connection //xpc connection
NSXPCConnection* xpcConnection = nil; NSXPCConnection* xpcConnection = nil;
@ -1621,22 +1726,12 @@ bail:
logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs"); logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs");
#endif #endif
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//set classes
// ->arrays/numbers ok to vend
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
forSelector: @selector(getVideoProcs:reply:) argumentIndex: 0 ofReply: YES];
//resume
[xpcConnection resume];
//poll while video is active //poll while video is active
while(YES == self.videoActive) while(YES == self.videoActive)
{ {
//pool
@autoreleasepool {
//init wait semaphore //init wait semaphore
waitSema = dispatch_semaphore_create(0); waitSema = dispatch_semaphore_create(0);
@ -1644,6 +1739,20 @@ bail:
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs"); logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs");
#endif #endif
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//set classes
// ->arrays/numbers ok to vend
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
forSelector: @selector(getVideoProcs:reply:) argumentIndex: 0 ofReply: YES];
//resume
[xpcConnection resume];
//invoke XPC service to get (new) video procs //invoke XPC service to get (new) video procs
// ->will generate user notifications for any new processes // ->will generate user notifications for any new processes
@ -1652,7 +1761,7 @@ bail:
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"[MONITOR THREAD] found %lu new video procs: %@", (unsigned long)videoProcesses.count, videoProcesses]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"[MONITOR THREAD] found %lu new video procs: %@", (unsigned long)videoProcesses.count, videoProcesses]);
#endif #endif
//generate a notification for each process //generate a notification for each process
// ->double check video is still active though... // ->double check video is still active though...
@ -1672,26 +1781,27 @@ bail:
//signal sema //signal sema
dispatch_semaphore_signal(waitSema); dispatch_semaphore_signal(waitSema);
//invalidate
[xpcConnection invalidate];
}]; }];
//wait until XPC is done //wait until XPC is done
// ->XPC reply block will signal semaphore // ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
}//pool
//nap //nap
[NSThread sleepForTimeInterval:5.0f]; [NSThread sleepForTimeInterval:5.0f];
}//run until video (camera) is off }//run until video (camera) is off
//bail //pool
}
bail: bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off"); logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off");

View File

@ -10,6 +10,7 @@
#import "Logging.h" #import "Logging.h"
#import "Utilities.h" #import "Utilities.h"
#import "AppDelegate.h" #import "AppDelegate.h"
#import "XPCProtocol.h"
@interface AppDelegate () @interface AppDelegate ()
@ -29,16 +30,30 @@
//preferences //preferences
NSDictionary* preferences = nil; NSDictionary* preferences = nil;
//logged in user info
NSMutableDictionary* userInfo = nil;
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, @"starting login item app logic"); logMsg(LOG_DEBUG, @"starting login item app logic");
#endif #endif
//get user
userInfo = loggedinUser();
if(nil == userInfo[@"user"])
{
//err msg
logMsg(LOG_ERR, @"failed to determine logged-in user");
//bail
goto bail;
}
//drop group privs //drop group privs
setgid(getgid()); setgid([userInfo[@"gid"] intValue]);
//drop user privs //drop user privs
setuid(getuid()); setuid([userInfo[@"uid"] intValue]);
//load preferences //load preferences
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]]; preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
@ -99,10 +114,12 @@
//dbg msg //dbg msg
// ->and to file // ->and to file
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging intialized"); logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging intialized (login item)");
} }
//spawn 'heartbeat' thread to XPC to keep it open
[NSThread detachNewThreadSelector:@selector(heartBeat) toTarget:self withObject:nil];
//create/init av event monitor //create/init av event monitor
avMonitor = [[AVMonitor alloc] init]; avMonitor = [[AVMonitor alloc] init];
@ -194,6 +211,60 @@ bail:
return; return;
} }
//ping XPC service to keep it alive
-(void)heartBeat
{
//pool
@autoreleasepool {
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
//wait semaphore
dispatch_semaphore_t waitSema = nil;
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume
[xpcConnection resume];
//forever
while(YES)
{
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
#ifdef DEBUG
//dbg msg
logMsg(LOG_DEBUG, @"sending XPC heart beat request");
#endif
//XPC service to begin baselining mach messages
// ->wait, since want this to compelete before doing other things!
[[xpcConnection remoteObjectProxy] heartBeat:^(BOOL reply)
{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//nap
[NSThread sleepForTimeInterval:3.0f];
}
}//pool
return;
}
//going bye-bye //going bye-bye
// ->close logging // ->close logging
-(void)applicationWillTerminate:(NSNotification *)notification -(void)applicationWillTerminate:(NSNotification *)notification
@ -202,7 +273,7 @@ bail:
logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight ending"); logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight ending");
//log msg //log msg
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized"); logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (login item)");
//stop logz //stop logz
deinitLogging(); deinitLogging();

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View File

@ -31,7 +31,6 @@
//version label/string //version label/string
@property (weak) IBOutlet NSTextField *windowText; @property (weak) IBOutlet NSTextField *windowText;
/* METHODS */ /* METHODS */
//save stuff into iVars //save stuff into iVars

View File

@ -137,9 +137,6 @@
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
}]; }];
} }

View File

@ -66,6 +66,10 @@
//create/update status item menu //create/update status item menu
-(void)updateStatusItemMenu:(NSArray*)devices -(void)updateStatusItemMenu:(NSArray*)devices
{ {
//pool
@autoreleasepool
{
//menu //menu
NSMenu* menu = nil; NSMenu* menu = nil;
@ -194,6 +198,8 @@
//tie menu to status item //tie menu to status item
self.statusItem.menu = menu; self.statusItem.menu = menu;
}//pool
return; return;
} }

View File

@ -18,14 +18,44 @@ int main(int argc, const char * argv[])
//return var //return var
int iReturn = 0; int iReturn = 0;
//logged in user info
NSMutableDictionary* userInfo = nil;
//pool
@autoreleasepool
{
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@)", [[NSProcessInfo processInfo] arguments], NSUserName()]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@/%@)", [[NSProcessInfo processInfo] arguments], NSUserName(), loggedinUser()]);
#endif #endif
//check for uninstall/install flags, and process to remove from whitelist //check for uninstall/install flags, and process to remove from whitelist
if(2 == argc) if(2 == argc)
{ {
//drops privs when installing/uninstalling
// do here, only for these as they then bail
if( (0 == strcmp(argv[1], CMD_INSTALL)) ||
(0 == strcmp(argv[1], CMD_UNINSTALL)) )
{
//get user
userInfo = loggedinUser();
if(nil == userInfo[@"user"])
{
//err msg
logMsg(LOG_ERR, @"failed to determine logged-in user");
//bail
goto bail;
}
//drop group privs
setgid([userInfo[@"gid"] intValue]);
//drop user privs
setuid([userInfo[@"uid"] intValue]);
}
//install //install
if(0 == strcmp(argv[1], CMD_INSTALL)) if(0 == strcmp(argv[1], CMD_INSTALL))
{ {
@ -34,9 +64,6 @@ int main(int argc, const char * argv[])
logMsg(LOG_DEBUG, @"running install logic"); logMsg(LOG_DEBUG, @"running install logic");
#endif #endif
//drop user privs
setuid(getuid());
//install //install
if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_INSTALL_FLAG)) if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_INSTALL_FLAG))
{ {
@ -74,9 +101,6 @@ int main(int argc, const char * argv[])
logMsg(LOG_DEBUG, @"running uninstall logic"); logMsg(LOG_DEBUG, @"running uninstall logic");
#endif #endif
//drop user privs
setuid(getuid());
//uninstall //uninstall
if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_UNINSTALL_FLAG)) if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_UNINSTALL_FLAG))
{ {
@ -130,7 +154,8 @@ int main(int argc, const char * argv[])
//launch app normally //launch app normally
iReturn = NSApplicationMain(argc, argv); iReturn = NSApplicationMain(argc, argv);
//bail }//pool
bail: bail:
return iReturn; return iReturn;
@ -173,9 +198,6 @@ void unWhiteList(NSString* process, NSNumber* device)
//close connection //close connection
[xpcConnection invalidate]; [xpcConnection invalidate];
//nil out
xpcConnection = nil;
}]; }];

View File

@ -244,7 +244,7 @@ bail:
else else
{ {
//log msg //log msg
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging initialized"); logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging initialized (main app)");
} }
} }
//when logging is disabled //when logging is disabled
@ -252,7 +252,7 @@ bail:
else else
{ {
//log msg //log msg
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized"); logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (main app)");
//close //close
deinitLogging(); deinitLogging();
@ -539,7 +539,6 @@ bail:
goto bail; goto bail;
} }
//bail //bail
bail: bail:

View File

@ -15,11 +15,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key> <key>LSUIElement</key>

View File

@ -21,7 +21,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="611" height="270"/> <rect key="contentRect" x="196" y="240" width="611" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/> <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<value key="minSize" type="size" width="500" height="100"/> <value key="minSize" type="size" width="500" height="100"/>
<value key="maxSize" type="size" width="1500" height="500"/> <value key="maxSize" type="size" width="1500" height="500"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO"> <view key="contentView" wantsLayer="YES" id="se5-gp-TjO">

View File

@ -413,7 +413,7 @@
8B57559319DA3E9500799E6B /* Project object */ = { 8B57559319DA3E9500799E6B /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0820; LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Cory Bohon"; ORGANIZATIONNAME = "Cory Bohon";
TargetAttributes = { TargetAttributes = {
7DC9C8111D641A350017D143 = { 7DC9C8111D641A350017D143 = {
@ -620,14 +620,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -667,14 +673,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

View File

@ -33,6 +33,12 @@
/* PROPERTIES */ /* PROPERTIES */
//camera assistant pid
@property pid_t cameraAssistantProcess;
//core audio pid
@property pid_t coreAudioProcess;
//flag indicating video is active //flag indicating video is active
@property BOOL videoActive; @property BOOL videoActive;
@ -49,8 +55,6 @@
// ->IOService:/AppleACPIPlatformExpert/IOPMrootDomain/RootDomainUserClient // ->IOService:/AppleACPIPlatformExpert/IOPMrootDomain/RootDomainUserClient
@property(nonatomic, retain)NSMutableDictionary* userClients; @property(nonatomic, retain)NSMutableDictionary* userClients;
/* METHODS */ /* METHODS */
//singleton interface //singleton interface

View File

@ -23,8 +23,11 @@ static NSArray* ignoredProcs = nil;
@synthesize audioActive; @synthesize audioActive;
@synthesize userClients; @synthesize userClients;
@synthesize videoActive; @synthesize videoActive;
@synthesize coreAudioProcess;
@synthesize machSendersAudio; @synthesize machSendersAudio;
@synthesize machSendersVideo; @synthesize machSendersVideo;
@synthesize cameraAssistantProcess;
//init //init
-(instancetype)init -(instancetype)init
@ -78,13 +81,28 @@ static NSArray* ignoredProcs = nil;
// ->logic only exec'd while camera/mic is not in use, so these are all just baselined procs // ->logic only exec'd while camera/mic is not in use, so these are all just baselined procs
-(void)start -(void)start
{ {
//camera assistant //flag
pid_t cameraAssistant = 0; BOOL nap = NO;
//baseline forever //baseline forever
// ->though logic will skip if video or mic is active (respectively) // ->though logic will skip if video or mic is active (respectively)
while(YES) while(YES)
{ {
//le sleep?
if(nap == YES)
{
//nap
[NSThread sleepForTimeInterval:30];
}
//set flag
// from now on, want to wait a bit
nap = YES;
//pool
@autoreleasepool
{
//sync baselining //sync baselining
@synchronized(self) @synchronized(self)
{ {
@ -97,31 +115,36 @@ static NSArray* ignoredProcs = nil;
#endif #endif
//find camera assistant //find camera assistant
// ->first look for 'VDCAssistant' // only do this once, or again, if it died
cameraAssistant = findProcess(VDC_ASSISTANT); if( (0 == self.cameraAssistantProcess) ||
if(0 == cameraAssistant) (YES != isProcessAlive(self.cameraAssistantProcess)) )
{ {
//look for 'AppleCameraAssistant' //find camera assistant
cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); // ->first look for 'VDCAssistant'
self.cameraAssistantProcess = findProcess(VDC_ASSISTANT);
if(0 == self.cameraAssistantProcess)
{
//look for 'AppleCameraAssistant'
self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT);
}
} }
//sanity check //baseline
if(0 == cameraAssistant) if(0 != self.cameraAssistantProcess)
{ {
//nap for a minute //dbg msg
[NSThread sleepForTimeInterval:60]; #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"camera assistent process: %d", self.coreAudioProcess]);
#endif
//next //enumerate procs that have send mach messages
continue; self.machSendersVideo = [self enumMachSenders:self.cameraAssistantProcess];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersVideo.count, self.machSendersVideo]);
#endif
} }
//enumerate procs that have send mach messages
self.machSendersVideo = [self enumMachSenders:cameraAssistant];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersVideo.count, self.machSendersVideo]);
#endif
} }
//only baseline if audio isn't active //only baseline if audio isn't active
@ -132,29 +155,47 @@ static NSArray* ignoredProcs = nil;
logMsg(LOG_DEBUG, @"baselining mach senders for audio..."); logMsg(LOG_DEBUG, @"baselining mach senders for audio...");
#endif #endif
//enumerate procs that have send mach messages //find core audio
self.machSendersAudio = [self enumMachSenders:findProcess(CORE_AUDIO)]; // only do this once, or again, if it died
if( (0 == self.coreAudioProcess) ||
(YES != isProcessAlive(self.coreAudioProcess)) )
{
//find core audio
self.coreAudioProcess = findProcess(CORE_AUDIO);
}
//dbg msg //baseline
#ifdef DEBUG if(0 != self.coreAudioProcess)
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersAudio.count, self.machSendersVideo]); {
//dbg msg
//dbg msg #ifdef DEBUG
logMsg(LOG_DEBUG, @"baselining i/o registry entries for audio..."); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"camera core audio process: %d", self.coreAudioProcess]);
#endif #endif
//enumerate procs that have i/o registry entries //enumerate procs that have send mach messages
self.userClients = [self enumDomainUserClients]; self.machSendersAudio = [self enumMachSenders:self.coreAudioProcess];
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined i/or registry senders: %@", (unsigned long)self.userClients.count, self.userClients]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersAudio.count, self.machSendersVideo]);
#endif
//dbg msg
logMsg(LOG_DEBUG, @"baselining i/o registry entries for audio...");
#endif
//enumerate procs that have i/o registry entries
self.userClients = [self enumDomainUserClients];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined i/or registry senders: %@", (unsigned long)self.userClients.count, self.userClients]);
#endif
}
} }
}
}//sync
//nap for a minute }//pool
[NSThread sleepForTimeInterval:60];
} }
return; return;
@ -166,18 +207,19 @@ static NSArray* ignoredProcs = nil;
//current procs //current procs
NSMutableArray* videoProcs = nil; NSMutableArray* videoProcs = nil;
//pool
@autoreleasepool
{
//mach senders //mach senders
NSMutableDictionary* currentSenders = nil; NSMutableDictionary* currentSenders = nil;
//candidate video procs //candidate video procs
// ->those that have new mach message // ->those that have new mach message
NSMutableArray* candidateVideoProcs = nil; NSMutableArray* candidateVideoProcs = nil;
//pid of camera assistant process //foreground app
pid_t cameraAssistant = 0; pid_t activeApp = 0;
//'frontmost' application
pid_t activeApp = -1;
//alloc //alloc
candidateVideoProcs = [NSMutableArray array]; candidateVideoProcs = [NSMutableArray array];
@ -186,17 +228,24 @@ static NSArray* ignoredProcs = nil;
// ->prevent baselining thread from doing anything // ->prevent baselining thread from doing anything
@synchronized(self) @synchronized(self)
{ {
//first look for 'VDCAssistant' //find camera assistant
cameraAssistant = findProcess(VDC_ASSISTANT); // only do this once, or again, if it died
if(0 == cameraAssistant) if( (0 == self.cameraAssistantProcess) ||
(YES != isProcessAlive(self.cameraAssistantProcess)) )
{ {
//look for 'AppleCameraAssistant' //find camera assistant
cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); // ->first look for 'VDCAssistant'
self.cameraAssistantProcess = findProcess(VDC_ASSISTANT);
if(0 == self.cameraAssistantProcess)
{
//look for 'AppleCameraAssistant'
self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT);
}
} }
//sanity check //sanity check
if(0 == cameraAssistant) if(0 == self.cameraAssistantProcess)
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process"); logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process");
@ -207,7 +256,7 @@ static NSArray* ignoredProcs = nil;
//get procs that currrently have sent Mach msg to *Assistant //get procs that currrently have sent Mach msg to *Assistant
// ->returns dictionary of process id, and number of mach messages // ->returns dictionary of process id, and number of mach messages
currentSenders = [self enumMachSenders:cameraAssistant]; currentSenders = [self enumMachSenders:self.cameraAssistantProcess];
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -219,7 +268,7 @@ static NSArray* ignoredProcs = nil;
{ {
//add any candidate procs //add any candidate procs
// ->those that have new mach message // ->those that have new mach message
if( [currentSenders[processID] intValue] > [self.machSendersVideo[processID] intValue]) if([currentSenders[processID] intValue] > [self.machSendersVideo[processID] intValue])
{ {
//ignore client/requestor //ignore client/requestor
if(clientPID == processID.intValue) if(clientPID == processID.intValue)
@ -270,8 +319,9 @@ static NSArray* ignoredProcs = nil;
videoProcs = [self sampleCandidates:candidateVideoProcs]; videoProcs = [self sampleCandidates:candidateVideoProcs];
}//sync }//sync
}//pool
//bail
bail: bail:
return videoProcs; return videoProcs;
@ -283,6 +333,10 @@ bail:
//current procs //current procs
NSMutableArray* audioProcs = nil; NSMutableArray* audioProcs = nil;
//pool
@autoreleasepool
{
//current mach senders //current mach senders
NSMutableDictionary* currentSenders = nil; NSMutableDictionary* currentSenders = nil;
@ -302,9 +356,6 @@ bail:
//itersection set //itersection set
NSMutableSet* intersection = nil; NSMutableSet* intersection = nil;
//pid of coreaudio process
pid_t coreAudio = 0;
//'frontmost' application //'frontmost' application
pid_t activeApp = -1; pid_t activeApp = -1;
@ -314,16 +365,22 @@ bail:
//alloc array //alloc array
newUserClients = [NSMutableArray array]; newUserClients = [NSMutableArray array];
//alloc array
candidateAudioProcs = [NSMutableArray array];
//sync this logic //sync this logic
// ->prevent baselining thread from doing anything // ->prevent baselining thread from doing anything
@synchronized(self) @synchronized(self)
{ {
//find coreaudio //find coreaudio
coreAudio = findProcess(CORE_AUDIO); //find core audio
if(0 == coreAudio) // only do this once, or again, if it died
if( (0 == self.coreAudioProcess) ||
(YES != isProcessAlive(self.coreAudioProcess)) )
{
//find core audio
self.coreAudioProcess = findProcess(CORE_AUDIO);
}
//sanity check
if(0 == self.coreAudioProcess)
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to find coreaudio process"); logMsg(LOG_ERR, @"failed to find coreaudio process");
@ -334,7 +391,7 @@ bail:
//get procs that currrently have sent Mach msg to core audio //get procs that currrently have sent Mach msg to core audio
// ->returns dictionary of process id, and number of mach messages // ->returns dictionary of process id, and number of mach messages
currentSenders = [self enumMachSenders:coreAudio]; currentSenders = [self enumMachSenders:self.coreAudioProcess];
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -509,8 +566,9 @@ bail:
audioProcs = [self sampleCandidates:candidateAudioProcs]; audioProcs = [self sampleCandidates:candidateAudioProcs];
}//sync }//sync
}//pool
//bail
bail: bail:
return audioProcs; return audioProcs;
@ -523,6 +581,10 @@ bail:
//senders //senders
NSMutableDictionary* senders = nil; NSMutableDictionary* senders = nil;
//pool
@autoreleasepool
{
//results from 'lsmp' cmd //results from 'lsmp' cmd
NSString* results = nil; NSString* results = nil;
@ -610,8 +672,9 @@ bail:
//add/inc to dictionary //add/inc to dictionary
senders[processID] = @([senders[processID] unsignedIntegerValue] + 1); senders[processID] = @([senders[processID] unsignedIntegerValue] + 1);
} }
}//pool
//bail
bail: bail:
return senders; return senders;
@ -621,6 +684,13 @@ bail:
// ->returns dictionary of process id, and number of user client entries // ->returns dictionary of process id, and number of user client entries
-(NSMutableDictionary*)enumDomainUserClients -(NSMutableDictionary*)enumDomainUserClients
{ {
//array of RootDomainUserClients
NSMutableDictionary* clients = nil;
//pool
@autoreleasepool
{
//matching service //matching service
io_service_t matchingService = 0; io_service_t matchingService = 0;
@ -630,9 +700,6 @@ bail:
//kids //kids
io_registry_entry_t child = 0; io_registry_entry_t child = 0;
//array of RootDomainUserClients
NSMutableDictionary* clients = nil;
//client creator //client creator
CFTypeRef creator = 0; CFTypeRef creator = 0;
@ -670,6 +737,9 @@ bail:
//always release child //always release child
IOObjectRelease(child); IOObjectRelease(child);
//unset
child = 0;
//if couldn't get a creator //if couldn't get a creator
// ->might just not be of RootDomainUserClient, so skip // ->might just not be of RootDomainUserClient, so skip
if(0 == creator) if(0 == creator)
@ -696,9 +766,11 @@ bail:
//release //release
CFRelease(creator); CFRelease(creator);
//unset
creator = 0;
} }
//bail
bail: bail:
//release iterator //release iterator
@ -706,6 +778,9 @@ bail:
{ {
//release //release
IOObjectRelease(iterator); IOObjectRelease(iterator);
//unset
iterator = 0;
} }
//release obj //release obj
@ -713,10 +788,14 @@ bail:
{ {
//release //release
IOObjectRelease(matchingService); IOObjectRelease(matchingService);
//unset
matchingService = 0;
} }
}//pool
return clients; return clients;
} }
//invoke 'sample' to confirm candidates are using CMIO/video/av inputs //invoke 'sample' to confirm candidates are using CMIO/video/av inputs
@ -726,6 +805,10 @@ bail:
//av procs //av procs
NSMutableArray* avProcs = nil; NSMutableArray* avProcs = nil;
//pool
@autoreleasepool
{
//results from 'sample' cmd //results from 'sample' cmd
NSString* results = nil; NSString* results = nil;
@ -807,6 +890,8 @@ bail:
[avProcs addObject:processID]; [avProcs addObject:processID];
} }
}//pool
return avProcs; return avProcs;
} }
@ -814,6 +899,10 @@ bail:
// ->this looks for that file and deletes it // ->this looks for that file and deletes it
-(void)deleteSampleFile:(NSString*)processPath -(void)deleteSampleFile:(NSString*)processPath
{ {
//pool
@autoreleasepool
{
//error //error
NSError* error = nil; NSError* error = nil;
@ -864,6 +953,8 @@ bail:
} }
}//all files }//all files
}//pool
//bail //bail
bail: bail:
@ -876,9 +967,10 @@ bail:
// ->extra logic is executed to 'refresh' iVars when video is disabled // ->extra logic is executed to 'refresh' iVars when video is disabled
-(void)updateVideoStatus:(BOOL)isEnabled -(void)updateVideoStatus:(BOOL)isEnabled
{ {
//camera assistant //pool
pid_t cameraAssistant = 0; @autoreleasepool
{
//sync //sync
@synchronized(self) @synchronized(self)
{ {
@ -889,16 +981,23 @@ bail:
// ->re-enumerate mach senders // ->re-enumerate mach senders
if(YES != isEnabled) if(YES != isEnabled)
{ {
//first, look for 'VDCAssistant' //find camera assistant
cameraAssistant = findProcess(VDC_ASSISTANT); // only do this once, or again, if it died
if(0 == cameraAssistant) if( (0 == self.cameraAssistantProcess) ||
(YES != isProcessAlive(self.cameraAssistantProcess)) )
{ {
//look for 'AppleCameraAssistant' //find camera assistant
cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); // ->first look for 'VDCAssistant'
self.cameraAssistantProcess = findProcess(VDC_ASSISTANT);
if(0 == self.cameraAssistantProcess)
{
//look for 'AppleCameraAssistant'
self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT);
}
} }
//sanity check //sanity check
if(0 == cameraAssistant) if(0 == self.cameraAssistantProcess)
{ {
//err msg //err msg
logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process"); logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process");
@ -908,11 +1007,13 @@ bail:
} }
//enumerate mach senders //enumerate mach senders
self.machSendersVideo = [self enumMachSenders:cameraAssistant]; self.machSendersVideo = [self enumMachSenders:self.cameraAssistantProcess];
} }
}//sync }//sync
}//pool
//bail //bail
bail: bail:
@ -923,6 +1024,10 @@ bail:
// ->extra logic is executed to 'refresh' iVars when audio is disabled // ->extra logic is executed to 'refresh' iVars when audio is disabled
-(void)updateAudioStatus:(BOOL)isEnabled -(void)updateAudioStatus:(BOOL)isEnabled
{ {
//pool
@autoreleasepool
{
//sync //sync
@synchronized(self) @synchronized(self)
{ {
@ -933,14 +1038,30 @@ bail:
// ->re-enumerate mach senders & i/o registry user clients // ->re-enumerate mach senders & i/o registry user clients
if(YES != isEnabled) if(YES != isEnabled)
{ {
//enumerate mach senders //find coreaudio
self.machSendersAudio = [self enumMachSenders:findProcess(CORE_AUDIO)]; //find core audio
// only do this once, or again, if it died
if( (0 == self.coreAudioProcess) ||
(YES != isProcessAlive(self.coreAudioProcess)) )
{
//find core audio
self.coreAudioProcess = findProcess(CORE_AUDIO);
}
//enumerate i/o registry user clients //enumerate
self.userClients = [self enumDomainUserClients]; if(0 != self.coreAudioProcess)
{
//enumerate mach senders
self.machSendersAudio = [self enumMachSenders:self.coreAudioProcess];
//enumerate i/o registry user clients
self.userClients = [self enumDomainUserClients];
}
} }
} }
}//pool
return; return;
} }

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.1.2</string> <string>1.2.0</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2017 Objective-See. All rights reserved.</string> <string>Copyright (c) 2017 Objective-See. All rights reserved.</string>
<key>XPCService</key> <key>XPCService</key>

View File

@ -12,7 +12,6 @@
#import "Enumerator.h" #import "Enumerator.h"
#import "OverSightXPC.h" #import "OverSightXPC.h"
@implementation OverSightXPC @implementation OverSightXPC
@synthesize machSenders; @synthesize machSenders;
@ -23,7 +22,7 @@
-(void)initialize:(void (^)(void))reply -(void)initialize:(void (^)(void))reply
{ {
//start enumerating //start enumerating
// ->will forever baseline current mach msg procs // will forever baseline current mach msg procs
[NSThread detachNewThreadSelector:@selector(start) toTarget:[Enumerator sharedManager] withObject:nil]; [NSThread detachNewThreadSelector:@selector(start) toTarget:[Enumerator sharedManager] withObject:nil];
//reply //reply
@ -32,6 +31,23 @@
return; return;
} }
//heartbeat
// need as otherwise kernel might kill XPC
-(void)heartBeat:(void (^)(BOOL))reply
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"heartbeat request");
#endif
//nap
[NSThread sleepForTimeInterval:3.0f];
reply(YES);
return;
}
//call into emumerate to get (new) video proc //call into emumerate to get (new) video proc
-(void)getVideoProcs:(BOOL)polling reply:(void (^)(NSMutableArray *))reply -(void)getVideoProcs:(BOOL)polling reply:(void (^)(NSMutableArray *))reply
{ {
@ -69,7 +85,7 @@
{ {
//set status //set status
[[Enumerator sharedManager] updateAudioStatus:status]; [[Enumerator sharedManager] updateAudioStatus:status];
//reply //reply
reply(); reply();
@ -118,7 +134,6 @@
//happy //happy
wasAdded = YES; wasAdded = YES;
//bail
bail: bail:
//reply //reply
@ -192,7 +207,6 @@ bail:
} }
} }
//bail
bail: bail:
//reply //reply
@ -220,7 +234,6 @@ bail:
//happy //happy
wasKilled = YES; wasKilled = YES;
//bail
bail: bail:
//reply //reply
@ -236,7 +249,4 @@ bail:
exit(0); exit(0);
} }
@end @end

View File

@ -51,6 +51,4 @@ OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
@end @end
#endif /* main_h */ #endif /* main_h */

View File

@ -7,7 +7,7 @@
// //
#import "main.h" #import "main.h"
#import "Logging.h"
/* GLOBALS */ /* GLOBALS */
@ -26,8 +26,8 @@ pid_t clientPID = 0;
@implementation ServiceDelegate @implementation ServiceDelegate
//automatically invoked //automatically invoked
//->allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection // allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection
// note: we only allow binaries signed by Objective-See to talk to this! // note: we only allow binaries signed by Objective-See to connect & talk to this!
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection -(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{ {
//flag //flag
@ -39,6 +39,11 @@ pid_t clientPID = 0;
//signing req string //signing req string
NSString *requirementString = nil; NSString *requirementString = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"new client connection");
#endif
//init signing req string //init signing req string
requirementString = [NSString stringWithFormat:@"anchor trusted and certificate leaf [subject.CN] = \"%@\"", SIGNING_AUTH]; requirementString = [NSString stringWithFormat:@"anchor trusted and certificate leaf [subject.CN] = \"%@\"", SIGNING_AUTH];
@ -68,11 +73,16 @@ pid_t clientPID = 0;
//resume //resume
[newConnection resume]; [newConnection resume];
//grab client/requestor's pid
clientPID = audit_token_to_pid(((ExtendedNSXPCConnection*)newConnection).auditToken);
//happy //happy
shouldAccept = YES; shouldAccept = YES;
//grab client/requestor's pid //dbg msg
clientPID = audit_token_to_pid(((ExtendedNSXPCConnection*)newConnection).auditToken); #ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"accepted new client connection (pid: %d)", clientPID]);
#endif
//bail //bail
bail: bail:
@ -110,7 +120,7 @@ int main(int argc, const char *argv[])
installExceptionHandlers(); installExceptionHandlers();
//create the delegate for the service. //create the delegate for the service.
delegate = [ServiceDelegate new]; delegate = [[ServiceDelegate alloc] init];
//set up the one NSXPCListener for this service //set up the one NSXPCListener for this service
// ->handles incoming connections // ->handles incoming connections
@ -126,7 +136,6 @@ int main(int argc, const char *argv[])
//happy //happy
status = 0; status = 0;
//bail
bail: bail:
return status; return status;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -17,7 +17,7 @@
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5"> <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<rect key="contentRect" x="196" y="240" width="486" height="301"/> <rect key="contentRect" x="196" y="240" width="486" height="301"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1058"/> <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<view key="contentView" id="se5-gp-TjO"> <view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="486" height="301"/> <rect key="frame" x="0.0" y="0.0" width="486" height="301"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
@ -98,7 +98,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd">
<rect key="frame" x="158" y="220" width="182" height="19"/> <rect key="frame" x="158" y="220" width="182" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Version:" id="bBK-v0-ypq"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Version:" id="bBK-v0-ypq">

View File

@ -28,7 +28,7 @@
#define PRODUCT_URL @"https://objective-see.com/products/oversight.html" #define PRODUCT_URL @"https://objective-see.com/products/oversight.html"
//product version url //product version url
#define PRODUCT_VERSION_URL @"https://objective-see.com/products.json" #define PRODUCT_VERSIONS_URL @"https://objective-see.com/products.json"
//patreon url //patreon url
#define PATREON_URL @"https://www.patreon.com/objective_see" #define PATREON_URL @"https://www.patreon.com/objective_see"
@ -129,9 +129,6 @@
//path to xattr //path to xattr
#define XATTR @"/usr/bin/xattr" #define XATTR @"/usr/bin/xattr"
//path to sudo
#define SUDO @"/usr/bin/sudo"
//path to facetime //path to facetime
#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime" #define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime"
@ -187,5 +184,4 @@
//log to file flag //log to file flag
#define LOG_TO_FILE 0x10 #define LOG_TO_FILE 0x10
#endif #endif

View File

@ -9,7 +9,7 @@
#import <signal.h> #import <signal.h>
//install exception/signal handlers //install exception/signal handlers
void installExceptionHandlers(); void installExceptionHandlers(void);
//exception handler for Obj-C exceptions //exception handler for Obj-C exceptions
void exceptionHandler(NSException *exception); void exceptionHandler(NSException *exception);
@ -19,6 +19,3 @@ void signalHandler(int signal, siginfo_t *info, void *context);
//display error window //display error window
void displayErrorWindow(NSDictionary* errorInfo); void displayErrorWindow(NSDictionary* errorInfo);

View File

@ -13,15 +13,13 @@
void logMsg(int level, NSString* msg); void logMsg(int level, NSString* msg);
//prep/open log file //prep/open log file
BOOL initLogging(); BOOL initLogging(void);
//get path to log file //get path to log file
NSString* logFilePath(); NSString* logFilePath(void);
//de-init logging //de-init logging
void deinitLogging(); void deinitLogging(void);
//log to file //log to file
void log2File(NSString* msg); void log2File(NSString* msg);

View File

@ -31,7 +31,7 @@ void logMsg(int level, NSString* msg)
level &= ~LOG_TO_FILE; level &= ~LOG_TO_FILE;
//alloc/init //alloc/init
// ->always start w/ 'OVERSIGHT' + pid // ->always start w/ 'LULU' + pid
logPrefix = [NSMutableString stringWithFormat:@"OVERSIGHT(%d)", getpid()]; logPrefix = [NSMutableString stringWithFormat:@"OVERSIGHT(%d)", getpid()];
//if its error, add error to prefix //if its error, add error to prefix
@ -44,8 +44,8 @@ void logMsg(int level, NSString* msg)
//debug mode logic //debug mode logic
#ifdef DEBUG #ifdef DEBUG
//in debug mode. promote debug msgs to LOG_NOTICE //in debug mode promote debug msgs to LOG_NOTICE
// ->OS X/macOS only shows LOG_NOTICE and above in the system log // OSX/macOS only shows LOG_NOTICE and above
if(LOG_DEBUG == level) if(LOG_DEBUG == level)
{ {
//promote //promote
@ -54,29 +54,23 @@ void logMsg(int level, NSString* msg)
#endif #endif
//log to syslog //log to syslog if a level was specified
syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]); // as code doesn't use LOG_EMERG (0), this check is ok
if(0 != level)
{
//log to syslog
syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]);
}
//when a message is to be logged to file //when a message is to be logged to file
// ->log it to file and syslog, when logging is enabled // ->log it, when logging is enabled
if(YES == shouldLog) if(YES == shouldLog)
{ {
//but only when logging is enabled //but only when logging is enable
if(nil != logFileHandle) if(nil != logFileHandle)
{ {
//log //log
log2File(msg); log2File(msg);
//promote to notice for syslog
if(LOG_DEBUG == level)
{
//promote
level = LOG_NOTICE;
}
//also syslog
// ->should result in 1 log msg, (in release), as all LOG_TO_FILE are at LOG_DEBUG level
syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]);
} }
} }

View File

@ -15,11 +15,11 @@
/* FUNCTIONS */ /* FUNCTIONS */
//get OS version //get OS version
NSDictionary* getOSVersion(); NSDictionary* getOSVersion(void);
//get app's version //get app's version
// ->extracted from Info.plist // ->extracted from Info.plist
NSString* getAppVersion(); NSString* getAppVersion(void);
//set dir's|file's group/owner //set dir's|file's group/owner
BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL recursive); BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL recursive);
@ -39,10 +39,10 @@ NSBundle* findAppBundle(NSString* binaryPath);
//get app's version //get app's version
// ->extracted from Info.plist // ->extracted from Info.plist
NSString* getAppVersion(); NSString* getAppVersion(void);
//query interwebz to get latest version //query interwebz to get latest version
NSString* getLatestVersion(); NSString* getLatestVersion(void);
//determine if there is a new version //determine if there is a new version
// -1, YES or NO // -1, YES or NO
@ -72,7 +72,8 @@ void makeModal(NSWindowController* windowController);
BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag); BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag);
//get logged in user //get logged in user
NSString* loggedinUser(); // name, uid, and gid
NSMutableDictionary* loggedinUser(void);
//find a process by name //find a process by name
pid_t findProcess(NSString* processName); pid_t findProcess(NSString* processName);
@ -81,6 +82,9 @@ pid_t findProcess(NSString* processName);
void makeTextViewHyperlink(NSTextField* textField, NSURL* url); void makeTextViewHyperlink(NSTextField* textField, NSURL* url);
//get active application //get active application
pid_t frontmostApplication(); pid_t frontmostApplication(void);
//check if process is alive
BOOL isProcessAlive(pid_t processID);
#endif #endif

View File

@ -274,17 +274,27 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
//output //output
NSData *output = nil; NSData *output = nil;
//dispatch group
dispatch_group_t dispatchGroup = 0;
//init task //init task
task = [NSTask new]; task = [[NSTask alloc] init];
//init pipe //init pipe
outPipe = [NSPipe pipe]; outPipe = [NSPipe pipe];
//create dispatch group
dispatchGroup = dispatch_group_create();
//set task's path //set task's path
task.launchPath = binaryPath; task.launchPath = binaryPath;
//set task's args //set task's args
task.arguments = arguments; if(nil != arguments)
{
//add
task.arguments = arguments;
}
//set task's output to pipe //set task's output to pipe
// ->but only if we're waiting for exit // ->but only if we're waiting for exit
@ -299,6 +309,16 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"@exec'ing %@ (args: %@)", binaryPath, arguments]); logMsg(LOG_DEBUG, [NSString stringWithFormat:@"@exec'ing %@ (args: %@)", binaryPath, arguments]);
#endif #endif
//enter dispatch
dispatch_group_enter(dispatchGroup);
//set task's termination to leave dispatch group
task.terminationHandler = ^(NSTask *task){
//leave
dispatch_group_leave(dispatchGroup);
};
//wrap task launch //wrap task launch
@try @try
{ {
@ -333,7 +353,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
#endif #endif
//wait till exit //wait till exit
[task waitUntilExit]; dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
//dbg msg //dbg msg
#ifdef DEBUG #ifdef DEBUG
@ -345,6 +365,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
//bail //bail
bail: bail:
return output; return output;
} }
@ -756,59 +777,54 @@ bail:
//query interwebz to get latest version //query interwebz to get latest version
NSString* getLatestVersion() NSString* getLatestVersion()
{ {
//version data //product version(s) data
__block NSData* versionData = nil; NSData* productsVersionData = nil;
//version dictionary //version dictionary
NSDictionary* versionDictionary = nil; NSDictionary* productsVersionDictionary = nil;
//latest version //latest version
NSString* latestVersion = nil; NSString* latestVersion = nil;
//run in background if main thread //get version from remote URL
if(YES == [NSThread isMainThread]) productsVersionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSIONS_URL]];
{ if(nil == productsVersionData)
//run in background
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//get version data
versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]];
});
}
//no need to background
else
{
//get version from remote URL
versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]];
}
//sanity check
if(nil == versionData)
{ {
//bail //bail
goto bail; goto bail;
} }
//convert JSON to dictionary //convert JSON to dictionary
versionDictionary = [NSJSONSerialization JSONObjectWithData:versionData options:0 error:nil]; // ->wrap as may throw exception
@try
//sanity check {
if(nil == versionDictionary) //convert
productsVersionDictionary = [NSJSONSerialization JSONObjectWithData:productsVersionData options:0 error:nil];
if(nil == productsVersionDictionary)
{
//bail
goto bail;
}
}
@catch(NSException* exception)
{ {
//bail //bail
goto bail; goto bail;
} }
//extract latest version //extract latest version
latestVersion = versionDictionary[@"latestVersion"]; latestVersion = [[productsVersionDictionary objectForKey:@"OverSight"] objectForKey:@"version"];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"latest version: %@", latestVersion]);
#endif
//bail
bail: bail:
return latestVersion; return latestVersion;
} }
//wait until a window is non nil //wait until a window is non nil
// ->then make it modal // ->then make it modal
void makeModal(NSWindowController* windowController) void makeModal(NSWindowController* windowController)
@ -981,14 +997,27 @@ bail:
} }
//get logged in user //get logged in user
NSString* loggedinUser() // name, uid, and gid
NSMutableDictionary* loggedinUser()
{ {
//user info
NSMutableDictionary* userInfo = nil;
//store //store
SCDynamicStoreRef store = nil; SCDynamicStoreRef store = nil;
//user //user
NSString* user = nil; NSString* user = nil;
//uid
uid_t uid = 0;
//gid
gid_t gid = 0;
//allco dictionary
userInfo = [NSMutableDictionary dictionary];
//create store //create store
store = SCDynamicStoreCreate(NULL, CFSTR("GetConsoleUser"), NULL, NULL); store = SCDynamicStoreCreate(NULL, CFSTR("GetConsoleUser"), NULL, NULL);
if(NULL == store) if(NULL == store)
@ -997,8 +1026,17 @@ NSString* loggedinUser()
goto bail; goto bail;
} }
//get user //get user and uid/gid
user = CFBridgingRelease(SCDynamicStoreCopyConsoleUser(store, NULL, NULL)); user = CFBridgingRelease(SCDynamicStoreCopyConsoleUser(store, &uid, &gid));
//add user
userInfo[@"user"] = user;
//add uid
userInfo[@"uid"] = [NSNumber numberWithUnsignedInt:uid];
//add uid
userInfo[@"gid"] = [NSNumber numberWithUnsignedInt:gid];
//bail //bail
bail: bail:
@ -1010,7 +1048,7 @@ bail:
CFRelease(store); CFRelease(store);
} }
return user; return userInfo;
} }
//find a process by name //find a process by name
@ -1059,7 +1097,7 @@ pid_t findProcess(NSString* processName)
//get name //get name
processPath = getProcessPath(pids[i]); processPath = getProcessPath(pids[i]);
if( (nil == processPath) || if( (nil == processPath) ||
(0 == processPath.length) ) (0 == processPath.length) )
{ {
//skip //skip
continue; continue;
@ -1134,3 +1172,31 @@ pid_t frontmostApplication()
return NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier; return NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier;
} }
//check if process is alive
BOOL isProcessAlive(pid_t processID)
{
//ret var
BOOL bIsAlive = NO;
//signal status
int signalStatus = -1;
//send kill with 0 to determine if alive
// -> see: http://stackoverflow.com/questions/9152979/check-if-process-exists-given-its-pid
signalStatus = kill(processID, 0);
//is alive?
if( (0 == signalStatus) ||
( (0 != signalStatus) && (errno != ESRCH) ) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"agent (%d) is ALIVE", processID]);
#endif
//alive!
bIsAlive = YES;
}
return bIsAlive;
}

View File

@ -14,6 +14,10 @@
//start enumerator //start enumerator
-(void)initialize:(void (^)(void))reply; -(void)initialize:(void (^)(void))reply;
//heartbeat
// need as otherwise kernel might kill XPC
-(void)heartBeat:(void (^)(BOOL))reply;
//get (new) audio procs //get (new) audio procs
-(void)getAudioProcs:(void (^)(NSMutableArray *))reply; -(void)getAudioProcs:(void (^)(NSMutableArray *))reply;

1
contributors.txt Normal file
View File

@ -0,0 +1 @@
patrick wardle