Я наконец смог заставить его работать, изучив исходный код для cocoasudo , как упомянуто nm
Сложная часть заключалась в том, что AuthorizationExecuteWithPrivileges дает дочернему процессу euid («эффективный идентификатор пользователя») 0 / root, чтобы он мог делать вещи, требующие root-доступа, но это не меняет uid дочернего процесса («реальный идентификатор пользователя»));для него остается значение идентификатора пользователя родительского процесса, поэтому на самом деле не рассматривается остальной системой как корневой процесс.
Эта деталь вызвала ряд вещейв моем shell-скрипте работать некорректно при запуске таким способом;с одной стороны, при выполнении сценария с использованием /bin/bash
, даже если whoami
указывает, что он выполняется от имени пользователя root, действия, требующие доступа с правами root (например, копирование файла в каталог, доступный только для записи с правами root), по-прежнему завершаются ошибкой. Любопытно, что /bin/sh
, похоже, не страдает от этой проблемы.
Другим важным камнем преткновения было то, что моему сценарию нужно было вызвать launchctl load -w com.mycompany.myproduct.plist
, чтобы запустить System LaunchDaemon, но launchctl
основывает свое решение наустанавливать ли службу как специфичную для пользователя по сравнению с общесистемной на основе реального идентификатора пользователя, так что моя служба всегда будет устанавливаться как специфичная для пользователя служба (работающая без корневого доступа и запускаемая только при входе текущего пользователя)чем в качестве общесистемной службы, работающей от имени пользователя root.
После долгих догадок и поисков, я был направлен в раздел «Советы и подсказки» в нижней части Apple Tech Note TN2083 , в котором говорится:
ВАЖНО: Для того, чтобы это работало правильно, вы должны запустить launchctl от имени пользователя root (и его 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;
}
Это было намного больше работы, чем я думаю, чтобы было понять, но теперь все работает. Я описываю это здесь, чтобы избавить следующего человека от боли.