Возвращает при успешном завершении указатель на статически размещаемую строку ввода пароля или NULL при ошибке

Функция getpass() сначала отключает отображение на экране и всю обработку специальных символов управления терминалом (таких как символ прерывания, обычно это Ctrl+C). (Способы изменения этих настроек терминала рассматриваются в главе 58.) Затем на экран выводится строка с приглашением на ввод и считывается введенная строка, а в качестве результата выполнения функции возвращается строка ввода с завершающим нулевым байтом и удаленным следующим за ней символом новой строки. (Эта строка размещается статически и поэтому будет перезаписана при следующем вызове getpass().) Перед возвращением getpass() восстанавливает настройки терминала до их исходного состояния.

Прочитав пароль с помощью функции getpass(), программа из листинга 8.2 проверяет его. При этом функция crypt() используется для его шифрования и проверки того, что получившаяся строка в точности совпадает зашифрованному паролю, записанному в теневом файле паролей. Если пароль совпадает, идентификатор пользователя выводится на экран, как в следующем примере:

$ su Для чтения теневого файла паролей нужны привилегии

Password:

# ./check_password

Username: mtk

Password: Набирается пароль, который не отображается на экране

Successfully authenticated: UID=1000

Программа в листинге 8.2 определяет размер массива символов, содержащего имя пользователя. Для этого применяется значение, возвращенное выражением sysconf(_SC_LOGIN_NAME_MAX), которое выдает максимальный размер имени пользователя в главной системе. Использование sysconf() объясняется в разделе 11.2.

Листинг 8.2. Аутентификация пользователя с применением теневого файла паролей

users_groups/check_password.c

#define _BSD_SOURCE /* Получение объявления getpass() из */

#define _XOPEN_SOURCE /* Получение объявления crypt() из */

#include

#include

#include

#include

#include "tlpi_hdr.h"

int

main(int argc, char *argv[])

{

char *username, *password, *encrypted, *p;

struct passwd *pwd;

struct spwd *spwd;

Boolean authOk;

size_t len;

long lnmax;

lnmax = sysconf(_SC_LOGIN_NAME_MAX);

if (lnmax == -1) /* Если предел не определен, */

lnmax = 256; /* выбираем наугад */

username = malloc(lnmax);

if (username == NULL)

errExit("malloc");

printf("Username: ");

fflush(stdout);

if (fgets(username, lnmax, stdin) == NULL)

exit(EXIT_FAILURE); /* Выход при встрече EOF */

len = strlen(username);

if (username[len — 1] == '\n')

username[len — 1] = '\0'; /* Удаление завершающего '\n' */

pwd = getpwnam(username);

if (pwd == NULL)

fatal("couldn't get password record");

spwd = getspnam(username);

if (spwd == NULL && errno == EACCES)

fatal("no permission to read shadow password file");

if (spwd!= NULL) /* Если есть запись теневого пароля */

pwd->pw_passwd = spwd->sp_pwdp; /* Использование теневого пароля */

password = getpass("Password: ");

/* Шифрование пароля с немедленным уничтожением незашифрованной версии */

encrypted = crypt(password, pwd->pw_passwd);

for (p = password; *p!= '\0';)

*p++ = '\0';

if (encrypted == NULL)

errExit("crypt");

authOk = strcmp(encrypted, pwd->pw_passwd) == 0;

if (!authOk) {

printf("Incorrect password\n");

exit(EXIT_FAILURE);

}

printf("Successfully authenticated: UID=%ld\n", (long) pwd->pw_uid);

/* Здесь совершаем то, ради чего аутентифицировались… */

exit(EXIT_SUCCESS);

}

users_groups/check_password.c

В листинге 8.2 проиллюстрирован важный момент, касающийся решения вопросов безопасности. Программы, читающие пароль, должны немедленно его зашифровать и стереть незашифрованную версию из памяти. Тем самым будет минимизирована возможность аварийного завершения программы с образованием файла дампа ядра, который может быть прочитан для обнаружения пароля.

Существуют и другие пути раскрытия незашифрованного пароля. Например, пароль может быть прочитан из своп-файла привилегированной программой, если виртуальная страница памяти, содержащая пароль, сбрасывается на диск. Кроме того, в попытке обнаружения пароля процесс с достаточным уровнем привилегий может прочитать /dev/mem (виртуальное устройство, представляющее физическую память компьютера в виде последовательного потока байтов).

Функция getpass() фигурировала в SUSv2 с пометкой LEGACY (устаревшая), где отмечалось, что ее название вводит в заблуждение и она предоставляет функциональные возможности, которые в любом случае можно легко реализовать. Из SUSv3 спецификация getpass() была удалена. Тем не менее она встречается во многих реализациях UNIX.

8.6. Резюме
Перейти на страницу:

Похожие книги