Versions Compared

Key

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

...

In this example, the user can specify a locked device or a FIFO file name, causing the program to hang on the call to openfopen().

Code Block
bgColor#ffcccc

char *filename;
FILE *file;

/* initialize filename */

if (!fgets(filename, sizeof(filename), stdin)) {
  /* handle error */
}

if (open(file = fopen(filename, O_WRONLY"wb")) == -1NULL) {
  /* handle error */
}

/* operate on file */

fclose(file);

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 fildesfd;
int flags;
char *filename;

/* initialize filename */

if (!fgets(filename, sizeof(filename), stdin)) {
  /* handle error */
}

if ((lstat(filename, &orig_st) != 0) 
 || (!S_ISREG(orig_st.st_mode))) 
{
  /* handle error */
}

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

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

if (fstat(fildesfd, &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(fildesfd, F_GETFL)) == -1) {
  /* handle error */
}

if ((fcntl(fildesfd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
  /* handle error */
}

/* operate on file */

close(fd);

The above code does contain an intractable TOCTOU race condition; where an attacker can 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.

...