I am currently developing a No-Sandbox application.
What I want to achieve is to use AuthorizationCopyRights in a No-Sandbox application to elevate to root, then register SMAppService.daemon after elevation, and finally call the registered daemon from within the No-Sandbox application.
Implementation Details
Here is the Plist that I am registering with SMAppService:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.agent</string>
<key>BundleProgram</key>
<string>/usr/local/bin/test</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/test</string>
<string>login</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Code that successfully performs privilege escalation (a helper tool popup appears)
private func registerSMAppServiceDaemon() -> Bool {
let service = SMAppService.daemon(plistName: "com.example.plist")
do {
try service.register()
print("Successfully registered \(service)")
return true
} catch {
print("Unable to register \(error)")
return false
}
}
private func levelUpRoot() -> Bool {
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 {
if !registerSMAppServiceDaemon() {
return false
}
return true
}
return false
}
}
}
Error Details
Unable to register Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}
The likely cause of this error is that /usr/local/bin/test is being bundled. However, based on my understanding, since this is a non-sandboxed application, the binary should be accessible as long as it is run as root.
Trying
post as mentioned in the response, placing the test binary under Contents/Resources/ allows SMAppService to successfully register it. However, executing the binary results in a different error.
Here is the plist at that time.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.agent</string>
<key>BundleProgram</key>
<string>Contents/Resources/test</string>
<key>ProgramArguments</key>
<array>
<string>Contents/Resources/test</string>
<string>login</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Here is the function at that time.
private func executeBin() {
let bundle = Bundle.main
if let binaryPath = bundle.path(forResource: "test", ofType: nil) {
print(binaryPath)
let task = Process()
task.executableURL = URL(fileURLWithPath: binaryPath)
task.arguments = ["login"]
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
do {
try task.run()
let outputData = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = String(data: outputData, encoding: .utf8) {
print("Binary output: \(output)")
}
task.waitUntilExit()
if task.terminationStatus == 0 {
print("Binary executed successfully")
} else {
print("Binary execution failed with status: \(task.terminationStatus)")
}
} catch {
print("Error executing binary: \(error)")
}
} else {
print("Binary not found in the app bundle")
}
}
Executed After Error
Binary output:
Binary execution failed with status: 5
Are there any other ways to execute a specific binary as root when using AuthorizationCopyRights? For example, by preparing a Helper Tool?