faccessat(2) проверить права доступа пользователя к файлу

Other Alias

access

ОБЗОР

#include <unistd.h>


int access(const char *pathname, int mode);
#include <fcntl.h> /* определения констант of AT_* */
#include <unistd.h>

int faccessat(int dirfd, const char *pathname, int mode, int flags);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

faccessat():

Начиная с glibc 2.10:
_XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
До glibc 2.10:
_ATFILE_SOURCE

ОПИСАНИЕ

access проверяет, имеет ли вызвавший процесс права доступа к файлу pathname. Если pathname является символьной ссылкой, то проверяются права доступа к файлу, на который она ссылается.

Аргумент mode --- это маска выполняемых проверок доступа; может быть равна значению F_OK, или состоять из одного или несколько побитово сложенных R_OK, W_OK и X_OK. F_OK проверяет существование файла. R_OK, W_OK и X_OK запрашивают проверку, соответственно, существования файла и возможности его чтения, записи или выполнения.

Проверка осуществляется с использованием реального, а не эффективного идентификатора пользователя (UID) и группы (GID) вызвавшего процесса. Эффективные идентификаторы будут использоваться при действительной попытке выполнения той или иной операции с файлом (например, open(2)). Аналогичным образом, для пользователя root, при проверке используется набор разрешающих мандатов, а не набор эффективных мандатов; для не root-пользователей при проверке используется пустой набор мандатов.

Это дает программам с set-user-ID или снабжённых мандатами простой способ проверить права доступа вызвавшего пользователя. Другими словами, access() не отвечает на вопрос «могу я прочитать/записать/выполнить этот файл?». Он отвечает на немного другой вопрос: «(предположим я исполняемый файл с setuid) может ли пользователь, запустивший меня прочитать/записать/выполнить этот файл?», что даёт программам с set-user-ID возможность не дать злонамеренным пользователям прочитать файлы, которые пользователи не должны читать.

Если вызвавший процесс имеет соответствующие привилегии (например, его реальный UID равен нулю), то проверка X_OK пройдёт успешно для обычного файла, если у него установлено право на выполнение в любой группе бит: у владельца, группы или остальных.

faccessat()

Системный вызов faccessat() работает также как системный вызов access(), за исключением случаев, описанных здесь.

Если в pathname задан относительный путь, то он считается относительно каталога, на который ссылается файловый дескриптор dirfd (а не относительно текущего рабочего каталога вызывающего процесса, как это делается в access()).

Если в pathname задан относительный путь и dirfd равно специальному значению AT_FDCWD, то pathname рассматривается относительно текущего рабочего каталога вызывающего процесса (как access()).

Если в pathname задан абсолютный путь, то dirfd игнорируется.

Значение flags составляется из побитово сложенных следующих значений:

AT_EACCESS
Выполнять проверку, доступа используя эффективный идентификатор пользователя и группы. По умолчанию в faccessat() используются реальные идентификаторы (как в access()).
AT_SYMLINK_NOFOLLOW
Если значение pathname является символьной ссылкой, не разыменовывать её, а выдать информацию о самой ссылке.

Смотрите в openat(2) объяснение необходимости faccessat().

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении (есть все запрошенные права или значение mode равно F_OK и файл существует) возвращается ноль. При ошибке (по крайней мере, одно право из mode было не удовлетворено, значение mode равно F_OK и файл не существует, или случилась другая ошибка), возвращается -1, а errno устанавливается должным образом.

ОШИБКИ

Вызов access() и faccessat() будут завершаться с ошибкой если:
EACCES
Запрошенный тип доступа не удовлетворён или в одном из каталогов в pathname не разрешён поиск. (См. также path_resolution(7).)
ELOOP
Во время определения pathname встретилось слишком много символьных ссылок.
ENAMETOOLONG
Слишком длинное значение аргумента pathname.
ENOENT
Компонент пути pathname не существует или является повисшей символьной ссылкой.
ENOTDIR
Компонент пути, использованный как каталог в pathname, в действительности таковым не является.
EROFS
Запрошено право на запись в файл, расположенный в файловой системе, доступной только для чтения.

Вызов access() и faccessat() могут завершиться с ошибкой, если:

EFAULT
Аргумент pathname указывает за пределы доступного адресного пространства.
EINVAL
Аргумент mode был задан неверно.
EIO
Произошла ошибка ввода-вывода.
ENOMEM
Недостаточное количество памяти ядра.
ETXTBSY
Запрошены права на запись для исполняемого файла, который сейчас выполняется.

В faccessat() дополнительно могут возникнуть следующие ошибки:

EBADF
dirfd не является правильным файловым дескриптором.
EINVAL
Указано неверное значение в flags.
ENOTDIR
Значение pathname содержит относительный путь и dirfd содержит файловый дескриптор, указывающий на файл, а не на каталог.

ВЕРСИИ

Вызов faccessat() был добавлен в ядро Linux версии 2.6.16; поддержка в glibc доступна с версии 2.4.

СООТВЕТСТВИЕ СТАНДАРТАМ

access(): SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.

faccessat(): POSIX.1-2008.

ЗАМЕЧАНИЯ

Предупреждение: Использование этих вызовов для проверки, например, разрешено ли пользователю открытие файла перед реальным выполнением open(2), создаёт брешь в безопасности, так как пользователь может использовать короткий промежуток времени между проверкой и открытием файла для управления им. По этой причине лучше избегать использования данного системного вызова (в только что описанном примере, безопасной альтернативой будет временное переключение эффективного пользовательского идентификатора процесса на реальный идентификатор и вызов open(2)).

Вызов access() всегда разыменовывает символьные ссылки. Если вам нужно проверить права символьной ссылки, используйте вызов faccessat(2) с флагом AT_SYMLINK_NOFOLLOW.

Эти вызовы возвращают ошибку, если отказано в любом из типов доступа mode, даже если разрешены остальные типы.

Если вызывающий процесс имеет соответствующие привилегии (например, суперпользователя), то POSIX.1-2001 разрешает реализации сообщить об успешном выполнении при проверке X_OK даже, если ни один из битов выполнения файла не установлен. В Linux так не происходит.

Файл доступен только в случае, если для каждого каталога в пути, указанном в pathname, имеется право выполнять поиск (то есть, установлен бит выполнения). Если какой-то каталог недоступен, то вызов access() вернёт ошибку, независимо от имеющихся прав файла.

Проверяются только биты доступа, но не тип файла или его содержимое. Поэтому, если обнаруживается, что в каталог можно писать, то это, вероятно, означает, что в этом каталоге можно создавать файлы, и что в этот каталог нельзя писать как в файл. Также, файл DOS может быть посчитан как «исполняемый», но вызов execve(2) всё равно не сможет его запустить.

Эти вызовы access() могут некорректно работать на файловых системах NFSv2 со включённым преобразованием UID-ов, потому что это преобразование происходит на сервере и спрятано от клиента, который пытается проверить права (в NFS версии 3 и выше выполняется проверка на сервере). Похожие проблемы могут возникать при монтировании FUSE.

Отличия между библиотекой C и ядром

Нижележащий системный вызов faccessat() имеет только первые три аргумента. Флаги AT_EACCESS и AT_SYMLINK_NOFOLLOW на самом деле реализованы внутри обёрточной функции glibc faccessat(). Если задан какой-то из этих флагов, то обёрточная функция использует fstatat(2) для определения прав доступа.

Замечания по glibc

В старых ядрах, где faccessat() отсутствует (и если не указаны флаги AT_EACCESS и AT_SYMLINK_NOFOLLOW), обёрточная функция glibc использует access(). Если pathname является относительным путём, то glibc собирает путь относительно символической ссылки в /proc/self/fd, которая соответствует аргументу dirfd.

ДЕФЕКТЫ

В ядрах версии 2.4 (и более ранних) есть некоторая странность в работе теста X_OK для суперпользователя. Если для всех категорий право исполнения отключено для файла-не каталога, то тест access() возвращает -1 только когда mode задан как X_OK; если в mode также указан флаг R_OK или W_OK, то access() вернёт для таких файлов 0. Ранние версии ядер 2.6 (до 2.6.3 включительно) ведут себя также как ядра 2.4.

В ядрах до версии 2.6.20 эти вызовы игнорировали влияние флага MS_NOEXEC, если он был установлен с помощью mount(2) для содержащей файл файловой системы. Начиная с версия ядра 2.6.20, этот флаг учитывается.