Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
bgColor#ffcccc
if (!fgets(filename, sizeof(filename), stdin)) {
    /* handle error */
}

if (open(filename, O_WRONLY) == -1) {
    /* handle error */
}

Compliant Solution (POSIX)

...

Code Block
bgColor#ccccff
#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_namefilename, &orig_st) != 0) || (!S_ISREG(orig_st.st_mode))) {
  /* handle error */
}

/* A TOCTOU race condition exists here, see below */

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 */

The above code does contain an intractable TOCTOU race condition; where an attacker could alter the file referenced by filename following the call to lstat() but before the call to open(). The switch will be discovered after the file is opened, but opening the file cannot be prevented in the case where this action itself causes undesired behavior.

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:

Type

Note on effect

another regular file

The fstat() verification will fail fails

FIFO

Either open() will error out return and set errno to ENXIO or the open() will succeed and the fstat() verification will fail

symbolic link

open() will error out return if O_NOFOLLOW is available, otherwise the fstat() verification will fail

special device

Usually the fstat() verification will fail on st_mode. This could still be a problem if the device is one for which just opening (or closing) it causes something to happen. If st_mode compares equal, then the device is one which, after opening, appears to be a regular file. It would then fail the fstat() verification on st_dev and /or st_ino (unless it happens to be the same file, as could happen with /dev/fd/* on Solaris, but this would not be a problem)

Keep in mind that this 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 filescan be prevented if the effected files are maintained in a secure directory.

Compliant Solution (Windows)

...