Некоторые приложения требуют, чтобы пользователи прошли аутентификацию. Обычно требуется ввести имя пользователя (имя для входа в систему) и пароль. Приложение для этих целей может работать с собственной базой данных пользовательских имен и паролей. Но иногда необходимо или удобно позволять пользователям вводить их стандартные имена пользователей и пароли, определенные в файлах /etc/passwd и /etc/shadow. (В остальной части раздела будет считаться, что в системе включен режим использования теневых паролей и что эти зашифрованные пароли хранятся в файле /etc/shadow.) Наглядными примерами таких программ могут послужить сетевые приложения, предоставляющие какие-либо формы для входа в удаленную систему, например ssh и ftp. Они должны проверить допустимость имени пользователя и пароля точно так же, как это делают программы стандартного входа в систему.
Из соображений безопасности системы UNIX шифруют пароли, используя алгоритм
#define _XOPEN_SOURCE
#include
char *crypt(const char *
Возвращает указатель на статично выделенную строку, содержащую при успешном завершении зашифрованный пароль, или NULL при ошибке
Работа функции crypt() предусматривает получение ключа key (то есть пароля) длиной до восьми символов и применение к нему разновидности алгоритма Data Encryption Standard (DES). Аргумент salt является строкой из двух символов, чье значение используется для внесения помех в алгоритм (его изменения), то есть для применения технологии, затрудняющей взлом зашифрованного пароля. Функция возвращает указатель на статически выделенную 13-символьную строку, являющуюся зашифрованным паролем.
Подробности, касающиеся алгоритма DES, можно найти по адресу http://www.itl.nist.gov/fipspubs/fip46-2.htm. Как уже ранее упоминалось, вместо DES могут использоваться другие алгоритмы. Например, применение алгоритма MD5 приводит к созданию 34-символьной строки, начинающейся с символа доллара ($), который позволяет функции crypt() отличать пароли, зашифрованные с помощью DES, от паролей, зашифрованных с помощью MD5.
При рассмотрении вопроса шифрования паролей здесь употребляется слово «шифрование», что не совсем верно отражает действительность. Если выражаться точнее, то DES использует заданную строку пароля в качестве ключа шифрования для зашифровки фиксированной строки битов, а MD5 представляет собой сложный тип функции хеширования. Результат в обоих случаях получается один и тот же: не поддающееся расшифровке и необратимое преобразование входного пароля.
И аргумент salt, и шифруемый пароль состоят из символов, выбранных из 64-символьного набора [a-zA-Z0-9/.]. Таким образом, аргумент salt («соль»), состоящий из двух символов, может стать причиной изменения алгоритма шифрования любым из 64 × 64 = 4096 возможных способов. Это означает, что вместо предварительного шифрования целого словаря и проверки зашифрованного пароля на совпадение со всеми словами в словаре взломщику придется проверять пароль на соответствие 4096 зашифрованным версиям словарей.
Зашифрованный пароль, возвращенный функцией crypt(), содержит в двух первых символах копию исходного значения «соли». Это означает, что при шифровании потенциально подходящего пароля можно получить соответствующее значение «соли» из значения зашифрованного пароля, уже хранящегося в файле /etc/shadow. (Такие программы, как passwd(1), при шифровании нового пароля создают произвольное значение «соли».) Фактически функция crypt() игнорирует любые символы в строке «соли», кроме первых двух. Поэтому можно указать в качестве аргумента salt сам зашифрованный пароль.
Если нужно воспользоваться функцией crypt() в Linux, следует откомпилировать программы с ключом — lcrypt, чтобы они были скомпонованы с библиотекой crypt.
В листинге 8.2 показано, как функция crypt() применяется для аутентификации пользователя. Программа в этом листинге сначала считывает имя пользователя, а затем извлекает соответствующую парольную запись и (если таковая существует) теневую запись в файле паролей. Если парольная запись не будет найдена или же если у программы нет полномочий на чтение из теневого файла паролей (для этого требуются полномочия привилегированного пользователя или принадлежность к группе shadow), то программа выводит на экран сообщение об ошибке, а затем осуществляет выход. Затем программа считывает пароль пользователя с помощью функции getpass().
#define _BSD_SOURCE
#include
char *getpass(const char *