Как программно получить привилегии root?
Я пишу какое-то программное обеспечение (на С++, для Linux/Mac OSX), которое работает как незащищенный пользователь, но в какой-то момент ему необходимы привилегии root (для создания нового виртуального устройства).
Запуск этой программы как root не является вариантом (главным образом для проблем безопасности), и мне нужно знать идентификатор (uid) "реального" пользователя.
Есть ли способ имитировать поведение команды "sudo" (запросить пароль пользователя) для временного получения привилегий root и выполнения конкретной задачи? Если да, то какие функции я бы использовал?
Большое спасибо за вашу помощь!
Ответы
Ответ 1
Оригинальный ответ
Вы можете рассмотреть переключатель setuid самого исполняемого файла. В Википедии есть статья , которая даже показывает вам разницу между geteuid()
и getuid()
довольно эффективно, первая из которых заключается в том, чтобы узнать, кто вы "подражание", а второе - "кто". Процесс sudo, например, geteuid должен возвращать 0 (root) и getuid ваш идентификатор пользователя, однако его подпроцессы действительно выполняются с правами root (вы можете проверить это с помощью sudo id -u -r
).
Я не думаю, что есть способ легко программно получить root-доступ - в конце концов, применяя принцип наименьших привилегий, зачем вам это нужно? Обычная практика заключается в том, чтобы запускать только ограниченные части кода с повышенными привилегиями. Многие демоны и т.д. Также настроены в современных системах, чтобы работать как их собственный пользователь с большинством привилегий, в которых они нуждаются. Это только для очень специфических операций (установка и т.д.), Что привилегии root действительно необходимы.
Обновление 2013 года
Мой оригинальный ответ стоит (хотя сам мой 2013 год мог бы лучше справиться с этим, чем мой 2010), но если вы разрабатываете приложение, которое требует доступа root, вам может потребоваться точно, какой именно корневой доступ необходим и рассмотрим использование возможностей POSIX (справочная страница). Они отличаются от безопасности на основе возможностей, как это реализовано в L4 et al. Возможности POSIX позволяют вашему приложению получать подмножество полномочий root. Например, CAP_SYS_MODULE
позволит вам вставлять модули ядра, но не дает вам других корневых мощностей. Это используется в дистрибутивах, например. У Fedora есть возможность полностью удалить двоичные файлы setuid с неразборчивым корневым доступом.
Это важно, потому что, как программист, ваш код, безусловно, идеален! Но библиотеки, на которые вы зависите (вздох, если только вы их написали!) Могут иметь уязвимости в них. Используя возможности, вы можете ограничить использование этого эксплойта и спасти себя и свою компанию от проверки безопасности. Это делает всех счастливее.
Ответ 2
Если вам нужны root-привилегии каждый раз, лучше всего запустить вашу программу как root и отбросить их (в подпроцессе) с помощью setuid и setgid. Это то, что делает apache, когда ему нужно связать с ограниченным портом 80.
Если получение прав root - это исключение вместо правила, и программа запускается интерактивно, другим способом является создание программы add_interface и выполнение
sudo add_interface args
и разрешите проверку подлинности sudo для вас. Вместо sudo вы можете использовать графический интерфейс, такой как gksu, gksudo, kdesu или kdesudo. Я бы не попытался реализовать безопасный ввод пароля самостоятельно; это может быть сложной проблемой, и вы, вероятно, оставите зияющие дыры в безопасности и проблемы с функциональностью (поддерживаете ли вы считыватели отпечатков пальцев?).
Другой альтернативой является polkit, ранее называвшийся PolicyKit.
Ответ 3
Вы не можете получить привилегии root, вы должны начать с них и уменьшить свои привилегии по мере необходимости. Обычно вы устанавливаете программу с установленным битом setuid: это запускает программу с эффективным идентификатором пользователя владельца файла. Если вы запустите ls -l
на sudo
, вы увидите, что он установлен таким образом:
-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo
Пока ваша программа работает с привилегиями root, вы можете вызвать системный вызов setuid(2)
, чтобы изменить ваш эффективный идентификатор пользователя для некоторого не привилегированного пользователя. Я полагаю (но не пробовал это), что вы можете установить свою программу с правами root с битом setuid, сразу же уменьшить привилегию, а затем восстановить привилегию по мере необходимости (возможно, однако, что, как только вы опустите свою привилегию, вы не будете сможете восстановить его).
Лучшее решение - вырвать часть вашей программы, которая должна запускаться от имени root, и установить ее с включенным битом setuid. Разумеется, вам необходимо принять разумные меры предосторожности, чтобы не вызывать его за пределами вашей основной программы.
Ответ 4
Обычно это делается путем создания двоичного suid-root.
Один из способов управления этим, чтобы атаки на вашу программу были трудными, заключается в том, чтобы свести к минимуму код, выполняемый как root:
int privileged_server(int argc, char **argv);
int unprivileged_client(int argc, char **argv, int comlink);
int main(int argc, char **argv) {
int sockets[2];
pid_t child;
socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */
child = fork();
if (child < 0) {
perror("fork");
exit(3);
} elseif (child == 0) {
close(sockets[0]);
dup2(sockets[1], 0);
close(sockets[1]);
dup2(0, 1);
dup2(0, 2); /* or not */
_exit(privileged_server(argc, argv));
} else {
close(sockets[1]);
int rtn;
setuid(getuid());
rtn = unprivileged_client(argc, argv, sockets[0]);
wait(child);
return rtn;
}
}
Теперь непривилегированный код говорит о привилегированном коде через fd comlink (который является подключенным сокетом). Соответствующий привилегированный код использует stdin/stdout как конец комлинка.
Привилегированный код должен проверять безопасность каждой операции, которую он должен выполнять, но поскольку этот код является небольшим по сравнению с непривилегированным кодом, это должно быть достаточно простым.
Ответ 5
Вы можете взглянуть на эти API:
setuid, seteuid, setgid, setegid, ...
Они определены в заголовке <unistd.h>
в системах Linux (мало что знают о MAC, но у вас тоже есть аналогичный заголовок).
Одна из проблем, которые я вижу, заключается в том, что процесс должен иметь достаточные привилегии для изменения идентификаторов пользователя/группы. В противном случае вызов вышеуказанных функций приведет к ошибке с errorno
, установленной на EPERM
.
Я предлагаю вам запустить вашу программу в качестве пользователя root
, сменить эффективный идентификатор пользователя (используя seteuid
) для неимущего пользователя в самом начале. Затем, когда вам нужно повысить права доступа, запросите пароль, затем снова используйте seteuid
, чтобы вернуться к пользователю root
.
Ответ 6
В OS X вы можете использовать функцию AuthorizationExecuteWithPrivileges
. На странице "Задачи служб авторизации" есть подробное обсуждение этих (и связанных) функций.
Здесь немного кода на С++ для выполнения программы с правами администратора:
static bool execute(const std::string &program, const std::vector<std::string> &arguments)
{
AuthorizationRef ref;
if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) {
return false;
}
AuthorizationItem item = {
kAuthorizationRightExecute, 0, 0, 0
};
AuthorizationRights rights = { 1, &item };
const AuthorizationFlags flags = kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagPreAuthorize
| kAuthorizationFlagExtendRights;
if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) {
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return false;
}
std::vector<char*> args;
for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
args.push_back(it->c_str());
}
args.push_back(0);
OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0);
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return status == errAuthorizationSuccess;
}
Ответ 7
Вы можете попробовать запустить команду для создания виртуального устройства (в том числе sudo) с помощью фоновой оболочки. Попросите пароль пользователя в своем собственном диалоговом окне и подключите его к оболочке, когда sudo попросит об этом. Существуют и другие решения, такие как использование gksu, но они не гарантируются на всех машинах.
Вы не запускаете всю свою программу как root, а только малую ее часть, которая требует root. Вы должны создать отдельный процесс для этого, и sudo может вам помочь.