Права доступа к каналу для программы setuid
Я расширяю некоторое программное обеспечение (автором которого я не являюсь), которое работает под GNU / Linux (Ubuntu 14.04) и состоит из manager
процесс и несколько worker
процессы. Менеджер может запустить работника с помощью командной строки, которую я могу указать в файле конфигурации.
После запуска работника менеджер общается с ним по трубе. По соображениям безопасности мы решили разрешить работникам работать не с администратором, а с другим пользователем (давайте назовем их manager-user
а также worker-user
). Это достигается написанием небольшого сценария-оболочки, который переключает пользователя с su
и начинает новый работник. После этого менеджер может общаться через рабочий канал с рабочим процессом. Этот подход работает уже много месяцев.
В качестве альтернативы su
мы рассмотрели использование setuid
немного, чтобы запустить рабочих. Итак, мы написали C
обертка, которая может быть вызвана менеджером для запуска рабочего. Если мы настроим оболочку, которая будет принадлежать manager-user
, рабочий запускается правильно (но, конечно, с неправильными привилегиями). Если мы настроим оболочку, которая будет принадлежать worker-user
и установить setuid
немного, затем рабочие запускаются, но затем выходят, потому что они не могут подключиться к менеджеру.
Итак, мой вопрос: как работает setuid
Исполняемый файл влияет на разрешение на каналах, созданных как родительским, так и дочерним процессом? Может ли быть так, что рабочие процессы, запущенные через setuid-wrapper, не имеют разрешения на открытие каналов менеджера (или наоборот)? Если это так, как мы можем изменить эти разрешения?
У меня мало опыта использования setuid
поэтому любая информация / объяснения приветствуются.
2 ответа
Unix pipe
может быть названным или неназванным. Именованный канал реализован в виде файла, который имеет стандартные биты прав доступа пользователя, группы и владельца мира.
Неназванный канал также имеет разрешения, но они ограничены euid
, egid
, а также umask
на месте во время создания трубы.
Итак, если ваш worker-user
является setuid
другому пользователю с другими правами группы, если только egid
является общим с главным процессом, он не сможет использовать права пользователя или группы для доступа к каналу, который был создан родительским процессом.
Конечно, с определенным umask
значения, безымянные мировые разрешения канала позволят процессам связываться по каналу, но любой процесс сможет читать / записывать этот канал. Быть неназванным более безопасно, чем именованный канал, но предоставление мировых разрешений любому каналу не является хорошей практикой безопасности.
Возможное решение для этого варианта использования (чтобы два коммуникационных процесса выполнялись под разными пользователями) состояло бы в том, чтобы оба manager-user
а также worker-user
процессы находятся в одной группе, и имеют umask
очищается от битов групповой пермиссии во время создания канала, так что оба процесса могут читать и записывать безымянный канал.
Так что если
manager-user
принадлежит группеteam
,worker-user
также принадлежит группеteam
,- оба процесса имеют свою группу
umask
биты очищены (нет значений1x
..7x
) - до создания безымянной трубы
Тогда manager-user
должен иметь возможность писать на безымянном канале и worker-user
должна быть в состоянии прочитать его (или наоборот, в зависимости от того, как канал используется), даже если они работают как отдельные пользователи.
Увидеть man
страница на chmod
для деталей о битах разрешения.
Используйте анонимный канал, используя pipe()
функция, как это (пример заимствован из немецкой Википедии):
# check the link above for #includes and const definitons
int main(void) {
int fd[2], n, i;
pid_t pid;
char line[MAX_CHARS];
// Create the pipe
if (pipe(fd) < 0)
fprintf(stderr, "Failed to create pipe()");
// Fork child
if ((pid = fork()) > 0) {
// Parent process
close(fd[0]);
fprintf(stdout, "Parent : ");
fgets(line, MAX_CHARS, stdin);
write(fd[1], line, strlen(line));
if (waitpid(pid, NULL, 0) < 0)
fprintf(stderr, "Error: waitpid()");
}
else {
// Child process
close(fd[1]);
n = read(fd[0], line, MAX_CHARS);
for (i = 0; i < n; i++)
line[i] = toupper(line[i]);
fprintf(stderr, "Child : ");
write(STDOUT_FILENO, line, n);
}
exit(0);
}
Вышеприведенная программа создает однонаправленный канал, где родительский процесс содержит конец чтения, а дочерний процесс - конец записи.