Upgrade to Pro — share decks privately, control downloads, hide ads and more …

XPC What I Did There?

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

XPC What I Did There?

A whirlwind tour of the XPC API wrapped in a story of learning.

Avatar for Mikey Ward

Mikey Ward

June 04, 2014

Other Decks in Programming

Transcript

  1. 3

  2. The best-laid plans of mice. Goal: Mac-native HTTP Proxy like

    Charles.app • Hijack/inspect HTTP connections • Add itself as the global HTTP proxy • Allow scripting of modifications to requests/responses
  3. SystemConfiguration.framework The audience for this document comprises two main groups:

    •Developers of network configuration or dialer software. •Developers of applications that need to request and use network connections. + (Reachability*) reachabilityWithHostName: (NSString*) hostName; { Reachability* retVal = NULL; SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); if(reachability!= NULL) { retVal= [[[self alloc] init] autorelease]; if(retVal!= NULL) { retVal->reachabilityRef = reachability; retVal->localWiFiRef = NO; } } return retVal; }
  4. SystemConfiguration.h //     //  Dear  developer:   //  

      //  Once  you  are  done  trying  to  utilize     //  SystemConfiguration.framework,   //  and  have  realized  what  a  terrible  mistake  that  was,   //  please  increment  the  following  counter  as  a  warning   //  to  the  next  developer:   //     const  NSInteger  totalHoursWasted  =  42;   *Stolen from http://stackoverflow.com/questions/184618/
  5. Using networksetup with NSTask: + (NSString *)outputOfTaskWithPath:(NSString *)path arguments:(NSArray *)args

    { NSPipe *pipe = [[NSPipe alloc] init]; NSPipe *resultPipe = [[NSPipe alloc] init]; NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:path]; [task setStandardInput:pipe]; [task setStandardOutput:resultPipe]; [task setArguments:args]; [task launch]; NSData *result = [[resultPipe fileHandleForReading] readDataToEndOfFile]; NSString *resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; return resultString; }
  6. AuthorizationExecuteWithPrivileges() // Create authorization reference AuthorizationRef authorizationRef; OSStatus status; status

    = AuthorizationCreate( NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); // Run the tool using the authorization reference char *tool = "/sbin/dmesg"; char *args[] = {NULL}; FILE *pipe = NULL; status = AuthorizationExecuteWithPrivileges( authorizationRef, tool, kAuthorizationFlagDefaults, args, &pipe); sauce: http://www.michaelvobrien.com/blog/2009/07/authorizationexecutewithprivileges-a-simple-example/
  7. Daemons and Services Programming Guide Creating XPC Services The XPC

    Services API, part of libSystem, provides a lightweight mechanism for basic interprocess communication integrated with Grand Central Dispatch (GCD) and launchd. The XPC Services API allows you to create lightweight helper tools, called XPC services, that perform work on behalf of your application. There are two main reasons to use XPC services: privilege separation and stability.
  8. Daemons and Services Programming Guide Other mechanisms for dividing an

    application into smaller parts, such as NSTask and posix_spawn(2) OS X Developer Tools Manual Page, do not let you put each part of the application in its own sandbox, so it is not possible to use them to implement privilege separation. An XPC service can inherit the sandbox of the main application, or it can have its own sandbox. Putting the XPC service in its own sandbox allows you to implement privilege separation.
  9. Fetching an image // In the window controller implementation -

    (void)loadURL:(NSURL *)url { NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSImage *image = [[NSImage alloc] initWithData:data]; dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.layer.contents = image; }); }]; [task resume]; }
  10. Fetching an image // In the window controller implementation -

    (void)loadURL:(NSURL *)url { NSString *serviceName = @"com.bignerdranch.net-service"; NSXPCConnection *connection = [[NSXPCConnection alloc] initWithServiceName:serviceName]; connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BNRDownload)]; [connection resume]; id downloader = connection.remoteObjectProxy; [downloader downloadImageAtURL:url withReply:^(BNRDownloadedImage *dl, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.layer.contents = dl.image; }); }]; }
  11. NSSecureCoding / BNRDownloadedImage + (BOOL)supportsSecureCoding { return YES; } -

    (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { self.sourceURL = [coder decodeObjectOfClass:[NSURL class] forKey:@"sourceURL"]; NSData *data = [coder decodeObjectOfClass:[NSData class] forKey:@"imageData"]; self.image = [NSKeyedUnarchiver unarchiveObjectWithData:data]; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.image]; [coder encodeObject:data forKey:@"imageData"]; [coder encodeObject:self.sourceURL forKey:@"sourceURL"]; }
  12. net-service’s main.m int main(int argc, const char *argv[]) { NSXPCListener

    *listener = [NSXPCListener serviceListener]; BNRConnectionHandler *handler = [[BNRConnectionHandler alloc] init]; listener.delegate = handler; [listener resume]; return 0; }
  13. net-service’s BNRConnectionHandler #pragma mark - NSXPCListenerDelegate - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection

    *)newConnection { newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BNRDownload)]; newConnection.exportedObject = [[BNRDownloader alloc] init]; [newConnection resume]; return YES; }
  14. net-service’s BNRDownloader #pragma mark - BNRDownload Protocol - (void)downloadImageAtURL:(NSURL *)url

    withReply:(void (^)(BNRDownloadedImage *, NSError *))replyBlock { NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSImage *image = [[NSImage alloc] initWithData:data]; BNRDownloadedImage *dl = [BNRDownloadedImage new]; dl.image = image; dl.sourceURL = url; dispatch_async(dispatch_get_main_queue(), ^{ if (replyBlock) { replyBlock(dl, error); } }); }]; [task resume]; }
  15. Understanding the Structure and Behavior XPC services are managed by

    launchd, which launches them on demand, restarts them if they crash, and terminates them (by sending SIGKILL) when they are idle. This is transparent to the application using the service, except for the case of a service that crashes while processing a message that requires a response. In that case, the application can see that its XPC connection has become invalid until the service is restarted by launchd. Because an XPC service can be terminated suddenly at any time, it must be designed to hold on to minimal state—ideally, your service should be completely stateless, although this is not always possible. By default, XPC services are run in the most restricted environment possible—sandboxed with minimal filesystem access, network access, and so on. Elevating a service’s privileges to root is not supported. Further, an XPC service is private, and is available only to the main application that contains it. Privelege separation
  16. Understanding the Structure and Behavior XPC services are managed by

    launchd, which launches them on demand, restarts them if they crash, and terminates them (by sending SIGKILL) when they are idle. This is transparent to the application using the service, except for the case of a service that crashes while processing a message that requires a response. In that case, the application can see that its XPC connection has become invalid until the service is restarted by launchd. Because an XPC service can be terminated suddenly at any time, it must be designed to hold on to minimal state—ideally, your service should be completely stateless, although this is not always possible. By default, XPC services are run in the most restricted environment possible—sandboxed with minimal filesystem access, network access, and so on. Elevating a service’s privileges to root is not supported. Further, an XPC service is private, and is available only to the main application that contains it. Privelege separation
  17. - (BOOL)blessTheChild { BOOL result = NO; AuthorizationItem authItem =

    { kSMRightBlessPrivilegedHelper, 0, NULL, 0 }; AuthorizationRights authRights = { 1, &authItem }; AuthorizationFlags flags = (kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights); AuthorizationRef authRef = NULL; OSStatus status = AuthorizationCreate( &authRights, kAuthorizationEmptyEnvironment, flags, &authRef); if (status == errAuthorizationSuccess){ result = SMJobBless( kSMDomainSystemLaunchd, “com.bignerdranch.net-service”, authRef, NULL); } return result; } Using SMJobBless() #import <ServiceManagement/ServiceManagement.h>
  18. Fetching an image with an XPC service // In the

    window controller implementation - (void)loadURL:(NSURL *)url { NSString *serviceName = @"com.bignerdranch.net-service"; NSXPCConnection *connection = [[NSXPCConnection alloc] initWithServiceName:serviceName]; connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BNRDownload)]; [connection resume]; id downloader = connection.remoteObjectProxy; [downloader downloadImageAtURL:url withReply:^(BNRDownloadedImage *dl, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.layer.contents = dl.image; }); }]; }
  19. Fetching an image with a mach service // In the

    window controller implementation - (void)loadURL:(NSURL *)url { [self blessTheChild]; NSString *serviceName = @"com.bignerdranch.net-service"; NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:NSXPCConnectionPrivileged]; connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BNRDownload)]; [connection resume]; id downloader = connection.remoteObjectProxy; [downloader downloadImageAtURL:url withReply:^(BNRDownloadedImage *dl, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.layer.contents = dl.image; }); }]; }
  20. XPC What I Did There? Mikey Ward / Big Nerd

    Ranch (or, “How I Learned to Stop Worrying and Love the Docs”) @wookiee / [email protected]