I have a windowed application and windowless helper, sitting inside the app bundle, and working as login item. App can start and stop the helper, everything woks there. The problem is that I need to create some bidirectional communication channel between them. And it should work in both sandboxed and not sandboxed versions, desirably in OS X 10.7+.
I've investigated the topic and find that XPC can provide peer-to-per connection. I've read related Apple docs, as well as few topics below:
Is possible to use Mac OS X XPC like IPC to exchange messages between processes? How?
Communicate with another app using XPC
http://afewguyscoding.com/2012/07/ipc-easy-introducing-xpc-nsxpcconnection/
https://www.objc.io/issues/14-mac/xpc/
But I can't find any description of how should I organize my XCode project. I have two targets: "Main App" and "Helper App". Now I need to add the third one, taking XPC Service, as a template. OK, but what to do next? Where this XPC bundle should be located to be available for both applications? Note, that helper sits in the main app bundle, as it's a login item. So, I need some clear instruction or just a XCode project sample.
Thanks,
Alex
Alright for anyone that has been struggling with this, I was finally able to 100% get communication working between two application processes, using NSXPCConnection
The key to note is that you can only create an NSXPCConnection to three things.
An XPCService. You can connect to an XPCService strictly through
a name
A Mach Service. You can also connect to a Mach Service
strictly through a name
An NSXPCEndpoint. This is what we're
looking for, to communicate between two application processes.
The problem being that we can't directly transfer an NSXPCEndpoint from one application to another.
It involved creating a machservice Launch Agent (See this example for how to do that) that held an NSXPCEndpoint property. One application can connect to the machservice, and set that property to it's own [NSXPCListener anonymousListener].endpoint
Then the other application can connect to the machservice, and ask for that endpoint.
Then using that endpoint, an NSXPCConnection can be created, which successfully established a bridge between the two applications. I have tested sending objects back and forth, and it all works as expected.
Note that if your application is sandboxed, you will have to create an XPCService, as a middle man between your Application and the Machservice
I'm pretty pumped that I got this working-- I'm fairly active in SO, so if anybody is interested in source code, just add a comment and I can go through the effort to post more details
Some hurdles I came across:
You have to launch your machservice, these are the lines:
OSStatus err;
AuthorizationExternalForm extForm;
err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
if (err == errAuthorizationSuccess) {
NSLog(#"SUCCESS AUTHORIZING DAEMON");
}
assert(err == errAuthorizationSuccess);
Boolean success;
CFErrorRef error;
success = SMJobBless(
kSMDomainSystemLaunchd,
CFSTR("DAEMON IDENTIFIER HERE"),
self->_authRef,
&error
);
Also, every time you rebuild your daemon, you have to unload the previous launch agent, with these bash commands:
sudo launchctl unload /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/PrivilegedHelperTools/com.example.apple-samplecode.EBAS.HelperTool
(With your corresponding identifiers, of course)
Related
I am trying hard to follow the example of SimpleTunnel given by Apple.
I try to track how they make the customized call.
However I cannot link the relationship between the connect button action with starting a new tunnel.
I tried to track it with PacketTunnelProvider but without success.
I know they are override classes. I cannot find the point where the whole VPN connection starts.
My goal is to create a SSL VPN tunnel.
After asking Apple and a few trial and error, I can finally trigger the extension part.
Prerequisite: (Network Extension permission)
Add a new target -> Packet Tunnel Provider
Trigger the extension by
NEVPNConnection *conn = [manager connection];
NSError *connError;
[conn startVPNTunnelWithOptions:settingsDict andReturnError:&connError];
Debug with the following steps
(1) Build & run the app
(2) Stop the app
(3) Debug > attach to process by PID or name > Enter "PacketTunnel"
(4) Start the app from your iPhone screen and you can debug for the extension
Hope the small steps I experienced can help the others to start.
However, there are more upcoming questions and I need to check!
The sample application and Packet Tunnel provider runs as a separate process. sample application is called as container app and the packet tunnel provider runs as app extension. These two components uses IPC for communication.
In sample application whenever connect toggle button is enabled startVPNTunnel() API will be called and the OS starts the packet tunnel provider which in turn calls your overrided method startTunnelWithOptions(). So this is where you start your connection to the VPN server.
To answer your question link the connect action to a method that invokes startVPNTunnel() which in turn triggers packet tunnel provider. You cannot directly invoke start packet tunnel provider without the container application.
Same gets applied to stop your VPN tunnel
Hope this answer helps you
if you are asking about the connect / enable buttons inside the SimpleTunnel app, then startVPNTunnel() is the call used in startStopToggled() method of StatusViewController.swift file
if you are asking about how the extension handles vpn connection start (after configuration is done), then OS network system calls startTunnelWithOptions() in PacketTunnelProvider.swift depending on how the tunnel is configured. for eg: of on-demand is enabled for this tunnel, OS will try to setup/start the tunnel whenever there is network activity. if not, OS will try to start tunnel, when you go to Settings|VPN and try to switch ON the config. This is similar to the iOS8 personal vpn connection stuff.
I'm experimenting with the Distributed Shell example in YARN 2.2 and am hoping that someone can clarify what the difference between a managed and and an un-managed application manager is?
For example the following lines appear in the client code
// unmanaged AM
appContext.setUnmanagedAM(true);
but I am unable to find documentation explaining the difference this line makes to the execution behaviour.
Many thanks.
The setUnmanagedAM(true) is used for debugging purposes i.e. it runs an application manager in local mode and does not submit it to a cluster so it is easier to step into code and debug.
You can see it in use in the hadoop-yarn-applications-unmanaged-am-launcher.jar that ships with yarn
Check the respective JIRA tickets: JIRA-420 and JIRA-419 (client side)
Currently, the RM itself manages the AM by allocating a container for it and negotiating the launch on the NodeManager and manages the AM lifecycle. Thereafter, the AM negotiates resources with the RM and launches tasks to do the real work.
It would be a useful improvement to enhance this model by allowing the AM to be launched independently by the client without requiring the RM. These AM's would be launched on a gateway machine that can talk to the cluster. This would open up new use cases such as the following
1) Easy debugging of AM, specially during initial development. Having the AM launched on an arbitrary cluster node makes it hard to looks at logs or attach a debugger to the AM. If it can be launched locally then these tasks would be easier.
2) Running AM's that need special privileges that may not be available on machines managed by the NodeManager
Blog post with more implementation details on unmanaged AM: click-me
Example of how Impala manages its resources with the help of unmanaged applications: Llama
Is there a way to know the return code or process ID of the process which gets executed when the privileged helper tool is installed as a launchdaemon and launched via SMJobSubmit().
I have an application which to execute some tasks in privileged manner uses the SMJobSubmit API as mentioned here.
Now in order to know whether the tasks succeeded or not, I will have to do one of the following.
The best option is to get the return code of the executable that ran.
Another option would be if I could create a pipe between my application and the launchd.
If the above two are not possible, I will have to resort to some hack like writing a file in /tmp location and reading it from my app.
I guess SMJobSubmit internally submits the executable with a launchdaemon dictionary to the launchd which is then responsible for its execution. So is there a way I could query launchd to find out the return code for the executable run with the label "mylabel".
There is no way to do this directly.
SMJobSubmit is a simple wrapper around a complicated task. It also returns synchronously despite launching a task asynchronously. So, while it can give you an error if it fails to submit the job, if it successfully submits a job that fails to run, there is no way to find that out.
So, you will have to explicitly write some code to communicate from your helper to your app, to report that it's up and running.
If you've already built some communication mechanism (signals, files, Unix or TCP sockets, JSON-RPC over HTTP, whatever), just use that.
If you're designing something from scratch, XPC may be the best answer. You can't use XPC to launch your helper (since it's privileged), but you can manually create a connection by registering a Mach service and calling xpc_connection_create_mach_service.
I'm trying to run a process with elevated privileges - specifically OpenVPN, which requires root privileges to add routes to the system.
Looking around for existing examples around leads me to AuthorizationExecuteWithPrivileges, which seems to be now deprecated.
I tried the new SMJobBless method but I have a few questions regarding its viability for this purpose. As I understand it, I can create a separate privileged tool and communicate with it via sockets to ask the tool to perform privileged commands. However, I can't seem to figure out how I can start the OpenVPN process and capture its standard output in real time doing it this way as the main application would not be starting the process itself.
Another option is to use setuid on the OpenVPN executable. Could I possibly use the helper installed by SMJobBless to set the file permissions and setuid on the executable, then run it normally via NSTask?
Edit:
Lastly is there some way to just run one single command with privileges without having to install anything permanently? Although this new method is more secure, it seems very heavy handed.
I managed to go the SMJobBless method by using a helper and communicating it with XPC (the method shown on Nathan de Vries's Blog). Using this helper I set the permissions on the external process to 04555 (setuid, rx). Then the SMJob is removed as it is no longer required. Essentially emulating an "one-off" privileged job.
Following that I was able to use NSTask to start the process and capture its output in my main application.
Additionally I have a check at the start to see if the permissions are set right on the executable, if not the SMJob helper is re-blessed and permissions set.
If anyone has a cleaner solution, feel free to share. Thanks!
I had the same problem as you, needed it for a OpenVPN Manager App for MacOs X. Your solution is far from optimum because you open the openvpn binary for everyone setting setuid root.
This is a security hole and should be avoided, as it is totally unnecessary when you are using smjobbless helper. This helper runs as root and could do everything you want for you and with administrative privileges, so you can launch openvpn via this helper without setting setuid root on openvpn binary.
Apple designed this process as only your App, the Main App, can communicate with this helper as your Main App and your helper are signed with your developer certificates.
Any malicious App can't use this helper.
When you look at Nathans code you see, that he managed it to send messages to this helper and to get answers from this helper.
In his example there is sth like "Hey there Helper App" and the answer is "Hey there Host App".
So to get sth useful out of this you only have to send commands to the helper app, extract these commands on helper side and launch them with elevated privileges as the helper App runs with elevated privileges.
Look at Nathans code, there is sty like (in smjobblessappcontroller.m):
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
const char* request = "openvpn --config OpenvpnConnection.ovpn";
xpc_dictionary_set_string(message, "request", request);
[self appendLog:[NSString stringWithFormat:#"Sending request: %s", request]];
xpc_connection_send_message_with_reply(connection, message, dispatch_get_main_queue(), ^(xpc_object_t event) {
const char* response = xpc_dictionary_get_string(event, "reply");
[self appendLog:[NSString stringWithFormat:#"Received response: %s.", response]];
});
With this you send the openvpn command to your helper App. You only have to extract this command on helper side to launch the process with elevated privileges.
Look at smjobblesshelper.c and do sth like (in __XPC_Peer_Event_Handler else branch):
const char *response = xpc_dictionary_get_string(event, "request");
In string response you have your openvpn command, now simple launch it:
system(response);
Thats all, this goes with elevated privileges. Now you can use this in your App perhaps in an IBAction push button in your main app, to start openvpn connections as you want every time a user clicks this button.
I'm writing a Winsock LSP (Layered Service Provider) DLL that needs to communicate with a windows service.
The communication is done using memory mapped files and events for synchronization. Everything works fine if the application is not running as a service but if it does it cannot find any events or file mappings (I get ERROR_FILE_NOT_FOUND error on OpenEvent).
I suspect this happens because when running as a service, the application runs as SYSTEM user and LSP is loaded by applications that run as local user.
I think that this could be solved by using a proper SECURITY_DESCRIPTOR but I don't know what should it be set to.
Any ideas on how to make this work?
Thanks,
Depending on the OS, it might be a problem of sessions. If you take another look at documentation for CreateEvent, CreateMemoryMapping etc., you will notice GLOBAL\ prefix to object name. This prefix (among with SESSION\x\ prefix) define visibility scope of the object. GLOBAL prefixes are seen across the whole system, while objects without prefix in the name are local to specific (current if the \SESSION prefix is omitted) session. Sessions appeared in Terminal Services for Windows XP, then got themselves into the OS in Windows 2003 Server and later.