...
| Code Block | ||
|---|---|---|
| ||
char *file_name;
/* initialize file_name */
FILE *fd = fopen(file_name, "w");
if (fd == NULL) {
/* handleHandle Errorerror */
}
/*... write to file ...*/
fclose(fd);
fd = NULL;
/*
* A race condition here allows for an attacker to switch
* out the file for another
*/
/* ... */
fd = fopen(file_name, "r");
if (fd == NULL) {
/* handleHandle Errorerror */
}
/*... read from file ...*/
fclose(fd);
fd = NULL;
|
...
Reopening a file stream should generally be avoided. However, this may sometimes be necessary in long-running applications to avoid depleting available file descriptors.
This compliant solution uses a "check, use, check" pattern to ensure that the file opened for reading is the same file that was opened for writing. In this solution, the file is opened for writing using the open() function. If the file is successfully opened, the fstat() function is used to read information about the file into the stat orig_st structure. This information is compared with existing When the file is reopened for reading, information about the file (stored in the dev and ino variables) is read into the new_st structure and the st_dev and st_ino fields in orig_st and new_st are compared to improve identification.
| Code Block | ||
|---|---|---|
| ||
struct stat orig_st;
struct stat new_st;
char *file_name;
/* initialize file_name */
int fd = open(file_name, O_WRONLY);
if (fd == -1) {
/* handleHandle Errorerror */
}
/*... write to file ...*/
if (fstat(fd, &orig_st) == -1) {
/* handleHandle error */
}
close(fd);
fd = -1;
/* ... */
fd = open(file_name, O_RDONLY);
if (fd == -1) {
/* handleHandle error */
}
if (fstat(fd, &new_st) == -1) {
/* handleHandle error */
}
if ((orig_st.st_dev != new_st.st_dev) ||
(orig_st.st_ino != new_st.st_ino)) {
/* file was tampered with! */
}
/*... read from file ...*/
close(fd);
fd = -1;
|
...
This compliant solution may not work in some cases. For instance, a long-running service might choose to occasionally re-open reopen a log file to add log messages, but leave the file closed, so that the log file may be periodically rotated. In this case, the inode number would change, and this solution would no longer apply.
...
A simpler solution is to simply not re-open reopen the file. In this code example, the file is opened once for both writing and reading. Once writing is complete, the fseek() function resets the file pointer to the beginning of the file, and its contents are read back (see FIO07-C. Prefer fseek() to rewind()).
Because the file is not re-openedreopened, the possibility of an attacker tampering with the file between the writes and subsequent reads is eliminated.
| Code Block | ||
|---|---|---|
| ||
char *file_name;
FILE *fd;
/* initialize file_name */
fd = fopen(file_name, "w+");
if (fd == NULL) {
/* handleHandle Errorerror */
}
/*... write to file ...*/
/* go to beginning of file */
fseek(fd, 0, SEEK_SET);
/*... read from file ...*/
fclose(fd);
fd = NULL;
|
Be sure to use fflush() after writing data to the file, in accordance with FIO39-C. Do not alternately input and output from a stream without an intervening flush or positioning call.
Noncompliant Code Example (
...
Owner)
In this noncompliant code example, the programmer's intent is to open a file for reading, but only if the user running the process owns the specified file. This is a more restrictive requirement than that that imposed by the operating system, which only requires that the effective user have permissions to read the file. The code, however, relies solely on the file name to identify the file.
| Code Block | ||
|---|---|---|
| ||
char *file_name;
FILE *fd;
/* initialize file_name */
fd = fopen(file_name, "w+");
if (fd == NULL) {
/* handleHandle Errorerror */
}
/* read user's file */
fclose(fd);
fd = NULL;
|
If this code is run with superuser privileges, for example, as part of a setuid-root program, an attacker can exploit this program to read files for which the real user normally lacks sufficient privileges, including files not owned by the user.
Compliant Solution (POSIX) (
...
Owner)
In this compliant solution, the file is opened using the open() function. If the file is successfully opened, the fstat() function is used to read information about the file into the stat structure. This information is compared with existing information about the real user (obtained by the getuid() and getgid() functions).)
| Code Block | ||
|---|---|---|
| ||
struct stat st;
char *file_name;
/* initialize file_name */
int fd = open(file_name, O_RDONLY);
if (fd == -1) {
/* Handle Errorerror */
}
if ((fstat(fd, &st) == -1) ||
(st.st_uid != getuid()) ||
(st.st_gid != getgid())) {
/* file does not belong to user */
}
/*... read from file ...*/
close(fd);
fd = -1;
|
By matching the file owner's user and group IDs to the processes process's real user and group IDs, this program now successfully restricts access to files owned by the real user of the program. This solution can be used to verify that the owner of the file is the one the program expects, reducing opportunities for attackers to replace configuration files with malicious ones, for example.
...
| Wiki Markup |
|---|
\[[Drepper 06|AA. C References#Drepper 06]\] Section 2.2.1 "Identification When Opening" \[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.19.3, "Files," and Section 7.19.4, "Operations on Files" \[[ISO/IEC PDTR 24772|AA. C References#ISO/IEC PDTR 24772]\] "EWR Path Traversal" \[[MITRE 07|AA. C References#MITRE 07]\] [CWE ID 37|http://cwe.mitre.org/data/definitions/37.html], "Path Issue - Slash Absolute Path"; [CWE ID 38|http://cwe.mitre.org/data/definitions/38.html], "Path Issue - Backslash Absolute Path"; [CWE ID 39|http://cwe.mitre.org/data/definitions/39.html], "Path Issue - Drive Letter or Windows Volume"; [CWE ID 62|http://cwe.mitre.org/data/definitions/62.html], "UNIX Hard Link"; [CWE ID 64|http://cwe.mitre.org/data/definitions/64.html], "Windows Shortcut Following (.LNK)"; and [CWE ID 65|http://cwe.mitre.org/data/definitions/65.html], "Windows Hard Link" \[[Open Group 04|AA. C References#Open Group 04]\] "The open function," and "The fstat function" \[[Seacord 05|AA. C References#Seacord 05]\] Chapter 7, "File I/O" |
...