...
| Wiki Markup |
|---|
Device files in UNIX can be a security risk when an attacker is ablecan to access them in an unauthorized way. For instance, if attackers can read or write to the {{/dev/kmem}} device, they may be able to alter their 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 others all can lead to problems \[[Garfinkel 96|AA. C References#Garfinkel 96]\]. |
...
| Code Block | ||
|---|---|---|
| ||
char *file_name;
FILE *file;
/* initialize file_name */
if (!fgets(file_name, sizeof(file_name), stdin)) {
/* handle error */
}
if ((file = fopen(file_name, "wb")) == NULL) {
/* handle error */
}
/* operate on file */
fclose(file);
|
Compliant Solution (POSIX)
| Wiki Markup |
|---|
POSIX defines the {{O_NONBLOCK}} flag to {{open()}}, which ensures that delayed operations on a file doesdo not hang the program \[[Open Group 04|AA. C References#Open Group 04]\]. |
When opening a FIFO with
O_RDONLYorO_WRONLYset:
- If
O_NONBLOCKis set, anopen()for reading only will return without delay. Anopen()for writing only will return an error if no process currently has the file open for reading.- If
O_NONBLOCKis clear, anopen()for reading only will block the calling thread until a thread opens the file for writing. Anopen()for writing only will block the calling thread until a thread opens the file for reading.When opening a block special or character special file that supports non-blocking opens:
- If
O_NONBLOCKis set, theopen()function will return without blocking for the device to be ready or available. Subsequent behaviour of the device is device-specific.- If
O_NONBLOCKis clear, theopen()function will block the calling thread until the device is ready or available before returning.Otherwise, the behaviour behavior of
O_NONBLOCKis unspecified.
...
| Code Block | ||
|---|---|---|
| ||
#ifdef O_NOFOLLOW
#define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK
#else
#define OPEN_FLAGS O_NONBLOCK
#endif
/* ... */
struct stat orig_st;
struct stat open_st;
int fd;
int flags;
char *file_name;
/* initialize file_name */
if (!fgets(file_name, sizeof(file_name), stdin)) {
/* handle error */
}
if ((lstat(file_name, &orig_st) != 0)
|| (!S_ISREG(orig_st.st_mode)))
{
/* handle error */
}
/* A TOCTOU race condition exists here, see below */
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)) {
/* 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) {
/* handle error */
}
/* operate on file */
close(fd);
|
The above code does contain an intractable TOCTOU race condition; where 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.
There are essentially four cases that can result from Essentially, an attacker switching can switch out the a file for one of the following:the file types shown in the table with the specified effect.
Type | Note on effect |
|---|---|
another regular file | The |
FIFO | Either |
symbolic link | |
special device | Usually the |
...