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 time of creation to time of use (TOCTOU) race conditions.
Non-Compliant Code Example
The only function available on POSIX systems to collect information about a symbolic link rather than its target is the lstat()
function. 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; struct stat lstat_info; int fd; /* ... */ if (lstat(filename, &lstat_info) == -1) { /* handle error */ } if (!S_ISLNK(lstat_info.st_mode)) { if ((fd = open(filename, O_RDWR, 0600)) == -1) { /* handle error */ } } write(fd, userbuf, userlen);
This code contains a time of creation to time of use (TOCTOU) race condition betwen the call to lstat()
and the subsequent call to open()
because both functions operate on a file name [[FIO01-A. Prefer functions that do not rely on file names for identification]] that can be manipulated asynchronously to the execution of the program.
Compliant Solution
This compliant solution eliminates the race condition by:
- calling the
lstat()
the filename. - calling
open()
to open the file. - calling
fstat()
on the file descriptor returned byopen()
. - comparing the fine information returned by the calls to
lstat()
andfstat()
to ensure that the files are the same.
char *filename; struct stat lstat_info, fstat_info; int fd; /* ... */ if (lstat(filename, &lstat_info) == -1) { /* handle error */ } if ((fd = open(filename, O_RDWR, 0600)) == -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) { write(fd, userbuf, userlen); }
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 fstat()
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()
[[FIO05-A. Identify files using multiple file attributes]].
Risk Assessment
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 |
3 (high) |
3 (likely) |
2 (medium) |
P18 |
L1 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[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 05]] Chapter 7, "File I/O"