Учимся использовать и создавать свои модули PAM
Ты, конечно же, знаешь, что учетные данные и пароли в большинстве
*nix хранятся в таких файлах, как /etc/passwd, /etc/shadow,
/etc/master.passwd. Но знаешь ли ты, что эти файлы относятся лишь к
стандартной аутентификации, которая легко может быть изменена? В свою
систему ты можешь внедрить более сложные системы аутентификации, вплоть
до использования смарт-карт, электронных ключей и сканера сетчатки
глаза. Причем для этого совсем не надо патчить ОС, перекомпилировать
ядро и выполнять множество громоздких операций.
[что такое PAM]
Во всех современных unix-like системах механизм аутентификации
отделен от программ, с помощью которых она осуществляется. Это стало
возможным благодаря использованию PAM (Pluggable Authentication Modules)
- Подключаемых Аутентификационных Модулей. Такие программы, как login,
su, passwd, в современных системах сами не работают с паролями, а делают
запрос к PAM, которая осуществляет всю процедуру аутентификации и
возвращает ответ: УСПЕХ (PAM_SUCCESS) или НЕУДАЧА (PAM_AUTH_ERR), а
прикладная программа лишь выводит сообщение пользователю в зависимости
от ответа PAM. Таким образом, прикладным программам совершенно неважно,
каким образом PAM осуществляет аутентификацию, поэтому администратор
может внедрить иные механизмы аутентификации, не перекомпилируя никаких
прикладных программ. А как это сделать, сейчас мы узнаем.
[три ветви PAM]
Впервые PAM появился в операционной системе Solaris 2.3, после чего
был адаптирован для Linux, BSD и прочих *nix. Сейчас существует три
основных ветви развития PAM: Solaris PAM, Linux-PAM и OpenPAM. Linux-PAM
используется почти всеми современными Linux. OpenPAM впервые стал
применяться во FreeBSD 5.x и NetBSD 3.x, до этого BSD-системы
использовали Linux-PAM. В целом все три ветви имеют лишь незначительные
отличия. Дальше пойдет разговор применительно к Linux-PAM, но иногда я
буду указывать расхождения с другими ветвями.
[архитектура PAM]
Как следует из самого названия, PAM состоит из модулей. Именно модули
отвечают за то, как будет осуществляться аутентификация. Модули
представляют собой обычные динамические библиотеки с расширением «.so»,
стандартно распложенные в каталоге /usr/lib/security (хотя это не
обязательно).
Настройка модулей для работы с конкретным приложением осуществляется с
помощью конфигурационных файлов, расположенных в /etc/pam.d (в
некоторых системах используется только один файл /etc/pam.conf). Каждому
приложению, которое использует аутентификацию, соответствует свой файл
конфигурации с одноименным названием. В конфигурационных файлах
указывается последовательность вызовов PAM-модулей с дополнительными
параметрами. Изменяя конфигурационный файл (например, добавляя новый
PAM-модуль), можно изменить процедуру аутентификации.
Файл конфигурации состоит из четырех столбцов. В первом столбце
указывается тип модуля (всего существует четыре типа модулей: auth,
account, password и sessions). Во втором столбце находится флаг
контроля, который указывает, как система будет реагировать при удачном
или неудачном прохождении соответствующего модуля. Флагов также
существует всего четыре: required, requisite, sufficient и optional. В
третьем поле указывается имя модуля и его расположение в системе.
Некоторые модули имеют необязательные параметры, которые указываются в
четвертом столбце. Символ решетки (#) используется для комментариев.
Строки с одинаковым типом образуют так называемый стек модулей,
позволяющий получить многошаговую аутентификацию, включающую несколько
различных процедур аутентификации.
Практически на все случаи жизни в системе существуют стандартные
модули, из которых можно строить различные системы аутентификации. Но в
некоторых случаях необходимый модуль нужно писать самому. Для этого в
PAM есть PAM API, позволяющий программисту использовать функции PAM.
Таким образом, если ты присоединишь к своему компьютеру сканер
сетчатки глаза, то тебе будет необходимо написать новый модуль с помощью
PAM API, который бы обращался к устройству и возвращал от него ответ.
[типы модулей]
Как уже отмечалось, всего существует четыре типа модулей:
- auth. Модули такого типа выполняют функции
аутентификации пользователей в системе (проверяют наличие пользователя в
системе, осуществляют ввод имени и пароля, разрешают доступ в ту или
иную группу и т.п.). Auth-модуль должен обязательно предоставлять
следующие две функции:
pam_sm_authenticate() выполняет задачу аутентификации пользователя,
то есть сравнивает введенный ключ со значением в базе данных;
pam_sm_setcred() открывает доступ прикладной программе к такой
информации о пользователе, как его ID, членство в группах и лимит
ресурсов. Для корректной инициализации в модуле должна быть включена
строка «#define PAM_SM_AUTH» после включения «#include
<security/pam_modules.h>».
- account. Модули такого типа проверяют
доступность аккаунта на основе ресурсов системы (время суток или
загрузка сервера). Account-модуль должен обязательно предоставлять
функцию pam_sm_acct_mgmt(), которая проверяет, доступен ли запрашиваемый
аккаунт. Для корректной инициализации в модуле должна быть включена
строка «#define PAM_SM_ACCOUNT».
- password. Модули этого типа предназначены для
смены пароля по запросу пользователя или по истечению срока действия.
Password-модуль должен обязательно предоставлять функцию
pam_sm_chauthtok(), которая изменяет ключ аутентификации, проверяет, не
использовался ли он ранее, а заодно и его стойкость. Для корректной
инициализации в модуле должна быть включена строка «#define
PAM_SM_PASSWORD».
- session. Данные модули используются для
выполнения определенных действий перед началом сеанса и перед его
окончанием. Такими действиями могут быть, например, добавление записей в
log-файлы, подготовка пользовательского окружения, запуск каких-нибудь
служб и т.д. Session-модуль должен предоставлять следующие две функции:
pam_sm_open_session() выполняет задачи, связанные с установкой сеанса;
pam_sm_close_session() отвечает за завершение сеанса. Для корректной
инициализации в модуле должна быть включена строка «#define
PAM_SM_SESSION».
В Linux-PAM существует всего четыре флага контроля:
- required. Если модуль выдал ошибку, то цепочка продолжит выполняться, но запрос будет отклонен.
- requisite. Если произошла ошибка модуля, то цепочка немедленно заканчивается, и запрос отклоняется.
- sufficient. Если модуль выполнился нормально, и
никакой предыдущий модуль в цепочке не потерпел неудачу, то цепочка
заканчивается, и принимается положительное решение о предоставлении
доступа. В случае ошибки модуль игнорируется, и выполняется остальная
часть цепочки.
- optional. Модуль с этим флагом не критичен для
аутентификации и используется как дополнительный, то есть модуль будет
выполнен, но его результат игнорируется.
В OpenPAM существует еще один флаг - binding, который по действию
похож на sufficient, но в случае когда модуль выдает ошибку, запрос
будет отклонен, хотя остальная часть цепочки выполнится (флаг sufficient
игнорирует модуль в случае ошибки). [стандартные модули]
В системе в обязательном порядке присутствует множество стандартных
модулей PAM, которые администратор может использовать для построения
цепочек. Список стандартных модулей можно увидеть с помощью команды:
% find / -name pam_*.so
Рассмотрим некоторые модули и заодно укажем тип, который они могут принимать:
pam_access.so. Тип account. Предоставляет или
запрещает доступ на основании файла /etc/security/access.conf. Строки
этого файла имеют следующий формат:
«права:пользователи:откуда»,
где:
Права - знак «+» (разрешить) или знак «-» (запретить).
Пользователи - ALL, имя пользователя или пользователь@узел, где узел
соответствует имени локальной машины, иначе запись игнорируется.
Откуда - одно или несколько имен файлов терминалов (без префикса
/dev/), имена узлов, доменные имена (начинающиеся с точки), IP-адреса (с
точкой на конце, например, 172.85.10.10.), ALL или LOCAL.
Параметров данный модуль не имеет.
- pam_cracklib.so. Тип password. Проверяет пароли
на стойкость. Имеет необязательные параметры: debug - заносит отладочную
информацию в файл журнала; retry=N - число попыток ввода пароля, по
исчерпанию которых возвращается ошибка (по умолчанию дается одна
попытка); diffok=N - должно быть изменено минимум N символов при смене
пароля; minlen=N - минимальный размер пароля; dcredit=N, ucredit=N,
lcredit=N, ocredit=N - в пароле должно присутствовать минимум N цифр,
строчных, прописных букв и других символов.
- pam_deny.so. Тип любой. Данный модуль на любой
запрос отвечает PAM_AUTH_ERR. Бывает полезен для быстрого отключения
сервиса (если добавить в начало цепочки) или для завершения цепочки
sufficient модулей.
- pam_lastlog.so. Тип auth. Заносит в файлы utmp,
utmpx, wtmp, wtmpx, lastlog и lastlogx сведения о том, когда и откуда
пользователь вошел в систему. Имеет параметры: debug - записывать
подробную информацию в syslog; nodate, noterm, nohost, silent -
отбрасывать дату, имя терминала, имя хоста или все вместе; never - если
пользователь первый раз входит в систему, то его приветствуют таким
сообщением: «Добро пожаловать...».
- pam_limits.so. Тип session. Позволяет накладывать
ограничения на пользователей, вошедших в систему. Требует файл
/etc/security/limits.conf и поддержку ядра для ограничений.
- pam_nologin.so. Тип auth. Проверяет наличие файла
/etc/nologin. Если он существует, то в систему может войти только root,
а остальным будет выдано на экран содержимое этого файла.
- pam_permit.so. Простейший модуль, который на
каждый запрос просто возвращает PAM_SUCCESS. Этот модуль бывает полезен в
качестве метки или для предотвращения появления пустой цепочки.
- pam_pwdb.so. Тип любой. Обычно является
альтернативой модулю pam_unix.so. Предоставляет интерфейс к файлам
passwd и shadow. Полезные параметры: nullok - можно использовать пустые
пароли; md5, shadow, bigcrypt - различные способы шифрования пароля.
- pam_rootok.so. Тип auth. Допускает пользователя к сервису, только если его uid=0.
- pam_time.so. Тип account. Позволяет ограничить
доступ к службе в зависимости от времени. Настраивается с помощью
конфигурационного файла /etc/securitty/time.conf.
- pam_warn.so. Тип auth и password. Просто заносит сообщение о своем вызове в syslog. Параметров не имеет.
- pam_wheel.so. Тип auth. Позволяет получать права
суперпользователя только пользователям группы wheel. Один из полезных
параметров: group=XXX - использовать указанную группу вместо wheel.
Существует еще особый модуль pam_stack.so, который
может быть любого типа и всегда используется с параметром service=XXX,
где XXX - название конфигурационного файла другого сервиса. Этот модуль
предназначен для включения цепочки модулей из другого конфигурационного
файла. Например, запись «/lib/security/pam_stack.so service=system-auth»
означает, что нужно выполнить цепочку модулей из конфигурационного
файла system-auth.
В разных *nix существуют свои стандартные модули, хотя рассмотренные модули присутствуют почти в каждой системе. [пример использования стандартных модулей]
В качестве примера изменим поведение команды su. Напомню, su
предоставляет возможность регистрироваться в системе под другим именем, в
том числе root. Сделаем так, чтобы только пользователи одной группы (
«ivan») могли получать права («root») при помощи su. Для этого нужно
добавить следующие две строки в начало конфигурационного файла su в
каталоге /etc/pam.d:
auth sufficient /lib/security/pam_rootok.so
auth required /lib/security/pam_wheel.so group=ivan
[программирование PAM-приложений]
Обращаться к модулям PAM имеет смысл только в том случае, когда в
твоей программе нужно осуществлять аутентификацию. На диске к журналу ты
можешь найти исходный код простейшего PAM-приложения (я назвал его
appl_pam.c).
Компиляция выполняется следующим образом:
% gcc -o appl_pam appl_pam.c -lpam -lpam_misc
Программа appl_pam просто запрашивает у пользователя его пароль, и
если пароль введен верно, то выводит Authentication OK, иначе - Not
Authenticated.
Вызывать функции PAM API в программе можно только после вызова
функции pam_start(), которая инициализирует библиотеку PAM, а
заканчиваться работа должна функцией pam_end(). Первый аргумент в
pam_start() - это имя сервиса. В appl_pam я задействовал сервис passwd.
Хочу обратить внимание на второй параметр: getenv(«LOGNAME»), который
определяет имя пользователя из стандартной переменной окружения LOGNAME.
Если второй параметр сделать нулевым значением, то функция будет
запрашивать не только пароль, но и логин пользователя. Третьим
аргументом является ссылка на объект диалога. В четвертую переменную
pam_start() запишет дескриптор сеанса, который будет использоваться
всеми последующими функциями PAM API в программе. Приложение должно обеспечить «функцию диалога». Она используется для
прямой связи между подгружаемым модулем и приложением и обеспечивает
модулю средство, которое будет запрашивать у пользователя пароль и т.д.
В pam_test() используется стандартная функция диалога misc_conv(),
выполняющая терминальный ввод-вывод, адрес которой вначале программы мы
указали в структуре pam_conv. Можно написать свою функцию, использующую
другие способы общения с пользователем, например, всплывающее окно,
голосовой ввод-вывод и т.д.
В программе вызывается функция pam_authenticate(). Во втором ее
аргументе указываются различные флаги. Значение 0 означает стандартные
установки.
Замечу, что имена основных PAM-функций в приложениях немного
отличаются от PAM-функций, использующихся в модулях (отсутствует вставка
_sm_). Например, в модулях используется функция pam_sm_chauthtok(), а в
приложениях - pam_chauthtok().
[программирование PAM-модулей]
Программировать свои модули в целом несложно. Все зависит от того,
какой алгоритм ты будешь в нем реализовывать, а именно: будет ли твой
модуль обращаться к оборудованию, например, к USB-ключу, или
использовать криптографический протокол. На диске к журналу ты можешь
найти исходник аутентификационного модуля pam_test. Он хорошо
прокомментирован, поэтому здесь я только дам общие сведения. Модуль
получает от пользователя пароль, сравнивает его со статическим паролем и
выдает результат. Так как модуль является типа AUTH, то он обязательно
должен реализовывать две функции: pam_sm_authenticate() и
pam_sm_setcred(). Дескриптор pamh позволяет связываться модулю с
приложением и получать от него данные с помощью специальных PAM-функций.
Имя пользователя определяется с помощью PAM-функции pam_get_user(), а
пароль принимается с помощью функции pam_get_item().
Модуль и приложение, которое его вызывает, могут обмениваться
сообщениями. Структура сообщения, передаваемого от модуля к приложению,
определена в security/pam_appl.h как:
struct pam_message {
int msg_style;
const char *msg;
};
Допустимые опции для msg_style:
- PAM_PROMPT_ECHO_OFF - получить строку без повторения любого текста;
- PAM_PROMPT_ECHO_ON - получить строку пока текст отображается эхом;
- PAM_ERROR_MSG - отображать ошибку;
- PAM_TEXT_INFO - отображать текст.
Структура ответа от приложения к модулю имеет следующий вид:
struct pam_response {
char *resp; int resp_retcode;
};
Компиляция PAM-модуля осуществляется точно так же, как и компиляция обычной динамической библиотеки:
% gcc -с -fPIC pam_test.c
% gcc -shared -fPIC -o pam_test.so pam_test.o
Чтобы проверить работу модуля, добавь в конфигурационный файл login
(/etc/pam.d/login) - в конец списка модулей типа auth - следующую
строку:
auth required /lib/security/pam_test.so
[PAM для хакера]
И напоследок скажу о безопасности PAM. Если хакер сумеет подменить
один из модулей PAM на троянскую версию, то скомпрометированными
окажутся все программы, которые используют этот модуль. Например, такой
модуль, как pam_unix.so или pam_pwdb.so, используются практически всеми
программами аутентификации (login, passwd, sshd, su и т. д.). Поэтому
если ты админ и заметил, что один из модулей PAM в твоей системе был
изменен, то знай - у тебя серьезные неприятности.
Источник: http://www.xakep.ru/magazine/xa/086/112/1.asp |