SMAppService.daemon as root

Hello,

I was wondering, is it possible to run SMAppService.daemon... as root?

let service = SMAppService.daemon(plistName: "myApp.agent.plist")

Also, is it possible to launch the SMAppService.daemon without the XPC connection? The daemon currently supports grpc. I was thinking about running it via Process?

Replies

I’m not sure what you’re asking here:

  • Are you trying to SMAppService install a daemon from code that’s already running as root?

  • Are you asking whether the daemon installed by SMAppService will run as root?

The answer to the first is… well… sheesh… I have no idea, but it seems like a bad idea. SMAppService is intended to be used by apps; it’s right there in the name.

The answer to the second is simply “Yes.”

Also, is it possible to launch the SMAppService.daemon without the XPC connection?

Once you install a daemon using SMAppService, it’s like any other launchd daemon. You can use the MachServices property to have it launch on demand in response to XPC messages. However, that’s not required. It can, for example, use the Sockets property to listen on a TCP or Unix domain socket.


The daemon currently supports grpc. I was thinking about running it via Process?

Hmmm, I think we’re using a different definition of daemon. On macOS a daemon is a process that runs in the global context, typically as root. See Technote 2083 Daemons and Agents.

If your app spawns a child process using Process, it can’t be a daemon because it’s running in the GUI login context of the user who ran your app.

So, taking a step back, what characteristics are you expecting from this process:

  • Do you need it to run as root? Or, equivalently, as some role account user that’s not the same as the user who ran your app?

  • What lifecycle are you looking for? Should it stop when your app quits? Or run for while this user is logged in? Or run forever, even if no user is logged in?

  • Is your app sandboxed?

Share and Enjoy

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

Thanks for such a swift answer. Off topic, you are a forum here and you have helped more than once with your answers in my career. You are my hero.🏅 Thank you 🙏

Hmm, sorry for not such a brief question, let me provide more context.

I am developing a vpn app, not sandboxed, which needs a system daemon(grpc with socket) to be running. I have managed to include the daemon in the app, now thinking of the best approach to Bless and Authorize the non sandboxed daemon. I need to run the daemon as root, it does not need to be shut down when app quits.

Super good idea to spawn daemon via Sockets as that is what I am listening to on grpc.

The documentation regarding daemon - it feels like it is super scarce. There is the introduction of SMAppService and deprecation of SMJobBless. SMAppService too fresh to find proper samples, while SMJobBless quite old to find something.

What would be the best approach for a (non sandboxed) vpn app + daemon to be signed outside of the App Store?

That is the idea I am working with so far:

        var authRef: AuthorizationRef?
        let status = AuthorizationCreate(nil, nil, [], &authRef)
        if status != errAuthorizationSuccess {
            return false
        }

        let rightName = kSMRightBlessPrivilegedHelper

        return rightName.withCString { cStringName -> Bool in
            var authItem = AuthorizationItem(
                name: cStringName,
                valueLength: 0,
                value: nil,
                flags: 0
            )

            return withUnsafeMutablePointer(to: &authItem) { authItemPointer -> Bool in
                var authRights = AuthorizationRights(count: 1, items: authItemPointer)
                let authFlags: AuthorizationFlags = [.interactionAllowed, .preAuthorize, .extendRights]
                let status = AuthorizationCopyRights(authRef!, &authRights, nil, authFlags, nil)
                if status == errAuthorizationSuccess {
                    // Place to execute your authorized action:
                    var cfError: Unmanaged<CFError>?
                    if !SMJobBless(kSMDomainSystemLaunchd, "myApp.agent" as CFString, authRef!, &cfError) {
                        print("SMJobBless error: \(String(describing: cfError))")

                        return false
                    }
                    return true
                }
                return false
            }
        }

But 'SMJobBless' was deprecated in macOS 13.0: Please use SMAppService instead.

I am developing a vpn app … which needs a system daemon(grpc with socket) to be running.

Is your VPN product based on Network Extension? If so, why do you need a daemon?

What would be the best approach for a (non sandboxed) vpn app

  • daemon to be signed outside of the App Store?

Direct distribution, outside of the Mac App Store, requires Developer ID signing. SMAppService imposes no additional constraints [1].

As to which one you should use, I strongly favour SMAppService. The only reason not to use it is if your app has to deploy to older systems.

If you want to explore SMAppService, I recommend that you start here. That post creates an agent, but the setup for a daemon is very similar.

Share and Enjoy

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

[1] This is in contrast to SMJobBless, which takes you down a code signing requirements rabbit hole.