1

我有一个常规的旧 MacOS/X GUI 进程,由用户双击图标启动,因此以该用户的权限运行。

我想做的是让这个 GUI 进程产生一个子进程并通过它的 stdin/stdout 与子进程通信。这一切都是可行和有效的。

诀窍是我希望子进程以 root 身份运行,因为子进程需要做一些需要 root 访问权限的事情。

我希望这将需要打开一个对话框,要求用户输入他的管理员密码,这对于这个用例来说是可以的。

一种方法是让父进程运行如下 shell 命令:

/usr/bin/osascript -e 'do shell script "/bin/bash ./my_script.sh" with administrator privileges'

...并my_script.sh运行了代表子进程的程序,我认为这会起作用,但是有没有更优雅的方法来完成这个(即不需要在某处写出一个 shell 脚本到磁盘上,并产生更多用户友好的密码请求器,而不是神秘地说“osascript 想要进行更改”的密码请求器)?

4

1 回答 1

0

通过研究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;
}

这比我认为应该弄清楚的工作量要多得多,但现在一切正常。我在这里描述它是为了减轻下一个人的痛苦。

于 2019-10-05T01:50:52.277 回答