эквивалент setuid для пользователей без полномочий root

Есть ли у Linux некоторый C-интерфейс, похожий на setuid , который позволяет программе переключиться на другого пользователя, используя, например, имя пользователя / пароль? Проблема с setuid заключается в том, что он может использоваться только суперпользователями.

Я запускаю простой веб-сервис, который требует выполнения заданий как зарегистрированного пользователя. Таким образом, основной процесс выполняется как root, и после того, как пользователь запустит в него вилки и вызовет setuid для переключения на соответствующий uid. Тем не менее, я не совсем уверен в том, что основной proc работает от root. Я предпочел бы, чтобы он работал как другой пользователь и имел некоторый механизм для переключения на другого пользователя, подобного su (но без запуска нового процесса).

Нет, изменить UID нельзя, используя только имя пользователя и пароль. (Понятие «пароль» не распознается kernelм каким-либо образом – оно существует только в пользовательском пространстве.) Чтобы переключиться с одного не-корневого UID на другой, вы должны стать root как промежуточный шаг, обычно с помощью exec() включение бинарного набора.

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

Во-первых, setuid() может определенно использоваться не суперпользователями. Технически все, что вам нужно в Linux, – это возможность CAP_SETUID (и / или CAP_SETGID ) для переключения на любого пользователя. Во-вторых, setuid() и setgid() могут изменять идентификатор процесса между реальным ( пользователем, выполнившим процесс ), эффективным ( владельцем двоичного файла setuid / setgid ) и сохраненными идентификаторами.

Однако ничто из этого не имеет особого отношения к вашей ситуации.

Существует относительно простое, но чрезвычайно надежное решение: используйте помощник root setuid, раздвоенный и выполненный вашим деmonoм службы, прежде чем он создаст какие-либо streamи, и используйте пару сокетов домена Unix для связи между помощником и службой, служба, передающая оба его учетные данные и дескрипторы файла конечной точки канала для помощника при выполнении пользовательских двоичных файлов. Помощник будет проверять все на безопасном уровне, и если все будет в порядке, оно будет вилка и выполнение желаемого помощника пользователя, с указанными конечными точками трубки, подключенными к стандартным входам, стандартным выходом и стандартной ошибкой.

Процедура службы для запуска помощника, как можно раньше, заключается в следующем:

  1. Создайте пару сокетов домена Unix, используемую для привилегированных сообщений между службой и помощником.

  2. Вилка.

  3. В дочернем элементе закройте все лишние дескрипторы файлов, сохранив только один конец пары сокетов. Переназначить стандартный ввод, вывод и ошибку в /dev/null .

  4. В родителе закройте дочерний конец пары сокетов.

  5. В дочернем случае выполните привилегированный вспомогательный двоичный файл.

  6. Родитель отправляет простое сообщение, возможно, без каких-либо данных, но с дополнительным сообщением, содержащим его учетные данные.

  7. Вспомогательная программа ожидает первоначального сообщения от службы. Когда он получает его, он проверяет учетные данные. Если учетные данные не передаются, он немедленно прекращает работу.

Учетные данные в вспомогательном сообщении определяют исходный процесс « UID , GID и PID . Хотя процесс должен заполнить их, kernel ​​проверяет, что они истинны. Помощник, конечно же, проверяет, что UID и GID как ожидалось (соответствуют учетной записи, которую должна выполнять служба как), но фокус в том, чтобы получить статистику по файлу, на который указывает ссылка на /proc/PID/exe . Это подлинный исполняемый файл процесса, который отправил учетные данные. Вы должны убедиться, что он такой же, как у установленного системного демона (принадлежащего root: root, в двоичном каталоге системы).

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

Эта атака тривиально побеждена тремя дальнейшими шагами:

  1. Вспомогательная программа генерирует (криптографически безопасное) псевдослучайное число, например 1024 бита, и отправляет его обратно родительскому.

  2. Родитель отправляет номер обратно, но снова добавляет свои учетные данные в вспомогательное сообщение.

  3. Вспомогательная программа проверяет, что UID , GID и PID не изменились, и что /proc/PID/exe все еще указывает на правильный двоичный демона службы. (Я бы просто повторил полные проверки.)

На шаге 8 помощник уже установил, что другой конец сокета выполняет двоичный файл, который он должен выполнять. Отправляя ему случайный куки-файл, он должен отправить обратно, означает, что другой конец не может «набивать» сокет сообщениями заранее. Конечно, это предполагает, что злоумышленник не может заранее угадать псевдослучайное число. Если вы хотите быть осторожным, вы можете прочитать подходящий файл cookie из /dev/random , но помните, что это ограниченный ресурс (может блокироваться, если для ядра недостаточно случайности). Я бы просто прочитал, скажем, 1024 бит (128 байтов) из /dev/urandom и использовать их.

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

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

  1. Создает необходимые каналы (один для подачи стандартного ввода в двоичный файл пользователя, один для возврата стандартного вывода из двоичного файла пользователя)

  2. Отправляет сообщение помощнику, содержащему

    • Идентичность для запуска двоичного кода как; либо имя пользователя (и группы), либо UID и GID (ы)
    • Путь к двоичному
    • Параметры командной строки, заданные двоичной
    • Вспомогательное сообщение, содержащее дескрипторы файла для пользовательских двоичных конечных точек данных

Всякий раз, когда хелпер получает такое сообщение, он вилки. В дочернем случае он заменяет стандартный ввод и вывод файловыми дескрипторами в вспомогательном сообщении, изменяет идентификатор с помощью setresgid() и setresuid() и / или initgroups() , меняет рабочий каталог на место где-то подходящим и выполняет двоичный файл пользователя. Родительский вспомогательный процесс закрывает дескрипторы файла в вспомогательном сообщении и ждет следующего сообщения.

Если помощник выходит, когда больше нет ввода из сокета, он автоматически выйдет, когда служба выйдет.

Я мог бы предоставить некоторый пример кода, если есть достаточный интерес. Есть много деталей, чтобы получить право, поэтому код немного утомительно писать. Однако, правильно написано, оно более безопасно, чем, например, Apache SuEXEC.