File names on many operating systems, including Windows and UNIX, may be used to access special files, which are actually devices. Reserved MS-DOS device Reserved Microsoft Windows device names include AUX, CON, PRN, COM1, and LPT1. Device or paths using the \\.\ device namespace. Device files on UNIX systems are used to apply access rights and to direct operations on the files to the appropriate device drivers.
...
Device files in UNIX can be a security risk when an attacker can access them in an unauthorized way. For example, if attackers can read or write to the /dev/kmem device, they may be able to alter their the priority, UID, or other attributes of their process or simply crash the system. Similarly, access to disk devices, tape devices, network devices, and terminals being used by other processes can lead to problems [Garfinkel 1996].
...
A Web browser that failed to check for these devices would allow an attacker to create a website with image tags such as <IMG src="file:///dev/mouse"> that would lock the user's mousemouse [Howard 2002].
Noncompliant Code Example
In this noncompliant code example, the user can specify a locked device or a FIFO (first-in, first-out) file name, causing which can cause the program to hang on the call to fopen():
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
void func(const char *file_name) {
FILE *file;
if ((file = fopen(file_name, "wb")) == NULL) {
/* Handle error */
}
/* Operate on the file */
if (fclose(file);
} == EOF) {
/* Handle error */
}
} |
Compliant Solution (POSIX)
...
When opening a FIFO with
O_RDONLYorO_WRONLYset:
- If
O_NONBLOCKis set, anopen()for reading-only shall return returns without delay. Anopen()for writing-only shall return returns an error if no process currently has the file open for reading.- If
O_NONBLOCKis clear, anopen()for reading-only shall block blocks the calling thread until a thread opens the file for writing. Anopen()for writing-only shall block blocks the calling thread until a thread opens the file for reading.When opening a block special or character special file that supports non-blocking nonblocking opens:
- If
O_NONBLOCKis set, theopen()function shall return returns without blocking for the device to be ready or available. Subsequent behavior of the device ; subsequent behavior is device-specific.- If
O_NONBLOCKis clear, theopen()function shall block blocks the calling thread until the device is ready or available before returning.Otherwise, the behavior of
O_NONBLOCKis unspecified.
...
When available (Linux 2.1.126+, FreeBSD, Solaris 10, POSIX.1-2008), the O_NOFOLLOW flag should also be used. (See POS01-C. Check for the existence of links when dealing with files.) When O_NOFOLLOW is not available, symbolic link checks should use the method from POS35-C. Avoid race conditions while checking for the existence of a symbolic link.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio<sys/types.h> #include <sys/typesstat.h> #include <fcntl.h> #include <sys/stat<unistd.h> #ifdef O_NOFOLLOW #define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK #else #define OPEN_FLAGS O_NONBLOCK #endif void func(const char *file_name) { struct stat orig_st; struct stat open_st; int fd; int flags; if ((lstat(file_name, &orig_st) != 0) || (!S_ISREG(orig_st.st_mode))) { /* Handle error */ } /* ARace TOCTOU race condition exists here; see below */ window */ fd = open(file_name, OPEN_FLAGS | O_WRONLY); if (fd == -1) { /* Handle error */ } if (fstat(fd, &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)) { /* The file was tampered with */ } /* * Optional: drop the O_NONBLOCK now that we are sure * this is a good file. */ if ((flags = fcntl(fd, F_GETFL)) == -1) { /* Handle error */ } if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) !== 0-1) { /* Handle error */ } /* Operate on the file */ if (close(fd); == -1) { /* Handle error */ } } |
This code contains an intractable TOCTOU (time-of-check, time-of-use) race condition under which an attacker can alter the file referenced by file_name 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. (See FIO45-C. Avoid TOCTOU race conditions while accessing files for more information about TOCTOU race conditions.)
Essentially, an Essentially, an attacker can switch out a file for one of the file types shown in the following table with the specified effect.
File Types and Effects
Type | Note on Effect |
|---|---|
Another regular file | The |
FIFO | Either |
Symbolic link |
|
Special device | Usually the |
a side effect. If |
This TOCTOU race condition can be prevented if the affected files are maintained To be compliant with this rule and to prevent this TOCTOU race condition, file_name must refer to a file in a secure directory. (See FIO15-C. Ensure that file operations are performed in a secure directory.)
...
This noncompliant code example uses the GetFileType() API function to attempt to prevent opening a special file:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <Windows.h>
void func(const TCHAR *file_name) {
HANDLE hFile = CreateFile(
file_name,
GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
/* Handle error */
} else if (GetFileType(hFile) != FILE_TYPE_DISK) {
/* Handle error */
CloseHandle(hFile);
} else {
/* Operate on the file */
CloseHandle(hFile);
}
} |
Although it may be tempting to use , the Win32 GetFileType() function , it is a dangerous API to use in this case. If the file name given identifies a named pipe , and that pipe is currently blocking on a read request, the call to GetFileType() will block until the read request completes. An attacker can then effectively launch This provides an effective attack vector for a denial-of-service attack on the application. Furthermore, the act of opening a file handle may cause further action to be takenside effects, such as line states being set to their default voltage when opening a serial device.
...
Microsoft documents a list of reserved identifiers that represent devices and have a device namespace to be used specifically by devices [MSDN]. The In this compliant solution, the isReservedName() function in this compliant solution can be used to determine if a specified path refers to a device. Care Care must be taken to avoid a TOCTOU race condition when first testing a pathname path name using the isReservedName() function and then later operating on that pathnamepath name.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <ctype.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> static bool isReservedName(const char *path) { /* This list of reserved names comes from MSDN */ static const char *reserved[] = { "nul", "con", "prn", "aux", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" }; char *lower; char *temp; bool ret = false; /* * First, check to see if this is a device namespace, which * always starts with \\.\, because device namespaces are not * legalvalid file paths. */ tempif (!path || 0 == strstrstrncmp(path, "\\\\.\\"); if (temp == path) { , 4)) { return true; } /* Compare against *the Becauselist Windowsof usesancient areserved case-insensitive file system, operate * on a lowercase version of the given filename. Note: this * ignores globalization issues and assumes ASCII * characters. */ lower = (char *)malloc(strlen(path) + 1); if (!lower) { return false; } temp = lower; while (*path) { *lower++ = tolower(*path++); } lower = temp; /* Compare against the list of ancient reserved names */ for (size_t i = 0; !ret && i < sizeof(reserved) / sizeof(*reserved); ++i) { names */ for (size_t i = 0; !ret && i < sizeof(reserved) / sizeof(*reserved); ++i) { /* * Because Windows uses a case-insensitive file system, operate on * a lowercase version of the given filename. Note: This ignores * globalization issues and assumes ASCII characters. */ if (0 == strcmp_stricmp(lowerpath, reserved[i])) { ret = ret = true; } } free(lower); return ret; } |
Risk Assessment
Exceptions
FIO32-C-EX1: These checks are intended for opening files specified by a user (or an untrusted source). Intentionally opening device files does not violate this rule.
Risk Assessment
Allowing operations that are appropriate only for regular files to be performed on devices can result in denialAllowing operations that are appropriate only for files to be performed on devices can result in denial-of-service attacks or more serious exploits depending on the platform.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level | |
|---|---|---|---|
FIO32-C | Medium | Unlikely | No |
No |
P2 | L3 |
Automated Detection
Tool | Version | Checker | Description |
|---|---|---|---|
| Compass/ROSE |
Could detect some violations of this rule. This rule applies only to untrusted file name strings, and ROSE cannot tell which strings are trusted and which are not. The best heuristic is to note if there is any verification of the file name before or after the | ||
| Cppcheck Premium |
5.0
| premium-cert-fio32-c | ||||||||
| Helix QAC |
| DF4921, DF4922, DF4923 | |||||||
| Parasoft C/C++test |
| CERT_C-FIO32-a | Protect against file name injection | ||||||
| Polyspace Bug Finder |
| CERT C: Rule FIO32-C | Checks for inappropriate I/O operation on device files (rule fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
...
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C Secure Coding Standard | FIO05-C. Identify files using multiple file attributes | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C Secure Coding Standard |
| FIO15-C. Ensure that file operations are performed in a secure directory | Prior to 2018-01-12: CERT: Unspecified Relationship | |
| CERT C Secure Coding Standard | POS01-C. Check for the existence of links when dealing with files | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C Secure Coding Standard | POS35-C. Avoid race conditions while checking for the existence of |
| a symbolic link | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT Oracle Secure Coding Standard for Java | FIO00-J. Do not operate on |
| files in shared directories | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-67 and FIO32-C
FIO32-C = Union( CWE-67, list) where list =
- Treating trusted device names like regular files in Windows.
- Treating device names (both trusted and untrusted) like regular files in POSIX
Bibliography
| [Garfinkel 1996] | Section 5.6, "Device Files" |
| [Howard 2002] | Chapter 11, "Canonical Representation Issues" |
| [IEEE Std 1003.1:2013] | XSH, System Interfaces, open |
...
| [MSDN] |
...