2

我正在为 MacOSX(使用 Qt 和 C++)编写安装程序/自动更新程序。我需要升级权限才能覆盖 app 文件夹中的旧文件。

我的升级代码基于以下示例: http: //www.michaelvobrien.com/blog/2009/07/authorizationexecutewithprivileges-a-simple-example/ 我尝试使用管理员权限重新启动我现有的应用程序,如下所示:

void MainDialog::EscalatePrivileges()
{
    AuthorizationRef authorizationRef;
    OSStatus status;

    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);

    char* tool = QApplication::instance()->applicationFilePath().toLocal8Bit().data();
    char* args[] = { "STARTUPDATE", NULL };
    FILE* pipe = NULL;

    status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, AuthorizationFlagDefaults, args, &pipe);
    QApplication::instance()->quit();
}

但是,我收到错误 -60031(无法启动工具)。问题:

a) 为什么会失败?我怀疑是因为工作文件夹设置不正确...?(我可以以某种方式设置工具的工作文件夹吗?)

编辑: 好的,想通了: args[] - 数组本身需要以 NULL 结尾。已经在上面的代码中修复了。

b) 其他互联网消息来源称,AuthorizationExecuteWithPrivileges 功能已被弃用,出于安全考虑不应使用。有人可以举例说明如何以更好的方式做到这一点吗?

4

2 回答 2

4

也许我的回答对你来说迟了,但我希望它可以帮助其他 Qt 开发人员。我在 Qt 中创建了一个项目来展示如何使用 SMJobBless 签名、安装执行特权帮助工具;你可以在这里看到代码:https ://github.com/mbsanchez/QtPrivilegedHelperExample

我创建了它,因为没有关于如何安装特权帮助工具的文档,这是在 QtCreator 上使用 C++ 开发的。

编辑:我将解释执行此操作的过程。

问题:您有一个应用程序“AppA”,您希望使用 QtCreator 在 C++ 上开发的另一个应用程序“AppB”的管理权限来执行它。

解决方案:由于 Mac Os X 10.7 不推荐使用 AuthorizationExecuteWithPrivileges 功能,因此您将使用 SMJobBless 代替。但是关于 SMJobBless 的所有文档和示例都在 Xcode 上,直到现在还没有关于 C++ 的内容。

要使用 SMJobBless,您将开发第三个应用程序“AppC”,该应用程序通常称为帮助工具,将使用 SMJobBless 作为特权帮助工具安装,并由 launchd 作为守护程序执行。然后,就像 AppC 以管理员权限执行一样,AppC 执行的任何应用程序都将获得管理员权限。因此,如果您从 AppC 执行 AppA,AppA 将以管理员权限运行。这里有 3 件事很重要:

  1. AppB 将使用 SMJobBless 安装特权帮助工具 (AppC),因此,AppB 需要一个 Info.plist 文件,其中包含可以安装的特权帮助工具列表(在本例中为 AppC)。

    <?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>CFBundleIdentifier</key>
        <string>com.example.AppB</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        ...
        <key>SMPrivilegedExecutables</key>
        <dict>
            <key>com.example.AppC</key>
            <string>anchor apple generic and identifier &quot;com.example.AppC&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = XXXXXXXXXX)</string>
        </dict>
    </dict>
    </plist>
    

    信息列表

  2. AppC需要两个plist文件,第一个包含launchd任务的信息,系统使用什么来启动AppC作为守护进程(AppC不会被AppB执行,一旦安装了AppC,系统会启动它); 第二个包含有关哪些应用程序可以将其安装为帮助工具(在本例中为 AppB)的信息。

    <?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.AppC</string>
        <key>StandardErrorPath</key>
        <string>/var/log/com.example.appc.log</string>
        <key>Sockets</key>
        <dict>
            <key>com.example.AppC</key>
            <dict>
                 <key>SockFamily</key>
                 <string>Unix</string>
                 <key>SockPathMode</key>
                 <integer>438</integer>
                 <key>SockPathName</key>
                 <string>/var/run/com.example.AppC.socket</string>
                 <key>SockType</key>
                 <string>Stream</string>
            </dict>
        </dict>
    </dict>
    </plist>
    

    AppC-Launchd.plist

    <?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>CFBundleIdentifier</key>
        <string>com.example.AppC</string>
        ...
        <key>SMAuthorizedClients</key>
        <array>
            <string>anchor apple generic and identifier com.example.AppB and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = XXXXXXXXXX)</string>
        </array>
    </dict>
    </plist>
    

    AppC-Info.plist

  3. AppC 将通过在编译器的链接标志(qmake 中的 QMAKE_LFLAGS)中使用“-sectcreate __TEXT __info_plist myinfo.plist -sectcreate __TEXT __launchd_plist mylaunchd.plist”与两个 plist 文件链接。这会将两个 plist 文件嵌入特权帮助应用程序 (AppC) 的 __TEXT 部分。

    QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$PWD/AppC-Info.plist -sectcreate __TEXT __launchd_plist $$PWD/AppC-Launchd.plist
    

    此标志在 AppC.pro 中设置

  4. plist 文件中每次出现 XXXXXXXXXX 都会更改为您的苹果开发者证书的组织单位。

  5. AppB 和 AppC 将使用有效的苹果开发者证书通过协同签名工具进行签名。
  6. AppC 将被复制到 AppB 的包中 Contents/Library/LaunchServices
  7. AppA 将被复制到 Contents/Resources 中的 AppB 的包中
  8. AppB Info.plist 将被复制到 App Bundle 中。
  9. 接下来,您将使用编码工具对 AppB Bundle 进行签名。
  10. 此示例使用 unix socket 与 AppC 启动守护进程通信 AppB,然后 AppC 将启动服务器连接并等待来自 AppB 的命令,当 AppC 收到命令时它将执行 AppA(您可以使用 execvp C 函数或任何您想要的)。
  11. AppB 将启动客户端连接,并在需要运行 AppA 时向 AppC 发送命令。

我试图在这里解释所有细节,但我认为通过检查我的代码你会更好地了解解决方案。

于 2016-04-23T18:12:24.220 回答
1

是的,不推荐使用 AuthorizationExecuteWithPrivileges。

Apple 现在提供了一种更安全的方法来处理提升的应用程序,即将需要提升的代码分解为另一个帮助应用程序,该应用程序被赋予特殊权限并以提升的权限启动。这样,如果您的应用程序由于恶意软件或代码注入而被滥用,它也无法获得任何提升的访问权限。辅助应用程序也与您的应用程序签署,因此只有您的应用程序可以请求启动它。

如果您看一下 Apple 的SMJobBless 示例,它会显示您是如何做到这一点的。首先看一下ReadMe.txt,我还建议在 Google 上搜索 SMJobBless,因为有很多关于它的讨论。

于 2013-07-05T08:12:17.123 回答