Глава 16. Pluggable Authentication Modules (PAM)

Русский перевод: Михаил Сгибнев

Содержание

16.1. Краткое содержание
16.2. Introduction
16.3. Термины и соглашения
16.3.1. Определения
16.3.2. Примеры
16.4. Основное в PAM
16.4.1. Средства и примитивы
16.4.2. Модули
16.4.3. Цепочки и правила
16.4.4. Транзакции
16.5. Конфигурирование PAM
16.5.1. Файл политик PAM
16.5.2. Разбор строки конфигурации
16.5.3. Политики
16.6. Модули PAM
16.6.1. Общие модули
16.6.2. Специфичные для FreeBSD модули PAM
16.6.3. Модули PAM, специфичные для NetBSD
16.7. Программирование приложений PAM
16.8. Программирование модулей PAM
16.9. Пример приложения PAM
16.10. Пример модуля PAM
16.11. Простая функция сеанса связи PAM
16.12. Для дополнительного прочтения

16.1. Краткое содержание

В этой главе описываются механизм и принципы работы библиотек Подключаемых Аутентификационных Модулей (PAM), будет показано как сконфигурировать PAM, внедрить его в приложение и написать модуль PAM.

16.2. Introduction

Библиотеки Pluggable Authentication Modules (PAM)являются обьединенным API для сервисов, требующих аутентификацию и позволяет системным администраторам добавлять новые методы аутентификации просто инсталировав новый модуль PAM и модифицировав правила аутентификации в файле конфигурации.

Впервые PAM был описан и разработан в 1995 Vipin Samar и Charlie Lai из Sun Microsystems и с тех пор не сильно изменился. В 1997 году Open Group опубликовала предварительную спецификацию X/Open Single Sign-on (XSSO), которая стандартизировала PAM API и добавляла расширения для единственной (или, скорее, интегрированной) подписи. В то время, когда писалась эта глава, стандарт еще не был утвержден.

Хотя эта глава больше применима к FreeBSD 5.x и NetBSD 3.x, которые используют OpenPAM, она может использоваться и при работе с FreeBSD 4.x, которая использует Linux-PAM и другими ОС, такими как Linux и Solaris™.

16.3. Термины и соглашения

16.3.1. Определения

Терминология в PAM довольно запутанная. Ни Neither Samar и Lai’s ни спецификация XSSO не делали попыток первоначального определения терминов и обьектов, входящих в PAM, поэтому с течением времени возникла неоднозначность и путанница. Первая попытка установления непротиворечивой и однозначной терминологии была предпринята в подробном докладе, написанным Эндрю Г. Морган (автор Linux-PAM) в 1999 году. Хотя это и был огромный шаг вперед, по мнению самого Моргана, терминология еще далека от совершенства. Поэтому начнем мы с точного определения всех акторов и обьектов, включенных в PAM.

аккаунт
Привилегии, которые претендент запрашивает у арбитра.

претендент
Пользователь или обьект, запрашивающий аутентификацию.

арбитр
Пользователь или обьект, который имеет право проверить полномочия претендента и предоставить или запретить предоставление привелегии.

цепочка
Последовательность модулей, которые будут вызваны в ответ на запрос PAM. Цепочка включает информацию о порядке вызова модулей, передаваемых параметрах, и методе интерпретации результатов.

клиент
Приложение, ответственное за инициализацию запроса на аутентификацию и готовое получить от него необходимую аутентификационную информацию.

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

модуль
Набор из одной или более связанных функций, осуществляющих уникальный метод аутентификации, собранный в один (обычно, динамически загружаемый) бинарный файл и идентифицируемый уникальным именем.

политика
Законченный набор инструкций, описывающих как PAM обрабатывает запросы на обслуживание. Политика обычно состоит из четырех цепочек, по одной на каждое средство, хотя некоторые сервисы не используют все четыре средства.

сервер
Приложение, действующее от имени арбитра, для общения с клиентом, получения аутентификационной информации, проверки полномочий претендента и предоставления доступа.

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

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

ключ
Некая информация, связанная с аккаунтом, такая как пароль или кодовая фраза, позволяющая претенденту идентифицировать себя.

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

16.3.2. Примеры

В этом разделе мы проиллюстрируем значения некоторых терминов на простых примерах.

16.3.2.1. Одна программа в роли и клиента и сервера

В этом примере, пользователь alice использует su(1) для использования учетной записи root.

$ whoami
alice
$ ls -l `which su`
-r-sr-xr-x  1 root  wheel  10744 Dec  6 19:06 /usr/bin/su
$ su -
Password: xi3kiune
# whoami
root
  • Здесь претендентом будет alice.
  • Аккаунтом будет root.
  • Процесс su(1) выступает в роли и клиента и сервера.
  • Ключом аутентификации будет xi3kiune.
  • Арбитром является root, поскольку у su(1) выставлен setuid root.

16.3.2.2. Разделенный клиент и сервер

В этом примере показано, как eve инициирует соединение ssh(1) с login.example.com, и логинится под именем bob. Боб должен был выбрать лучший пароль!

$ whoami
eve
$ ssh bob@login.example.com
bob@login.example.com's password: god
Last login: Thu Oct 11 09:52:57 2001 from 192.168.0.1
NetBSD 3.0 (LOGIN) #1: Thu Mar 10 18:22:36 WET 2005

Welcome to NetBSD!
$
  • Претендентом является eve.
  • Клиентом будет процесс ssh(1), выполняемый eve.
  • В роли сервера выступит процесс sshd(8) на login.example.com
  • Аккаунтом является bob.
  • Ключ аутентификации — god.
  • Хотя это и не показано в нашем примере, арбитром является root.

16.3.2.3. Простая политика

Вот политика по умолчанию для FreeBSD sshd:

sshd	auth		required	pam_nologin.so	no_warn
sshd	auth		required	pam_unix.so	no_warn try_first_pass
sshd	account		required	pam_login_access.so
sshd	account		required	pam_unix.so
sshd	session		required	pam_lastlog.so	no_fail
sshd	password	required	pam_permit.so
  • Эта политика обращается к сервису sshd, (который может состоять не только из сервера sshd(8)).
  • auth, account, session и password будут средствами.
  • pam_nologin.so, pam_unix.so, pam_login_access.so, pam_lastlog.so и pam_permit.so являются модулями. Из этого примера ясно, что pam_unix.so предоставляет не менее двух средств (аутентификация и управление аккаунтом).

Есть несколько отличий между политиками PAM на FreeBSD и NetBSD:

  • По умолчанию, каждая конфигурация сделана для /etc/pam.d.
  • Если конфигурации не существует, то вы не получите доступа в систему, в отличие от FreeBSD, где имеется политика по умолчанию, принимающая аутентификацию.
  • Для аутентификации в NetBSD необходимо наличие не менее одного модуля required, requisite или binding.

16.4. Основное в PAM

16.4.1. Средства и примитивы

API PAM предлагает шесть различных примитивов аутентификации, сгруппированных в четырех средствах, которые описаны ниже.

auth
Аутентификация. Это средство занимается подтверждением претендента и установлением данных аккаунта. Оно предоставляет два примитива:

  • pam_authenticate(3) подтверждает подлинность претендента, обычно запрашивая ключ аутентификации и сравнивая это со значением, сохраненным в базе данных или полученный от сервера аутентификации.
  • pam_setcred(3) утанавливает данные аккаунта, такие как ID пользователя, членство в группах и лимит ресурсов.
account
Управление аккаунтом. Это средство проверяет доступность аккаунта, опираясь на такие данные, как время суток или загрузку сервера. Оно предоставляет единственный примитив:

  • pam_acct_mgmt(3) проверяет, что запрашиваемый аккаунт доступен.
session
Управление сессией. Это средство обрабатывает задачи, связанные с установкой и окончанием сеанса, типа учета входа в систему. Оно обеспечивает два примитива:

  • pam_open_session(3) исполняет задачи, связанные с установкой сеанса: добавляет запись в базах данных utmp и wtmp, запускает агента SSH и т.д.
  • pam_close_session(3) отвечает за разрыв сеанса: добавляет записи в базы данных utmp и wtmp, останавливает агента SSH и т.д.
password
Управление паролями. Это средство используется для смены аутентификационного ключа, связанного с аккаунтом, по запросу пользователя или по истечению срока действия. Предоставляется один примитив:

  • pam_chauthtok(3) изменяет ключ аутентификации, дополнительно проверяя его стойкость и не использовался ли он ранее.

16.4.2. Модули

Модули — это очень важная часть концепции PAM, в конце концов, они «M» в слове «PAM». Модуль PAM — самостоятельная программа, которая реализует примитивы в одном или более средствах для одного специфического механизма, например для базы паролей UNIX®, NIS, LDAP и Radius.

16.4.2.1. Именование модулей

FreeBSD и NetBSD реализуют каждый механизм в одном модуле, называемом pam_mechanism.so (для примера, pam_unix.so для механизма UNIX®). Другие реализации могут включать в себя отдельные модули для разных средств и содержать имя средства как имя механизма, например в Solaris™ имеется модуль pam_dial_auth.so.1, используемый для аутентификации dialup пользователей. Так же, почти каждый модуль имеет страницу руководства man, тем же именем, например pam_unix(8) описывает работу модуля pam_unix.so.

16.4.2.2. Нумерация версий модулей

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

OpenPAM, с другой стороны, ищет модули, которые имеют тот же самый номер версии как и библиотека PAM (в настоящее время 2 в FreeBSD и 0 в NetBSD) и загружает модуль , не соответствующий версии, только в случае, если нет подходящего модуля. Таким образом унаследованные модули могут быть обеспечены для унаследованных приложений, при этом обеспечивается работа приложений, завязанных на новые версии модулей.

Хотя модули PAM в Solaris™ и имеют номер версии, но он входит в имя модуля и должен быть включен в конфигурацию.

16.4.2.3. Путь к модулям

Нет общего каталога для чтобы хранить модули PAM. В FreeBSD, они расположены в /usr/lib, в NetBSD вы можете найти их в /usr/lib/security.

16.4.3. Цепочки и правила

Когда сервер инициирует транзакцию PAM, библиотека пытается загрузить правила для сервиса, указанного в вызове pam_start(3). Политика описывает метод обработки аутентификационного запроса и определяется в конфигурационном файле. Это и есть другая основная концепция PAM: администратор имеет возможность настраивать правила безопасности системы (в глобальном смысле), просто редактируя текстовый файл.

Политика состоит из четырех цепочек, по одной для каждого из четырех средств PAM. Каждая цепочка представляет собой последовательность инструкций конфигурации, определение модуля и некоторые (дополнительные) параметры доступа пройти к модулю, и флаги управления, которые описывают, как интерпретировать код возврата от модуля.

Понимание контрольных флагов является основным в понимании конфигурационных файлов PAM. Есть множество различных флагов:

binding
Если модуль был выполнен и небыло ошибок в предыдущих модулях, то цепочка немедленно заканчивается и принимается положительное решение о предоставлении доступа. Если модуль выдал ошибку, остальная часть цепочки выполнится, но запрос будет отклонен.

Этот флаг был введен Sun в Solaris™ 9 (SunOS™ 5.9)и поддерживается в OpenPAM.

required
Если модуль выполнен и выполнена остальная часть цепочки, то доступ предоставляется. Если модуль выдал ошибку, то цепочка продолжит выполняться, но запрос будет отклонен.

requisite
Если модуль выполнен и выполнена остальная часть цепочки, то доступ предоставляется. Если поизошла ошибка модуля, цепочка немедленно заканчивается, и запрос отклоняется.

sufficient
Если модуль выполняется нормально и никакой предыдущий модуль в цепочке не терпел неудачу, цепочка заканчивается и и принимается положительное решение о предоставлении доступа. В случае ошибки модуль игнорируется и выполняется остальная часть цепочки.

Поскольку семантика этого флага несколько запутанна, мы рекомендуем использовать binding, там где это возможно.

optional
Модуль будет выполнен, но его результат игнорируется. Если все модули в цепочке отмечены флагом optional, то доступ всегда будет предоставлен.

Когда сервер вызывает один из шести примитивов PAM, PAM отыскивает цепочку для средства, к которой примитив принадлежит и вызывает каждый из модулей, перечисленный в цепочке в порядке следования, пока не достигает конца, или не будет принято решение об окончании обработки (в случае выполнения с флагами binding или sufficient или ошибки с флагом requisite).

Обратите внимание, что возможно, хотя не очень обычно, перечислить модуль несколько раз в пределах одной цепочки. Например, модуль, который ищет имена пользователя и пароли на сервере каталогов, может быть вызван несколько раз с различными параметрами, определяющими различные серверы каталогов. PAM обрабатывает различные вызовы того же самого модуля в той же самой цепочке как и различные, несвязанные модули.

16.4.4. Транзакции

Жизненный цикл типичной транзакции PAM описан ниже. Обратите внимание, что в случае сбоя на любом шаге, сервер обязан доложить о возникшей ошибке клиенту и прервать транзакцию.

  1. В случае необходимости, сервер получает привилегии арбитра через механизм, независимый от PAM— в общем случае он стартует как root или имеет setuid root.
  2. Сервер вызывает pam_start(3) для инициализации библиотеки PAM, указания имени сервиса и аккаунта, регистрации и поддержания связи.
  3. Сервер получает различную информацию, касающуюся транзакции (такую как имя претендента и имя хоста, на котором выполняется клиент) и посылает его PAM, используя pam_set_item(3).
  4. Сервер вызывает pam_authenticate(3) для аутентификации претендента.
  5. Сервер вызывает pam_acct_mgmt(3) для проверки доступности и валидности запрашиваемого аккаунта. Если пароль корретен но устарел, man.pam.acct.mgmt.3; вернет PAM_NEW_AUTHTOK_REQD вместо PAM_SUCCESS.
  6. Если предыдущий шаг вернул PAM_NEW_AUTHTOK_REQD, сервер вызовет pam_chauthtok(3) для смены клиентом ключа аутентификации аккаунта.
  7. Теперь, когда претендент был должным образом заверен, сервер вызывает pam_setcred(3), чтобы установить полномочия требуемой учетной записи. Это возможно, потому что все действия осуществляются от имени арбитра и подтверждаются его полномочиями.
  8. Как только полномочия будут утановлены, сервер вызовет pam_open_session(3) для открытия сессии.
  9. Теперь сервер может предоставить клиенту сервис, например, запустить оболочку.
  10. Для разрыва сеанса вызывается pam_close_session(3).
  11. В заключение, сервер вызывает pam_end(3) для уведомления библиотеки PAM, что сессия закончена и используемые ресурсы могут быть освобождены.

16.5. Конфигурирование PAM

16.5.1. Файл политик PAM

16.5.1.1. Файл /etc/pam.conf

Традиционно, файл политик PAM называется /etc/pam.conf. Каждая строка этого файла один шаг в цепочке, как показано ниже:

login   auth    required        pam_nologin.so  no_warn

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

Отдельная цепочка создается для каждой пары сервис/средство. Для пары сервис/средство важен порядок следования строк, для отдельных сервисов или средств проядок следования не важен. Примеры в оригинальной документации сгруппированы по средствам и Solaris™ продолжает придерживаться этого в pam.conf, а FreeBSD группирует строки по сервисам. Оба эти подхода имеют место быть.

16.5.1.2. Каталог /etc/pam.d

OpenPAM и Linux-PAM поддерживают дополнительный механизм конфигурации, который является предпочтительным в FreeBSD и NetBSD. В этой схеме, каждая политика содержится в отдельном файле, именуемом по имени службы, к которому она обращается. Эти файлы находятся в /etc/pam.d/.

Файлы, содержащие по одной политике на сервер, имеют только четыре поля, вместо пяти у pam.conf: опускается имя сервиса. Таким образом, вместо строки в pam.conf мы получаем файл /etc/pam.d/login следующего содержания:

auth    required        pam_nologin.so  no_warn

Как следствие этого упрощенного синтаксиса, появляется возможность использовать ту же самую политику для нескольких услуг, привязывая разные имена сервисов к одному файлу. Например, чтобы использовать одинаковую политику для su и sudo, можно сделать следующим образом:

# cd /etc/pam.d
# ln -s su sudo

Эта конструкция будет работать, так как имя сервиса определяется не в файле конфигурации, а в его названии. Только поэтому один файл можно использовать для нескольких сервисов.

Так как политика кдля каждой службы сохраняется в отдельном файле, механизм pam.d позволяет сильно упростить установку политик для сторонних программых продуктов.

16.5.1.3. Порядок поиска политик

Как мы видели выше, политики PAM могут находиться в в множестве мест. Если для указанной службы не было найдено ни одного файла конфигурации, будет использоваться /etc/pam.d/other, в случае, если этот файл не существует, будет искаться соответствующая запись в файле /etc/pam.conf или использоваться сервис «other».

Главное, что необходимо понять, это то, что конфигурирование PAM основано на цепочках.

16.5.2. Разбор строки конфигурации

Как указывалось в главе Файл политик PAM, какдая строка файла конфигурации /etc/pam.conf состоит из четырех или более полей: имя службы, имя средства, контрольный флаг, имя модуля и необязательные аргументы модуля.

Имя сервиса обычно(но не всегда) означает имя вызываемого приложения. Если вы не уверены, то обратитесь к документации на приложение для определения используемого имени сервиса.

Обратите внимание, что если вы используете /etc/pam.d/ вместо /etc/pam.conf, имя сервиса определяется именем файла политики и не присутствует в файле конфигурации.

Средство — одно из ключевых слов, описанных в разделе Средства и примитивы section.

Аналогично, управляющий флаг — одно из четырех ключевых слов, описанных в разделе Цепочки и правила и указывает, как как интерпретировать код возврата от модуля. Linux-PAM поддерживает дополнительный синтаксис, который позволяет вам определить действие, связанное с кодом возврата, но этого стоит избегать, так как это не является стандартным поведением и пересекается с фукциями запросов вызовов сервисов (и очень отличается от пути Solaris™ и OpenPAM).

16.5.3. Политики

Для правильного конфигурирования PAM нужно понимание порядка интерпретации политик.

Когда приложение вызывает pam_start(3), библиотека PAM загружает политику для указанного сервиса и создает четыре цепочки модулей (по одной на каждое средство). Если одна или более этих цепочек пусты, то они заменяются соответствующими цепочками из политики для сервиса other.

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

Таблица 16.1. Выполнение цепочек PAM

PAM_SUCCESS PAM_IGNORE other
binding if (!fail) break; fail = true;
required fail = true;
requisite fail = true; break;
sufficient if (!fail) break;
optional

Если в конце цепочки fail — истина, или когда выполняется «break», диспетчер возвращает код ошибки, выданный первым модулем, но котором произошел сбой. В противном случае, возвращается PAM_SUCCESS.

Первое исключение состоит в том, что ошибка PAM_NEW_AUTHTOK_REQD будет обработана как успешный вход, то есть в случае, если нет ошибок в модулях и не менее одного модуля вернули PAM_NEW_AUTHTOK_REQD, диспетчер возвратит PAM_NEW_AUTHTOK_REQD.

Второе исключение состоит в том, что pam_setcred(3) обрабатывает модули binding и sufficient как будто они required.

Третье и последнее исключение состоит в том, что pam_chauthtok(3) выполняет цепочку дважды (один раз предварительный и второй раз с новым паролем) и на предварительной стадии модули binding и sufficient обрабатываются, как будто они required.

16.6. Модули PAM

16.6.1. Общие модули

16.6.1.1. pam_deny(8)

Модуль pam_deny(8) является одним из самых простых доступных модулей, на любой запрос он отвечает PAM_AUTH_ERR. Это бывает полезно для быстрого отключения сервиса (добавьте в начало каждой цепочки) или для завершения цепочки sufficient модулей.

16.6.1.2. pam_echo(8)

Модуль pam_echo(8) просто возвращает аргумент как сообщение PAM_TEXT_INFO. Этот модуль может быть полезен при отладке или вывода перед началом аутентификации сообщения «Unauthorized access will be prosecuted».

16.6.1.3. pam_exec(8)

Модуль pam_exec(8) в качестве первого параметра принимает имя выполняемой программы, все последующие принимаются как аргументы запускаемой программы. Возможным применением может быть монтирование пользовательского каталога при его входе в систему.

16.6.1.4. pam_ftpusers(8)

Модуль pam_ftpusers(8) отработает правильно только в том случае, если пользователь перечислен в /etc/ftpusers. В настоящее время в NetBSD этот модуль не понимает расширенный синтаксис ftpd(8), но это будет исправлено в более поздных версиях.

16.6.1.5. pam_group(8)

Модуль pam_group(8) принимает или отклоняет претендента на основе членства его в определенной группе (обычно, wheel для su(1)). Это, в первую очередь, предназначено для сохранения традиционного стиля работы BSD su(1), но может применяться для ограничения доступа определенных групп пользователей к сервисам.

В NetBSD есть аргумент authenticate, с помощью которого пользователя просят подтвердить подлинность использования его собственного пароля.

16.6.1.6. pam_guest(8)

Модуль pam_guest(8) позволяет входить в систему по заранее установленному гостевому имени. В качестве пароля могут быть введены любые данные, аутентификация проходит только имени. pam_guest(8) может применяться для предоставления анонимного доступа на FTP

16.6.1.7. pam_krb5(8)

Модуль pam_krb5(8) предоставляет функции идентификации пользователя с помощью Kerberos 5. У пользователя запрашивается пароль и получается новый Kerberos TGT. TGT проверяется, получая тикет для локального хоста. Полученные ключи сохраняются в кэше, соответствующим образом устанавливается переменная KRB5CCNAME. Кэш должен быть разрушен при выходе пользователя из системы kdestroy(1).

16.6.1.8. pam_ksu(8)

Модуль pam_ksu(8) обеспечивает только сервис аутентификации для Kerberos 5, чтобы определить, действительно ли претендент уполномочен получить привилегии аккаунта.

16.6.1.9. pam_lastlog(8)

Модуль pam_lastlog(8) обеспечивает услуги управления сеанса. Он делает запись в базе данных utmp(5), utmpx(5), wtmp(5), wtmpx(5), lastlog(5) и lastlogx(5).

16.6.1.10. pam_login_access(8)

Модуль pam_login_access(8) обеспечивает выполнение примитива управления учетной записью, который предписывает ограничения входа в систему, указанные в таблице login.access(5).

16.6.1.11. pam_nologin(8)

Модуль pam_nologin(8) отклоняет не-root запросы на аутентификацию, в случае, если существует файл /var/run/nologin. Этот файл обычно создается shutdown(8) за пять минут до выключения системы.

16.6.1.12. pam_permit(8)

Модуль pam_permit(8) — один из простейших доступных. На каждый запрос он возвращает PAM_SUCCESS. Этот модуль бывает полезен в качестве метки или для предотвращения появления пустой цепочки.

16.6.1.13. pam_radius(8)

Модуль pam_radius(8) предоставляет сервис аутентификации через протокол RADIUS (Remote Authentication Dial In User Service) protocol.

16.6.1.14. pam_rhosts(8)

Модуль pam_rhosts(8) предоставляет только сервис аутентификации. Он выполняется без сообщения от ошибке если идентификатор целевого пользователя не 0 и удаленный хост и пользователь имеются в /etc/hosts.equiv или ~/.rhosts.

16.6.1.15. pam_rootok(8)

Модуль pam_rootok(8) сообщает об успехе, если и только если реальный пользовательский идентификатор процесса, вызывающий его — 0. Это полезно для несетевых услуг типа su(1) или passwd(1), к которым root должен иметь автоматический доступ.

16.6.1.16. pam_securetty(8)

Модуль pam_securetty(8) предоставляет только сервис учетной записи. Он применяется когда претендент запрашивает полномочия суперпользователя или процесс присоединяется к insecure TTY.

16.6.1.17. pam_self(8)

Модуль pam_self(8) сообщает об успехе если имена претендента и целевой учетной записи совпадают. Это наиболее полезно для несетевых услуг типа su(1), где тождество претендента может быть легко проверено.

16.6.1.18. pam_ssh(8)

Модуль pam_ssh(8) обеспечивает услуги и сеанса и идентификации. Сервис аутентификации позволяет пользователям, защитившим свои секретные ключи в каталоге ~/.ssh кодовыми фразами, ввести кодовую фразу для подтверждения своей подлинности. Сервис сеанса запускает ssh-agent(1) и производит предварительную загрузку ключей, которые расшифровываются в фазе аутентификации. Эта особенность особенно полезна для входа на консоль локальной машины или входа в X (используя xdm(1) or или другой понимающий PAM X менеджер).

Этот модуль осуществляет фундаментальнуюо схему аутентификации пароля. Необходимо принять все меры, по обеспечению работы модуля только по безопасному соединению, в противном случае, кодовая фраза SSH может быть скомпрометирована.

Рассмотрим еще один аспект использования pam_ssh(8). Пользователи часто предполагают, что установленные права доступа будут надежной защитой для ключей и указывают простую кодовую фразу или не указывают ее вовсе. Так как администратор системы не имеет никаких эффективных средств для контроля качества кодовых фраз, то есть риск компрометации системы.

16.6.1.19. pam_unix(8)

Модуль pam_unix(8) реализует традиционную аутентификацию UNIX® по паролю, используя getpwnam(3) в FreeBSD или getpwnam_r(3) в NetBSD для получения доступа к целевой учетной записи для сравнения пароля учетной записи и пароля, введенного претендентом. Также, этот модуль предоставляет сервис управления учетной записью (смена пароля при истечении его срока действия) и сервис смены пароля. Это, вероятно, самый полезный модуль, так как большинство администраторов хотело бы придерживаться исторически сложившейся методики аутентификации.

16.6.2. Специфичные для FreeBSD модули PAM

16.6.2.1. pam_opie(8)

Модуль pam_opie(8) реализует метод аутентификации opie(4). Система opie(4) построена на механизме вопрос-ответ, где ответ на каждый вопрос является прямой функцией от вопроса и кодовой фразы, так что ответ может быть легко вычислен «на лету» тем, кому известна кодовая фраза, при этом исчезает потребность в списках пользователей. Кроме того, opie(4) никогда не использует вопрос, на который был дан корректный ответ, что позволяет избежать уязвимости «replay attacks».

16.6.2.2. pam_opieaccess(8)

pam_opieaccess(8) — это модуль-компаньон к pam_opie(8). Его цель состоит в том, чтобы установить ограничения, описанные в opieaccess(5), которые регулируют условия, при которых пользователю, который обычно подтверждал подлинность себя, используя opie(4), разрешают использовать дополнительные методы. Это наиболее часто используется, чтобы запретить использование аутентификации по паролю от недоверенных хостов.

Для эффективной работы, модуль pam_opieaccess(8) должен быть указан как requisite сразу после записи sufficient для pam_opie(8), и перед любыми другими модулями в цепочке auth .

16.6.2.3. pam_passwdqc(8)

Модуль pam_passwdqc(8) просто проверяет стойкость пароля. В дополнение к проверке паролей на стойкость, этот модуль предлагает поддержку кодовых фраз и может обеспечить генерирование случайного пароля.

16.6.2.4. pam_tacplus(8)

Модуль pam_tacplus(8) предоставляет аутентификацию с помощью протокола TACACS+.

16.6.3. Модули PAM, специфичные для NetBSD

16.6.3.1. pam_skey(8)

Модуль pam_skey(8) реализует метод аутентификации по одноразовым паролям (S/Key One Time Password (OTP)), используя базу данных /etc/skeykeys.

16.7. Программирование приложений PAM

Этот раздел еще не написан.

16.8. Программирование модулей PAM

Этот раздел еще не написан.

16.9. Пример приложения PAM

Ниже представлена простейшая реализация su(1) для использования PAM. Обратите внимание на то, что используется специфичная для OpenPAM функция сеанса связи openpam_ttyconv(3), прототипом которой является security/openpam.h. Если вы хотите реализовать это приложение на системе, использующей другую реализацию библиотек PAM, то должны использовать другую функцию сеанса связи. Стабильную функцию сеанса связи удивительно сложно реализовать, представленная в Простая функция сеанса связи PAM хороша для примера, но не должна использоваться в реальных приложениях.

#include <sys/param.h>
#include <sys/wait.h>

#include <err.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include <security/pam_appl.h>
#include <security/openpam.h>	/* for openpam_ttyconv() */

extern char **environ;

static pam_handle_t *pamh;
static struct pam_conv pamc;

static void
usage(void)
{

	fprintf(stderr, "Usage: su [login [args]]n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	char hostname[MAXHOSTNAMELEN];
	const char *user, *tty;
	char **args, **pam_envlist, **pam_env;
	struct passwd *pwd;
	int o, pam_err, status;
	pid_t pid;

	while ((o = getopt(argc, argv, "h")) != -1)
		switch (o) {
		case 'h':
		default:
			usage();
		}

	argc -= optind;
	argv += optind;

	if (argc > 0) {
		user = *argv;
		--argc;
		++argv;
	} else {
		user = "root";
	}

	/* initialize PAM */
	pamc.conv = &openpam_ttyconv;
	pam_start("su", user, &pamc, &pamh);

	/* set some items */
	gethostname(hostname, sizeof(hostname));
	if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
		goto pamerr;
	user = getlogin();
	if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
		goto pamerr;
	tty = ttyname(STDERR_FILENO);
	if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
		goto pamerr;

	/* authenticate the applicant */
	if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
		goto pamerr;
	if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
		pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
	if (pam_err != PAM_SUCCESS)
		goto pamerr;

	/* establish the requested credentials */
	if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
		goto pamerr;

	/* authentication succeeded; open a session */
	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
		goto pamerr;

	/* get mapped user name; PAM may have changed it */
	pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
	if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
		goto pamerr;

	/* export PAM environment */
	if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
		for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
			putenv(*pam_env);
			free(*pam_env);
		}
		free(pam_envlist);
	}

	/* build argument list */
	if ((args = calloc(argc + 2, sizeof *args)) == NULL) {
		warn("calloc()");
		goto err;
	}
	*args = pwd->pw_shell;
	memcpy(args + 1, argv, argc * sizeof *args);

	/* fork and exec */
	switch ((pid = fork())) {
	case -1:
		warn("fork()");
		goto err;
	case 0:
		/* child: give up privs and start a shell */

		/* set uid and groups */
		if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
			warn("initgroups()");
			_exit(1);
		}
		if (setgid(pwd->pw_gid) == -1) {
			warn("setgid()");
			_exit(1);
		}
		if (setuid(pwd->pw_uid) == -1) {
			warn("setuid()");
			_exit(1);
		}
		execve(*args, args, environ);
		warn("execve()");
		_exit(1);
	default:
		/* parent: wait for child to exit */
		waitpid(pid, &status, 0);

		/* close the session and release PAM resources */
		pam_err = pam_close_session(pamh, 0);
		pam_end(pamh, pam_err);

		exit(WEXITSTATUS(status));
	}

pamerr:
	fprintf(stderr, "Sorryn");
err:
	pam_end(pamh, pam_err);
	exit(1);
}

16.10. Пример модуля PAM

Ниже представлена минимальная реализация pam_unix(8), предоставляющая только сервис аутентификации. Этот модуль должен работать со всеми реализациями PAM, но применяет расширения OpenPAM: обратите внимание на использование pam_get_authtok(3), который черезвычайно упрощает реализацию подсказки пользователю на ввод пароля..

#include <sys/param.h>

#include <pwd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <security/pam_modules.h>
#include <security/pam_appl.h>

#ifndef _OPENPAM
static char password_prompt[] = "Password:";
#endif

#ifndef PAM_EXTERN
#define PAM_EXTERN
#endif

PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{
#ifndef _OPENPAM
	struct pam_conv *conv;
	struct pam_message msg;
	const struct pam_message *msgp;
	struct pam_response *resp;
#endif
	struct passwd *pwd;
	const char *user;
	char *crypt_password, *password;
	int pam_err, retry;

	/* identify user */
	if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
		return (pam_err);
	if ((pwd = getpwnam(user)) == NULL)
		return (PAM_USER_UNKNOWN);

	/* get password */
#ifndef _OPENPAM
	pam_err = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
	if (pam_err != PAM_SUCCESS)
		return (PAM_SYSTEM_ERR);
	msg.msg_style = PAM_PROMPT_ECHO_OFF;
	msg.msg = password_prompt;
	msgp = &msg;
#endif
	for (retry = 0; retry < 3; ++retry) {
#ifdef _OPENPAM
		pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
		    (const char **)&password, NULL);
#else
		resp = NULL;
		pam_err = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
		if (resp != NULL) {
			if (pam_err == PAM_SUCCESS)
				password = resp->resp;
			else
				free(resp->resp);
			free(resp);
		}
#endif
		if (pam_err == PAM_SUCCESS)
			break;
	}
	if (pam_err == PAM_CONV_ERR)
		return (pam_err);
	if (pam_err != PAM_SUCCESS)
		return (PAM_AUTH_ERR);

	/* compare passwords */
	if ((!pwd->pw_passwd[0] && (flags & PAM_DISALLOW_NULL_AUTHTOK)) ||
	    (crypt_password = crypt(password, pwd->pw_passwd)) == NULL ||
	    strcmp(crypt_password, pwd->pw_passwd) != 0)
		pam_err = PAM_AUTH_ERR;
	else
		pam_err = PAM_SUCCESS;
#ifndef _OPENPAM
	free(password);
#endif
	return (pam_err);
}

PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{

	return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{

	return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{

	return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{

	return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
	int argc, const char *argv[])
{

	return (PAM_SERVICE_ERR);
}

#ifdef PAM_MODULE_ENTRY
PAM_MODULE_ENTRY("pam_unix");
#endif

16.11. Простая функция сеанса связи PAM

Функция сеанса связи, представленная ниже — очень упрощенная версия openpam_ttyconv(3) из OpenPAM. Она полностью функциональна, и должна дать читателю хорошую базу относительно того, как функция сеанса связи должна себя вести, но она слишком проста для реального использования. Даже если вы не используете OpenPAM, не стесняйтесь изучать исходный текст и приспосабливать openpam_ttyconv(3) к вашим требованиям. Мы полагаем, что она будет столь же стабильна, как ориентированная на tty функция сеанса связи.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <security/pam_appl.h>

int
converse(int n, const struct pam_message **msg,
	struct pam_response **resp, void *data)
{
	struct pam_response *aresp;
	char buf[PAM_MAX_RESP_SIZE];
	int i;

	data = data;
	if (n <= 0 || n > PAM_MAX_NUM_MSG)
		return (PAM_CONV_ERR);
	if ((aresp = calloc(n, sizeof *aresp)) == NULL)
		return (PAM_BUF_ERR);
	for (i = 0; i < n; ++i) {
		aresp[i].resp_retcode = 0;
		aresp[i].resp = NULL;
		switch (msg[i]->msg_style) {
		case PAM_PROMPT_ECHO_OFF:
			aresp[i].resp = strdup(getpass(msg[i]->msg));
			if (aresp[i].resp == NULL)
				goto fail;
			break;
		case PAM_PROMPT_ECHO_ON:
			fputs(msg[i]->msg, stderr);
			if (fgets(buf, sizeof buf, stdin) == NULL)
				goto fail;
			aresp[i].resp = strdup(buf);
			if (aresp[i].resp == NULL)
				goto fail;
			break;
		case PAM_ERROR_MSG:
			fputs(msg[i]->msg, stderr);
			if (strlen(msg[i]->msg) > 0 &&
			    msg[i]->msg[strlen(msg[i]->msg) - 1] != 'n')
				fputc('n', stderr);
			break;
		case PAM_TEXT_INFO:
			fputs(msg[i]->msg, stdout);
			if (strlen(msg[i]->msg) > 0 &&
			    msg[i]->msg[strlen(msg[i]->msg) - 1] != 'n')
				fputc('n', stdout);
			break;
		default:
			goto fail;
		}
	}
	*resp = aresp;
	return (PAM_SUCCESS);
 fail:
        for (i = 0; i < n; ++i) {
                if (aresp[i].resp != NULL) {
                        memset(aresp[i].resp, 0, strlen(aresp[i].resp));
                        free(aresp[i].resp);
                }
        }
        memset(aresp, 0, n * sizeof *aresp);
	*resp = NULL;
	return (PAM_CONV_ERR);
}

16.12. Для дополнительного прочтения

Литература

Документация

Making Login Services Independent of Authentication Technologies. Vipin Samar Charlie Lai. Sun Microsystems.

X/Open Single Sign-on Preliminary Specification. The Open Group. 1-85912-144-6. June 1997.

Pluggable Authentication Modules. Andrew G. Morgan. October 6, 1999.

Руководство пользователя

PAM Administration. Sun Microsystems.

Related Web pages

Домашняя страница OpenPAM. Dag-Erling Sm?rgrav. ThinkSec AS.
Домашняя страница Linux-PAM. Andrew G. Morgan.

Домашняя страница Solaris PAM. Sun Microsystems.