Подтвердить что ты не робот

Как программно получить привилегии root?

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

Запуск этой программы как root не является вариантом (главным образом для проблем безопасности), и мне нужно знать идентификатор (uid) "реального" пользователя.

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

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

4b9b3361

Ответ 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 может вам помочь.