...
This TOCTOU race condition can be prevented if the effected files are maintained in a secure directory. (See FIO15-C. Ensure that file operations are performed in a secure directory.)
...
Noncompliant Code Example (Windows)
Microsoft documents a list of reserved identifiers that represent devices, as well as have a device namespace to be used specifically by devices [MSDN]. This compliant solution tests the given file name against these constructs:This noncompliant code example uses the GetFileType() API:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <ctype<Windows.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> static bool isReservedNamevoid func(const charTCHAR *pathfile_name) { /* ThisHANDLE listhFile 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 \\.\, since device namespaces are not legal file paths. */ temp = strstr(path, "\\\\.\\"); if (temp == path) { return true; } /* Since 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. */ 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) { if (0 == strcmp(lower, reserved[i])) { ret = true; } } free(lower); return ret; } |
...
= 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);
}
} |
Compliant Solution (Windows)
While 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. That allows an attacker to effectively launch a denial-of-service attack on your application. Furthermore, the act of opening a file handle may cause further action to be taken, 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, as well as have a device namespace to be used specifically by devices [MSDN]. This compliant solution tests the given file name against these constructs:
| 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 \\.\, since device namespaces are not
legal file paths. */
temp = strstr(path, "\\\\.\\");
if (temp == path) {
return true;
}
/* Since 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. */
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) {
if (0 == strcmp(lower, reserved[i])) {
ret = true;
}
}
free(lower);
return ret;
} |
Risk Assessment
Allowing 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.
...