Как программно получить root-привилегии? - PullRequest
31 голосов
/ 20 марта 2010

Я пишу какое-то программное обеспечение (на C ++ для Linux / Mac OSX), которое работает как непривилегированный пользователь, но в какой-то момент ему нужны привилегии root (для создания нового виртуального устройства).

Запуск этой программы от имени root не является опцией (в основном из-за проблем с безопасностью), и мне нужно знать имя (uid) «реального» пользователя.

Есть ли способ имитировать поведение команды "sudo" (запросить пароль пользователя) для временного получения привилегий root и выполнения конкретной задачи? Если да, то какие функции я бы использовал?

Большое спасибо за помощь!

Ответы [ 7 ]

20 голосов
/ 20 марта 2010

Если вам нужны привилегии root каждый раз, лучше всего запустить вашу программу как root и отбросить их (в подпроцессе) с помощью setuid и setgid . Это то, что делает apache, когда ему нужно привязаться к ограниченному порту 80.

Если получение root-прав является исключением, а не правилом, и программа запускается в интерактивном режиме, другой способ - написать программу add_interface и выполнить

sudo add_interface args

и позвольте sudo выполнять аутентификацию для вас. Вместо sudo вы можете использовать графический интерфейс, такой как gksu, gksudo, kdesu или kdesudo. Я бы не стал сам вводить безопасный ввод пароля; это может быть сложной задачей, и вы, вероятно, оставите зияющие дыры в безопасности и проблемы с функциональностью (Поддерживаете ли вы сканеры отпечатков пальцев?).

Другой альтернативой является polkit , ранее называемый PolicyKit.

15 голосов
/ 20 марта 2010

Оригинальный ответ

Вы могли бы рассмотреть переключение setuid на самом исполняемом файле. В Википедии есть статья , которая даже показывает вам разницу между geteuid() и getuid() довольно эффективно: первая - для того, чтобы выяснить, кто вы "подражаете", а вторая - для того, кто вы ». Процесс sudo, например, geteuid должен вернуть 0 (root) и getuid id вашего пользователя, однако его подпроцессы действительно выполняются как root (вы можете проверить это с помощью sudo id -u -r).

Я не думаю, что есть способ легко программно получить root-доступ - в конце концов, зачем вам нужен принцип наименьших привилегий? Обычная практика - запускать только ограниченные части кода с повышенными привилегиями. Многие современные демоны и т. Д. Также настроены в современных системах для работы в качестве своего собственного пользователя с большинством необходимых им привилегий. Только для очень специфических операций (монтирование и т. Д.) Привилегии root действительно необходимы.

2013 обновление

Мой первоначальный ответ остается прежним (хотя мой «я» в 2013 году мог бы справиться с этим лучше, чем мой в 2010 году), но если вы разрабатываете приложение, требующее root-доступа, вы можете решить, какой именно root-доступ необходим и рассмотрите возможность использования POSIX Capabilities (man-страница) . Они отличаются от безопасности на основе возможностей , как реализовано в L4 et al. Возможности POSIX позволяют вашему приложению получить набор полномочий root. Например, CAP_SYS_MODULE позволит вам вставить модули ядра, но не даст вам никаких других полномочий root. Это используется в дистрибутивах, например В Fedora есть функция полного удаления двоичных файлов setuid с неизбирательным доступом к корневому каталогу.

Это важно, потому что, как программист, ваш код явно совершенен! Но библиотеки, от которых вы зависите (вздохните, если бы вы их написали!), Могли иметь уязвимости. Используя возможности, вы можете ограничить использование этого эксплойта и спасти себя и свою компанию от проверки безопасности. Это делает всех счастливее.

7 голосов
/ 20 марта 2010

Вы не можете получить привилегии root, вы должны начать с них и уменьшать свои привилегии по мере необходимости. Обычный способ сделать это - установить программу с установленным битом «setuid»: при этом программа запускается с эффективным идентификатором пользователя владельца файла. Если вы запустите ls -l на sudo, вы увидите, что он установлен таким образом:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo

Пока ваша программа работает с правами суперпользователя, вы можете вызвать системный вызов setuid(2), чтобы изменить эффективный идентификатор пользователя на непривилегированного пользователя. Я считаю (но не пробовал этого), что вы можете установить свою программу как root с включенным битом setuid, немедленно уменьшить привилегии, а затем восстановить привилегии по мере необходимости (однако, возможно, что когда вы снизите свои привилегии, вы не будете быть в состоянии восстановить его).

Лучшее решение - выделить часть вашей программы, которая должна работать от имени пользователя root, и установить ее с включенным битом setuid. Вам, конечно, нужно будет принять разумные меры предосторожности, чтобы он не мог быть вызван вне вашей основной программы.

4 голосов
/ 20 марта 2010

Обычно это делается путем создания вашего двоичного suid-корня.

Один из способов справиться с этим, чтобы атаки на вашу программу были сложными, состоит в том, чтобы минимизировать код, который запускается от имени пользователя 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 в качестве конца comlink.

Привилегированный код должен проверять безопасность каждой операции, которую он должен выполнять, но, поскольку этот код невелик по сравнению с непривилегированным кодом, это должно быть достаточно просто.

2 голосов
/ 10 февраля 2016

В OS X вы можете использовать функцию AuthorizationExecuteWithPrivileges. На странице Задачи службы авторизации подробно обсуждаются эти (и связанные с ними) функции.

Вот немного кода C ++ для выполнения программы с правами администратора:

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;
}
2 голосов
/ 20 марта 2010

Возможно, вы захотите взглянуть на эти API:

setuid, seteuid, setgid, setegid, ...

Они определены в заголовке <unistd.h> в системах Linux (не очень много знаете о MAC, но вы должны иметь там аналогичный заголовок).

Одна проблема, которую я вижу, состоит в том, что процесс должен иметь достаточные привилегии для изменения своих идентификаторов пользователей / групп. В противном случае вызов вышеупомянутых функций приведет к ошибке с errorno, установленным на EPERM.

Я бы посоветовал вам запустить вашу программу как пользователь root, изменив эффективный идентификатор пользователя (используя seteuid) на пользователя с ограниченными правами в самом начале. Затем, когда вам понадобится повысить права доступа, запросите пароль, затем снова введите seteuid, чтобы вернуться к пользователю root.

0 голосов
/ 20 марта 2010

Вы можете попробовать запустить команду для создания виртуального устройства (включая sudo) через фоновую оболочку. Спросите пароль пользователя в собственном диалоговом окне и передайте его в оболочку, когда sudo запросит его. Существуют и другие решения, такие как использование gksu, но они не обязательно будут доступны на каждой машине.

Вы запускаете не всю программу от имени пользователя root, а только небольшую ее часть, для которой требуется root. Вы должны создать отдельный процесс для этого, и sudo может вам помочь.

...