Чтение онлайн

ЖАНРЫ

Linux программирование в примерах
Шрифт:

11.6.2. Изменение действительного и эффективного ID

Работа с двумя различными ID пользователей представляет для программиста приложения проблему. Могут быть вещи, которые программе нужно сделать, работая с эффективным UID, а другие вещи — работая с действительным UID.

Например, до того, как в системах Unix появилось управление заданиями, многие программы предоставляли переходы в оболочку, т.е. способ запуска команды или интерактивной оболочки из текущей программы. Хорошим примером этого является редактор

ed
: набор командной строки, начинающейся с '
!
', запускает оставшуюся часть строки в качестве команды оболочки. Набрав '
!sh
', вы получаете интерактивную оболочку. (Это работает до сих пор — попробуйте!) Предположим,
описанная ранее гипотетическая игровая программа также предоставляет переход в оболочку: она должна быть запущена от имени действительного пользователя, а не эффективного. В противном случае, редактирование файла счета или многие гораздо худшие вещи становятся для игрока тривиальной задачей!

Таким образом, имеется явная потребность в возможности замены эффективного UID действительным UID. Более того, полезна возможность обратного переключения эффективного UID на первоначальный. (В этом причина необходимости наличия сохраненного set-user ID; появляется возможность восстановления первоначальных привилегий, которые были у процесса при его запуске.)

Как и для множества Unix API, различные системы решили проблему разными способами, иногда с использованием одного и того же API, но с другой семантикой, а иногда введением другого API. Погружение в исторические подробности годится лишь для создания головной боли, поэтому мы не будем с этим беспокоиться. Вместо этого мы рассмотрим, что предоставляет POSIX и как работает каждый API. Более того, наше обсуждение фокусируется на значениях действительных и эффективных UID; значения GID работают аналогичным образом, поэтому мы не будем хлопотать с повторением подробностей для этих системных вызовов. Функции следующие:

#include <sys/types.h> /* POSIX */

#include <unistd.h>

int seteuid(uid_t euid); /* Установка эффективного ID */

int setegid(gid_t egid);

int setuid(uid_t uid);

 /* Установка эффективного ID, root устанавливает все */

int setgid(gid_t gid);

int setreuid(uid_t ruid, uid_t euid);

 /* Совместимость с BSD, устанавливаются оба */

int setregid(gid_t rgid, gid_t egid);

Есть три набора функций. Первые два были созданы POSIX:

int seteuid(uid_t euid)

Эта функция устанавливает лишь эффективный UID. Обычный пользователь (не

root
) может установить в качестве ID лишь в значения действительного, эффективного или сохраненного set-user ID. Приложения, которые будут переключать эффективный UID. должны использовать исключительно эту функцию.

Процесс с эффективным UID, равным нулю, может установить в качестве эффективного UID любое значение. Поскольку в качестве значения эффективного UID можно установить также сохраненный set-user ID, процесс может восстановить свои привилегии root с помощью другого вызова

seteuid
.

int setegid(gid_t egid)

Эта функция делает для эффективного ID группы то, что

seteuid
делает для эффективного ID пользователя.

Следующий набор функций предлагает первоначальный API Unix для изменения действительных и эффективных UID и GID. В модели POSIX эти функции являются тем. что должна использовать программа с setuid-root для постоянного изменения действительного или эффективного UID:

int setuid(uid_t uid)

Для обычного пользователя эта функция также устанавливает лишь эффективный UID. Как и для

seteuid
, значением эффективного UID может быть любое из текущих значений действительного, эффективного иди сохраненного set-user ID. Изменение не постоянно; эффективный UID может быть изменен последующим вызовом на другое значение (из того же исходного набора).

Однако, для

root
эта функция устанавливает в данное значение все три значения для действительного, эффективного и сохраненного set-user ID. Более того, изменение постоянно; прежнее ID нельзя восстановить. (Это имеет смысл: раз изменился сохраненный set-user ID, нет другого ID
для восстановления.)

int setgid(gid_t gid)

Эта функция делает для эффективного ID группы то же, что

setuid
делает для эффективного ID пользователя. Используется то же разграничение между обычными пользователями и
root
.

ЗАМЕЧАНИЕ. Возможность изменения ID группы зависит от эффективного ID пользователя. Эффективный GID, равный 0, не имеет особых привилегий.

Наконец, POSIX представляет для исторической совместимости две функции из BSD 4.2. В новом коде их лучше не использовать. Однако, поскольку вы, вероятно, увидите использующий эти функции старый код, мы их здесь опишем.

int setreuid(uid_t ruid, uid_t euid)

Устанавливает данные значения в качестве действительного и эффективного UID. Значение -1 для

ruid
или
euid
оставляет соответствующие ID без изменения. (Это похоже на
chown
; см. раздел 5.5.1 «Смена владельца файла:
chown
,
fchown
и
lchown
».)

root
может устанавливать в качестве действительного и эффективного ID любое значение. В соответствии с POSIX пользователи, не являющиеся
root
, могут изменять лишь эффективный ID; то, что случится, если обычный пользователь попытается изменить действительный UID, «не определено». Однако, справочная страница GNU/Linux setreuid(2) разъясняет поведение Linux, в качестве действительного UID может быть установлено значение действительного или эффективного UID, а в качестве эффективного UID может быть значение действительного, эффективного или сохраненного set-user ID. (Для других систем см. справочную страницу setreuid(2).)

int setregid(gid_t rgid, gid_t egid)

Делает для действительных и эффективных ID групп то же, что

setreuid
делает для действительных и эффективных ID пользователя. Используется то же разграничение между обычными пользователями и
root
.

Сохраненный set-user ID в модели BSD не существует, поэтому лежащей в основе

setreuid
и
setregid
идеей было упростить переключение между действительным и эффективным ID:

setreuid(geteuid, getuid); /* обмен действительным и эффективным */

Однако, с принятием POSIX модели сохранения set-user ID и функций

seteuid
и
setegid
функции BSD не следует использовать в новом коде. Даже документация BSD 4.4 помечает эти функции как устаревшие, рекомендуя вместо них
seteuid
/
setuid
и
setegid
/
setgid
.

11.6.3. Использование битов setuid и setgid

Есть важные случаи, в которых действующая как

root
программа должна безвозвратно изменить все три значения действительного, эффективного и сохраненного set-user ID на ID обычного пользователя. Наиболее очевидным случаем является программа
login
, которую вы используете (либо непосредственно, либо удаленно) каждый раз при регистрации в системе GNU/Linux или Unix. Имеется иерархия программ, как очерчено на рис. 11.1.

Рис. 11.1. От

init
через
getty
через
login
к shell

Код для

login
слишком сложен, чтобы показать здесь, поскольку он имеет дело с рядом задач, не имеющих отношения к текущему обсуждению. Но мы можем очертить шаги, которые происходят во время регистрации, следующим образом:

1. 

init
является самым первым процессом. Его PID равен 1. Все другие процессы являются его потомками. Ядро вручную создает процесс 1 во время загрузки и запускает в нем
init
. Он действует с действительным и эффективным UID, равными нулю, т.е. как
root
.

Поделиться с друзьями: