Using SMJobBless in modern Xcode

I am trying to figure out how to programatically install a per-user launchd agent - I have an executable Swift script I wrote and I need macOS to enforce it always be running. I found the SMJobBless sample code which I could play with to see how this works, but it hasn't been updated since it was last built with Xcode 4.6. As you can imagine it doesn't compile in Xcode 10. I was able to get it to build by upgrading to the recommended project settings, increasing the deployment target, and selecting my team for the two targets. Following the ReadMe I need to run ./SMJobBlessUtil.py setreq to configure the Info.plists appropriately. These instructions are out of date but eskimo was kind enough to provide updated instructions here to find the .app url. But when I do this and run the command I receive the following output:


MacBook:SMJobBless Jordan$ ./SMJobBlessUtil.py setreq /Users/Jordan/Library/Developer/Xcode/DerivedData/SMJobBless-dffakkidazmiowcishyrborysygm/Build/Products/Debug/SMJobBlessApp.app SMJobBlessApp/SMJobBlessApp-Info.plist SMJobBlessHelper/SMJobBlessHelper-Info.plist
Traceback (most recent call last):
  File "./SMJobBlessUtil.py", line 424, in 
    main()
  File "./SMJobBlessUtil.py", line 418, in main
    setreq(appArgs[1], appArgs[2], appArgs[3:])
  File "./SMJobBlessUtil.py", line 360, in setreq
    appToolDict[bundleID] = toolNameToReqMap[bundleID]
KeyError: '$(PRODUCT_BUNDLE_IDENTIFIER)'

It would seem this python script isn't able to work with the newer project structures, not surprisingly. I wasn't able to find any other information on how to accomplish this task in the modern days. So could you please explain how to go about this? 🙂


I have an executable .swift file and a .plist that works when loaded from ~/Library/LaunchAgents/ ready to be added to an existing Xcode project. Thanks!

Replies

SMJobBless is for privileged helpers. You don't need that for a launch agent. You don't even need Xcode for a launch agent.

Although the SMJobBless sample code may not be needed for the OP's purpose, it's still an interesting question. I'm working on a GUI app that needs to get data by running a command line program that requires su.

There are a number of problems with the Python script, mostly because it was presumably written for Python 2.7 and it doesn't seem possible to run Python 2.x on an M1 Mac running macOS 12:

  • the #! line needs to be updated to /usr/bin/python3 (or to the path for your Homebrew install of a later version)
  • the syntax for exception handling is different and needs to be updated
  • issues with decoding UTF8 binary data into string objects
  • the syntax of the output from the codesign utility has apparently changed so parsing that needs to be updated
  • the plistlib functions are different, instead of passing a file to readPlist need to pass an open file object to load
  • same for plistlib.writePlist(path) to plistlib.dump(file object)
  • instead of using dict.has_key(key) use key in dict
  • in isinstance check, use str instead of basestring
  • replace dict.iteritems with dict.items
  • use Python3 print statement syntax

I've attached an updated file with the fixes needed to run with Python 3.8.9.

Sadly, that doesn't solve the problem. I think this process is flawed, perhaps because the underlying codesign utility has changed. The idea of the script is you build the app and tool, run the script to get the code signatures for each, populate those code signatures into the corresponding plists (app references the tool's code signature and the tool references the app's signature). Then do a clean build with the updated plists and run the script to check that everything is right.

The check always fails because changing the plists changes the app and the tool, so when it tries to verify the code signatures, they are different from what we got previously. This just can't converge!

Is there some new method for GUI apps to be able to invoke a shell app that requires su?

I've attached an updated file with the fixes needed to run with Python 3.8.9.

Neat-o! Thanks.

Is there some new method for GUI apps to be able to invoke a shell app that requires su?

No.

Indeed, SMJobBless was designed for that either. The point of that API is to allow apps to escalate privileges in a limited fashion. Shell scripts have a huge attack surface, and so passing a shell script to your privileged helper tool undermines the whole idea.

So, I see two issues here:

  • Is SMJobBless the right option?

  • How do you get SMJobBless to work with modern tooling?

I think you should tackle them in order; there’s not much point jumping through the hoop required to get SMJobBless to work if you then decide it’s not the right option.

So, why are you planning to do with escalation privileges?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"