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 */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Objective-See";
TargetAttributes = {
7D24C8641D2CDEA7009932EE = {
@ -229,7 +229,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
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 */
@ -273,14 +273,20 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = 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_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -319,14 +325,20 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = 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_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -239,7 +239,7 @@ bail:
{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
@ -414,9 +414,7 @@ bail:
{
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
}
return bRet;
@ -474,7 +472,7 @@ bail:
}
//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
{
//event dictionary
@ -517,9 +515,14 @@ bail:
[devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}];
}
//send msg to status menu
// ->update menu to show (all) devices & their status
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
//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
event[EVENT_TIMESTAMP] = [NSDate date];
@ -579,9 +582,6 @@ bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]);
@ -594,7 +594,14 @@ bail:
event[EVENT_PROCESS_ID] = processID;
//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
@ -605,7 +612,13 @@ bail:
event[EVENT_PROCESS_ID] = @0;
//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
@ -625,11 +638,15 @@ bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//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
@ -694,8 +711,27 @@ bail:
// ->invoked when video changes & just calls helper function
CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[])
{
//invoke helper function
[self handleVideoNotification:deviceID addresses:addresses];
//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 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
@ -764,6 +800,9 @@ bail:
//event dictionary
NSMutableDictionary* event = nil;
//devices
NSMutableArray* devices = nil;
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
@ -772,6 +811,9 @@ bail:
//init dictionary
event = [NSMutableDictionary dictionary];
//init array for devices
devices = [NSMutableArray array];
//sync
@synchronized (self)
@ -781,10 +823,29 @@ bail:
// ->updates 'audioActive' iVar
[self setAudioDevStatus:deviceID];
//send msg to status menu
// ->update menu to show (all) devices & their status
[((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 camera
if(nil != self.camera)
{
//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
event[EVENT_TIMESTAMP] = [NSDate date];
@ -804,28 +865,28 @@ bail:
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume
[xpcConnection resume];
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
//tell XPC about audio status
// ->for example, when audio is active, will stop baselining
[[xpcConnection remoteObjectProxy] updateAudioStatus:self.audioActive 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);
//if video just started
// ->ask for video procs from XPC
//if audio just started
// ->ask for audio procs from XPC
if(YES == self.audioActive)
{
//dbg msg
@ -843,9 +904,6 @@ bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]);
@ -858,7 +916,13 @@ bail:
event[EVENT_PROCESS_ID] = processID;
//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
@ -869,7 +933,13 @@ bail:
event[EVENT_PROCESS_ID] = @0;
//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
@ -889,11 +959,14 @@ bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//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
@ -930,7 +1003,27 @@ bail:
// ->invoked when audio changes & just calls helper function
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
@ -957,6 +1050,9 @@ bail:
// ->handles extra logic like ignore whitelisted apps, disable alerts (if user has turned that off), etc
-(void)generateNotification:(NSMutableDictionary*)event
{
//pool
@autoreleasepool {
//notification
NSUserNotification* notification = nil;
@ -1291,6 +1387,8 @@ bail:
}
}
}//pool
//bail
bail:
@ -1307,6 +1405,9 @@ bail:
// ->handle rule creation, blocking/killing proc, etc
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
//pool
@autoreleasepool {
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
@ -1524,11 +1625,11 @@ bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
}];
}//user clicked 'block'
}//pool
//bail
bail:
@ -1607,9 +1708,13 @@ bail:
}
//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
{
//pool
@autoreleasepool {
//xpc connection
NSXPCConnection* xpcConnection = nil;
@ -1621,22 +1726,12 @@ bail:
logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs");
#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
while(YES == self.videoActive)
{
//pool
@autoreleasepool {
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
@ -1644,6 +1739,20 @@ bail:
#ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs");
#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
// ->will generate user notifications for any new processes
@ -1652,7 +1761,7 @@ bail:
//dbg msg
#ifdef DEBUG
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
// ->double check video is still active though...
@ -1672,26 +1781,27 @@ bail:
//signal sema
dispatch_semaphore_signal(waitSema);
//invalidate
[xpcConnection invalidate];
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
}//pool
//nap
[NSThread sleepForTimeInterval:5.0f];
}//run until video (camera) is off
//bail
//pool
}
bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off");

View File

@ -10,6 +10,7 @@
#import "Logging.h"
#import "Utilities.h"
#import "AppDelegate.h"
#import "XPCProtocol.h"
@interface AppDelegate ()
@ -29,16 +30,30 @@
//preferences
NSDictionary* preferences = nil;
//logged in user info
NSMutableDictionary* userInfo = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"starting login item app logic");
#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
setgid(getgid());
setgid([userInfo[@"gid"] intValue]);
//drop user privs
setuid(getuid());
setuid([userInfo[@"uid"] intValue]);
//load preferences
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
@ -99,10 +114,12 @@
//dbg msg
// ->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
avMonitor = [[AVMonitor alloc] init];
@ -194,6 +211,60 @@ bail:
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
// ->close logging
-(void)applicationWillTerminate:(NSNotification *)notification
@ -202,7 +273,7 @@ bail:
logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight ending");
//log msg
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized");
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (login item)");
//stop logz
deinitLogging();

View File

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

View File

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

View File

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

View File

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

View File

@ -18,14 +18,44 @@ int main(int argc, const char * argv[])
//return var
int iReturn = 0;
//logged in user info
NSMutableDictionary* userInfo = nil;
//pool
@autoreleasepool
{
//dbg msg
#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
//check for uninstall/install flags, and process to remove from whitelist
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
if(0 == strcmp(argv[1], CMD_INSTALL))
{
@ -34,9 +64,6 @@ int main(int argc, const char * argv[])
logMsg(LOG_DEBUG, @"running install logic");
#endif
//drop user privs
setuid(getuid());
//install
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");
#endif
//drop user privs
setuid(getuid());
//uninstall
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
iReturn = NSApplicationMain(argc, argv);
//bail
}//pool
bail:
return iReturn;
@ -173,9 +198,6 @@ void unWhiteList(NSString* process, NSNumber* device)
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
}];

View File

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

View File

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

View File

@ -21,7 +21,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="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="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="maxSize" type="size" width="1500" height="500"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,6 @@
#import "Enumerator.h"
#import "OverSightXPC.h"
@implementation OverSightXPC
@synthesize machSenders;
@ -23,7 +22,7 @@
-(void)initialize:(void (^)(void))reply
{
//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];
//reply
@ -32,6 +31,23 @@
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
-(void)getVideoProcs:(BOOL)polling reply:(void (^)(NSMutableArray *))reply
{
@ -69,7 +85,7 @@
{
//set status
[[Enumerator sharedManager] updateAudioStatus:status];
//reply
reply();
@ -118,7 +134,6 @@
//happy
wasAdded = YES;
//bail
bail:
//reply
@ -192,7 +207,6 @@ bail:
}
}
//bail
bail:
//reply
@ -220,7 +234,6 @@ bail:
//happy
wasKilled = YES;
//bail
bail:
//reply
@ -236,7 +249,4 @@ bail:
exit(0);
}
@end

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<?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>
<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"/>
</dependencies>
<objects>
@ -17,7 +17,7 @@
<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"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="486" height="301"/>
<autoresizingMask key="autoresizingMask"/>
@ -98,7 +98,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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"
//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
#define PATREON_URL @"https://www.patreon.com/objective_see"
@ -129,9 +129,6 @@
//path to xattr
#define XATTR @"/usr/bin/xattr"
//path to sudo
#define SUDO @"/usr/bin/sudo"
//path to facetime
#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime"
@ -187,5 +184,4 @@
//log to file flag
#define LOG_TO_FILE 0x10
#endif

View File

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

View File

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

View File

@ -31,7 +31,7 @@ void logMsg(int level, NSString* msg)
level &= ~LOG_TO_FILE;
//alloc/init
// ->always start w/ 'OVERSIGHT' + pid
// ->always start w/ 'LULU' + pid
logPrefix = [NSMutableString stringWithFormat:@"OVERSIGHT(%d)", getpid()];
//if its error, add error to prefix
@ -44,8 +44,8 @@ void logMsg(int level, NSString* msg)
//debug mode logic
#ifdef DEBUG
//in debug mode. promote debug msgs to LOG_NOTICE
// ->OS X/macOS only shows LOG_NOTICE and above in the system log
//in debug mode promote debug msgs to LOG_NOTICE
// OSX/macOS only shows LOG_NOTICE and above
if(LOG_DEBUG == level)
{
//promote
@ -54,29 +54,23 @@ void logMsg(int level, NSString* msg)
#endif
//log to syslog
syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]);
//log to syslog if a level was specified
// 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
// ->log it to file and syslog, when logging is enabled
// ->log it, when logging is enabled
if(YES == shouldLog)
{
//but only when logging is enabled
//but only when logging is enable
if(nil != logFileHandle)
{
//log
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 */
//get OS version
NSDictionary* getOSVersion();
NSDictionary* getOSVersion(void);
//get app's version
// ->extracted from Info.plist
NSString* getAppVersion();
NSString* getAppVersion(void);
//set dir's|file's group/owner
BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL recursive);
@ -39,10 +39,10 @@ NSBundle* findAppBundle(NSString* binaryPath);
//get app's version
// ->extracted from Info.plist
NSString* getAppVersion();
NSString* getAppVersion(void);
//query interwebz to get latest version
NSString* getLatestVersion();
NSString* getLatestVersion(void);
//determine if there is a new version
// -1, YES or NO
@ -72,7 +72,8 @@ void makeModal(NSWindowController* windowController);
BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag);
//get logged in user
NSString* loggedinUser();
// name, uid, and gid
NSMutableDictionary* loggedinUser(void);
//find a process by name
pid_t findProcess(NSString* processName);
@ -81,6 +82,9 @@ pid_t findProcess(NSString* processName);
void makeTextViewHyperlink(NSTextField* textField, NSURL* url);
//get active application
pid_t frontmostApplication();
pid_t frontmostApplication(void);
//check if process is alive
BOOL isProcessAlive(pid_t processID);
#endif

View File

@ -274,17 +274,27 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
//output
NSData *output = nil;
//dispatch group
dispatch_group_t dispatchGroup = 0;
//init task
task = [NSTask new];
task = [[NSTask alloc] init];
//init pipe
outPipe = [NSPipe pipe];
//create dispatch group
dispatchGroup = dispatch_group_create();
//set task's path
task.launchPath = binaryPath;
//set task's args
task.arguments = arguments;
if(nil != arguments)
{
//add
task.arguments = arguments;
}
//set task's output to pipe
// ->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]);
#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
@try
{
@ -333,7 +353,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
#endif
//wait till exit
[task waitUntilExit];
dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
//dbg msg
#ifdef DEBUG
@ -345,6 +365,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait)
//bail
bail:
return output;
}
@ -756,59 +777,54 @@ bail:
//query interwebz to get latest version
NSString* getLatestVersion()
{
//version data
__block NSData* versionData = nil;
//product version(s) data
NSData* productsVersionData = nil;
//version dictionary
NSDictionary* versionDictionary = nil;
NSDictionary* productsVersionDictionary = nil;
//latest version
NSString* latestVersion = nil;
//run in background if main thread
if(YES == [NSThread isMainThread])
{
//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)
//get version from remote URL
productsVersionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSIONS_URL]];
if(nil == productsVersionData)
{
//bail
goto bail;
}
//convert JSON to dictionary
versionDictionary = [NSJSONSerialization JSONObjectWithData:versionData options:0 error:nil];
//sanity check
if(nil == versionDictionary)
// ->wrap as may throw exception
@try
{
//convert
productsVersionDictionary = [NSJSONSerialization JSONObjectWithData:productsVersionData options:0 error:nil];
if(nil == productsVersionDictionary)
{
//bail
goto bail;
}
}
@catch(NSException* exception)
{
//bail
goto bail;
}
//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:
return latestVersion;
}
//wait until a window is non nil
// ->then make it modal
void makeModal(NSWindowController* windowController)
@ -981,14 +997,27 @@ bail:
}
//get logged in user
NSString* loggedinUser()
// name, uid, and gid
NSMutableDictionary* loggedinUser()
{
//user info
NSMutableDictionary* userInfo = nil;
//store
SCDynamicStoreRef store = nil;
//user
NSString* user = nil;
//uid
uid_t uid = 0;
//gid
gid_t gid = 0;
//allco dictionary
userInfo = [NSMutableDictionary dictionary];
//create store
store = SCDynamicStoreCreate(NULL, CFSTR("GetConsoleUser"), NULL, NULL);
if(NULL == store)
@ -997,8 +1026,17 @@ NSString* loggedinUser()
goto bail;
}
//get user
user = CFBridgingRelease(SCDynamicStoreCopyConsoleUser(store, NULL, NULL));
//get user and uid/gid
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:
@ -1010,7 +1048,7 @@ bail:
CFRelease(store);
}
return user;
return userInfo;
}
//find a process by name
@ -1059,7 +1097,7 @@ pid_t findProcess(NSString* processName)
//get name
processPath = getProcessPath(pids[i]);
if( (nil == processPath) ||
(0 == processPath.length) )
(0 == processPath.length) )
{
//skip
continue;
@ -1134,3 +1172,31 @@ pid_t frontmostApplication()
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
-(void)initialize:(void (^)(void))reply;
//heartbeat
// need as otherwise kernel might kill XPC
-(void)heartBeat:(void (^)(BOOL))reply;
//get (new) audio procs
-(void)getAudioProcs:(void (^)(NSMutableArray *))reply;

1
contributors.txt Normal file
View File

@ -0,0 +1 @@
patrick wardle