macOS 中的特权文件复制(将辅助二进制文件安装到 /usr/local/bin)
Privileged file copy in macOS (Installing a helper binary to /usr/local/bin)
我的主应用程序包中有一个辅助二进制文件 mytool
,我需要将其复制到 /usr/local/bin
。
现在 bin
可能并不总是存在或具有写入权限,因此标准 NSWorkspace
调用将失败。我研究了不同的方法来做到这一点,但 none 令人满意(或者我做错了)
正在为 NSWorkspace.requestAuthorization
获取 replaceFile
的授权
这似乎不起作用,因为在尝试用我的包中的文件“替换”/usr/local/bin/mytool
中的文件后,我仍然遇到权限错误。
通过AuthorizationCreate
手动获取授权。
这里的问题是 AuthorizationExecuteWithPrivileges
已被弃用(或者在我的情况下甚至在 Swift 中不可用),并且 SMJobBless
似乎只适用于更长的时间 运行辅助进程。另外 SMJobBless
要求我的辅助工具有自己的 Info.plist
,它没有,因为它只是一个普通的二进制文件
那么我如何设法在 Swift 中执行特权文件复制?
PS:该应用未被沙盒化,因此 NSOpenPanel
无济于事。
好吧,我使用 dlsym
挖出了已弃用的 API,因为除了手动询问用户密码外别无他法,除非已弃用,否则我不想这样做API 完全消失。
所以我现在要做的是使用 AuthorizationExecuteWithPrivileges
验证对 mytool --install
的调用,如下所示:
import Foundation
import Security
public struct Sudo {
private typealias AuthorizationExecuteWithPrivilegesImpl = @convention(c) (
AuthorizationRef,
UnsafePointer<CChar>, // path
AuthorizationFlags,
UnsafePointer<UnsafeMutablePointer<CChar>?>, // args
UnsafeMutablePointer<UnsafeMutablePointer<FILE>>?
) -> OSStatus
/// This wraps the deprecated AuthorizationExecuteWithPrivileges
/// and makes it accessible by Swift
///
/// - Parameters:
/// - path: The executable path
/// - arguments: The executable arguments
/// - Returns: `errAuthorizationSuccess` or an error code
public static func run(path: String, arguments: [String]) -> Bool {
var authRef: AuthorizationRef!
var status = AuthorizationCreate(nil, nil, [], &authRef)
guard status == errAuthorizationSuccess else { return false }
defer { AuthorizationFree(authRef, [.destroyRights]) }
var item = kAuthorizationRightExecute.withCString { name in
AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
}
var rights = withUnsafeMutablePointer(to: &item) { ptr in
AuthorizationRights(count: 1, items: ptr)
}
status = AuthorizationCopyRights(authRef, &rights, nil, [.interactionAllowed, .preAuthorize, .extendRights], nil)
guard status == errAuthorizationSuccess else { return false }
status = executeWithPrivileges(authorization: authRef, path: path, arguments: arguments)
return status == errAuthorizationSuccess
}
private static func executeWithPrivileges(authorization: AuthorizationRef,
path: String,
arguments: [String]) -> OSStatus {
let RTLD_DEFAULT = dlopen(nil, RTLD_NOW)
guard let funcPtr = dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges") else { return -1 }
let args = arguments.map { strdup([=10=]) }
defer { args.forEach { free([=10=]) }}
let impl = unsafeBitCast(funcPtr, to: AuthorizationExecuteWithPrivilegesImpl.self)
return impl(authorization, path, [], args, nil)
}
}
如果您想使用 public API 执行此操作(意味着不使用已弃用的 API、调用 Apple Script、通过 Process 等),那么实现此目的的唯一方法是使用 SMJobBless
.不管是好是坏,这是 Apple 仍然官方支持的唯一选项。
如果您想在 /usr/local/bin
中安装您的二进制文件,那么该二进制文件本身不需要 Info.plist。您想要创建一个 不同的 帮助工具,它可以通过 SMJobBless
安装,可以将您的二进制文件复制到 /usr/bin/local
。它将能够做到这一点,因为 SMJobBless
安装的辅助工具始终以 root 身份运行。完成所有这些操作后,您可以自行卸载使用 SMJobBless
安装的帮助工具。不可否认它相当复杂。
如果您确实想走这条路,请查看 SwiftAuthorizationSample。
我的主应用程序包中有一个辅助二进制文件 mytool
,我需要将其复制到 /usr/local/bin
。
现在 bin
可能并不总是存在或具有写入权限,因此标准 NSWorkspace
调用将失败。我研究了不同的方法来做到这一点,但 none 令人满意(或者我做错了)
正在为
获取NSWorkspace.requestAuthorization
replaceFile
的授权这似乎不起作用,因为在尝试用我的包中的文件“替换”
/usr/local/bin/mytool
中的文件后,我仍然遇到权限错误。通过
AuthorizationCreate
手动获取授权。这里的问题是
AuthorizationExecuteWithPrivileges
已被弃用(或者在我的情况下甚至在 Swift 中不可用),并且SMJobBless
似乎只适用于更长的时间 运行辅助进程。另外SMJobBless
要求我的辅助工具有自己的Info.plist
,它没有,因为它只是一个普通的二进制文件
那么我如何设法在 Swift 中执行特权文件复制?
PS:该应用未被沙盒化,因此 NSOpenPanel
无济于事。
好吧,我使用 dlsym
挖出了已弃用的 API,因为除了手动询问用户密码外别无他法,除非已弃用,否则我不想这样做API 完全消失。
所以我现在要做的是使用 AuthorizationExecuteWithPrivileges
验证对 mytool --install
的调用,如下所示:
import Foundation
import Security
public struct Sudo {
private typealias AuthorizationExecuteWithPrivilegesImpl = @convention(c) (
AuthorizationRef,
UnsafePointer<CChar>, // path
AuthorizationFlags,
UnsafePointer<UnsafeMutablePointer<CChar>?>, // args
UnsafeMutablePointer<UnsafeMutablePointer<FILE>>?
) -> OSStatus
/// This wraps the deprecated AuthorizationExecuteWithPrivileges
/// and makes it accessible by Swift
///
/// - Parameters:
/// - path: The executable path
/// - arguments: The executable arguments
/// - Returns: `errAuthorizationSuccess` or an error code
public static func run(path: String, arguments: [String]) -> Bool {
var authRef: AuthorizationRef!
var status = AuthorizationCreate(nil, nil, [], &authRef)
guard status == errAuthorizationSuccess else { return false }
defer { AuthorizationFree(authRef, [.destroyRights]) }
var item = kAuthorizationRightExecute.withCString { name in
AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
}
var rights = withUnsafeMutablePointer(to: &item) { ptr in
AuthorizationRights(count: 1, items: ptr)
}
status = AuthorizationCopyRights(authRef, &rights, nil, [.interactionAllowed, .preAuthorize, .extendRights], nil)
guard status == errAuthorizationSuccess else { return false }
status = executeWithPrivileges(authorization: authRef, path: path, arguments: arguments)
return status == errAuthorizationSuccess
}
private static func executeWithPrivileges(authorization: AuthorizationRef,
path: String,
arguments: [String]) -> OSStatus {
let RTLD_DEFAULT = dlopen(nil, RTLD_NOW)
guard let funcPtr = dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges") else { return -1 }
let args = arguments.map { strdup([=10=]) }
defer { args.forEach { free([=10=]) }}
let impl = unsafeBitCast(funcPtr, to: AuthorizationExecuteWithPrivilegesImpl.self)
return impl(authorization, path, [], args, nil)
}
}
如果您想使用 public API 执行此操作(意味着不使用已弃用的 API、调用 Apple Script、通过 Process 等),那么实现此目的的唯一方法是使用 SMJobBless
.不管是好是坏,这是 Apple 仍然官方支持的唯一选项。
如果您想在 /usr/local/bin
中安装您的二进制文件,那么该二进制文件本身不需要 Info.plist。您想要创建一个 不同的 帮助工具,它可以通过 SMJobBless
安装,可以将您的二进制文件复制到 /usr/bin/local
。它将能够做到这一点,因为 SMJobBless
安装的辅助工具始终以 root 身份运行。完成所有这些操作后,您可以自行卸载使用 SMJobBless
安装的帮助工具。不可否认它相当复杂。
如果您确实想走这条路,请查看 SwiftAuthorizationSample。