Операционные системы - статьи

Делаем собственный PAM-модуль


После теории посмотрим в глубь PAM. Попытаемся разобраться как же оно работает.

Пример1: Новый вход в систему.

Вы уже наверно во сне видите изрядно доставшие строки

login:***

Password:*******

Эта схема придуманная много лет назад продолжает служить и сейчас. Но не паролем единым жив человек. Давайте сделаем что-то новенькое, необычное. Будет и самому интересно и вредителей введет в заблуждение, ибо такого не будет не на одной другой машине.

До PAM'а нам пришлось бы переписывать весь login, а теперь мы просто сделаем новый модуль, соберем его как библиотеку и припишем в сценарий аутентификации /etc/pam.d/login. Модуль будет называться просто pam_test.

Но сначала о том, что же мы будем делать. Имеется следующая интересная схема аутентификации: система выдает пользователю несколько случайных чисел. Пользователь берет определенные из них, поставляет в ТОЛЬКО ЕМУ известный многочлен, например,

(дата)*x*x+3*x-(текущий час)*y+z

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

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

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

Как вы прочитали ранее модули различаются по классам. Наш будет класса AUTH. То есть отвечать за аутентификацию.



Это его текст.

/*Это простейший модуль PAM*/

// Включаем необходимые заголовочные файлы.

#include <security/pam_modules.h> #include <stdarg.h> #include <time.h>

//Это определит тип нашего модуля

#define PAM_SM_AUTH #define MAX_V 30

// Самая интересная функция. // Именно она реализует наш неповторимый алгоритм аутентификации. // Подобные внешние функции должны существовать во всех модулях данного класса.


PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags ,int argc, const char **argv) { unsigned int ctrl; int retval; const char *name, *p; char *right; /*special variables*/ long x1,x2,x3,x4,y;

time_t mytime; struct tm *mytm;

/*готовимся к аутентификации*/ mytime=time(0); mytm=localtime(&mytime);

srandom(mytime); x1=random()%MAX_V; x2=random()%MAX_V; x3=random()%MAX_V; x4=random()%MAX_V;

//завели несколько случайных величин, а заодно узнали и время.

/* получим имя пользователя */

// Получаем имя пользователя // Вся мудрость PAM в том, что приглашение "login: " появится если имя еще не известно, // иначе мы сразу получим ответ, сгенерированный предыдущими модулями.

retval = pam_get_user(pamh, &name, "login: ");

/*получим пароль используя диалог*/

{ struct pam_conv *conv; struct pam_message *pmsg[3],msg[3]; struct pam_response *response;

retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;

// Сами мы не знаем как будет осущестляться диалог, это забота программы (в нашем случае этим займется login). Мы // лишь только укажем параметры, вид приглашения и более того, можем задать сразу несколько приглашений, если надо // получить сразу несколько ответов

pmsg[0] = &msg[0]; msg[0].msg_style = PAM_PROMPT_ECHO_OFF; msg[0].msg=malloc(100); snprintf(msg[0].msg,60,"Second Password:%d:%d:%d:%d:",x1,x2,x3,x4);

retval = conv->conv(1, ( const struct pam_message ** ) pmsg , &response, conv->appdata_ptr); // Нам дали указатель на диалоговую функцию. Ее и запускаем. /*просчитаем правильный ответ*/ y=2*x1*mytm->tm_mday+x3*mytm->tm_hour; right=malloc(100); snprintf(right,20,"%d",y); // Сравним с ответом пользователя. Ответ формируется диалоговой функцией в специальном формате. if (!(strcmp(right,response->resp))){ return PAM_SUCCESS; }else{ return PAM_AUTH_ERR; } }/*диалог*/ return PAM_SUCCESS; // Нашим результатом будет да или нет. Как прервать программу разберется основной модуль PAM. }



/* * The only thing _pam_set_credentials_unix() does is initialization of * UNIX group IDs. * * Well, everybody but me on linux- pam is convinced that it should not * initialize group IDs, so I am not doing it but don't say that I haven't * warned you. -- AOY * Перевожу: Единственная вещь которую делает pam_set_cred это инициализация Идентификаторов групп... короче в данном случае это нам совершенно не нужно */

PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags ,int argc, const char **argv) { unsigned int ctrl; int retval;

retval = PAM_SUCCESS; //Чтобы никто не заметил, что мы ничего не делаем ответим, что все в порядке return retval; }

// Это определение необходимо для статической линковки модулей PAM в приложениях. #ifdef PAM_STATIC struct pam_module _pam_unix_auth_modstruct = { "pam_test", pam_sm_authenticate, pam_sm_setcred, NULL, NULL, NULL, NULL, }; #endif

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

Работа с другими классами модулей абсолютно идентична. Так что попробуйте сделать еще что-нибудь свое.


Содержание раздела