Книга: Операционная система UNIX

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

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

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

Однако нельзя не отметить еще четыре идентификатора, играющие решающую роль при доступе к системным ресурсам: идентификатор пользователя UID, эффективный идентификатор пользователя EUID, идентификатор группы GID и эффективный идентификатор группы EGID. Эти идентификаторы определяют права процесса в файловой системе, и как следствие, в операционной системе в целом. Запуская различные команды и утилиты, можно заметить, что порожденные этими командами процессы полностью отражают права пользователя UNIX. Причина проста — все процессы, которые запускаются, имеют идентификатор пользователя и идентификатор группы. Исключение составляют процессы с установленными флагами SUID и SGID.

При регистрации пользователя в системе утилита login(1) запускает командный интерпретатор, — login shell, имя которого является одним из атрибутов пользователя. При этом идентификаторам UID (EUID) и GID (EGID) процесса shell присваиваются значения, полученные из записи пользователя в файле паролей /etc/passwd. Таким образом, командный интерпретатор обладает правами, определенными для данного пользователя.

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

Казалось бы, эту стройную систему могут "испортить" утилиты с установленными флагами SUID и SGID. Но не стоит волноваться — как правило, такие программы не позволяют порождать другие процессы, в противном случае, эти утилиты необходимо немедленно уничтожить!

На рис. 2.10. показан процесс наследования пользовательских идентификаторов в рамках одного сеанса работы.


Рис. 2.10. Наследование пользовательских идентификаторов

Для получения значений идентификаторов процесса используются следующие системные вызовы:

#include <sys/types.h>
#include <unistd.h>
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);

Эти функции возвращают для сделавшего вызов процесса соответственно реальный и эффективный идентификаторы пользователя и реальный и эффективный идентификаторы группы.

Процесс также может изменить значения этих идентификаторов с помощью системных вызовов:

#include <sys/types.h>
#include <unistd.h>
int setuid(uid_t uid);
int setegid(gid_t egid);
int seteuid(uid_t euid);
int setgid(gid_t gid);

Системные вызовы setuid(2) и setgid(2) устанавливают сразу реальный и эффективный идентификаторы, а системные вызовы seteuid(2) и setegid(2) — только эффективные.

Ниже приведен фрагмент программы login(1), изменяющей идентификаторы процесса на значения, полученные из записи файла паролей. В стандартной библиотеке имеется ряд функций работы с записями файла паролей, каждая из которых описывается структурой passwd, определенной в файле <pwd.h>. Поля этой структуры приведены в табл. 2.17.

Таблица 2.17. Поля структуры passwd

Поле Значение
char *pw_name Имя пользователя
char *pw_passwd Строка, содержащая пароль в зашифрованном виде; из соображения безопасности в большинстве систем пароль хранится в файле /etc/shadow, а это поле не используется
uid_t pw_uid Идентификатор пользователя
gid_t pw_gid Идентификатор группы
char *pw_gecos Комментарий (поле GECOS), обычно реальное имя пользователя и дополнительная информация
char *pw_dir Домашний каталог пользователя
char *pw_shell Командный интерпретатор

Функция, которая потребуется для нашего примера, позволяет получить запись файла паролей по имени пользователя. Она имеет следующий вид:

#include <pwd.h>
struct passwd *getpwnam(const char *name);

Итак, перейдем к фрагменту программы:

...
struct passwd *pw;
char logname[MAXNAME];
/* Массив аргументов при запуске
   командного интерпретатора */
char *arg[MAXARG];
/* Окружение командного интерпретатора */
char *envir[MAXENV];
...
/* Проведем поиск записи пользователя с именем logname,
   которое было введено на приглашение "login:" */
pw = getpwnam(logname);
/* Если пользователь с таким именем не найден, повторить
   приглашение */
if (pw == 0)
 retry();
 /* В противном случае установим идентификаторы процесса
    равными значениям, полученным из файла паролей и запустим
    командный интерпретатор */
else {
 setuid(pw->pw_uid);
 setgid(pw->pw_gid);
 execve(pw->pw_shell, arg, envir);
}
...

Вызов execve(2) запускает на выполнение программу, указанную в первом аргументе. Мы рассмотрим эту функцию в разделе "Создание и управление процессами" далее в этой главе.

Оглавление книги


Генерация: 0.654. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз