пятница, 12 сентября 2014 г.

Linux. Упражнения с set(u|g)id и sticky bit

В прошлый раз я исследовал на практике, как работают разрешения rwx в Linux. Сегодня я продолжу упражнения с правами доступа к файлам и каталогам в Linux и рассмотрю атрибуты setuid, setgid и sticky bit. (Забегая вперед, скажу, что приключение оказалось интереснее, чем я ожидал.) Все приводимые далее примеры я буду выполнять в консоли Ubuntu 14.04.

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

Увидеть эти атрибуты процессов позволяет команда ps:

ay@tsuki:~$ ps -o uid,user,gid,group,pid,comm
  UID USER       GID GROUP      PID COMMAND
 1002 ay        1002 ay        2627 bash
 1002 ay        1002 ay        2689 ps

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

  • setuid - сделать эффективным пользователем процесса владельца исполняемого файла, и
  • setgid - сделать эффективной группой процесса группу исполняемого файла.

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

Классический пример использования setuid - утилита passwd, которая, при изменении каждым пользователем своего пароля, вносит изменения в файл /etc/passwd, доступный для записи только root:

ay@tsuki:~$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2002 авг.  27 21:15 /etc/passwd

Повторю: каждый пользователь, изменяя свой пароль, вносит изменения в файл, недоступный ему для записи!

Для того, чтобы утилита passwd могла изменять файл /etc/passwd, процесс passwd выполняется с правами эффективного пользователя - владельца исполняемого файла, а этим владельцем является root:

ay@tsuki:~$ ls -l `which passwd`
-rwsr-xr-x 1 root root 47032 февр. 17  2014 /usr/bin/passwd

Буква s в триаде user означает установленный атрибут setuid. Помимо passwd, имеются и другие файлы с установленным setuid. Вот некоторые из них:

ay@tsuki:~$ ls -l /usr/bin/* | grep rws
-rwsr-xr-x 1 root root      46424 февр. 17  2014 /usr/bin/chfn
-rwsr-xr-x 1 root root      41336 февр. 17  2014 /usr/bin/chsh
-rwsr-xr-x 1 root root      68152 февр. 17  2014 /usr/bin/gpasswd
...

В /usr/bin имеются также файлы с установленным setgid:

ay@tsuki:~$ ls -l /usr/bin/* | egrep '^-.....s'
-rwxr-sr-x 1 root tty       14688 июня   5  2013 /usr/bin/bsd-write
-rwxr-sr-x 1 root shadow    54968 февр. 17  2014 /usr/bin/chage
-rwxr-sr-x 1 root crontab   35984 февр.  9  2013 /usr/bin/crontab
...

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

Что если бы setuid был установлен у файла /bin/rm?

А если бы в системе отыскался файл, принадлежащий root, с правами доступа -rwsrwxrwx?

В качестве эксперимента с setuid напишу скрипт come2party для самозаписи пользователей на некое мероприятие. Файл с записями будет доступен для изменения только пользователю-владельцу скрипта - чтобы никто не мог вычеркнуть другого записавшегося.

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

ay@tsuki:~$ touch come2party.lst
ay@tsuki:~$ chmod 644 come2party.lst
ay@tsuki:~$ ls -l come2party.lst
-rw-r--r-- 1 ay ay 0 сент.  9 19:42 come2party.lst

Теперь создам скрипт и установлю необходимые разрешения:

ay@tsuki:~$ cat > come2party
#!/bin/sh
echo $USER >> /home/ay/come2party.lst
^D
ay@tsuki:~$ chmod 4755 come2party
ay@tsuki:~$ ls -l come2party
-rwsr-xr-x 1 ay ay 95 сент.  9 19:43 come2party

Атрибуты setuid, setgid и sticky bit кодируются 4-ой справа цифрой в восьмеричном представлении разрешений: 4 - setuid, 2 - setgid, 1 - sticky bit. В статье Linux. Упражнения с rwx мы уже встречались с четырехразрядным восьмеричным представлением разрешений - когда воспользовались командой umask.

Запишусь на вечеринку первым!

ay@tsuki:~$ come2party 
ay@tsuki:~$ cat come2party.lst 
ay

Теперь пусть запишется пользователь andrey:

andrey@tsuki:~$ /home/ay/come2party 
/home/ay/come2party: 2: /home/ay/come2party: cannot create /home/ay/come2party.lst: Permission denied

Неудача! Проверяю, что скрипт come2party имеет установленный атрибут setuid:

andrey@tsuki:~$ ls -l /home/ay/come2party
-rwsr-xr-x 1 ay ay 48 сент.  9 19:47 /home/ay/come2party

В чем же дело?

Оказывается, в Linux (и некоторых других ОС семейства Unix) атрибуты setuid и setgid для shell-скриптов не работают! Это ограничение связано с обеспечением безопасности.

На самом деле, скрипт come2party, как и любой shell-скрипт, выполняется программой-интерпретатором /bin/sh или иной, указанной в первой строке скрипта. Процесс, интерпретирующий скрипт, создается из двоичного исполняемого файла /bin/sh (или иного), который не имеет установленных setuid или setgid и потому выполняется с правами запускающего пользователя:

andrey@tsuki:~$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 июля  26 20:15 /bin/sh -> dash
andrey@tsuki:~$ ls -l `which dash`
-rwxr-xr-x 1 root root 121272 февр. 19  2014 /bin/dash
andrey@tsuki:~$ ls -l `which bash`
-rwxr-xr-x 1 root root 1017016 апр.  24 09:43 /bin/bash

"Несамостоятельность" выполнения скрипта /home/ay/come2party можно наглядно увидеть, если добавить в скрипт команду ps, отображающую выполняемую командную строку. Добавив команду ps, посмотрю на результат как пользователь andrey:

andrey@tsuki:~$ cat /home/ay/come2party
#!/bin/sh
ps -o uid,user,gid,group,pid,command
echo $USER >> /home/ay/come2party.lst
andrey@tsuki:~$ /home/ay/come2party
  UID USER       GID GROUP      PID COMMAND
 1000 andrey    1000 andrey    3324 bash
 1000 andrey    1000 andrey    3401 /bin/sh /home/ay/come2party
 1000 andrey    1000 andrey    3402 ps -o uid,user,gid,group,pid,command
/home/ay/come2party: 3: /home/ay/come2party: cannot create /home/ay/come2party.lst: Permission denied

Из вывода команды ps видно, что

  1. скрипт /home/ay/come2party передан в качестве параметра интерпретатору /bin/sh,
  2. процесс выполняется с эффективными пользователем и группой, равными текущему пользователю и его первичной группе.
(Можно также заметить, что порожденный процессом 3401 процесс 3402, исполняющий команду ps, унаследовал эффективных пользователя и группу процесса-родителя.)

Чтобы реализовать задуманный сценарий с самозаписью пользователей на вечеринку, мне нужен бинарный исполняемый файл come2party с установленным setuid. Что же касается текстовых файлов, содержащих интерпретируемые скрипты (bash, python, perl и другие), то для них setuid и setgid Linux не поддерживает.

Вместо создания собственного бинарника я решил проделать следующий трюк под пользователем ay:

  1. скопировать /bin/bash в домашний каталог,
  2. установить для /home/ay/bash атрибут setuid и
  3. прописать /home/ay/bash в первой строке скрипта come2party.

Тогда скрипт come2party будет выполняться интерпретатором /home/ay/bash с эффективным пользователем ay, который сможет перенаправить вывод команды echo в файл come2party.lst!

ay@tsuki:~$ cp /bin/bash bash
ay@tsuki:~$ chmod u+s bash
ay@tsuki:~$ ls -l bash
-rwsr-xr-x 1 ay ay 1017016 сент.  9 20:23 bash
ay@tsuki:~$ cat > come2party
#!/home/ay/bash
echo $USER >> /home/ay/come2party.lst
^D
ay@tsuki:~$ ls -l come2party
-rwxr-xr-x 1 ay ay 57 сент.  9 20:25 come2party

Очищу come2party.lst и запишусь первым:

ay@tsuki:~$ cat /dev/null > come2party.lst
ay@tsuki:~$ ls -l come2party.lst
-rw-r--r-- 1 ay ay 0 сент.  9 20:16 come2party.lst
ay@tsuki:~$ come2party 
ay@tsuki:~$ cat come2party.lst 
ay

Теперь пусть запишется пользователь andrey:

andrey@tsuki:~$ /home/ay/come2party 
/home/ay/come2party: 2: /home/ay/come2party: cannot create /home/ay/come2party.lst: Permission denied

Снова неудача!

Но этого не может быть: интерпретатор /home/ay/bash, скрипт come2party и файл come2party.lst принадлежат ay, и интепретатор выполняет скрипт с эффективными правами пользователя ay! Проверю, так ли это, выполнив команду ps при помощи домашнего bash:

andrey@tsuki:~$ /home/ay/bash -c 'ps -o uid,user,gid,group,pid,command'
  UID USER       GID GROUP      PID COMMAND
 1000 andrey    1000 andrey    3599 bash
 1000 andrey    1000 andrey    3751 ps -o uid,user,gid,group,pid,command

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

Получается, что атрибут setuid не работает с bash? Или вмешивается selinux?

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

Однако, выход имеется. Ключ -p позволяет отключить параноидальное поведение bash и добиться желаемого:

andrey@tsuki:~$ /home/ay/bash -pc 'ps -o uid,user,gid,group,pid,command'
  UID USER       GID GROUP      PID COMMAND
 1002 ay        1000 andrey    3777 ps -o uid,user,gid,group,pid,command

Ура :)

Пусть теперь пользователь ay изменит первую строчку скрипта /home/ay/come2party, чтобы интерпретатор запускался с ключом -p. Скрипт станет таким:

ay@tsuki:~$ cat come2party
#!/home/ay/bash -p
echo $USER >> /home/ay/come2party.lst

Теперь andrey сможет, наконец, записаться на вечеринку:

andrey@tsuki:~$ /home/ay/come2party 
andrey@tsuki:~$ cat /home/ay/come2party.lst
ay
andrey

Итак, мне удалось заставить setuid работать с копией интерпретатора bash, принадлежащей пользователю ay. Но возможности bash широки, и пользователь andrey может теперь не только записаться на вечеринку, но и многое другое. Например, удалить файлы, принадлежащие ay:

andrey@tsuki:~$ rm -f /home/ay/come2party.lst
rm: cannot remove ‘/home/ay/come2party.lst’: Permission denied
andrey@tsuki:~$ /home/ay/bash -pc 'rm -f /home/ay/come2party*'
andrey@tsuki:~$ ls -l /home/ay/come2party*
ls: cannot access /home/ay/come2party*: No such file or directory

Очевидно, что bash с setuid чертовски опасен для его владельца. Теперь я убежден, что setuid не стоит раздавать каким угодно исполняемым файлам.

В данном случае опасность может быть ликвидирована с помощью ее самой:

andrey@tsuki:~$ /home/ay/bash -pc 'rm -f /home/ay/bash'
andrey@tsuki:~$ ls -l /home/ay/bash
ls: cannot access /home/ay/bash: No such file or directory

На этом я завершу эксперименты с setuid для файлов, сделаю перерыв на кофе, и перейду к каталогам.

Из двух атрибутов setuid и setgid для каталогов в Linux используется только setgid.

Установка атрибута setgid для каталога приводит к тому, что файлы, создаваемые в этом каталоге, в качестве группы-владельца получают не первичную группу их создателя, а группу каталога. Файлы в каталоге как бы наследуют группу от каталога с атрибутом setgid.

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

Поставлю эксперимент. Для начала, создам нового пользователя ya, новую группу ayya и добавлю в нее пользователей ay и ya.

andrey@tsuki:~$ sudo useradd -m ya
[sudo] password for andrey: 
andrey@tsuki:~$ sudo passwd ya
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
andrey@tsuki:~$ sudo groupadd ayya
andrey@tsuki:~$ sudo usermod -aG ayya ay
andrey@tsuki:~$ sudo usermod -aG ayya ya
andrey@tsuki:~$ groups ay ya
ay : ay ayya
ya : ya ayya

Группа ayya является вторичной группой (secondary group) для пользователей ay и ya, а их первичные группы - персональные группы ay и ya, соответственно. Вторичные группы используются только для получения доступа к существующим файлам и каталогам. Тогда как первичная группа устанавливается в качестве группы-владельца созданных пользователем файлов и каталогов.

Создам каталог, для которого установлю группу ayya, полные разрешения для группы и атрибут setgid.

ay@tsuki:~$ mkdir shared
ay@tsuki:~$ chgrp ayya shared
ay@tsuki:~$ chmod g+sw shared
ay@tsuki:~$ ls -ld shared
drwxrwsr-x 2 ay ayya 4096 авг.  30 18:08 shared

Создам файл в этом каталоге как пользователь ay:

ay@tsuki:~$ echo It\'s $USER > ~ay/shared/created_by_$USER.txt

А теперь, как пользователь ya, создам там же другой файл и изменю файл, созданный пользователем ay:

ya@tsuki:~$ echo It\'s $USER > ~ay/shared/created_by_$USER.txt
ya@tsuki:~$ ls -l ~ay/shared
total 8
-rw-rw-r-- 1 ay ayya 8 авг.  30 18:11 created_by_ay.txt
-rw-rw-r-- 1 ya ayya 8 авг.  30 18:12 created_by_ya.txt
ya@tsuki:~$ echo It\'s $USER >> ~ay/shared/created_by_ay.txt
ya@tsuki:~$ cat ~ay/shared/created_by_ay.txt
It's ay
It's ya

Как видим, созданные в каталоге ~ay/shared файлы наследуют группу-владельца от каталога и доступны членам этой группы для изменения. setgid для каталога работает согласно теории!

Наконец, последний предмет для экспериментирования сегодня - атрибут sticky bit.

Как рассказывает Википедия, когда-то в ОС семейства Unix "липкий бит" использовался для удержания кода исполняемых файлов в свопе - для их быстрого повторного запуска. Но эта практика осталась в прошлом, и в Linux данный атрибут используется только с каталогами.

В Linux установленный для каталога sticky bit запрещает удаление и переименование файлов в этом каталоге всем, кроме root, владельца каталога и владельцев соответствующих файлов. Этот запрет работает независимо от разрешений каталога и имеет приоритет над ними.

Так, каталог /tmp имеет полные разрешения, в него пишут все, но не могут удалить чужие файлы и тем помешать работе других пользователей:

$ ls -ld /tmp
drwxrwxrwt 7 root root 4096 Apr 17 15:55 /tmp

Поставлю эксперимент, дав всем пользователям полный доступ в каталог /home/ay/dummy:

ay@tsuki:~$ chmod 777 dummy
ay@tsuki:~$ ls -ld dummy
drwxrwxrwx 2 ay ay 4096 авг.  27 22:55 dummy

Теперь пользователи andrey и ya создадут файлы в этом каталоге, после чего ya беспрепятственно удалит как свой, так и чужой файлы:

andrey@tsuki:~$ echo Hello > /home/ay/dummy/andrey\'s
andrey@tsuki:~$ ls -l /home/ay/dummy
total 4
-rw-rw-r-- 1 andrey andrey 6 сент.  9 22:13 andrey's
andrey@tsuki:~$ su - ya
Password: 
ya@tsuki:~$ echo Hello > /home/ay/dummy/ya\'s
ya@tsuki:~$ ls -l /home/ay/dummy
total 8
-rw-rw-r-- 1 andrey andrey 6 сент.  9 22:13 andrey's
-rw-rw-r-- 1 ya     ya     6 сент.  9 22:13 ya's
ya@tsuki:~$ rm -f /home/ay/dummy/*
ya@tsuki:~$ ls -l /home/ay/dummy
total 0

Теперь установлю на /home/ay/dummy атрибут sticky bit:

ay@tsuki:~$ chmod +t dummy
ay@tsuki:~$ ls -ld dummy
drwxrwxrwt 2 ay ay 4096 сент.  9 22:14 dummy

Пользователи andrey и ya снова создадут файлы в этом каталоге и ya попытается удалить чужой файл:

andrey@tsuki:~$ echo Hello > /home/ay/dummy/andrey\'s
andrey@tsuki:~$ ls -l /home/ay/dummy
total 4
-rw-rw-r-- 1 andrey andrey 6 сент.  9 22:15 andrey's
andrey@tsuki:~$ su - ya
Password: 
ya@tsuki:~$ echo Hello > /home/ay/dummy/ya\'s
ya@tsuki:~$ ls -l /home/ay/dummy
total 8
-rw-rw-r-- 1 andrey andrey 6 сент.  9 22:15 andrey's
-rw-rw-r-- 1 ya     ya     6 сент.  9 22:15 ya's
ya@tsuki:~$ rm -f /home/ay/dummy/andrey\'s
rm: cannot remove ‘/home/ay/dummy/andrey's’: Operation not permitted

Не получается! Так работает sticky bit.

Теперь удалю файлы как владелец каталога:

ay@tsuki:~$ rm -f dummy/*
ay@tsuki:~$ ls -l dummy
total 0

Кстати, сценарий самозаписи на вечеринку можно реализовать с помощью каталога с установленным "липким" битом. Для записи на вечеринку пользователи будут создавать файл с именем пользователя в таком каталоге. Другие пользователи не смогут удалить чужие файлы из этого каталога, а значит, не смогут "вычеркнуть" записавшихся ранее. Такое решение позволяет обойтись без использования setuid (и не требует создания бинарного файла специально для этого случая).

В заключение, удалю пользователей и группу, использовавшиеся для экспериментов:

andrey@tsuki:~$ sudo groupdel ayya
[sudo] password for andrey: 
andrey@tsuki:~$ sudo userdel ay
andrey@tsuki:~$ sudo userdel ya
andrey@tsuki:~$ sudo rm -Rf /home/ay /home/ya

3 комментария:

  1. Интересно, а возможно ли на samba 4 реализовать к шаре следующий двойственный вариант доступа: чтобы под одним пользователем можно было войти с логином и паролем с полным доступом ко всем папкам(подпапкам) и файлам в шаре. В то же время, в этой же шаре был публичный доступ только на чтение (выполнение) без возможности правки, за исключением только файлов в одной конкретной подпапке?
    Нет ли у вас мануала по реализации что-то подобного?

    ОтветитьУдалить
  2. Увы, с самбой не разбирался.

    ОтветитьУдалить
  3. Ну с самбу худо-бедно попробую победить сам. Те доступы и права что требуется, все-равно должно решаться чисто путем настройки правильных доступов для пользователей. А именно...
    Есть в системе user1 и user2 и общая папка. Надо, чтобы user1 имел полный доступ на все подпапки и файлы. А user2 только чтение-выполнение на все подпапки и файлы, кроме одной конкретной подпапки FOLDER, где должна быть полные права у обоих пользователей. Причем user2 не должен смочь переименовать и удалять эту подпапку.
    Есть соображения как это возможно реализовать?

    ОтветитьУдалить