macOS 中的特权文件复制(将辅助二进制文件安装到 /usr/local/bin)

Posted

技术标签:

【中文标题】macOS 中的特权文件复制(将辅助二进制文件安装到 /usr/local/bin)【英文标题】:Privileged file copy in macOS (Installing a helper binary to /usr/local/bin) 【发布时间】:2021-12-15 13:11:15 【问题描述】:

我的主应用程序包中有一个辅助二进制文件mytool,我需要将它复制到/usr/local/bin

现在bin 可能并不总是存在或具有写入权限,因此标准的NSWorkspace 调用将失败。我研究了不同的方法来做到这一点,但没有一个是令人满意的(或者我做错了)

    replaceFile 获得NSWorkspace.requestAuthorization 的授权

    这似乎不起作用,因为在尝试用我的包中的文件“替换”/usr/local/bin/mytool 中的文件后,我仍然遇到权限错误。

    通过AuthorizationCreate手动获取授权。

    这里的问题是 AuthorizationExecuteWithPrivileges 已被弃用(或者在我的情况下甚至在 Swift 中不可用),而 SMJobBless 似乎仅适用于运行时间更长的辅助进程。此外,SMJobBless 要求我的辅助工具拥有自己的 Info.plist,因为它只是一个普通的二进制文件,所以它没有它

那么如何在 Swift 中执行特权文件复制?

PS:该应用没有沙盒化,所以NSOpenPanel 没有帮助。

【问题讨论】:

使用NSOpenPanel 并将directoryURL 指向/usr/local/bin。并要求用户选择它。 好吧,Kaleidoscope 例如在没有NSOpenPanel 的情况下执行此操作(这是一种可怕的体验)。所以必须有办法做到这一点......或者他们正在使用已弃用的 API。 向用户询问密码,然后使用Process 使用sudo 执行命令。处理 sudo 和密码的例子见this answer 大安全OOF。我真的不想要求 sudo 密码。 @ElTomato 不必支持 MAS。无论如何都不能在 MAS 版本中使用外部助手 我不知道万花筒这个家伙是什么。如果我没记错的话,BBEdit 使用NSOpenPanel 并让用户选择一个文件路径。我也是这样做的。 【参考方案1】:

好吧,我使用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($0) 
        defer  args.forEach  free($0) 
        let impl = unsafeBitCast(funcPtr, to: AuthorizationExecuteWithPrivilegesImpl.self)
        return impl(authorization, path, [], args, nil)
    

【讨论】:

【参考方案2】:

如果您想使用公共 API 执行此操作(意味着不使用已弃用的 API、调用 Apple 脚本、通过 Process 进行脱壳等),那么实现此目的的唯一方法是使用 SMJobBless。无论好坏,这是 Apple 仍然官方支持的唯一选项。

如果您想在/usr/local/bin 中安装您的二进制文件,那么该二进制文件本身不需要有 Info.plist。你想创建一个不同的帮助工具,它可以通过SMJobBless 安装,可以将你的二进制文件复制到/usr/bin/local。它将能够做到这一点,因为SMJobBless 安装的帮助工具始终以 root 身份运行。完成所有这些操作后,您可以使用 SMJobBless 安装的帮助工具自行卸载。不可否认,它相当复杂。

如果您确实想走这条路,请查看SwiftAuthorizationSample。

【讨论】:

这正是我不想做的,因为它比使用已弃用的 API 更糟糕。 当然是你的选择。从复杂性的角度来看,它肯定要高得多。更好的方式是它是 100% 支持的。在许多情况下,Apple 最终会完全删除已弃用的 API。

以上是关于macOS 中的特权文件复制(将辅助二进制文件安装到 /usr/local/bin)的主要内容,如果未能解决你的问题,请参考以下文章

将包含二进制文件的文件夹结构复制到本机库文件夹

用户 homedir MacOS 中的 Bash 复制文件

带有 APFS 的 macOS:终端中的写时复制

macOS下安装tomcat服务器

Xcode 项目中的文件可以在 MacOS 上进行本地化吗

最强辅助!IDA 辅助工具Karta——二进制文件中搜索开源代码