Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Разработка совместимых с PAM приложений, часть 1

Оригинал: Writing PAM-Capable Applications, Part One
Автор: Jennifer Vesperman
Дата публикации: 4 Апреля 2002 г.
Перевод: А.Панин
Дата перевода: 22 Февраля 2013 г.

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

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

Обратитесь к статьям "Введение в PAM" и "Модули PAM" для более подробного описания системы PAM и методов ее использования.

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

Приложения и модули

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

Из-за этого разработчик приложения должен рассмотреть вопросы вызова модулей каждого типа и продумать список неиспользуемых функций. Аналогично разработчик модулей должен предоставить интерфейс для всех функций, которые может запросить приложение, даже в том случае, когда некоторые функции состоят только из возврата статуса с помощью оператора return PAM_SUCCESS.

Типы модулей

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

Четыре типа модулей:
auth
Аутентификация пользователя, действие модуля ограничено выяснением "является ли пользователь тем, кем он представился".
account
Управление учетной записью пользователя, действие модуля предусматривает выяснение таких аспектов, как: является ли учетная запись пользователя действительной, запрещен ли доступ пользователя к системе в течение некоторого промежутка времени, используется ли no-login и не истек ли срок действия пароля пользователя.
session
Обработка открытия и закрытия аутентификационных сессий. Некоторые формы аутентфикации предусматривают дополнительные запросы во время открытия и закрытия сессии.
password
Изменение аутентификационных данных, представленных в любых формах.

Пакеты и файлы

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

Для начала вам понадобится установить заголовочные файлы для разработки: в системе Debian это делается с помощью команды: apt-get install libpam0g-dev. Данная команда позволяет установить соответствующие заголовочные файлы, разместив их в директориях поиска g++.

Вам будет необходимо включить в исходный код приложения заголовочный файл при помощи директивы #include <security/pam_appl.h> и в том случае, если вы захотите использовать дополнительные функции, также включить заголовочный файл #include <security/pam_misc.h>.

При компиляции вам будет необходимо использовать параметр командной строки для статического связывания программы с файлами библиотеки libpam. В моем файле makefile используется следующая строка g++ -oapp_name -lpam -lpam_misc sourcefile.

Структура обмена данными

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

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

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

Я рекомендую использовать функцию misc_conv() из заголовочного файла pam_misc.h в качестве функции обмена данными для программ с интерфейсом командной строки. Для этого следует использовать директиву #include <security/pam_misc> и описать структуру:
static struct pam_conv conv = {
  misc_conv,
  NULL
};

Разработка собственной функции обмена данными

Структура обмена данными выглядит следующим образом:
struct pam_conv {
    int (*conv)(int num_msg,
        const struct pam_message **msg,
        struct pam_response **resp,
        void *appdata_ptr);
    void *appdata_ptr;
};

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

Параметр msg является массивом указателей на структуры сообщений (pam_message). Структуры сообщений формируются модулем и содержат сообщения, которые предлагаются модулем приложению для их демонстрации пользователю. Параметр num_msg задает длину этого массива.

Модуль с помощью параметра функции resp предоставляет указатель, который должен указывать на массив структур pam_response. Приложение должно сформировать ответ на каждое сообщение (конечно же, если они существуют) и разместить его в соответствующем элементе массива ответов. Память, зарезервированная для этого массива может освобождаться с помощью функции free() и должна резервироваться с помощью одной из функций семейства malloc().

Код C++ выглядит следующим образом:

Память для структуры ответа должна динамически резервироваться и освобождаться модулем. Примечание для использующих C++: так как библиотека PAM разработана с применением языка C, для резервирования памяти должна использоваться функция malloc(), а не оператор new.

В структуре ответов на данный момент может использоваться только один код ответа: 0, при этом он не значит ничего. Код ответа в данной структуре введен для будущего расширения возможностей системы Linux-PAM. Однако, существует четыре возможных стиля сообщений:
  • PAM_PROMPT_ECHO_OFF
  • PAM_PROMPT_ECHO_ON
  • PAM_ERROR_MSG
  • PAM_TEXT_INFO

Следует учесть, что значение PAM_MAX_MSG_SIZE на данный момент не регламентируется системой Linux-PAM и должно регламентироваться приложениями (в целях безопасности).

Работа с окружением

Система Linux-PAM предоставляет разработчику отдельное программное окружение, ассоциированное с хэндлом PAM. После создания окружение является пустым.
extern int pam_putenv(pam_handle_t *pamh, const char *name_value);
С помощью данной функции может быть осуществлена попытка установки, сброса значения и удаления переменной с заданным именем из окружения. Аргумент name_value является строкой, использующей завершающий нулевой байт (как в строках языка C). Доступные форматы данного аргумента:
name=value
Устанавливает значение "value" переменной окружения "name".
name=
Устанавливает в качестве значения переменной окружения "name" пустую строку.
name
Удаляет переменную окружения "name".
extern const char *pam_getenv(pam_handle_t *pamh, const char *name);
Функция возвращает значение переменной окружения Linux-PAM с заданным именем или значение NULL в случае ошибки.
extern const char * const *pam_getenvlist(pam_handle_t *pamh);
Функция возвращает указатель на актуальный список переменных окружения Linux-PAM, предназначенный только для чтения. Если вам нужна копия этого списка для его модификации, воспользуйтесь функцией pam_misc_copy_env().
Три оставшиеся функции описаны в заголовочном файле pam_misc.h:
extern int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env);
Функция копирует параметр (представленный списком указателей на переменные окружения) в программное окружение Linux-PAM.
extern char **pam_misc_copy_env(pam_handle_t *pamh);
Функция возвращает указатель на список переменных окружения, скопированный из программного окружения Linux-PAM.
extern char **pam_misc_drop_env(char **env);
Функция освобождает память, зарезервированную при использовании функции pam_misc_copy_env().

Установка значений элементов PAM

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

Информация о приложении:
PAM_SERVICE
Имя приложения в системе PAM, не обязательно то имя, которое видит пользователь. В целях безопасности следует задавать это имя в коде приложения или в файле настроек, доступном только для системного администратора. Этот элемент используется в функции pam_start().
PAM_CONV
Доступ с структуре обмена данными.
PAM_FAIL_DELAY
Используется только в случае, если стандартная функция задержки при неудачном входе не подходит для вашего приложения. В большинстве случаев не стоит обращать внимания на этот элемент.
Информация о пользователе:
PAM_USER
Имя пользователя, под которым он проходит аутентификацию.
PAM_USER_PROMPT
Приглашение, используемое модулем для сообщения пользователю о необходимости ввода пароля.
PAM_RUSER
Имя запрашивающего аутентификацию пользователя, обычно это имя пользователя, запустившего приложение.
Информация о системе:
PAM_RHOST
Имя узла для системы, с которой производится запрос аутентификации.
PAM_TTY
Имя терминала (для консольных приложений) или значение переменной $DISPLAY (для приложений с графическим интерфейсом). Вы можете получить имя терминала с помощью функции ttyname().

Установка значений параметров производится с помощью функции pam_set_item(), а получение их значений - с помощью функции pam_get_item(). При этом следует использовать хэндл PAM, возвращенный функцией pam_start(). В качестве значения аргумента item_type следует применять один из кодов элементов, описанных выше. Аргумент item является указателем на строку. Функции должны возвращать значение PAM_SUCCESS в случае успешного выполнения и другие коды в случае неудачи.

При использовании языка C++ возможен вызов этих функций с помощью кода, подобного представленному ниже:
retval = pam_get_item(pamh, PAM_SERVICE, &static_cast<const void*> (item));
extern int pam_set_item(pam_handle_t *pamh, int item_type,
                        const void *item);
extern int pam_get_item(const pam_handle_t *pamh, int item_type,
                        const void **item);

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

Имя пользователя

Модуль вызывает функцию pam_get_user() для получения имени пользователя. Если вам известно, под каким именем следует аутентифицировать пользователя, вы можете указать это с помощью функции pam_start() или с помощью pam_set_item(), Если вы не сделаете этого, функция pam_get_user() будет использовать функцию обмена данными и элемент PAM_USER_PROMPT для запроса имени пользователя.

Функция pam_fail_delay

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

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

Наличие данной функции не гарантируется, поэтому ее вызов должен быть защищен с помощью директив #ifdef.
#ifdef PAM_FAIL_DELAY
        extern int pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec);
#endif

В некоторых случаях стандартная функция задержки не подходит для приложения. Информация о разработке собственной функции задержки доступна в руководстве разработчика приложений PAM (PAM Application Developer's Guide).

Заключительные слова

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

Дополнительные ресурсы

Продолжение статьи: Разработка совместимых с PAM приложений, часть 2