¿Qué es una buena manera de simular O_NOFOLLOW en sistemas sin esta bandera?
-
04-10-2019 - |
Pregunta
Me gustaría ser capaz de forma segura open
simular con O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW
y O_CREAT | O_WRONLY | O_APPEND | O_NOFOLLOW
en sistemas que no O_NOFOLLOW
apoyo. algo que puedo lograr lo que estoy pidiendo con:
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);
pero entonces introducir una condición de carrera y, posiblemente, un problema de seguridad.
pensé tal vez crear un archivo ficticio con sólo el usuario pueda escribir, algo así como touch
ing filename
, haciendo la comprobación lstat
, y luego usando chmod
después de que termino de escribir (para corregir los bits de modo de archivo), pero podría ser pasando por alto algo importante (por ejemplo, si el archivo en filename
existe, no es un archivo normal, o es un enlace simbólico).
¿Qué opinas?
Solución
Su propuesta aún tiene una condición de carrera:
- Mallory crea el enlace que quiere que siga;
-
open()
el enlace conO_CREAT
; - Mallory reemplaza el vínculo con un archivo normal;
- Usted hace su prueba
lstat()
, que pasa (no un enlace); - Mallory reemplaza el archivo regular con el enlace de nuevo.
Se puede solucionar este problema para el caso no O_TRUNC
llamando fstat()
en su descriptor de fichero abierto, así como lstat()
en el camino, y la garantía de que los miembros .st_dev
y .st_ino
son los mismos.
Sin embargo, esto no funciona si está utilizando O_TRUNC
- en el momento en que haya descubierto el engaño, ya es demasiado tarde -. Mallory ya le ha inducido a truncar una de sus archivos importantes
Creo que la manera tradicional para eliminar el agujero sin apoyo O_NOFOLLOW
es:
- Crear un directorio temporal con
700
modo. De error (o reintento) simkdir()
falla debido a directorio existente; - Crea tu nuevo archivo en el directorio temporal;
- Uso
rename()
atómicamente para mover el archivo temporal al nombre de destino; - Eliminar el directorio temporal.