Lazarus Pascal: Writing a privileged helper tool with SMJobBless()

613 views Asked by At

I've created an application in the past, in Lazarus Pascal, which execute "dd" to write an image to a drive. For this, obviously, elevated rights are needed.

In the initial version I have used AuthorizationExecuteWithPrivileges() (link), even though not exactly intended for this purpose, it did work very well and very consistently. This function however has been depreciated since OSX 10.7, as it can be a security issue, and command line statements that redirect do not work properly either (redirecting output from zip as input for dd).

In the next version I've used a method described in the Lazarus Pascal Wiki (Executing External Programs), which basically starts a TProcess which my program communicates with. Using sudo -S dd ..., the users password is asked and entered to make sure he/she has the proper access rights. Obviously a little bit of a dirty hack method, and it shows, certain users experience issues with this.

After doing a lot of reading, it seems Apple prefers this to be done with a helper tool called SMJobBless(). I'm unfortunately not very experienced when it comes to Objective-C, the presented code seems very minimal at best and not very well documented either.

I was wondering if anyone has experience or could assist in "porting" this method to Lazarus Pascal ... I'm all in favor of doing it right. Alternative methods are most welcome as well of course!

Any help would be greatly appreciated.

2

There are 2 answers

0
Hanzaplastique On BEST ANSWER

Since it took me a lot of work and figured it would be helpful to others, here my final working solution. https://www.tweaking4all.com/software-development/lazarus-development/macos-smjobbless-elevated-privileges-lazarus-pascal/

You'll find there an example project and tons of info.

The steps to reproduce this are quite extensive, so here a short recap:

I've been using CFMessages to send messages to the Helper Tool since I had no bindings for NSXPCConnection.

The Helper Tool has to be based on the Lazarus Pascal template "program" or "simple program", and cannot based on any of the TApplication classes, and cannot create any treads. For the Helper Tool, one needs to create a info.plist and a launchd.plist, which both must be embedded into the binary.

The Main (test) Application can be any Lazarus Pascal application, but needs a proper Info.plist as well, indicating that the Helper Tool is allowed to start with elevated privileges.

The Helper Tool and the application app bundle both need to be signed with a valid Apple Developer ID.

Some missing bindings need to be put in place:

const  kSMRightBlessPrivilegedHelper = 'com.apple.ServiceManagement.blesshelper';
function SMJobBless(domain:CFStringRef; executableLabel:CFStringRef; auth:AuthorizationRef; outError:CFErrorRef): boolean; external name '_SMJobBless'; mwpascal; 
var kSMDomainSystemLaunchd: CFStringRef; external name '_kSMDomainSystemLaunchd';

And the proper frameworks needs to be included:

{$linkframework ServiceManagement}
{$linkframework Security}
{$linkframework Foundation}
{$linkframework CoreFoundation}
{$calling mwpascal}

And let's not forget to set callback function to handle incoming messages.

I hope this is useful to someone ... :-)

11
TheDarkKnight On

I'm unfortunately not very experienced when it comes to Objective-C

Don't let this put you off from using the example provided by Apple. If you look closely at the code in SMJobBlessAppController.m, you'll see that other than one line of Objective-C code, the rest is simply C.

The Objective-C line registers the helper application: -

if (![self blessHelperWithLabel:@"com.apple.bsd.SMJobBlessHelper" error:&error])

You'd use your own URI, instead of com.apple.bsd.SMJobBlessHelper.

All the other relevant lines are plain C functions. Breaking this down, you're left with: -

// Obtain rights 
AuthorizationCopyRights(self->_authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL)


//Start the helper
SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, self->_authRef, &cfError);

I've left out checking for error codes, but I hope this has shown just how little code you need to work with and very little Objective-C knowledge is required.