GitHub

Tensho

Заметки непутевого программиста

Set-User-ID и Set-Group-ID

08/10/2018

Благодаря великолепной книге “Advanced Programming in the UNIX Environment” (W. Richard Stevens, Stephen A. Rago) я бы хотел рассказать о малоизвестных битах режима исполняемых файлов set-user-ID и set-group-ID.

Привилегии UNIX систем, такие как возможность изменения системных настроек (текущей даты) или управление доступом чтения/записи конкретных файлов, базируются на UID (user ID) и GID (group ID).

Каждый процесс имеет несколько ассоциированных идентификаторов пользователей и групп, которые определяеются текущим пользователем, владельцем и режимом запуска исполняемого файла:

ID Описание
real user/group ID Кто мы на самом деле, берутся из /etc/passwd при логине текущего пользователя, обычно не меняются в течении всей сессии, однако процесс с привелегиями суперпользователя может их изменить.
effective user/group ID Фактические текущие идентификаторы пользователя и группы, который определяют привелегии использующиеся для проверки разрешений доступа данного процесса к ресурсам.
saved set-user-ID/set-group-ID Функция exec копирует (сохраняет) значения эффективных идентификаторов при старте выполнения программы на случай, если потребуется их восстановить позже. Существуют системные вызовы setuid/setgid, которые могут меня привелегии в процессе выполнения программы.

Когда мы запускаем на исполнение файл программы, effective user ID процесса обычно равен real user ID, а effective group ID соответственно равен real group ID. Однако, исполняемому файлу можно установить специальные флаги (биты режима файла) set-user-ID и set-group-ID, которые говорят следующее: “Когда этот файл будет запущен на исполнение, нужно выставить его effective user/group ID равный UID/GID владельца данного файла”. Другими словами это возможность застолбить привелегии владельца программы независимо от того, кто будет запускать ее в будущем. Луше всего это понять на примере. Сначала создадим самый обыкновенный исполняемый файл (ну если быть точнее, то интерпретируемый исполняемый файл для оболочки) и посмотрим, какие идентификаторы получит процесс во время запуска:

$ id # Кем я залогинен?
uid=501(tensho) gid=20(staff) ...
$ cat <<EOT > prog.rb # Пишем исходный код
#!/usr/bin/ruby

include Process

puts "Process uid=(#{uid}) gid=(#{gid}) euid=(#{euid}) egid=(#{egid})"
EOT
$ chmod +x prog.rb # Делаем файл исполняемым
$ ls -lah prog.rb
-rwxr-xr-x  1 tensho  staff   106B Oct  8 19:10 prog.rb
$ ./prog.rb # Запускаем программу
Process uid=(501) gid=(20) euid=(501) egid=(20)

Как видно реальный и эффективный идентификаторы совпадают с владельцем файла. Очевидно, что при смене пользователя мы увидим другие идентификаторы:

$ sudo -u nobody ./prog.rb # Запускаем программу с привелегиями пользователя nobody
Process uid=(4294967294) gid=(4294967294) euid=(4294967294) egid=(4294967294)
$ sudo ./prog.rb # Запускаем программу с привелегиями суперпользователя root
Process uid=(0) gid=(0) euid=(0) egid=(0)

Теперь посмотрим, как все будет выглядет при использовании set-user-ID/set-group-ID:

$ sudo chown root:wheel prog.rb # Меняем владельца файла на root:wheel
$ sudo chmod +s prog.rb # Выставляем set-user-id/set-group-id
$ ls -lah prog.rb # Буква 's' в отображаемом режиме файла указывает на установленный set-user-id/set-group-id
-rwsr-sr-x  1 root  wheel   106B Oct  8 19:10 prog.rb
$ ./prog.rb # Запускаем программу
Process uid=(501) gid=(20) euid=(501) egid=(20)

Как видно, идентификаторы не поменялись, хотя мы этого ожидали. Дело в том, что в Unix существуте ограничение, которое заставляет ядро не принимать во внимание set-user-ID/set-group-ID выставленные для скриптов оболочки в виду уязвимостей безопасности. Поэтому мы переведем Ruby скрипт на C и скомпилируем самый настоящий бинарник:

$ cat <<EOT > prog.c # Пишем исходный код
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int
main(void)
{
  printf("Process uid=(%d) gid=(%d) euid=(%d) egid=(%d)\n", getuid(), getgid(), geteuid(), getegid());
  exit(0);
}
EOT
$ clang prog.c -o prog # Компилируем
$ sudo chown root:wheel prog # Меняем владельца файла на root:wheel
$ sudo chmod +s prog # Выставляем set-user-id/set-group-id
$ ls -lah prog # Буква 's' в отображаемом режиме файла указывает на установленный set-user-id/set-group-id
-rwsr-sr-x  1 root  wheel   8.5K Oct  8 19:20 prog
$ ./prog # Запускаем программу
Process uid=(501) gid=(20) euid=(0) egid=(0)

Теперь все в порядке ^_^

set-user-ID/set-group-ID биты режима файла используется во многих системных утилитах, т.к. им нужно иметь привелегии работать с системными файлами будучи запущенными любым пользователем. Например passwd требуется привилегия изменять /etc/passwd файл, который принадлежит root пользоавтелю, даже если саму команду запускает непосредственно пользователь tensho.

Дополняшки