2010-03-20 6 views
29

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

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

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

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

ответ

12

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

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

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

2013 обновление

Мой первоначальный ответ стоит (хотя моя самость 2013 может сделать лучшую работу, чем мой 2010 один), но если вы разрабатываете приложение, которое требует корневого доступа, вы можете рассмотрите, какой именно корневой доступ необходим, и рассмотрим использование POSIX Capabilities(man page). Они отличаются от capability-based security, как это реализовано в L4 et al. Возможности POSIX позволяют вашему приложению получать подмножество полномочий root. Например, CAP_SYS_MODULE позволит вам вставлять модули ядра, но не дает вам других корневых мощностей. Это используется в дистрибутивах, например. Fedora has a feature to completely remove setuid binaries с неразборчивым корневым доступом.

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

+0

Да, мой подход не был правильным. Установка липкого бита - хороший способ сделать это. Большое спасибо. – ereOn

+0

Работает как очарование! Спасибо;) – ereOn

+2

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

0

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

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

+0

Спасибо за ваш ответ. К сожалению, программа не всегда требует привилегий root. Иногда вы можете просто использовать его таким образом, чтобы не выполнять эту конкретную задачу. В таком случае запрос пароля не требуется. Я нашел темы, которые говорят о setuid(). Я собираюсь исследовать это. – ereOn

+2

Является ли sudo гарантированно доступным на каждой машине? Я видел установки без него. – ziggystar

+0

Нет, он не настроен на Fedora по умолчанию. Я также лично блокирую пользователей не в группе 'wheel' от использования sudo или su в качестве расширения традиционного поведения unix. – 2010-03-20 16:26:47

19

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

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

sudo add_interface args 

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

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

+0

Aaaah, я только что опубликовал свой ответ, и это подошло - это хороший совет. +1. – 2010-03-20 16:25:53

+0

+1 для PolicyKit. –

7

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

+0

Спасибо! Кажется, что это путь. Я собираюсь попробовать и вернуть результаты;) – ereOn

2

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

setuid, seteuid, setgid, setegid, ... 

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

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

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

4

Обычно это делается путем создания двоичного 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 комлинка (который является подключенным сокетом). Соответствующий привилегированный код использует stdin/stdout как конец комлинка.

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

+0

ваш второй блок Я думаю, что вы имеете в виду 'child> 0' –

+0

На самом деле я считаю, что это должно быть' else if (child == 0) '- блок' else' 'wait's для дочернего элемента, поэтому ребенку нужно быть средним блоком' else if'. – user470379

+0

user470379 верен. Исправлена. – Joshua

2

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

Вот немного кода 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; 
} 
+0

Где мы называем эту функцию? И что будет параметром аргументов? Я попытался позвонить в начале основной функции, но он разбился. аргументы были нулевыми. Моя проблема заключается в создании файла в/var/log. Но разрешение необходимо. Как я могу справиться с этой проблемой? – gkhanacer

+0

Стоит упомянуть все библиотеки этого кода, не так ли? – Konstantin

Смежные вопросы