Stairways Software Icon

Stairways Software

Excellence In Software For Over Twenty Years – Only with a Mac

SMJobSubmit

Apple deprecated AuthorizationExecuteWithPrivileges but was not very clear about how to replace it - turns out it is easy enough to do using SBJobSubmit.

Nathan de Vries wrote a great article, Modern privileged helper tools using SMJobBless + XPC about using XPC with SMJobBless to install and communicate a privileged helper.

But all I wanted to do was execute my simple little script to handle the update for Keyboard Maestro in the case where the user did not have sufficient privileges to do it directly. It turns out that it is relatively easy to replace AuthorizationExecuteWithPrivileges with SMJobSubmit.

First, a digression on the difference between SMJobBless and SMJobSubmit, because it took me a while to figure it out.

SMJobBless installs (permanently) a launchd LaunchDaemon. You must ensure that your daemon is signed appropriately and add a bunch of other plist entries and such, all covered Apple's SMJobBless sample code. The system will ensure that only your signed application can add/remove the tool. Once the user has authenticated, the helper is installed and will permanently run (on demand) as a privileged tool. Nathan's sample code shows how to communicate with it, but nothing that I can see prevents anyone else from communicating with it, so there is still a huge security hole requiring you to provide some level of authentication on your communication stream.

SMJobSubmit on the other hand install temporarily a launchd LaunchDaemon. You provide the LaunchDaemon dictionary with a reference to your executable. The user must authenticate each time you do this. You can use the RunAtLoad key in the dictionary to have your tool launch immediately, and you can build your script at runtime to include parameters, so you may well be able to avoid communication altogether.

In both cases, you must use SMJobRemove to remove any previous version, even in the case of SMJobSubmit, since the job will stay in launchd's system (presumably until reboot) and will not be replaced. Note that while the code below passes false to the SMJobRemove wait parameter, if you plan on doing another SMJobBless or SMJobSubmit in the near future, you should set the flag to true otherwise the system may occasionally report errors or code signing issues (thanks to Bryan Christianson for this tip).

Here is some example code.

NSString* myLabel = @"com.mydomain.myhelper";

AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;

AuthorizationRef auth;
if( AuthorizationCreate( &authRights, kAuthorizationEmptyEnvironment, flags, &auth ) == errAuthorizationSuccess ) {
    (void) SMJobRemove( kSMDomainSystemLaunchd, (CFStringRef)myLabel, auth, false, NULL );

    NSMutableDictionary *plist = [NSMutableDictionary dictionary];
    [plist setObject:myLabel forKey:@"Label"];
    [plist setObject:[NSNumber numberWithBool:YES] forKey:@"RunAtLoad"];
    [plist setObject:executablePath forKey:@"Program"];
    CFErrorRef error;
    if ( SMJobSubmit( kSMDomainSystemLaunchd, (CFDictionaryRef)plist, auth, &error) ) {
        // Script is running
    } else {
        NSLog( @"Authenticated install submit failed with error %@", error );
    }
    if ( error ) {
        CFRelease( error );
    }

    (void) SMJobRemove( kSMDomainSystemLaunchd, (CFStringRef)myLabel, auth, false, NULL );
    AuthorizationFree( auth, 0 );
}

Updated Monday, August 6, 2012 at 12:00 AM. Permalink. 6 Comments.

Comments

Hey, this is great and I think SMJobSubmit is what I'm looking for, but how exactly do you specify "executablePath"?

Posted Thursday, September 13, 2012 11:13 AM by Rale.

Thank you so much. The Apple documentation is, as you know, horrendously sparse.

Posted Tuesday, September 25, 2012 11:50 AM by Joe.

I'm trying to do something very similar. Namely, wrap an uninstall script with an executable the runs the script. I want to do this because scripts cannot be signed for Mountain Lion Gatekeeper compliance.

I've pulled together a simple command line app (signed) based on your sample code. When it submits the job I see no error, but the script does not run.

Instead I see this error in the console:

--snip--

Dec 8 10:07:39 mymachine /usr/libexec/launchdadd[527]: FAILURE: Job com.mytest.script is not loaded in launchd.

--snip--

I've verified that my label, path to the script, and plist look correct. I'm running this test under Snow Leopard.

Any idea why this isn't working?

Thanks in advance.

Posted Saturday, December 8, 2012 06:01 PM by Allen Cronce.

Interesting. I didn't realize you could use the kSMRightBlessPrivilegedHelper flag with SMJobSubmit. That could be helpful. The scenario you have here would only seem to work for a "fire and forget" type of job (like your update script). Anything that requires communication back and forth still needs some mechanism like the XPC in Nathan's example.

One point though about the SMJobBless and Nathan's example. You mentioned that there might still be a security issue, but there isn't. The mechanism for the security is the XPC connection. It requires that both the caller and helper are signed by the same certificate and that both specifically mention the bundle ID of the other one inside the info.plist.

Posted Monday, August 19, 2013 08:11 AM by Scott Little.

Scott, I think that's only for installing or upgrading the tool. Take the modern "EvenBetterAuthorizationSample" project that Apple provides for instance, configure & run it once to install the tool, then build/run the app again without signing it and you'll realize the app can still talk to the helper.

Posted Sunday, August 16, 2015 08:52 PM by Zorg.

@ScottLittle wrote: "One point though about the SMJobBless and Nathan's example. You mentioned that there might still be a security issue, but there isn't. The mechanism for the security is the XPC connection. It requires that both the caller and helper are signed by the same certificate and that both specifically mention the bundle ID of the other one inside the info.plist."

That is incorrect (i.e. Peter N Lewis is right and ScottLittle is wrong). At the time you call SMJobBless() to install a helper, signatures of both the installing app and the helper are indeed checked. Nobody is questioning that. The security problem comes later: any unsigned application is allowed to send requests to the helper.

In other words, even if you use SMJobBless+XPC, you still have to write your helper very carefully from a security point of view, just as if you were writing a setuid-root helper.

Posted Friday, December 11, 2015 02:01 PM by Régis Duchesne.

Post Comment

Name: (optional)
URL: (optional)
Email: (optional, not published)
   
CAPTCHA: three eight six five (required)
  To prove you are human, please enter the number as digits (ie, 1-9).
Comment:
  You can use some HTML tags such as <b>, <i>, and <a href="">.
We reserve the right to remove any offensive or inappropriate comments.
Due to spam issues, comments are initially invisible until we review them,
you can see them (background red) and we can see them, but no one else.

Comment Preview

None yet.

Buy Now

User Database

Stairways