You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 37 Next »

Many common operating systems such as Windows and UNIX support symbolic (soft) links. Symbolic links can be created in UNIX using the ln -s command or in Windows by using directory junctions in NTFS or the Linkd.exe (Win 2K resource kit) or "junction" freeware.

If not properly performed, checking for the existence of symbolic links can lead to race conditions.

The POSIX lstat() function collects information about a symbolic link rather than its target. This non-compliant code example uses the lstat() function to collect information about the file, checks the st_mode field to determine if the file is a symbolic link, and then opens the file if it is not a symbolic link.

char *filename;
char *userbuf;
unsigned int userlen;

struct stat lstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
  /* handle error */
}

if (!S_ISLNK(lstat_info.st_mode)) {
   fd = open(filename, O_RDWR);
   if (fd == -1) {
       /* handle error */
   }
}
if (write(fd, userbuf, userlen) < userlen) {
  /* Handle Error */
}

This code contains a TOCTOU race condition between the call to lstat() and the subsequent call to open() because both functions operate on a file name that can be manipulated asynchronously to the execution of the program (see FIO01-A. Be careful using functions that use file names for identification).

This compliant solution eliminates the race condition by

  1. calling lstat() on the file name
  2. calling open() to open the file
  3. calling fstat() on the file descriptor returned by open()
  4. comparing the file information returned by the calls to lstat() and fstat() to ensure that the files are the same
char *filename;
char *userbuf;
unsigned int userlen;

struct stat lstat_info;
struct stat fstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
  /* handle error */
}

fd = open(filename, O_RDWR);
if (fd == -1) {
  /* handle error */
}

if (fstat(fd, &fstat_info) == -1) {
  /* handle error */
}

if (lstat_info.st_mode == fstat_info.st_mode &&
    lstat_info.st_ino == fstat_info.st_ino  &&
    lstat_info.st_dev == fstat_info.st_dev) {
  if (write(fd, userbuf, userlen) < userlen) {
    /* Handle Error */
  }
}

This eliminates the TOCTOU condition because fstat() is applied to file descriptors, not file names, so the file passed to fstat() must be identical to the file that was opened. The lstat() function does not follow symbolic links, but open() does. Comparing modes using the st_mode field is sufficient to check for a symbolic link.

Comparing i-nodes using the st_ino fields and devices using the st_dev fields ensures that the file passed to lstat() is the same as the file passed to fstat() (see FIO05-A. Identify files using multiple file attributes).

The tool Compass / ROSE does not currently detect TOCTOU race conditions; however it can be easily extended to do so. One must search for a call to open() or fopen(), and obtain the filename argument as well as the file descriptor return value. If the filename is a variable, the variable is referenced earlier in a function by stat() or lstat(), the file descriptor is assigned to a variable, but the variable is never the argument to a subsequent fstat() function call, then this rule is violated.

Time-of-creation-to-time-of-use (TOCTOU) race condition vulnerabilities can be exploited to gain elevated privileges.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

POS35-C

high

likely

medium

P18

L1

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

[[Dowd 06]] Chapter 9, "UNIX 1: Privileges and Files"
[[ISO/IEC 9899:1999]] Section 7.19, "Input/output <stdio.h>"
[[MITRE 07]] CWE ID 365, "Race Condition in Switch"
[[Open Group 04]] lstat(), fstat(), open()
[[Seacord 05a]] Chapter 7, "File I/O"


      50. POSIX (POS)      

  • No labels