Как лучше всего имитировать O_NOFOLLOW в системах без этого флага?

Я хотел бы безопасно имитировать open с O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW и O_CREAT | O_WRONLY | O_APPEND | O_NOFOLLOW в системах, которые не поддерживают O_NOFOLLOW. Я могу в некоторой степени добиться того, о чем прошу:

struct stat lst;
if (lstat(filename, &lst) != -1 && S_ISLNK(lst.st_mode)) {
    errno = ELOOP;
    return -1;
}

mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, mode);

но затем я ввожу состояние гонки и, возможно, проблему безопасности.

Я подумал о том, чтобы создать фиктивный файл, в котором только пользователь может писать, что-то вроде touching filename, выполнения lstat проверки, а затем использования chmod после того, как я закончу писать (чтобы исправить биты режима файла), но я мог упустить из виду что-то важное (например, если файл по адресу filename существует, не является обычным файлом или уже является символической ссылкой).

Как вы думаете?


person Daniel Trebbien    schedule 26.05.2010    source источник


Ответы (1)


В вашем предложении все еще есть условие гонки:

  • Мэллори создает ссылку, по которой он хочет, чтобы вы перешли;
  • Вы open() связываетесь с O_CREAT;
  • Мэллори заменяет ссылку на обычный файл;
  • Вы выполняете свой lstat() тест, который проходит (не ссылка);
  • Мэллори снова заменяет обычный файл ссылкой.

Вы можете исправить это для случая, отличного от O_TRUNC, вызвав fstat() в дескрипторе открытого файла, а также lstat() в пути и убедившись, что элементы .st_dev и .st_ino совпадают.

Однако это не работает, если вы используете O_TRUNC — к тому времени, когда вы обнаружите обман, будет уже слишком поздно — Мэллори уже убедила вас урезать один из ваших важных файлов.

Я считаю, что традиционный способ устранить дыру без поддержки O_NOFOLLOW:

  • Создайте временный каталог с режимом 700. Ошибка (или повторная попытка), если mkdir() не работает из-за существующего каталога;
  • Создайте новый файл во временном каталоге;
  • Используйте rename() для атомарного перемещения временного файла на целевое имя;
  • Удалите временный каталог.
person caf    schedule 26.05.2010