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

1107 #endif

1108 }

Переменные ngroups и groupset глобальные; их объявления не показаны. Макрос GETGROUPS_T (строка 1102) является типом для использования со вторым аргументом: на системе POSIX это gid_t, в противном случае int.

Строки 1085 и 1107 заключают в скобки все тело функции; на древних системах, в которых вообще нет наборов групп, тело функции пустое.

Строки 1086–1088 обрабатывают не-POSIX системы; до компиляции программы механизмом конфигурации определяется GETGROUPS_NOT_STANDARD. В этом случае код использует NGROUPS_MAX, как описано выше. (Даже а 2004 г. такие системы все еще существуют и используются; хотя, слава богу, число их уменьшается.)

Строки 1089–1094 для систем POSIX, причем нулевой параметр size используется для получения числа групп.

Строки 1096–1099 осуществляют проверку ошибок. Если возвращаемое значение 0, дополнительных групп нет, поэтому init_groupset() просто сразу возвращается.

Наконец, строка 1102 для выделения массива достаточного размера использует malloc() (посредством проверяющего ошибки макроса-оболочки, см. раздел 3.2.1.8 «Пример: чтение строк произвольной длины»). Затем строка 1104 заполняет этот массив.

11.3. Проверка для действительного пользователя: access()

В большинстве случаев значения эффективного и действительного UID и GID являются одними и теми же. Таким образом, не имеет значения, что проверка прав доступа к файлу осуществляется по эффективному ID, а не по действительному.

Однако, при написании приложения с setuid или setgid вы можете иногда захотеть проверить, является ли операция, разрешенная для эффективных UID и GID, также разрешенной для действительных UID и GID. В этом заключается задача функции access():

#include <unistd.h> /* POSIX */

int access(const char *path, int amode);

Аргумент path является путем к файлу для проверки действительных UID и GID. amode содержит объединение побитовым ИЛИ одного или нескольких из следующих значений:

R_OK  Действительный UID/GID разрешает чтение файла.

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: %s\n",

  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(). Это имеет то преимущество, что ядро может проверить файловую систему только для чтения или другие условия, которые не отражаются в правах доступа и владении файлами.