通过研究nm 提到的cocoasudo的源代码,我终于能够让它工作
棘手的部分是意识到AuthorizationExecuteWithPrivileges为子进程提供了 euid(“有效用户 ID”)为 0/root,因此它可以执行需要 root 访问权限的事情,但它不会更改子进程的 uid(“真实用户 ID” "); 它仍然设置为父进程的用户 ID,因此系统的其余部分并没有真正将其视为根进程。
当以这种方式启动时,该细节导致我的 shell 脚本中的许多事情无法正常工作;一方面,当使用 执行脚本时/bin/bash
,即使whoami
表明它是以 root 身份执行的,需要 root 访问权限的操作(例如将文件复制到 root-only-writeable 目录)仍然会失败。奇怪的是,/bin/sh
似乎并没有遇到这个问题。
另一个主要的绊脚石是我的脚本需要调用launchctl load -w com.mycompany.myproduct.plist
来启动系统 LaunchDaemon,但launchctl
它的决定是基于真实用户 ID 将服务安装为用户特定还是系统范围,所以我的服务总是会得到安装为特定于用户的服务(在没有 root 访问权限的情况下运行,并且仅在当前用户登录时启动),而不是作为以 root 身份运行的系统范围的服务。
经过大量猜测和谷歌搜索,我被引导到Apple 技术说明 TN2083底部的“提示和提示”部分,其中指出:
重要提示:要使其正常工作,您必须以 root 身份运行 launchctl(其 EUID 和 RUID 都必须为 0)。这可确保 launchctl 与 launchd 的主实例进行通信。
...并且由于(AFAIK)没有系统提供的命令行工具来调用setuid(0)
我,我不得不用 C 编写我自己的小脚本启动器工具:
int main(int argc, char ** argv)
{
if (argc >= 2)
{
if (setuid(0) == 0) // make us really root, not just "effectively root"
{
system(argv[1]); // then run the specified script file
return 0;
}
else perror("setuid");
}
else printf("Usage: setuid_script_launcher <path_to_script>\n");
return 10;
}
这比我认为应该弄清楚的工作量要多得多,但现在一切正常。我在这里描述它是为了减轻下一个人的痛苦。