УИП бит может быть установлен на исполняемый файл , так что при запуске, программа будет иметь привилегии владельца файла вместо реального пользователя, если они различны. В этом разница между эффективным UID (идентификатор пользователя) и реальным UID.
Некоторые распространенные утилиты, такие как passwd, принадлежат root и настраиваются таким образом по необходимости ( passwdтребуется доступ, /etc/shadowкоторый может быть прочитан только root).
Лучшая стратегия при этом - делать все, что вам нужно, в качестве суперпользователя, а затем снижать привилегии, чтобы избежать ошибок или неправильного использования при запуске root. Чтобы сделать это, вы устанавливаете эффективный uid процесса к его реальному uid. В POSIX C:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void report (uid_t real) {
printf (
"Real UID: %d Effective UID: %d\n",
real,
geteuid()
);
}
int main (void) {
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
}
Соответствующие функции, которые должны иметь эквивалент в большинстве языков более высокого уровня, если они обычно используются в системах POSIX:
getuid(): Получить настоящий UID.
geteuid(): Получить эффективный идентификатор.
seteuid(): Установить эффективный идентификатор пользователя.
Вы не можете ничего сделать с последним, неподходящим для реального uid, за исключением того, что в исполняемом файле был установлен бит setuid . Чтобы попробовать это, скомпилируйте gcc test.c -o testuid. Затем вам нужно, с привилегиями:
chown root testuid
chmod u+s testuid
Последний устанавливает бит setuid. Если вы теперь работаете ./testuidкак обычный пользователь, вы увидите, что процесс по умолчанию запускается с эффективным uid 0, root.
Как насчет сценариев?
Это варьируется от платформы к платформе , но в Linux вещи, которые требуют интерпретатора, включая байт-код, не могут использовать бит setuid, если он не установлен на интерпретаторе (что было бы очень глупо). Вот простой Perl-скрипт, который имитирует код C выше:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n";
Верные корням * nixy, perl имеет встроенные специальные переменные для эффективного uid ( $>) и реального uid ( $<). Но если вы попробуете то же самое chownи будете chmodиспользовать скомпилированный (из C, предыдущий пример) исполняемый файл, это не будет иметь никакого значения. Скрипт не может получить привилегии.
Ответ на это заключается в использовании двоичного файла setuid для выполнения скрипта:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
puts("Path to perl script required.");
return 1;
}
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
}
Скомпилируйте это gcc --std=c99 whatever.c -o perlsuidтогда chown root perlsuid && chmod u+s perlsuid. Теперь вы можете выполнить любой Perl-скрипт с эффективным uid 0, независимо от того, кому он принадлежит.
Аналогичная стратегия будет работать с php, python и т. Д. Но ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
ПОЖАЛУЙСТА , ПОЖАЛУЙСТА , НЕ оставить такого рода вещи , лежащие вокруг . Скорее всего, вы действительно хотите скомпилировать имя скрипта как абсолютный путь , т. Е. Заменить весь код на main():
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Убедитесь, что /opt/suid_scriptsвсе в нем доступно только для пользователей без полномочий root. Иначе кто-то может поменяться на что угодно whatever.pl.
Кроме того, помните, что многие языки сценариев позволяют переменным среды изменять способ выполнения сценария . Например, переменная среды может привести к загрузке библиотеки, предоставленной вызывающей стороной, что позволит вызывающей стороне выполнить произвольный код от имени пользователя root. Таким образом, если вы не знаете, что интерпретатор и сам скрипт устойчивы ко всем возможным переменным окружения, НЕ ДЕЛАЙТЕ ЭТОГО .
Так что мне делать вместо этого?
Более безопасный способ разрешить пользователю без полномочий root запускать сценарий от имени root - добавить правило sudo и запустить пользователя sudo /path/to/script. Sudo удаляет большинство переменных среды, а также позволяет администратору точно выбирать, кто может выполнить команду и с какими аргументами. См. Как запустить определенную программу от имени пользователя root без запроса пароля? для примера.
-privilegedоболочку, и, если они вызваны из ответственного скрипта, их также можно безопасно вызыватьsuid rootтаким образом. Я боюсь, что понятие ответственного сценария является чем-то вроде чепухи в реальном мире. В любом случае, вероятно, стоит упомянуть, насколько важно избегать