...
| Code Block | ||
|---|---|---|
| ||
#ifdef O_NOFOLLOW
#define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK
#else
#define OPEN_FLAGS O_NONBLOCK
#endif
/* ... */
struct stat orig_st;
struct stat open_st;
int fildes;
int flags;
if (!fgets(filename, sizeof(filename), stdin)) {
/* handle error */
}
if ((lstat(file_name, &orig_st) != 0) || (!S_ISREG(orig_st.st_mode))) {
/* handle error */
}
/* A TOCTOU race condition exists here, see notebelow */
if ((fildes = open(filename, OPEN_FLAGS | O_WRONLY)) == -1) {
/* handle error */
}
if (fstat(fildes, &open_st) != 0) {
/* handle error */
}
if ((orig_st.st_mode != open_st.st_mode) ||
(orig_st.st_ino != open_st.st_ino) ||
(orig_st.st_dev != open_st.st_dev)) {
/* file was tampered with */
}
/* Optional: drop the O_NONBLOCK now that we are sure this is a good file */
if ((flags = fcntl(fildes, F_GETFL)) == -1) {
/* handle error */
}
if ((fcntl(fildes, F_SETFL, flags & ~O_NONBLOCK) != 0) {
/* handle error */
}
/* operate on file */
|
Regarding the inevitable TOCTOU vulnerability between the lstat() check and open() call, there The above code does contain an unfixable TOCTOU race condition; where an attacker could substitute a rogue file (or symlink) for our good file. After we open the file, we will discover the switch, but if opening the file itself causes unwanted behavior, we cannot prevent the attacker from that behavior. There are essentially four cases that could result from an attacker switching out the file for one of the following:
...
Keep in mind that this TOCTOU race condition is not a problem if the program can guarantee a safe environment for itself by properly managing permissions and ensuring that an attacker cannot have write or modify access to files.
...