Процессы и права доступа
Для начала скажу, что значит команда wc. Команда wc принимает на вход текстовый поток и считает количество строк. Весь вывод, который мы получили в результате команды ls, был отправлен на вход команде wc и, в результате, был взят из этого ввода. Что на самом деле произошло? На самом деле, был организован канал между двумя процессами. Чтобы перейти к каналам нужно сказать что такое процесс. Процесс - это программа, работающая под управлением операционной системы. В файловой системе есть какие-то программы и, на самом деле, когда мы запускаем программы, порождается процесс.
$ ls py | wc
породил два процесса (один из них - ls, второй - wc) и организовал канал между ними.
Давайте запустим программy top. top, помимо того, что выводит кучу информации, выводит еще и табличку, которая отображает процессы, которые поместились на наш экран. Тут легко видеть, что в самой последней колонке - название команды, которая выполняется от этого процесса, от какого пользователя запущена. Linux - многопользовательская система и различные программы выполняются от различных пользователей.
Вопрос: зачем это сделано?
Ответ: для ограничения прав доступа.
Первой колонкой указан PID - идентификатор процесса. Зачем он нужен? Конкретные сигналы в юникс представлены числами. Для чего они нужны - для межпроцессного взаимодействия, например, чтобы синхронизировать работу процессов. Давайте пошлем сигнал процессу 1840. Работа процесса завершилась, потому что процесс принял сигнал о завершении. Сигнал - один из возможных механизмов межпроцессного взаимодействия и с помощью программы top мы отправили сигнал, который мягко завершает выполнение (точнее, не сам завершает - у разных процессов есть обработчики сигналов). Еще раз запустим top, введем комбинацию клавиш «ctrl+c». Запустим cat:
$ cat > somefile
Будем получать то, что мы вводим, сразу на вывод. В терминале у разных комбинаций клавиш есть возможность интерактивного взаимодействия с процессами. Например, нажав «ctrl+z» мы вышли из cat. На самом деле, этот процесс не был убит. Если мы запустим top то мы увидим, что от пользователя python под PID 1897 висит cat. Он на самом деле застоплен - об этом говорит его статус. А что такое застопленный процесс? - Это значит, что в данный момент они вообще не работают и находятся в состоянии ожидания того, чтобы их разбудили. Но при этом они не убиты: остановлены на какой-то своей инструкции, остались открытыми все файлы. Когда они проснутся, скорее всего, они даже не поймут, что были застоплены. Как нам разбудить процесс обратно? В юникс терминалах есть команда:
$ %
И вот мы вернулись в наш cat. Давайте еще раз его застопим. Линукс - многозадачная система. Если мы запустили какую-то команду без знака «&» - она не выполняется в фоне. Мы можем приостановить программу и сказать ей, чтобы она работала в фоне с помощью команды
$ bg
от английского boomerang. Откроем снова top, найдем cat - он все еще живой, застоплен, но он в фоне. Мы можем его разбудить, отправив ему сигнал
$ kill -CONT
Если мы хотим разбудить какой-то конкретный процесс, то после минуса мы вводим либо имя, либо номер сигнала, который мы хотим ему отправить. Мы можем посмотреть список сигналов с ман страницы
$ man 7 signal
В разных ос номера сигналов разные. Для стоп есть как минимум 2 сигнала:
SIGSTOP SIGTSTP.
Мы можем вернуть процесс обратно в фореграунд с помощью
$ fg
Наш cat снова в фореграунде. Как нам выйти из cat? Есть несколько вариантов. Сat завершит свою работу, когда заканчивается входной файл. Cat можно убить с помощью «сtrl+c».
$ cat -> somefile - перенаправление из потока вывода в файл somefile.
Если somefile не было в текущем каталоге - он создался и все что из cat записалось в этот файл, если же он существовал - он был перезаписан новым содержимым.
$ less somefile
и тут мы получаем 2 строчки вместо трех. Почему? Ввод-вывод буферизирован в линукс. И в данном случае мы не переводили строчку и не посылали признак конца файла, и тот вывод, который не успел сброситься из кэша, не попал в канал. Канал закрылся и все, что было в буфере, безвозвратно исчезло. Если вы программируете на С - всегда надо закрывать файлы перед завершением процесса, если вы хотите чтобы файлы были сохранены.
$ ls /etc
Попробуем почитать какой-нибудь каталог отсюда
$ ls /etc/tcb
- отказано в доступе - мы не можем прочесть содержимое каталога. Попробуем перенаправить вывод в файл
$ ls /etc/tcb >somefile
Почему мы видим текст ошибки, а он не оказался в файле? Это диагностическое сообщение, которое было отправлено не в поток вывода, а в поток ошибок. Это сделано, чтобы отделить данные, с которыми нам нужно работать, от служебной информации вроде этой. А если мы хотим записать - что нам делать? Мы можем перенаправлять потоки. На самом деле, когда мы пишем знак «>», мы перенаправляем поток вывода, который имеет дескриптор 1. Если хотим перенаправить ошибки - пишем 2:
$ ls /etc/tcb 2>somefile
Давайте напишем
$ ls /etc/tcb /
перенаправим поток ошибок в файл
$ ls /etc/tcb / 2>somefile
Давайте напишем
$ ls /etc/tcb / 2>&1>somefile
- мы поставили &, чтобы шелл не подумал, что мы хотим перенаправить вывод в файл, который называется 1, поэтому мы написали &.
$ ls /etc/tcb / somefile 2>&1
- порядок перенаправления важен.
А давайте откроем somefile - и тут у нас собственно то, что мы и хотели сделать. Если мы не хотим каждый раз уничтожать и перезаписывать файл - у нас есть возможность добавлять текст в конец файла, например
$ ls >>somefile
- добавить в конец этого файла вывод.
Что это все такое? Каждый раз, когда мы выполняем ls, у нас создается процесс с уникальным идентифакатором, у которого открыто 3 файла по умолчанию: стандартный поток ввода, вывода и ошибок.
$ cat <somefile
- мы команде cat на стандартный поток ввода отправили содержимое файла и так как там присутствует признак конца файла (он конечный), то после того, как мы его прочитали и отправили, мы его тут же вывели.
$ cat <somefile | sort
- отсортировал нам вывод.
$ cat | sort
- когда мы нажмем ctrl+c, поток ввода закрывается. Cat по мере нашего ввода отправляет через канал. Еще до того, как файл завершил работу, он получает данные, но когда он получил признак конца файла, он понимает что можно выводить, и он выводит в отсортированном виде. Что мы можем из этого полезного сделать?
$ echo «%» >/etc/tcb/myFile
- нам отказали в доступе.
Что это вообще значит? Откуда отказано в доступе взялось? У каждого файла в линуксе есть права доступа, и изначально в юниксе была только дискреционная модель прав доступа, она сохранилась по сей день, но была дополнена еще различными другими атрибутами.
$ ls -l /etc
- показывает нам файлы.
3 колонка - владелец файла 4 колонка - группа владельца файла.
Вопрос: Зачем нужны группы владельцев?
Ответ: В т.ч. для безопасности: чтобы процесс одного пользователя не нарушил файлы другого пользователя.
2 колонка - жесткие ссылки. 1 колонка самая страшная из всех. Здесь у нас 10 полей. Самое первое - тип файла. Тип файла в линуксах означает не то же самое, что в виндоус, которая по расширениям определяет тип файла. Тут, если, например стоит прочерк «-» - обычный файл, «d» - папка, «l» - символические ссылки, «b» - блочное устройство, «c» - character device - символьное устройство. «b» - это устройства, где можно работать поблоково и для которых есть произвольный доступ. Давайте вернемся в
$ ls -l /etc
Что означают остальные 9 полей? Эти поля как раз определяют права доступа на файл. На всех юниксовых файловых системах, которые были написан для юникс-систем, поддерживают дискреционную модель доступа. 9 полей можно разделить 3 по 3 группы. Первая тройка: rwx - этим определяются права доступа для владельца файла. Этот каталог: его владелец - root, и rwx значит, что root может читать (read), писать (write) и исполнять (execute) этот файл. В случае каталога это значит, что можно открыть его содержимое. Вторая тройка: то же самое, но для группы владельца. Все пользователи, которые являются членами данной группы, могут обладать описанными правами доступа. А тут 2 минуса в начале на месте r и w: это значит, что никто из участников этой группы, не считая владельца, не может ни читать, ни писать этот файл. Но может исполнять его.
Последняя тройка: что позволено делать всем остальным, кто не является совладельцем и не входит в группу. Совершается проверка. Является ли пользователь владельцем? Да - применяются разрешения, если нет - входит ли он в группу? Да - разрешения, нет - проверка, какие разрешения допускаются для остальных. Такая схема подразумевает, что если у владельцев файла меньше прав и при этом владелец входит в группу, где прав больше, то у него все равно будет меньше прав по сравнению с остальными участниками группы, так как владелец может делать то, что он может, и все. Из терминала командной строки мы выполняем разные команды, выполняя разные процессы. Как они порождаются? В классических юниксах есть один способ породить новый процесс - скопировать текущий. Для этого используется системный вызов
$ fork
Он создает дочерний процесс, который является точной копией процесса, который вызвал текущий процесс, но у него отличается при этом PID и то значение, которое вернуло системный вызов fork. В случае неуспеха возвращается -1.
fork уже давно работал по принципу copy on write. В современных линуксах уже очень давно нет fork, есть системный вызов clone - fork эмулируется. Так как мы работаем от пользователя python, то все порожденные процессы работают тоже от пользователя python.
$ cd /
выйдем из директории пользователя.
Вопрос: Есть ли возможность запустить программу от другого пользователя?
$ whoami
- мы питон
$ sudo -i $ whoami
- мы root! Почему пользователь python стал root?
$ which sudo
- откуда мы запускаем sudo? /usr/bin/sudo - отсюда. Это прописано в переменных окружения.
$ echo $PATH
- переменная окружения PATH. Тут указаны через двоеточие каталоги, где можно искать sudo. Там, где нашел - оттуда и запускается.
$export PATH= $sudo
- все пропало! Почему?
$ echo $PATH
—пусто, потому что у нас пропал PATH. Шелл глупый, он просто слепо доверяет РАТН.
$which sudo
вывело нам /usr/bin/sudo.
Когда мы вводим
$ ls -l ‘which sudo’
- получили то же самое, что получили бы если бы ввели
$ ls -l usr/bin/sudo
Программу можно запускать и по полному пути:
$/bin/ls -l /usr/bin/sudo
Есть еще один вариант подстановки:
$ ls -l $(which sudo)
Можно даже сделать подстановку подстановки:
$ ls -l $(which $(echo sudo))
Когда мы запускаем sudo, она сразу же запускается. Попробуем запустить sudo, не вводя пароль:
$sudo -i
Теперь откроем другой терминал
$ top $ ps u
- у нас там есть sudo, мы ее запустили и она уже работает.
Sudo - гибкая программа, ее можно настроить. В текущих настройках сначала проверяет, позволено ли пользователю запускать судо, в зависимости от настроек она может спрашивать пароль, а может вообще не включаться. Это может быть пароль пользователя, а может быть просто пароль. Долгое время в разных юниксах и в линуксе программа ping тоже была суидной. Тут она тоже такая, но, скорее всего она где-то там понижает свои привелегии.
Те программы, для которых в классической дискреционная модели нельзя понизить привелегии, выполняется механизм, который понижает их. За счёт этого получается достаточно безопасное решение - система подписей. Иметь суидные программы - потенциальная дыра в системе, небезопасно это. Итак, для того чтобы пинг работал, ему не нужно давать полный суид, а можно давать просто CAP_NET_RAW.
$ chmod -s /bin/ping
- с помощью chmod можно менять права доступа к файлу.
Мы сняли, если мы выйдем из рута и перейдем в питон пользователя, у нас при попытке пинговать ничего не получится. Если мы вернем обратно
$ chmod +s /bin/ping
- наш пользователь может спокойно пинговать.
Давайте снимем вид исполняемости с cat:
$ chmod -x /bin/cat $ ls -l /bin/cat
Попробуем выполнить cat:
$ /bin/cat
- отказано в доступе: для root файл оказался неисполняемым. Более того, если мы снимем предпочтения, то мы даже не сможем прочитать его.
Сделаем хитрость:
$ chmod o+x /bin/cat
«о» - значит для всех остальных, «+х» - вернуть
$ chmod 600 /bin/cat
- если мы почитаем ман - узнаем, что бывают числовые значения:
600 - владелец может и читать и писать, 700 - еще и исполняется, 40, 20, 10 - для группы, 4, 2, 1 - для пользователя.
$ ls -l /tmp
- у нас есть каталог tmр, он доступен для всех как угодно. Но проблема: это также означает, что любой пользователь может удалять не свои файлы. Существует небольшой стики бит: drwxrwxrwxt — «t» значит, что файлы могут удаляться только теми, кто их создал.
Современные линуксы выходят далеко за рамки дискреционной модели доступа и тут есть расширенные атрибуты: пользователь может сказать, что к его каталогу могут иметь доступ не только он, но и другие пользователи, которым разрешен доступ. В дискреционной модели доступа это сделать очень сложно.