Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
W_OK Действительный UID/GID разрешает запись в файл.
X_OK Действительный UID/GID разрешает исполнение файла или, в случае каталога, поиск в каталоге.
F_OK Проверка существования файла.
Проверяется каждый компонент в имени пути, а на некоторых реализациях при проверке для root access() может действовать, как если бы был установлен X_OK, даже если в правах доступа к файлу не установлены биты, разрешающие исполнение. (Странно, но верно: в этом случае предупрежденный вооружен.) В Linux нет такой проблемы.
Если path является символической ссылкой, access() проверяет файл, на который указывает символическая ссылка.
Возвращаемое значение равно 0, если операция для действительных UID и GID разрешена, и -1 в противном случае. Соответственно, если access() возвращает -1, программа с setuid может запретить доступ к файлу, с которым в противном случае эффективный UID/GID смог бы работать:
if (access("/some/special/file", R_OK|W_OK) < 0) {
fprintf(stderr, "Sorry: /some/special/file: %sn",
strerror(errno));
exit(1);
}
По крайней мере для серии ядра Linux 2.4, когда тест X_OK применяется к файловой системе, смонтированной с опцией noexec (см. раздел 8.2.1 «Использование опций монтирования»), тест успешно проходится, если права доступа к файлу имеют разрешение на исполнение. Это верно, несмотря на то, что попытка выполнить файл завершилась бы неудачей.
ЗАМЕЧАНИЕ. Хотя использование access() перед открытием файла является обычной практикой, существует состояние гонки открываемый файл может быть сброшен при подкачке между проверкой функцией access() и вызовом open(). Необходимо осмотрительное программирование, такое, как проверка владельца и прав доступа с помощью stat() и fstat() до и после вызовов access() и open().
Например, программа pathchk проверяет действительность имен путей. GNU версия использует access() для проверки того, что компоненты каталога данного пути действительны. Из Coreutils pathchk.c:
244 /* Возвращает 1, если PATH является годным к использованию
245 каталогом, 0 если нет, 2 если он не существует. */
246
247 static int
248 dir_ok(const char *path)
249 {
250 struct stat stats;
251
252 if (stat (path, &stats)) /* Nonzero return = failure */
253 return 2;
254
255 if (!S_ISDIR(stats.st_mode))
256 {
257 error(0, 0, _("'%s" is not a directory"), path);
258 return 0;
259 }
260
261 /* Используйте access для проверки прав доступа на поиск,
262 поскольку при проверке битов прав доступа st_mode они могут
263 потеряться новыми механизмами управления доступом. Конечно,
264 доступ теряется, если вы используете setuid. */
265 if (access (path, X_OK) != 0)
266 {
267 if (errno == EACCES)
268 error (0, 0, _("directory '%s' is not searchable"), path);
269 else
270 error(0, errno, "%s", path);
271 return 0;
272 }
273
274 return 1;
275 }
Код прост. Строки 252–253 проверяют, существует ли файл. Если stat() завершится неудачей, файл не существует. Строки 255–259 удостоверяют, что файл в самом деле является каталогом.
Комментарий в строках 261–264 объясняет использование access(). Проверки битов st_mode недостаточно: файл может находиться в файловой системе, которая смонтирована только для чтения, в удаленной файловой системе или в файловой системе, не принадлежащей Linux или Unix, или у файла могут быть атрибуты, предотвращающие доступ. Таким образом, лишь ядро может в действительности сказать, будет ли работать access. Строки 265–272 осуществляют проверку, выдавая сообщение об ошибке, определяемое значением errno (строки 267–270).
11.4. Проверка для эффективного пользователя: euidaccess() (GLIBC)
GLIBC предоставляет дополнительную функцию, которая работает подобно access(), но проверяет в соответствии с эффективными UID, GID и набором групп:
#include <unistd.h> /* CLIBC */
int euidaccess(const char *path, int amode);
Аргументы и возвращаемое значение имеют тот же смысл, как для access(). Когда равны эффективный и действительный UID и эффективный и действительный GID, euidaccess() вызывает для осуществления теста access(). Это имеет то преимущество, что ядро может проверить файловую систему только для чтения или другие условия, которые не отражаются в правах доступа и владении файлами.
В противном случае euidaccess() сравнивает значения владельца и группы файла со значениями эффективных UID и GID и набора групп, используя соответствующие биты прав доступа. Этот тест основан на сведениях о файле от stat().
Если вы пишете переносимую программу, но предпочитаете использовать этот интерфейс, достаточно просто извлечь исходный файл из архива GLIBC и приспособить его для общего использования.
11.5. Установка дополнительных битов доступа для каталогов
На современных системах setgid и «липкий» биты имеют особое значение при применении к каталогам.
11.5.1. Группа по умолчанию для новых файлов и каталогов
В оригинальной системе Unix, когда open() или creat() создавали новый файл, он получал эффективные UID и GID создавшего их процесса.
V7, BSD вплоть до BSD 4.1 и System V вплоть до Release 3 все трактовали каталоги как файлы. Однако, с добавлением дополнительного набора групп в BSD 4.2 способ создания новых каталогов изменился: новые каталоги наследовали группу родительского каталога. Более того, новые файлы также наследовали ID группы родительского каталога, а не эффективный GID создающего процесса.
Идея, лежащая в основе множества групп и каталогов, которые работают таким способом, была в усилении группового взаимодействия. У каждого проекта организации, использующего систему, была бы отдельная назначенная ему группа. Для каждой такой группы в группе этого проекта был бы каталог верхнего уровня, и все файлы проекта имели бы доступ на чтение и запись (а при необходимости и на исполнение). Вдобавок, новые файлы автоматически получают группу родительского каталога. Состоя одновременно в нескольких группах (наборе групп), пользователь мог бы как угодно перемещаться между проектами с помощью простой команды cd, а все файлы и каталоги сохраняли бы свою надлежащую группу.
Что происходит на современных системах? Ну, это еще один из немногих случаев, когда можно поймать двух зайцев. SunOS 4.0 придумал механизм, который был включен в System V Release 4; сегодня он используется по крайней мере в Solaris и GNU/Linux. Эти системы придают биту setgid родительского каталога нового файла или каталога следующее значение:
Бит setgid родительского каталога сброшен
Новые файлы и каталоги получают эффективный GID создающего процесса.
Бит setgid родительского каталога установлен
Новые файлы и каталоги получают GID родительского каталога. Новые каталоги наследуют также установленный бит setgid.
(До SunOS 4.0 бит setgid для каталогов не имел определенного значения.) Следующий сеанс показывает бит setgid в действии:
$ cd /tmp /* Перейти в /tmp */
$ ls -ld . /* Проверить его права доступа */
drwxrwxrwt 8 root root 4096 Oct 16 17:40 .
$ id /* Отметить текущие группы */
uid=2076(arnold) gid=42(devel) groups=19(floppy),42(devel),2076(arnold)
$ mkdir d1 ; ls -ld d1 /* Создать новый каталог */
drwxr-xr-x 2 arnold devel 4096 Oct 16 17:40 d1 /* Эффективный ID группы
наследуется */
$ chgrp arnold d1 /* Сменить группу */
$ chmod g+s d1 /* Добавить бит setgid */
$ ls -ld d1 /* Проверить изменение */
drwxr-sr-x 2 arnold arnold 4096 Oct 16 17:40 d1
$ cd d1 /* Перейти в него */
$ echo this should have group arnold on it > f1 /* создать новый файл */
$ ls -l f1 /* Проверить права доступа */
-rw-r--r-- 1 arnold arnold 36 Oct 16 17:41 f1
/* Унаследовано от родителя */
$ mkdir d2 /* Создать каталог */
$ ls -ld d2 /* Проверить права доступа */
drwxr-sr-x 2 arnold arnold 4096 Oct 16 17:51 d2
/* Группа и setgid унаследованы */
Файловые системы ext2 и ext3 для GNU/Linux работают указанным способом. Вдобавок они поддерживают специальные опции монтирования grpid и bsdgroups, которые делают «использование группы родительского каталога» семантикой по умолчанию. (Два имени означают одно и то же.) Другими словами, когда используются эти опции монтирования, в родительских каталогах не нужно устанавливать свои биты seigid.