История изменений
Исправление selivan, (текущая версия) :
0. Шелл распарсивает аргументы команды: раскрывает переменные, шаблоны имён файлов, выполняет встроенные подкоманды, если такие есть
1. Шелл ищет, что такое sudo: built-in ключевое слово, alias или файл в PATH
2. Шелл находит sudo, например, в /usr/bin/sudo
3. Шелл делает какой-то из системных вызовов семейства fork()
4. Родительский и дочерний процессы получают разные коды выхода от fork(). Тот, кто оказался дочерним, запускает вызов семейства exec() с аргументом /usr/bin/sudo
5. Если у пользователя, запустившего шелл, есть права на исполнение /usr/bin/sudo, операционная система загружает вмсето дочернего процесса программу из файла /usr/bin/sudo
6. Т. к. на /usr/bin/sudo стоит SUID-бит, получившийся процесс будет иметь Effective UID пользователя-владельца файла, то есть root
7. Запущенный с помощью fork() процесс унаследует от родителя все дескрипторы открытых файлов, в том числе 0,1 и 2, отвечающие за привязку ввода/вывода к соотв. консоли. После выполнения exec() дескрипторы также сохранятся. Также сохранится environment - переменные и их значения.
8. Программа sudo, запущенная с RUID=user и EUID=root, в свою очередь парсит переданные ей шеллом аргументы. Один(часто первый) из них - программа, которую оно так же как шелл ищёт в PATH; все последующие - её аргументы
9. sudo считывает настройки из /etc/sudoers
10. В настройках проверяется, имеет ли право пользователь запускать эту программу от имени другого пользователя(как правило, root)
11. Если можно - будет запрошен пароль или другой способ аутентификации пользователя(отпечаток пальца, фото морды лица) в соответствии с настройками linux pam в etc/pam.d/sudo. Или нет, если в sudoers стоит NOPASSWD
12. Предоставленные для аутентификации данные будут проверены с помощью linux PAM. Будут сделаны соотв. системные вызовы. Пароль может храниться в /etc/shadow в захешированном виде, может запрашиваться из LDAP, может откуда-то ещё
13. Если аутентификация ОК, будет сделан системный вызов семейства setuid(), чтобы сменить uid процесса, а потом - exec(), чтобы загрузить соотв. программу. Ей будут переданы все оставшиеся аргументы, которые не имели отношения к самому sudo. Т. к. RUID=0 благодаря SUID-биту, sudo имеет право сделать setuid() и стать любым другим пользователем. Прохождение аутентификации при этом не требуется, то есть пользователю может быть запрещён вход в систему, но с помощью sudo мы можем выполнять от его лица команды
Когда я говорю «вызов семейства fork()», имеется в виду, что в linux есть несколько системных вызовов, с похожей функциональностью и названиями. Какой именно будет использован - зависит от реализации, но общая идея та же.
Исправление selivan, :
0. Шелл распарсивает аргументы команды: раскрывает переменные, шаблоны имён файлов, выполняет встроенные подкоманды, если такие есть
1. Шелл ищет, что такое sudo: built-in ключевое слово, alias или файл в PATH
2. Шелл находит sudo, например, в /usr/bin/sudo
3. Шелл делает какой-то из системных вызовов семейства fork()
4. Родительский и дочерний процессы получают разные коды выхода от fork(). Тот, кто оказался дочерним, запускает вызов семейства exec() с аргументом /usr/bin/sudo
5. Если у пользователя, запустившего шелл, есть права на исполнение /usr/bin/sudo, операционная система загружает вмсето дочернего процесса программу из файла /usr/bin/sudo
6. Т. к. на /usr/bin/sudo стоит SUID-бит, получившийся процесс будет иметь Effective UID пользователя-владельца файла, то есть root
7. Запущенный с помощью fork() процесс унаследует от родителя все дескрипторы открытых файлов, в том числе 0,1 и 2, отвечающие за привязку ввода/вывода к соотв. консоли. После выполнения exec() дескрипторы также сохранятся. Также сохранится environment - переменные и их значения.
8. Программа sudo, запущенная с RUID=user и EUID=root, в свою очередь парсит переданные ей шеллом аргументы. Один(часто первый) из них - программа, которую оно так же как шелл ищёт в PATH; все последующие - её аргументы
9. sudo считывает настройки из /etc/sudoers
10. В настройках проверяется, имеет ли право пользователь запускать эту программу от имени другого пользователя(как правило, root)
11. Если можно - будет запрошен пароль или другой способ аутентификации пользователя(отпечаток пальца, фото морды лица) в соответствии с настройками linux pam в etc/pam.d/sudo. Или нет, если в sudoers стоит NOPASSWD
12. Предоставленные для аутентификации данные будут проверены с помощью linux PAM. Будут сделаны соотв. системные вызовы
13. Если аутентификация ОК, будет сделан системный вызов семейства setuid(), чтобы сменить uid процесса, а потом - exec(), чтобы загрузить соотв. программу. Ей будут переданы все оставшиеся аргументы, которые не имели отношения к самому sudo. Т. к. RUID=0 благодаря SUID-биту, sudo имеет право сделать setuid() и стать любым другим пользователем. Прохождение аутентификации при этом не требуется, то есть пользователю может быть запрещён вход в систему, но с помощью sudo мы можем выполнять от его лица команды
Когда я говорю «вызов семейства fork()», имеется в виду, что в linux есть несколько системных вызовов, с похожей функциональностью и названиями. Какой именно будет использован - зависит от реализации, но общая идея та же.
Исходная версия selivan, :
0. Шелл распарсивает аргументы команды: раскрывает переменные, шаблоны имён файлов, выполняет встроенные подкоманды, если такие есть
1. Шелл ищет, что такое sudo: built-in ключевое слово, alias или файл в PATH
2. Шелл находит sudo, например, в /usr/bin/sudo
3. Шелл делает какой-то из системных вызовов семейства fork()
4. Родительский и дочерний процессы получают разные коды выхода от fork(). Тот, кто оказался дочерним, запускает вызов семейства exec() с аргументом /usr/bin/sudo
5. Если у пользователя, запустившего шелл, есть права на исполнение /usr/bin/sudo, операционная система загружает вмсето дочернего процесса программу из файла /usr/bin/sudo
6. Т. к. на /usr/bin/sudo стоит SUID-бит, получившийся процесс будет иметь Effective UID пользователя-владельца файла, то есть root
7. Запущенный с помощью fork() процесс унаследует от родителя все дескрипторы открытых файлов, в том числе 0,1 и 2, отвечающие за привязку ввода/вывода к соотв. консоли. После выполнения exec() дескрипторы также сохранятся. Также сохранится environment - переменные и их значения.
8. Программа sudo, запущенная с RUID=user и EUID=root, в свою очередь парсит переданные ей шеллом аргументы. Один(часто первый) из них - программа, которую оно так же как шелл ищёт в PATH; все последующие - её аргументы
9. sudo считывает настройки из /etc/sudoers
10. В настройках проверяется, имеет ли право пользователь запускать эту программу от имени другого пользователя(как правило, root)
11. Если можно - будет запрошен пароль или другой способ аутентификации пользователя(отпечаток пальца, фото морды лица). Или нет, если в sudoers стоит NOPASSWD
12. Предоставленные для аутентификации данные будут проверены с помощью linux PAM, в соответствии с конфигом в /etc/pam.d/sudo. Будут сделаны соотв. системные вызовы
13. Если аутентификация ОК, будет сделан системный вызов семейства setuid(), чтобы сменить uid процесса, а потом - exec(), чтобы загрузить соотв. программу. Ей будут переданы все оставшиеся аргументы, которые не имели отношения к самому sudo. Т. к. RUID=0 благодаря SUID-биту, sudo имеет право сделать setuid() и стать любым другим пользователем. Прохождение аутентификации при этом не требуется, то есть пользователю может быть запрещён вход в систему, но с помощью sudo мы можем выполнять от его лица команды
Когда я говорю «вызов семейства fork()», имеется в виду, что в linux есть несколько системных вызовов, с похожей функциональностью и названиями. Какой именно будет использован - зависит от реализации, но общая идея та же.