русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Шаг 15 - Работа с паролями системы с помощью функции crypt()


Дата добавления: 2015-07-04; просмотров: 811; Нарушение авторских прав


Уже многие из Вас задали себе закономерный вопрос "как же работать с паролями", т.е. как получить тот хеш, о котором шла речь раньше. Давайте раберемся с этим.

За генерацию паролей отвечает функция crypt(). Подключить ее к программе можно так:

#define _XOPEN_SOURCE#include <unistd.h> char *crypt(const char *key, const char *salt);

Вместо заголовочного файла unistd.h для подключения функций шифрования можно использовать другой файл - crypt.h. В принципе без разницы, они оба имеют одинаковые определения функции crypt().

Для работы функции требуется два параметра:

· key - это секретный пароль пользователя, который требуется зашифровать

· salt - это так называемый "открытый ключ", или же в понятиях алгоритмов шифрования инициализационные данные. Данный параметр должен состоять из символов a-zA-Z0-9./, т.е. из любых латинских букв, цифр или некоторых символов.

Если же с паролем все ясно, то salt может заставить задуматься. Давайте посмотрим на что влияет этот параметр. Напишем тестовую программку crypt.c:

#include <stdlib.h>#include <crypt.h> int main(){ printf("crypt(\"password\",\"ab\") = \"%s\"\n",crypt("password","ab")); printf("crypt(\"password\",\"ab12\") = \"%s\"\n",crypt("password","ab12")); printf("crypt(\"password\",\"ac\") = \"%s\"\n",crypt("password","ac")); printf("crypt(\"password\",\"ac123\") = \"%s\"\n",crypt("password","ac123")); printf("crypt(\"password\",\"1a\") = \"%s\"\n",crypt("password","1a")); printf("crypt(\"password\",\"1a.\") = \"%s\"\n",crypt("password","1a.")); return 0;};

Данная программа пытается выработать хеш с разным сальтом для пароля "password". Компилируем программу:



dron~# gcc crypt.c -o crypt/tmp/ccBXde1R.o: In function `main':/tmp/ccBXde1R.o(.text+0x17): undefined reference to `crypt'/tmp/ccBXde1R.o(.text+0x3f): undefined reference to `crypt'/tmp/ccBXde1R.o(.text+0x67): undefined reference to `crypt'/tmp/ccBXde1R.o(.text+0x8f): undefined reference to `crypt'/tmp/ccBXde1R.o(.text+0xb7): undefined reference to `crypt'/tmp/ccBXde1R.o(.text+0xdf): more undefined references to `crypt' followcollect2: ld returned 1 exit status

Не так быстро. Просто так откомпилировать программу нельзя, надо обязательно подключить библиотеку libcrypt.a, делается это такой командой:

dron~# gcc crypt.c -o crypt -lcryptdron~# ./cryptcrypt("password","ab") = "abJnggxhB/yWI"crypt("password","ab12") = "abJnggxhB/yWI"crypt("password","ac") = "acBxUIBkuWIZE"crypt("password","ac123") = "acBxUIBkuWIZE"crypt("password","1a") = "1abtv8E0hkEd6"crypt("password","1a.") = "1abtv8E0hkEd6"

Посмотрите на результат работы функции. Хоть я и задавал разные salt некоторые хеши получились совершенно одинаковыми. Из этого можно сделать вывод и том, что в нем играют роль только первые два символа. Значения этих символов используются для шифрования пароля алгоритмом DES. Заметьте также, что salt входит в хеш как его начальная часть. И это правильно, ведь если его удалить, то неизвестно, что надо использовать для проверки достоверности пароля. На самом деле, как мы узнаем дальше, это сделано специально для того, чтобы можно было без особых мучений с паролем из /etc/shadow сразу же отправлять его в функцию crypt().

Хорошо. Нам теперь, думаю, стало понятно как работает алгоритм DES, а как же MD5 ? Ведь, как я уже говорил, пароли сегодня чаще всего хранятся в виде MD5. Хороший вопрос. Ответ на него я искал некоторое время. Исходя из того, что совершенно точно функция crypt() должна работать с MD5 я начал рыскать в исходнике gnu-pop3d, который совсем недавно чуть ли не переписал заново :) Пролистав все и не обнаружив ни #define ни хитрых #include я попробовал залезть в файл библиотеки /usr/lib/libcrypt.a. Получить список функций из нее можно командой nm:

dron~# nm /usr/lib/libcrypt.a crypt-entry.o:0000000000000117 t Letext0000000000000000 T __crypt_r U __md5_crypt U __md5_crypt_r U _ufc_dofinalperm_r U _ufc_doit_r U _ufc_foobar U _ufc_mk_keytab_r U _ufc_output_conversion_r U _ufc_setup_salt_r00000000000000d0 T crypt0000000000000000 W crypt_r00000000000000d0 W fcrypt0000000000000000 r md5_salt_prefix U strncmp U strncpy md5-crypt.o:000000000000070a t Letext U __assert_fail U __errno_location0000000000000690 T __md5_crypt0000000000000000 T __md5_crypt_r U __md5_finish_ctx U __md5_init_ctx...............

Посмотрите, список функций очень большой, поэтому не привожу весь, но видно определенно, что libcrypt.a содержит функции для работы с MD5. Но тогда как ?! Ведь нет никаких параметров дополнительных. А все оказалось куда проще %) Посмотрите на листинг, видите имя md5_salt_prefix. Не правда ли очень говорящее название ?! А теперь посмотрите типичный пароль закодированный с помощью MD5:

$1$/DrNy/Cv$ZBydbOBsEvdI5u5sib2X/0$1$02p9xyDo$gnkh4vts/rArhJselceTV1

Не видите ничего странного ?! Правильно, у них структура отличается от паролей на DES и выглядит следующим образом:

$1$..salt..$.........hash.........

Именно по этой структуре функция crypt() определяет каким методом ей шифровать пароль. Не поленимся однако и посмотрим исходники crypt() в библиотеке libc. Вот к примеру строки из файла crypt-entry.c:

/* Define our magic string to mark salt for MD5 encryptionreplacement. This is meant to be the same as for other MD5 basedencryption implementations. */ static const char md5_salt_prefix[] = "$1$"; ....... /* Try to find out whether we have to use MD5 encryption replacement.*/if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) return __md5_crypt_r (key, salt, (char *) data, sizeof (struct crypt_data));

Помоему классно :) Именно "магическая строчка" $1$ и является тем методом переключения между различными алгоритмами. Тут еще интересен вопрос о том, какой длины должен быть этот salt, изучая исходниках дальше Вы сможете найти в файле md5-crypt.c строчки:

/* Find beginning of salt string. The prefix should normally alwaysbe present. Just in case it is not. */if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof (md5_salt_prefix) - 1; salt_len = MIN (strcspn (salt, "$"), 8);key_len = strlen (key);

Тут не вооруженным глазом видно, что после $1$ ищется второй символ $ и берется длина строки ограниченная этими признаками. Далее выбирается минимум между длиной строки и 8, т.е. получается что salt в алгоритме MD5 может быть любой длины не больше 8-ми. Это и требовалось доказать, теперь давайте попробуем :)

#include <stdlib.h>#include <crypt.h> int main(){ printf("crypt(\"12345678\",\"$1$abasdlkasl123$\") = \"%s\"\n", crypt("password","$1$abasdlkasl123$")); printf("crypt(\"12345678\",\"$1$dfg$\") = \"%s\"\n", crypt("password","$1$dfg$")); return 0;};

Снова компилируем, и не забываем про библиотеку crypt:

dron~# gcc crypt1.c -o crypt1 -lcryptdron~# ./crypt1crypt("12345678","$1$abasdlkasl123$") = "$1$abasdlka$z9aVWR2l14E3WngLCABSt1"crypt("12345678","$1$dfg$") = "$1$dfg$fF0Vo9cC5CyBY827ltEdn0"

Все получилось :) А Вы как думали ?! И обратите внимание на то, что длинный salt в первом случае обрезался до 8-ми символов. Кстати, помоему длина 8 символов куда лучше, чем два. Это еще раз говорит о том, что метод MD5 лучше DES. И раз вообще заговорили про размер, то сравните длину получающихся хешей от работы этих алгоритмов.

Теперь, собственно говоря, сам процесс проверки пароля. Как Вы уже наверно поняли он сводится к простому сравнению, смотрим код:

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <pwd.h>#include <shadow.h> int main(int argc,int *argv){ struct passwd *userinfo; struct spwd *passw; uid_t userid; if (argc<2) { printf("Try to use: %s uin password\n",argv[0]); return 1; }; userid = (uid_t)atoi(argv[1]); userinfo = getpwuid(userid); if (userinfo != NULL){ passw = getspnam(userinfo->pw_name); if (passw != NULL){ printf("Try to test password for \"%s\": ",userinfo->pw_name); if (strcmp(passw->sp_pwdp,crypt(argv[2],passw->sp_pwdp))==0) printf ("Ok...\n"); else printf ("Failed...\n"); } else printf("Can't find password for user with UIN = %s\n",argv[1]); } else printf("Can't find user with UIN = %s\n",argv[1]); return 0;};

Теперь компилируем и запускаем:

dron~# ./testpasswdTry to use: ./testpasswd uin password dron~# ./testpasswd 1000 12345678Try to test password for "dron": Ok... dron~# ./testpasswd 1000 1234Try to test password for "dron": Failed...

Помоему мы научились проверять правильность паролей для пользователей :) Только не забывайте про то, что пароли из /etc/shadow доступны только из под root, но об этом мы не однократно говорили раньше.

И еще я все время пытаюсь Вам привить то, что исходники не только для того, чтобы их компилировать. Они нужны для того, чтобы изучать программирование и мы будем продолжать их просматривать :) Мало ли может глюки найдем %)



<== предыдущая лекция | следующая лекция ==>
Шаг 14 - Получение данных из shadow password | Шаг 16 - Получение информации о группах пользователей


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.145 сек.