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

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

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

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

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


person ereOn    schedule 20.03.2010    source источник


Ответы (7)


Исходный ответ

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

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

Обновление 2013 г.

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

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

person Community    schedule 20.03.2010
comment
Когда вы делаете это, вы должны быть очень осторожны с безопасностью, потому что ваше приложение теперь является целью эксплойтов для повышения привилегий. - person Ana Betts; 21.03.2010
comment
Когда вы это сделаете, как можно скорее сделайте то, что требует root-привилегий, и сразу после этого перейдите к реальному пользователю. - person Andreas; 30.06.2010
comment
Under sudo, for example, geteuid should return 0 (root) and getuid your user's id. Это неверно. См. Мой вопрос именно по этой проблеме: stackoverflow.com/questions/10272784/ - person chacham15; 27.04.2013
comment
@ chacham15 ах да. Сам Sudo должен видеть эти вещи, но унаследованные процессы - нет. Они наследуют только эффективный идентификатор пользователя в качестве своего реального идентификатора пользователя. Я отредактирую это, так как это непонятно. - person ; 27.04.2013
comment
Когда я делаю это в Mac OS X (Mountain Lion), я получаю. Приложение с идентификатором пакета (null) запускает setugid (), что недопустимо. когда пытаюсь запустить программу. Что я могу сделать? - person heinob; 25.10.2013
comment
@heinob Я не знаком с OSX, однако в последнее время было сосредоточено внимание на удалении setuid / setgid, поскольку они автоматически являются целями для злоумышленников, так что, возможно, это особенность OSX. Вы пробовали искать ТАК? Если это ничего не помогло, задайте свой вопрос. Мне был бы интересен ответ! - person ; 26.10.2013
comment
другой вариант - установить группу владения устройством с помощью правил udev и убедиться, что ваш пользователь является членом этой группы. Например. группы plugdev или disk - person activedecay; 14.01.2019

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

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

sudo add_interface args

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

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

person phihag    schedule 20.03.2010

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

person kdgregory    schedule 20.03.2010

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

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

person Joshua    schedule 20.03.2010
comment
ваш второй блок, я думаю, вы имеете в виду child > 0 - person Matt Joiner; 30.06.2010
comment
На самом деле я считаю, что это должен быть else if (child == 0) - else блок wait для дочернего элемента, поэтому дочерний элемент должен быть средним else if блоком. - person user470379; 10.12.2010
comment
Вам, вероятно, понадобится AF_UNIX, если вы хотите передавать дескрипторы файлов через сокет. - person Toby Speight; 02.08.2016
comment
Я намеренно проектирую так, чтобы не передавать дескрипторы файлов. - person Joshua; 02.08.2016

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

setuid, seteuid, setgid, setegid, ...

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

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

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

person themoondothshine    schedule 20.03.2010

В OS X вы можете использовать rel = "nofollow"> _ 1_. На странице служб авторизации есть некоторое подробное обсуждение этой (и связанных) функций.

Вот небольшой код 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;
}
person Frerich Raabe    schedule 10.02.2016
comment
Где мы вызываем эту функцию? А что было бы параметром arguments? Я попытался вызвать в начале основной функции, но она вылетела. аргументы были нулевыми. Моя проблема заключается в создании файла в / var / log. Но необходимо разрешение. Как я могу справиться с этой проблемой? - person gkhanacer; 03.11.2017
comment
Стоит упомянуть все библиотеки, которые нужны этому коду, не так ли? - person Konstantin; 16.11.2017

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

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

person Gerco Dries    schedule 20.03.2010
comment
Спасибо за ваш ответ. К сожалению, программе не всегда нужны права root. Иногда вы можете просто использовать его таким образом, чтобы ему не нужно было выполнять эту конкретную задачу. В таком случае пароль запрашивать не нужно. Я нашел темы, в которых говорится о setuid (). Я собираюсь исследовать это. - person ereOn; 20.03.2010
comment
Гарантированно ли sudo будет доступен на каждой машине? Я видел установки без него. - person ziggystar; 20.03.2010
comment
Нет, по умолчанию он не настроен в Fedora. Я также лично запрещаю пользователям, не входящим в группу wheel, использовать sudo или su как расширение традиционного поведения unix. - person ; 20.03.2010
comment
Вы, конечно, запускаете только ту часть кода, которая требует root-прав, через sudo. Если вам не нужны привилегии root, не спрашивайте пароль. Запуск всей программы через sudo НЕ был моим ответом, «команда» относится ТОЛЬКО к команде, которая требует привилегий root, а не ко всему приложению. - person Gerco Dries; 21.03.2010
comment
Разве программный центр Ubuntu не получает в интерактивном режиме привилегии root во время работы? Видимо через polkit. - person CMCDragonkai; 24.08.2016