В прошлый раз мы с Вами разобрались с тем, как получать информацию о пользователе из файла /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. Если хеши не совпадают, то значит пароли разные.