...
In rare cases, it is necessary to check for the existence of symbolic or hard links to ensure that a program is reading from an intended file and not a different file in another directory. In these cases, avoid creating a race condition when checking for the existence of symbolic links.
Also, see POS35-C. Avoid race conditions while checking for the existence of a symbolic link
Non-Compliant Code Example
...
If the process is running with elevated privileges, an attacker can exploit this code, for example, by creating a link from .conf to the /etc/passwd authentication file. The attacker can then overwrite data stored in the password file to create a new root account with no password. As a result, this attack can be used to gain root privileges on a vulnerable system.
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, and then checks the st_mode field to determine if the file is a symbolic link.
| Code Block | ||
|---|---|---|
| ||
struct stat lstat_info;
int fd;
if (lstat("some_file", &lstat_info) == -1) {
/* handle error */
}
if (!S_ISLNK(lstat_info.st_mode)) {
if ((fd = open("some_file", O_RDWR)) == -1) {
/* handle error */
}
}
write(fd, userbuf, userlen);
|
Unfortunately, this code is vulnerable to a TOCTOU race condition. An attacker can exploit this vulnerability by creating a link named "some_file" to an arbitrary file after the lstat() function but before the open() function.
Compliant Solution (Linux 2.1.26+, FreeBSD, Solaris 10, POSIX.1-2008)
...
| Code Block | ||
|---|---|---|
| ||
int fd;
if ((fd = open("some_file", O_RDWR | O_NOFOLLOW)) == -1) {
/* handle error */
}
write(fd, userbuf, userlen);
|
Compliant Solution
This compliant solution properly checks for the existence of a link and eliminates the race condition.
| Code Block | ||
|---|---|---|
| ||
struct stat lstat_info, fstat_info;
int fd;
if (lstat("some_file", &lstat_info) == -1) {
/* handle error */
}
if ((fd = open("some_file", O_RDWR)) == -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);
}
|
...
Risk Assessment
Failing to check for the existence of links can result in a critical system file being overwritten, leading to a data integrity violation.
...