Я на сервере Ubuntu 22.04. Я хочу запустить:
systemctl restart someService
но хотите сделать это в программе C.
Интуитивно я попробовал:
system("systemctl restart someService")
Это не сработало, даже если в самой моей программе для setUid установлено значение root, поскольку в самой systemctl бит setUid не установлен на root.
Я хотел бы написать программу и установить для ее uid значение root, чтобы любой мог выполнить ее для перезапуска определенной системной службы. Это возможно только при использовании некоторой прямой функции, а не системного вызова, как это было сделано выше. Какие-либо предложения?
А вы пробовали execve и друзей? Если вам нужно дождаться результата, возможно, вам придется объединить его с вилкой
Man-страница system специально рекомендует не вызывать ее из set-uid как корневой двоичный файл.
@AjayBrahmakshatriya Я все равно не хочу использовать вызов system(), поэтому я согласен с вашим предложением. Я не думал об execve и друзьях, но все же мне понадобится вызов system(). В любом случае я ищу вызов функции (кроме system()), чтобы выполнить ее как собственную функцию.
@AjayBrahmakshatriya Например, как выполнить systemctl restart sshd из программы на C?
@konsolebox Я не пытался указать полный путь к systemctl, но я пробовал другие исполняемые файлы (с полным путем), у которых для setuid не было установлено значение root, но для вызывающей программы setUid было установлено значение root. В любом случае, почему установка полного пути имеет значение, если в PATH нет другого systemctl?
Возможно, PATH сбрасывается. Это просто удаление ненужных возможностей.
@konsolebox с помощью system
вызывает команду через /bin/sh
, которая в некоторых дистрибутивах сбрасывает пользователя.
Похоже, вы решаете не ту задачу. Если вы заставите свою программу создать фиктивный файл, скажем, /tmp/dummy
, будет ли он принадлежать пользователю root?
@AjayBrahmakshatriya Верно. Интуитивно следующее, что нужно сделать, это вызвать двоичный файл напрямую с некоторыми функциями exec. Продолжайте сужать его.
Я не думаю, что существует системный вызов, который может выполнять работу systemctl в целом. Я думаю, что ваш подход к вызову команды systemctl
из вашей программы правильный. Но я не вдаюсь в соображения безопасности здесь. Вы должны быть очень осторожны при написании программ set-uid.
Теперь основная проблема с вашим кодом заключается в том, что system не следует использовать из двоичных файлов set-uid, потому что это не позволяет вам управлять переменными среды, которые могут быть установлены злонамеренно перед вызовом вашей программы для изменения поведения называется процессом. Кроме того, команда system
вызывает /bin/sh
для запуска вашей команды, которая в некоторых версиях Linux сбрасывает привилегии, как указано на справочной странице, указанной выше. Правильным подходом будет использование семейства функций execve, которые предлагают больше контроля и не создают оболочку. То, что вам нужно сделать, можно сделать следующим образом -
int main(int argc, char* argv[]) {
setuid(0);
setgid(0);
char *newargv[] = {"/usr/bin/systemctl", "restart", "someService", NULL};
char *newenviron[] = { NULL };
execve(newargv[0], newargv, newenviron);
perror("execve"); /* execve() returns only on error */
exit(EXIT_FAILURE);
}
Обратите внимание на пустую (или чистую) среду выше. Стоит отметить, что execve
не должен возвращаться, если нет ошибки. Если вам нужно дождаться возвращаемого значения от команды systemctl
, возможно, вам придется объединить это с fork
Работало отлично, но я установил uid для исполняемого файла после смены владельца на root, а не с помощью setuid(), setgid().
@Sunny есть разница между эффективным идентификатором пользователя (идентификатор, установленный при использовании setuid) и идентификатором пользователя, под которым работает программа. Вы можете вызывать setuid(0)
только в том случае, если у вас есть соответствующие привилегии, одна из которых — установить эффективный uid равным 0.
Но для выполнения setuid(0) вам также нужно, чтобы uid запущенной программы был от root. Поэтому мне не хватает преимущества использования setuid()
@Sunny не совсем, вы можете setuid(0)
если только ваш эффективный uid равен 0 (который отличается от uid и устанавливается, когда ваш двоичный файл имеет флаг +s)
Мой предыдущий комментарий должен был быть таким: «Но для выполнения setuid(0) вам понадобится эффективный uid программы…» Мой аргумент по-прежнему таков: если эффективный uid установлен на 0, зачем нужно использовать setuid(0) снова в программе?"
@Санни, верно! Намерение состояло в том, чтобы также «настоящий» UID и «настоящий» GID равным 0 (который в противном случае был бы установлен на ваш идентификатор пользователя). В основном это не имеет значения, за исключением доступа к файлам. Я не уверен, что systemctl
обращается к файлам, но это обычная практика, когда вы хотите, чтобы программа работала точно так же, как root. Подробнее о setuid
Попробуйте указать полный путь к
systemctl
и посмотрите, будет ли это иметь значение.