Шаг 14 - Получение данных из shadow password

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


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Кузин Андрей.