В прошлый раз мы с Вами разобрались с тем, как получать информацию о пользователе из файла /etc/passwd, но как оказалось получить пароль нам не удастся, потому что его там нет. А как быть, если он Вам нужен ? Ну, например, Вы решили написать собственный сервер POP3, который для авторизации требует наличие пароля.
Для работы со скрытыми паролями надо подключить файл shadow.h и вам станут доступны аналогичные процедуры:
#include <shadow.h> struct spwd *getspnam (const char *name);
Обратите внимание, что в файле shadow.h нет определения функции получения информации о пользователе по его UID. Т.е. для работы нужно знать имя пользователя, соответственно сначала нужно воспользоваться функцией getpwuid(), чтобы по UID получить имя.
Функция getspnam() возвращает структуру struct spwd, либо NULL в случае неудачи. Данная структура определена следующим образом:
struct spwd { char *sp_namp; /* Login name. */ char *sp_pwdp; /* Encrypted password. */ long int sp_lstchg; /* Date of last change. */ long int sp_min; /* Minimum number of days between changes. */ long int sp_max; /* Maximum number of days between changes. */ long int sp_warn; /* Number of days to warn user to change the password. */ long int sp_inact; /* Number of days the account may be inactive. */ long int sp_expire; /* Number of days since 1970-01-01 until account expires. */ unsigned long int sp_flag; /* Reserved. */ };
Как видите структура гораздо больше, чем passwd. Большинство полей отвечает за временные параметры пароля, такие как его минимальное и максимальное время жизни, а также время жизни всего аккаунта.
И наверняка Вы заметили, что я немного соврал Вам в прошлый раз, когда сказал, что /etc/shadow содержит информацию аналогичную /etc/passwd. Получается, что кроме логина и пароля Вы тут не найдете домашней директории и шелла. В принципе верно, зачем хранить одно и тоже в нескольких местах ?! Выходит, что нам не удастся "отбиться от коллектива" и придется пользоваться обоими файлами.
Давайте посмотрим, как работает эта функция. Напишем маленькую программку shadowtest.c:
#include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> #include <shadow.h> int main(){ struct passwd *userinfo; struct spwd *passw; uid_t userid; userid = getuid(); userinfo = getpwuid(userid); if (userinfo != NULL){ passw = getspnam(userinfo->pw_name); if (passw != NULL){ printf("user login: %s\n",userinfo->pw_name); printf("user home: %s\n",userinfo->pw_dir); printf("user shell: %s\n",userinfo->pw_shell); printf("user password: %s\n",userinfo->pw_passwd); printf("user shadow password: %s\n",passw->sp_pwdp); printf("user last change: %ld\n",passw->sp_lstchg); }; }; return 0; };
Компилируем и запускаем:
dron~# ./gcc shadowtest.c -o shadowtest dron~# ./shadowtest user login: root user home: /root user shell: /bin/bash user password: x user shadow password: $1$02p9xyDo$gnkh4vts/rArhJselceTV1 user last change: 12028
Как видите пароль нам получить удалось только из структуры struct spwd. Но он зашифрованный алгоритмом MD5, в данном случае настоящий пароль 12345678 (можете не мучаться над взломом :). Тут кстати следует поговорить о том, как хранятся пароли. Понятное дело, что если пароли будут храниться в виде plain text, т.е. в виде текста "как есть", то можно будет узнать пароль для любого пользователя совершенно спокойно. Современные правила безопасности вообще не разрешают хранить пароль в таком виде. Вместо этого пароль хранится в виде хеша от настояшего пароля. Функция вырабатывающая хеш берет настоящий пароль и вырабатывает на его основе уникальную последовательность чисел, которую не возможно обратно преобразовать в пароль, потому что математические функции работающие над выработкой пароля специально создаются однонаправленными. Создание таких процедур является сложной криптографической задачей и порой под силу только крупным научно-исследовательским институтам. К примеру у нас в России существуют засекреченные алгоритмы, некоторые из которых разрабатывались в течение 10 лет, так вот представьте каких трудов это стоит и представьте какие эти алгоритмы совершенные. Взять к примеру наш алгоритм шифрования ГОСТ 28147-89, который существует с 89 года и до сих пор остается одним из самых защищенных (он может иметь длину ключа 256 бит, в то время как DES имеет всего 56 бит и при нынешнем развитии компьютеров является чрезвычайно устаревшим). Однако для выработки хеша в системах Linux используются в основном алгоритмы DES и MD5, хотя первый уже используется крайне редко.
Так вот, сравнение правильности пароля происходит следующим образом. Программа получает настоящий пароль от пользователя, потом вырабатывает на его основе хеш и сравнивает его с тем, который она получила из файла /etc/shadow. Если хеши не совпадают, то значит пароли разные.