Pluggable Authentication Modules

Опубликовано в LinuxТеги: ,

Как это устроено?

На глобальном уровне РАМ — это набор API. Программы, запрашивающие у РАМ определенный функционал, делают системный вызов, например, getpwnam (). Система читает некоторый набор конфигурационных файлов и, если там указаны внешние библиотеки, вызывает функции, зарегистрированные как обработчики данного вызова. Обработчиков этих может быть множество, результат их работы будет интерпретироваться в зависимости от настроек. То есть фактически РАМ — набор «кубиков», из которых строится то или иное «здание».

Все настройки РАМ хранятся в каталоге /etc/pam.d, в файлах их имя, как правило, совпадает с названием программы, хотя это, конечно, не является обязательным условием. Исключение — файл system, содержащий общие настройки, обычно подключается через include.

РАМ предоставляет четыре сервиса:

Аутентификация/авторизация (auth)— аутентификация и авторизация пользователей на основе паролей или каких-либо еще разграничительных элементов (упомянутые USB-брелки и сканеры отпечатков пальцев тоже относятся сюда).

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

Управление пользовательскими бюджетами (session) различные действия с домашним каталогом пользователя и тому подобные вещи.

Управление паролями (password)— установка и замена паролей и прочих разграничительных элементов.

Далеко не все модули реализуют все сервисы, да это и не обязательно. Отработка запроса к РАМ делается последовательным выполнением всех описанных в файле модулей, у которых в поле «тип модуля» указан данный сервис. И хотя обычно строки сгруппированы по сервисам, это не является обязательным условием, а делается только для удобства чтения.

Файл РАМ — обычный текстовый файл, комментарии отмечаются значком # в первой позиции, строки подключения модуля к некоему сервису выглядят следующим образом:

<тип модуля> <флаг результата> <путь и имя модуля> <аргументы>

«Тип модуля» задается уже перечисленными выше названиями сервисов, а именно словами auth, account, session и password. Это поле указывает РАМ, описываемый модуль реализует данный сервис, и он должен быть вызван при запросе у РАМ этого сервиса.

«Флаг результата» задает поведение РАМ в случае возврата того или иного значения. В зависимости от данного флага РАМ предпримет различные действия в случае успешного или неуспешного завершения модуля. В качестве значения флага используется: required— для того чтобы сервис вернул успех, необходимо, чтобы данный модуль вернул успех. В случае успеха и провала выполняются последующие модули, но при провале независимо от возврата сервис вернет провал; requisite— для того чтобы сервис вернул успех, необходимо, чтобы данный модуль вернул успех. В случае провала модуля сервис немедленно возвращает провал. В случае успеха выполняются последующие модули; sufficient— успех необязателен. Если модуль вернул успех и НИ ОДИН предыдущий модуль не вернул провал, сервис немедленно возвращает успех. Если модуль вернул провал, результат игнорируется, выполняется следующий модуль; binding— успех необязателен. В случае провала модуля выполняются последующие модули, но в результате независимо от их возврата сервис вернет провал. В случае успеха выполняются последующие модули; optional— результат модуля игнорируется независимо от возврата.

Когда выполнены все модули, реализующие данный сервис (то есть в том случае, если сервис не завершил работу досрочно), в результате будет возвращен успех, если успех возвращали все модули с флагом required/requisite. Если хотя бы один модуль с флагом required вернет провал, сервис вернет провал:

< «путь и имя модуля» задают именно то, что сказано, — путь и имя файла модуля. Обратите внимание, что до первого обращения к модулю он не загружается в память, поэтому, если в нем содержатся ошибки, связанные с динамической загрузкой, например, имеется внешняя переменная, которая отсутствует в РАМ, модуль соберется, но работать не будет;

< «аргументы» задают просто текстовые параметры для модуля, которые могут быть им проанализированы. Есть несколько стандартных аргументов, но на самом деле никто не обязывает их поддерживать.

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

Использование РАМ дает возможность гибкой настройки процессов аутентификации и авторизации.

Локальные пользователи

Начнем с простейшего — РАМ-файла для программы qpopper, который носит нетипичное название рор3: auth required pam_unix.so no_warn try_first_pass account required pam_unix.so

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

В данном случае это модуль pam_unix.so, который производит проверку по стандартной парольной базе UNIX — файлу /etc/passwd и его скрытой части (во FreeBSD это /etc/master.passwd, в Linux — /etc/shadow). Если модуль возвращает провал, сервис тоже вернет провал, и программа выдаст заключение, что такого пользователя в системе не обнаружено.

Показанный набор аргументов является типичным — программа login (использующая файл сервиса login) имеет такой же набор аргументов плюс nullok. Означают они соответственно «не выдавать замечания пользователю о том, почему аутентификация не прошла» и «если пароль еще не запрашивали ни разу, то запросить». Nullok же означает, что если пароля нет, то аутентифицировать без пароля.

У этого флага есть такая интересная особенность — если модуль не имеет прав рута для того, чтобы прочитать скрытую часть базы паролей, то он может аутентифицировать любого пользователя с любым паролем. Другими интересными аргументами являются «use_first_pass», «local_pass», «nis_pass».

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

Второй и третий задают соответственно использование только локальной либо NIS+ парольных баз. Если же аргумент указывает на использование некоей базы, а она не настроена, модуль (а, следовательно, и сервис) вернет провал.

Данный модуль реализует простую и всем хорошо знакомую схему аутентификации по файлу паролей UNIX, причем, если учетная запись заблокирована, если у нее закончился или не начинался срок действия, доступ предоставлен не будет — сервис account модуля pam_unix.so как раз занимается проверкой этих вещей.

Усложняем задачу. Накладываем ограничение — пользователь должен входить в определенную группу (локальную, внешние источники аутентификации мы пока не рассматривали): auth required pam_unix.so no_warn try_first_pass auth required pam_group.so group=staff luser account required pam_unix.so.

Мы добавили в сервис auth второй модуль — pam_group.so. Этот модуль возвращает успех в том случае, если пользователь входит в заданную нами группу, в данном случае это группа staff.

Второй параметр — luser задает, откуда брать имя пользователя. Взять его можно из двух переменных окружения PAM_USER и PAM_RUSER, параметр luser указывает использование переменной PAM_USER, если нужен PAM_RUSER, применяется ruser.

PAM_RUSER — это всегда имя пользователя, сделавшего запрос, а не имя пользователя, который аутентифицируется. Иногда PAM_RUSER совпадает с PAM_USER, но рассчитывать на это нельзя. Использование PAM_RUSER полезно для логирования или в том случае, когда аутентификация опирается на эту переменную (rhosts, RSA hosts аутентификация), в других случаях переменная мало используется. Тем не менее по историческим причинам, если не указан ни luser, ни ruser, используется ruser.

Что же произойдет теперь? Пользователь введет логин и пароль (это работа модуля pam_unix.so), но если указанный логин не прописан в группе staff в файле /etc/group, то сервис вернет провал и пользователь получит в ответ login incorrect, даже если логин и пароль будут правильными. То же самое произойдет, если в пароле ошибка, но логин присутствует в группе staff, причем проверка на наличие логина в группе будет выполнена, даже если указан неверный пароль.

Что произойдет, если в первой строке написать requisite? В случае ввода правильного пароля и наличия логина в группе — ничего, работа сервиса не изменится. А вот в случае ввода неправильного пароля сервис немедленно вернет провал, не вызывая pam_group.so; то есть скорость реакции на неверный пароль несколько увеличится.

Что же произойдет, если во второй строке написать sufficient вместо required? Фактически мы ее вычеркнем. В случае провала он будет проигнорирован и сервис вернет успех независимо от того, входит пользователь в группу или нет. А что произойдет, если в ПЕРВОЙ строке написать sufficient вместо required? Ведь вроде бы мы не делаем ничего страшного. А произойдет то же самое — сервис вернет успех независимо от того, входит пользователь в группу или нет. Как, почему?

А потому, что sufficient позволяет сервису НЕМЕДЛЕННО вернуть успех в случае успеха модуля, и pam_group.so попросту не будет вызван.

Локальные и удаленные пользователи

Еще больше усложним задачу — пробуем аутентифицировать пользователя или по файлу /etc/passwd, или по домену Windows, доступному через pam_ldap.so: auth sufficient pam_unix.so no_warn try_first_pass local_pass debug auth required pam_ldap.so use_first_pass account sufficient pam_unix.so.

В сервисе auth вызываются два модуля — уже знакомый нам pam_unix.so и модуль проверки учетной записи по домену Windows pam_ldap.so. Этот модуль поддерживает большинство стандартных аргументов — use_first_pass, try_first_pass, debug, no_warn — и имеет свои собственные — ignore_unknown_user и ignore_authinfo_unavail.

Первый аргумент приведет к тому, что если пользователь отсутствует в LDAP, то вместо провала будет возвращено PAM_IGNORE, что заставит РАМ проигнорировать результат модуля, даже если флаг результата будет требовать успеха, второй приведет к тому же самому, если невозможно связаться с LDAP-сервером. Эти аргументы полезны в том случае, если модуль стоит не последним в списке, в нашем случае они бессмысленны.

Итак, что же теперь произойдет? Пользователь введет свой логин и пароль, который будет проверен по локальной базе (pam_unix.so), и если это правильный пароль (модуль вернул успех), то дальше не проверяется, сервис немедленно возвращает успех. Если же модуль вернул провал (это НЕ локальный пользователь), запускается модуль pam_ldap.so, который проверяет этот же логин. Если проверка успешна, сервис вернет успех (есть такой пользователь в LDAP), иначе провал (нет такого пользователя в LDAP).

Давайте разберемся почему мы изменили pam_unix.so в сервисе account с required на sufficient? Потому что у доменных пользователей нет полей, на которые он опирается при проверке, либо они эмулируются с «всегда правильными» значениями, и смысла в работе этого модуля попросту нет.

Что произойдет, если мы поменяет в первой строке sufficient на required? Если нет такого локального пользователя, required даст запустить pam_ldap.so, но «осадочек останется». И именно этот «осадочек» приведет к тому, что даже при наличии пользователя в LDAP и знании правильного пароля сервис вернет провал.

А если на requisite? Будет еще хуже — если pam_unix.so вернет провал, то сервис немедленно возвращает провал, даже не пробуя запустить pam_ldap.so.

Хорошо, а что будет, если, например, во второй строке поставить sufficient вместо required? Сюрприз — можно зайти под данным логином с ЛЮБЫМ паролем! Почему же?

А потому, что если первый модуль возвращает провал, он игнорируется, второй модуль тоже возвращает провал — и он снова игнорируется, а дальше-то все, сервис возвращает успех! Вот таким образом можно просто отключить парольную защиту любой программы, причем даже и не покушаясь на ее файлы. Так что будьте очень внимательны, когда устанавливаете флаг результата, просчитывайте все варианты!

Еще усложним задачу — теперь локальный пользователь должен входить в локальную группу staff, удаленный — в локальную группу staff и доменную группу winstaff: auth required pam_group.so group=staff luser auth sufficient pam unix.so no_warn try_first_pass local_pass debug auth required pam group.so group=winstaff luser auth required pam_ldap.so use_first_pass account sufficient pam_unix.so

Что произойдет? Если заходит локальный пользователь, входящий в группу staff, и он указывает правильный пароль, то после того, как модуль pam_unix.so вернул успех, сервис немедленно возвращает успех, не вызывая другие модули.

Если же локальный пользователь в группу не входит, модуль pam_group, вызванный первый раз, возвращает провал. Это не позволит сервису прекратить работу после вызова pam_unix.so, и он перейдет к дальнейшим вызовам. И даже если этот пользователь входит в группу winstaff и присутствует в LDAP (то есть модули pam_group.so при втором вызове и pam_ldap.so вернут успех), провал на первом вызове модуля pam_group.so приведет к тому, что сервис вернет провал.

Если же заходит доменный пользователь, то провал модуля pam_unix.so игнорируется, а остальные модули должны вернуть успех — провал хотя бы одного из них приведет к провалу сервиса.

Выглядит все это довольно сложно, поэтому обычно у модулей аутентификации через домен — pam_ldap.so, pam_winbind.so — есть собственные настройки на наличие пользователя в группе — в pam_ldap.so это можно сделать в конфиге, задав определенный фильтр отбора, а в pam_winbind.so есть аргумент require-membership- of=<группа>.

Комплексный пример

В качестве последнего примера разберем РАМ-файл dovecot, который реально используется в корпоративной почте: auth sufficient pam_unix.so no_warn try_first_pass local_pass debug auth required pam_ldap.so use_first_pass account sufficient pam_unix.so account required pam_mkhome.so mode=0700 session required pam_mkhome.so mode=0700 .

В сервисе auth используется уже знакомая нам комбинация pam_unix.so + pam_ldap.so, которая обеспечивает аутентификацию пользователя либо по локальному файлу /etc/passwd, либо по домену Windows.

Про то, почему в сервисе account модуль pam_unix.so включен с флагом результата sufficient, мы уже говорили. А вот модуль pam_mkhome.so нам еще не встречался. Его назначение — создавать пользователю домашний каталог и сделать в нем копии стандартного набора стартовых файлов, как это происходит при добавлении пользователя через adduser.

Стандартных аргументов он не поддерживает, задать можно всего три: debug=1 (включает отладку, она в этом модуле чрезвычайно подробная), mode=NNNN (задает режим создания файлов в формате команды chmod) и skel=<pathway> (задает путь к каталогу шаблонов для копирования файлов, по умолчанию /usr/share/skel).

Почему он вызывается и в сервисе account, и в сервисе session? Вообще, конечно, создание домашнего каталога — задача сервиса session. Но проблема в том, что первый запуск пользовательского шелла происходит до того, как сервис session будет запрошен, и первый логин будет сопровождаться непонятной надписью No homedir.

Поэтому, чтобы опередить первый логин, модуль вызывается в сервисе account. Флаг результата здесь значения не имеет, его можно исправить на что угодно, хоть на optional, если домашний каталог не будет создан, об этом будет выдано достаточное количество сообщений — отладка у модуля весьма полная.

Зачем же тогда он включен в сервис session? Из-за программы dovecot. Dovecot вообще не пользуется сервисом account при логине пользователя. Да и сервисом session он пользуется фиктивно — специально ради того, чтобы можно было создать домашний каталог. Вот этой возможностью мы и пользуемся.

Скачать модуль можно по ссылке.

* * *

Ачилов Р. Авторизация в Active Directory. //«Системный администратор», №4, 2012 г. — С. 10-14.

Начала РАМ — http://citforum.ru/operating_systems/articles/pam.shtml.

Ссылка для загрузки модуля pam_mkhome — http://www.sheltonsoft.ru/fileZ/software/pam_mkhome-1.3.tar.bz2.

Настройка РАМ — http://www.freebsd.org/doc/ru/articles/pam/pam-config.html.

Похожие записи:

  • Нет ничего похожего
 
Скрыть/Показать

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


 
Скрыть/Показать
Ноябрь 2017
Пн Вт Ср Чт Пт Сб Вс
« Мар    
 12345
6789101112
13141516171819
20212223242526
27282930  
Связаться со мной
Скрыть/Показать
Чат со мной - Обратится к нам в Skype
Архив записей
Скрыть/Показать
Скрыть/Показать
 
Скрыть/Показать

uptime узнать