Выбрать главу

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. Резюме

У каждого пользователя есть уникальное имя для входа в систему и связанный с ним числовой идентификатор. Пользователи могут принадлежать одной или нескольким группам, у каждой из которых также есть уникальное имя и связанный с ним числовой ID. Основная цель этих идентификаторов — доказательство факта принадлежности различных системных ресурсов (например, файлов) к группам и полномочий на доступ к ним.

Имя пользователя и идентификатор определяются в файле /etc/passwd, который содержит и другую информацию о пользователе. Принадлежность пользователя к той или иной группе определяется полями в файлах /etc/passwd и /etc/group. Еще один файл, /etc/shadow, может быть прочитан только привилегированными процессами. Он применяется для отделения конфиденциальной парольной информации от пользовательских сведений, находящихся в открытом доступе в файле /etc/passwd. Для извлечения информации из каждого из этих файлов предоставляются различные библиотечные функции.

Функция crypt(), которая может пригодиться для программ, нуждающихся в аутентификации пользователя, шифрует пароль точно так же, как и стандартная программа входа в систему.

8.7. Упражнения

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

printf("%s %s\n", getpwuid(uid1)->pw_name,

getpwuid(uid2)->pw_name);

8.2. Реализуйте функцию getpwnam(), используя функции setpwent(), getpwent() и endpwent().

9. Идентификаторы процессов

У каждого процесса есть набор связанных с ним числовых идентификаторов пользователей (UID) и идентификаторов групп (GID). Иногда их называют идентификаторами процесса. В число этих идентификаторов входят:

• реальный (real) ID пользователя и группы;

• действующий (effective) ID пользователя и группы;

• сохраненный установленный ID пользователя (saved set-user-ID) и сохраненный установленный ID группы (saved set-group-ID);

• характерный для Linux пользовательский и групповой ID файловой системы;

• дополнительные идентификаторы групп.

В этой главе будут подробно рассмотрены назначения этих идентификаторов процессов, а также системные вызовы и библиотечные функции, которые могут использоваться для их извлечения и изменения. Будут также рассмотрены понятия привилегированных и непривилегированных процессов и применение механизмов установленных идентификаторов пользователей и установленных идентификаторов групп, позволяющих создавать программы, выполняемые с полномочиями конкретного пользователя или группы.