SMJobBless for privileged helper Mac Catalyst

Does anyone know if there are any limitations for installing a privileged helper via SMJobBless from a Mac Catalyst application?

The ServiceManagement framework is not available on iOS, but I can create a Mac bundle and talk to that from the iOS layer. What I’m not sure about is if the bundle is allowed to install and communicate with a privileged helper?

Any one tried this?

Accepted Reply

Had to delete the App Sandbox.

Replies

This is what I've tried so far but I'm seeing an error: "The authorization was denied" when trying to do AuthorizationCreate:

import Foundation
import ServiceManagement

class HelperInstaller {

    func install(executableLabel: String) -> Void {

        let authItem = kSMRightBlessPrivilegedHelper.withCString { name in
            return AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
        }
        
        let pointer = UnsafeMutablePointer<AuthorizationItem>.allocate(capacity: 1)
        pointer.initialize(to: authItem)
        defer {
            pointer.deinitialize(count: 1)
            pointer.deallocate()
        }
        var authRights = AuthorizationRights(count: 1, items: pointer)
        
        var authRef: AuthorizationRef?
        let osStatus = AuthorizationCreate(&authRights, nil, [.interactionAllowed, .extendRights, .preAuthorize], &authRef)
        if osStatus != errAuthorizationSuccess {
            print("Helper install error: \(String(describing: SecCopyErrorMessageString(osStatus, nil)))")
        } else {
            var cfError: Unmanaged<CFError>?
            SMJobBless(kSMDomainSystemLaunchd, executableLabel as CFString, authRef, &cfError)
        }
    }
}

Had to delete the App Sandbox.

Had to delete the App Sandbox.

Right. The App Sandbox prevents apps from escalating privileges, including via SMJobBless.

The Service Management framework is not available on iOS, but I can create a Mac bundle and talk to that from the iOS layer.

FYI this technique is not officially supported. If you go down this path, make sure that you file a bug requesting that Service Management be made available to Mac Catalyst apps.

Please post your bug number, just for the record.

let authItem = kSMRightBlessPrivilegedHelper.withCString { name in
    return AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
}

Oi vey! that’s not safe. The docs for withCString(_:) say:

The pointer argument is valid only for the duration of the method’s execution.

For more background on this issue, see The Peril of the Ampersand.

Calling AuthorizationCopyRights (or, equivalently, AuthorizationCreate) from Swift is quite tricky. I’ve pasted a simple wrapper in below. Actually, I pasted in two (-: The first uses two withXxx(…) invocations. The second avoids that by taking advantage of the facilities discussed in the above-mentioned post.

Share and Enjoy

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


func QAuthorizationCopyRight(
    _ auth: AuthorizationRef,
    _ rightName: String,
    _ flags: AuthorizationFlags) throws
{
    try rightName.withCString { rightNamePtr in
        var right = AuthorizationItem(name: rightNamePtr, valueLength: 0, value: nil, flags: 0)
        try withUnsafeMutablePointer(to: &right) { rightPtr in
            var rights = AuthorizationRights(count: 1, items: rightPtr)
            let err = AuthorizationCopyRights(auth, &rights, nil, flags, nil)
            guard err == errSecSuccess else {
                throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
            }
        }
    }
}

func QAuthorizationCopyRight(
    _ auth: AuthorizationRef,
    _ rightPtr: UnsafeMutablePointer<AuthorizationItem>,
    _ flags: AuthorizationFlags) throws
{
    var rights = AuthorizationRights(count: 1, items: rightPtr)
    let err = AuthorizationCopyRights(auth, &rights, nil, flags, nil)
    guard err == errSecSuccess else {
        throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
    }
}

func QAuthorizationCopyRight(
    _ auth: AuthorizationRef,
    _ rightNamePtr: UnsafePointer<CChar>,
    _ flags: AuthorizationFlags) throws
{
    var right = AuthorizationItem(name: rightNamePtr, valueLength: 0, value: nil, flags: 0)
    try QAuthorizationCopyRight(auth, &right, flags)
}