28

Even though the API has been open since Mac OS X Leopard, there's surprisingly, and unfortunately, very little documentation on how to correctly use SMJobBless() for creating privileged helper tools. There are a lot of gotchas, even when copying code directly from Apple's sample project. Luckily, I've found my way around this, and have gotten the basis for my helper tool working.

However, it would seem that SMJobBless() only blesses the tool and copies it over, but doesn't run it. I've included code in my helper tool's main() function that should run, but doesn't (since NSLog() inexplicably doesn't work–according to the tiny bit of information I have found–I've tried syslog()ing some "Hello world" type strings, but nothing appears on the system console). There's no indication that the helper tool is launched at all.
The documentation is mostly useless. It simply says that after SMJobBless() is called, the helper tool is 'ready', with no indication of what 'ready' even means.

Furthermore, Apple's sample doesn't include any interprocess communication code, and doesn't explain how one is supposed to interact with the helper tool. Do you use Distributed Objects? Mach ports? Who knows? There's no official word on how to do it.

So, does anyone have any information on how to get this done? I've confirmed that the helper tool is installed, and authentication works, but I simply can't figure out how to launch the helper tool and communicate with it - there's simply such a gap in the documentation that this is a mystery for now. It's very frustrating; I can't be the only one with this problem (but there's little mention of it anywhere), and SMJobBless() obviously works somehow, since it's what Apple uses.

(Please don't mention AuthorizationExecuteWithPrivileges(). I'm not using it: it's deprecated, sure to go away, and is a major security hole. No thanks.)

Itai Ferber
  • 28,308
  • 5
  • 77
  • 83

6 Answers6

26

XPC isn't an option if you're trying to elevate privileges (from https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html):

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.

SMJobBless will install a helper tool and register it with Launchd, as in the SMJobBless example provided by Apple. The trick to getting your helper tool to actually launch is to simply attempt to connect to your helper tool's advertised services.

There was a WWDC2010 example called ssd that demonstrated a simple launchd client/server model via sockets. It's not available from Apple any longer, but I've found a link here: https://lists.apple.com/archives/macnetworkprog/2011/Jul/msg00005.html

I've incorporated the dispatch queue handling in the server code from the ssd example into the helper tool in the SMJobBless example and can confirm that my helper tool is indeed running (as root) when my main app attempts a connection on the appropriate port. See the WWDC2010 video on Launchd to understand the other mechanisms with which you can communicate with your helper tool (other than sockets).

I'm not sure I can legally redistribute the modified sources I have, but it should be fairly straightforward to merge the two projects and get your helper tool running.

Edit: Here is an example project I wrote that uses a distributed object for communication between the app and helper: https://www.dropbox.com/s/5kjl8koyqzvszrl/Elevator.zip

Cœur
  • 37,241
  • 25
  • 195
  • 267
xdissent
  • 949
  • 7
  • 7
  • 1
    Another tip - be sure to include the idle-exit code shown in the `ssd` example, since your helper tool may run forever if you leave it out. I mention it because I get the feeling you are trying to perform a one-off operation as root, similarly to how `AuthorizationExecuteWithPrivileges` worked. That's what led me down this path anyway. Good luck. – xdissent Feb 10 '12 at 19:19
  • Strange that Apple would remove a project so integral to understanding tasks like this. Thanks for the help! Looks like I have quite a bit of investigating to do... – Itai Ferber Feb 10 '12 at 19:34
  • 7
    Actually, I've just discovered that if you really don't need IPC and just want to mimic `AuthorizationExecuteWithPrivileges` using `SMJobBless`, you can add the following keys to your helper tool's launchd.plist: `RunAtLoad`: YES, `LaunchOnlyOnce`: YES, `OnDemand`: NO. Then your helper tool will run immediately upon blessing. This works perfectly using the SMJobBless example code from Apple. – xdissent Feb 10 '12 at 22:52
  • Fantastic! I was using `RunAtLoad` but nothing happened; so I just need to add `LaunchOnlyOnce` and `OnDemand` for it to run immediately. Why is that not in the documentation!? – Itai Ferber Feb 10 '12 at 23:04
  • This `RunAtLoad`/`LaunchOnlyOnce` method sounds great, but how would the main app get any results/errors back from the helper without using IPC? A Distributed Notification? – Jonukas Feb 11 '12 at 02:33
  • @Jonukas It depends on what kinds of errors or results you're expecting to pick up. But yes, distributed notifications would work, as would having the helper tool leave behind a log file (or something similar) for the main app to read in. – Itai Ferber Feb 11 '12 at 15:46
  • I only have one more question. The tool I'm using is an installer, so I want to be able to call it several times with different parameters (something that the default `SMJobBless()` function wouldn't help me with). Is there a way to install a tool with `SMJobBless()` without running it, and then submit it for running with different parameters with `SMJobSubmit()`? – Itai Ferber Feb 11 '12 at 15:48
  • @ItaiFerber no, SMJobBless will *always* overwrite your arguments when installing the launchd plist. Instead, you could use a distributed object that has different methods to handle your different tasks. Check out the example project I just added to my answer. Of course you'll need to change the signing certificates just like with the SMJobBless demo from Apple. – xdissent Feb 11 '12 at 15:54
  • @xdissent Fantastic. You definitely deserve the bounty. Hopefully, this helps Jonukas too. – Itai Ferber Feb 11 '12 at 15:56
  • @Jonukas I would strongly recommend against using distributed notifications. Any process can issue them and therefore hijack your helper tool or app. There are security concerns with distributed objects as well so you should make absolutely sure your helper can determine when it's safe to perform its tasks. For example, don't simply add a distributed object method like `- (void)runCommand:(NSString *)path` or you're in for a world of pain. Instead, something like `-(BOOL)installEtcFiles` would be better, and the helper should be able to determine if the work really needs to be done itself. – xdissent Feb 11 '12 at 16:02
  • @xdissent I dl'd your elevator project, made some changes so it ran for 10.6, added my create file method to a root directory and it works!!! CHAMPION! – Coderama Mar 02 '12 at 12:40
  • 8
    Just a note: "XPC isn't an option" isn't true. You can certainly use XPC to communicate with your privileged tool, just like you would with Mach ports or UNIX domain sockets -- just set up the tool's launchd.plist correctly. What *is* true is that your helper tool is not an "XPC service", which is what the XPC docs mostly focus on. – Kurt Revis Mar 18 '12 at 18:41
  • @Coderama — could you post your example somewhere? I'm trying to do the same (move copy/files into the system library) and I'm a bit lost. – Joe Habadas Sep 11 '12 at 22:56
  • You can use XPC IPC APIs with SMJobBless(). This answer is misleading. See Mark Aufflick's answer below and Kurt Revis' comment above. – ctpenrose Dec 06 '13 at 19:38
  • @Coderama I got the evelator project running and tool is blessed. But it never launches the helpertool ? any ideas ? – i.jameelkhan May 20 '14 at 20:21
  • @xdissent : I think your answer is Understandable more than apple's Documentation. I just downloaded your code and change your bundle identifier to my app's bundle identifier and change certificate. but it always gives error like "Failed to bless helper". Can you tell me exactly where i have to change identifier and certificates. – Malav Soni Apr 04 '15 at 05:20
  • "Failed to bless helper", sure because the helper is not installed, so can't find it. Is there any setting for that? – Mike97 Aug 06 '15 at 05:51
  • Ok, was my mistake (wrong Info.plist name in the Build Setting). What to say, your is the only working example found on the web that can do things with elevate privileges (I'm happy). What about deprecate methods "launch_data_new_string"...you have a replacement? Thank you men! – Mike97 Aug 10 '15 at 15:05
  • The last link is broken. – Muntashir Akon Feb 21 '19 at 10:44
12

In fact @KurtRevis's comment is right, you can use XPC APIs without using XPC services, and it is ideally suited to the job since.

Nathan de Vries has an excellent writeup of using XPC APIs with SMJobBless and has even modified the SMJobBless sample app to use mach XPC to both activate the job and for bidirectional communications:

http://atnan.com/blog/2012/02/29/modern-privileged-helper-tools-using-smjobbless-plus-xpc/

https://github.com/atnan/SMJobBlessXPC

Somewhat related to all this is avoiding unnecessary admin password prompts. See the following email list thread for ideas on how to check if the bundle version and code signature of an already installed helper match (which also allows you to remove a higher versioned helper in the case of a user downgrading):

http://www.cocoabuilder.com/archive/cocoa/309298-question-about-smjobbless.html

If you don't want to wade through the thread, here is a link to the modified SMJobBless sample project provided by Eric Gorr:

http://ericgorr.net/cocoadev/SMJobBless.zip

Also note that the ssd example mentioned in other answers here is still available online from Apple as part of the WWDC 2010 download bundle:

http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645

Mark
  • 1,304
  • 12
  • 22
7

Apple now (2015) has an "EvenBetterAuthorizationSample" that demonstrates installing a privileged helper tool and using the NSXPCConnection API to communicate between the app and the helper tool:

The README is some of the best (only?) documentation of SMJobBless() available.

Kristopher Johnson
  • 81,409
  • 55
  • 245
  • 302
4

I wrote a blog post on this a few months ago, which included a cleaned up version of Apple's SMJobBless sample. Might help...

http://www.bornsleepy.com/bornsleepy/os-x-helper-applications

Sam Deane
  • 1,553
  • 1
  • 13
  • 17
4

I feel your pain and am in the same boat. I'm in charge of the Mac version of an app that needs to perform various system configuration tasks. Of course some of these task need to be done with administrative rights. I started by using the sample code from BetterAuthorizationSample. It was a major pain to implement but it seemed to work. But then ran into cases where it would crash on some systems. I didn't understand everything that the BAS code did and my own lack of coding experience probably contributed to the problems. So I had to remove these privileged functions from my app.

Apple doesn't seem to care about the lack of documentation. See this message from the creator of the ServiceManagement framework. From his comments, I assume that XPC is the "intuitive replacement" he is referring to, but since it is only available on Lion, you'll still have to find another solution for Snow Leopard or earlier clients. It's also not clear to me if XPC can be used for privileged helpers (system level tasks that require admin or root access) or is just intended for privilege separation within your own app to make it more secure.

The BAS documentation is in desperate need of an update, but it also doesn't appear to be a top priority.

Now I'm attempting to rewrite my app from the ground up. Professional Cocoa Application Security by Graham Lee gives some insight on how to do use privileged helpers with SMJobBless, but doesn't go into much detail about on-demand access to launchd jobs.

So here's what I've been able to find:

If you want to launch your privileged helper on demand, you'll have to use an IPC socket. You should add a Sockets entry to your helper's launchd.plist. After you install the app with SMJobBless, the helper will need to "check-in" with launchd (via LAUNCH_KEY_CHECKIN) to get the socket file descriptors.

Sadly, the only mentions of LAUNCH_KEY_CHECKIN seem to be in the SampleD and BAS sample code.

I don't have any experience with sockets, so that's my roadblock at the moment. I'd like to use the highest level API I can, so I'm trying to find out if I can use any Objective-C classes for this (like NSStream).

You might find the launchd developers mailing list helpful. Another XPC option I just found out about is XPCKit. It's worth a look.

HTH

Jonukas
  • 706
  • 5
  • 18
0

Itai have you looked at the SMJobBless sample code from WWDC 2010? It includes a helper tool and app to bless it.

https://developer.apple.com/library/mac/#samplecode/SMJobBless/Listings/ReadMe_txt.html#//apple_ref/doc/uid/DTS40010071-ReadMe_txt-DontLinkElementID_3

Its README file says:

This sample as it stands does not actually run the helper tool. The following samples show how to [sic] a launchd job and set up interprocess communication:

Ken Aspeslagh
  • 11,484
  • 2
  • 36
  • 42
  • Yes. The reason I'm asking the question is because this is the only reference I could find, and specifically _because_ it doesn't show how to launch the helper tool. But thanks anyways. – Itai Ferber Feb 10 '12 at 19:30