Programmers frequently create temporary files. Temporary file directories are writable by everyone and include /tmp
, /var/tmp
, and C:\TEMP
and may be purged regularly (for example, every night or during reboot).
When two or more users, or a group of users, have write permission to a directory, the potential for sharing and deception is far greater than it is for shared access to a few files. The vulnerabilities that result from malicious restructuring via hard and symbolic links suggest that it is best to avoid shared directories.
Securely creating temporary files in a shared directory is error prone and dependent on: the version of the C runtime library used, the operating system, and the file system. Code that works for a locally mounted file system, for example, may be vulnerable when used with a remotely mounted file system.
Several rules apply to creating temporary files in shared directories including this one: temporary files must have unique names. Privileged programs that create files in world-writable directories can overwrite protected system files. An attacker who can predict the name of a file created by a privileged program can create a symbolic link (with the same name as the file used by the program) to point to a protected system file. Unless the privileged program is coded securely, the program will follow the symbolic link instead of opening or creating the file that it is supposed to be using. As a result, the protected system file referenced by the symbolic link can be overwritten when the program is executed.
Non-Compliant Code Example: fopen()
The following statement creates some_file
in the /tmp
directory.
FILE *fp = fopen("/tmp/some_file", "w");
If /tmp/some_file
already exists then that file is opened and truncated. If /tmp/some_file
is a symbolic link, then the target file referenced by the link is truncated.
To exploit this coding error, an attacker need only create a symbolic link called /tmp/some_file
before execution of this statement.
Non-Compliant Code Example: open()
The fopen()
function does not indicate if an existing file has been opened for writing or a new file has been created. However, the open()
function as defined in the Open Group Base Specifications Issue 6 [[Open Group 04]] provides such a mechanism. If the O_CREAT
and O_EXCL
flags are used together, the open()
function fails when the file specified by file_name
already exists. To prevent an existing file from being opened and truncated, include the flags O_CREAT
and O_EXCL
when calling open()
.
int fd = open("/tmp/some_file", O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
This call to open()
fails whenever /tmp/some_file
already exists, including when it is a symbolic link. This is a good thing, but a temporary file is presumably still required. One approach that can be used with open()
is to generate random file names and attempt to open()
each until a unique name is discovered. Luckily, there are predefined functions that perform this function.
Care should be observed when using O_EXCL
with remote file systems as it does not work with NFS version 2. NFS version 3 added support for O_EXCL
mode in open()
; see IETF RFC 1813 [[Callaghan 95]], in particular the EXCLUSIVE
value to the mode
argument of CREATE
.
Non-Compliant Code Example: tmpnam()
The C99 tmpnam()
function generates a string that is a valid file name and that is not the same as the name of an existing file [[ISO/IEC 9899-1999]]. Files created using strings generated by the tmpnam()
function are temporary in that their names should not collide with those generated by conventional naming rules for the implementation. The function is potentially capable of generating TMP_MAX
different strings, but any or all of them may already be in use by existing files. If the argument is not a null pointer, it is assumed to point to an array of at least L_tmpnam
chars; the tmpnam()
function writes its result in that array and returns the argument as its value.
... if (tmpnam(temp_file_name)) { /* temp_file_name may refer to an existing file */ t_file = fopen(temp_file_name,"wb+"); if (!t_file) { /* Handle Error */ } } ...
Unfortunately, this solution is still non-compliant because it violates [[FIO32-C]], [[FI040-C]], [[FI041-C]], [[FI042-C]].
Non-Compliant Code Example: tmpnam_s()
(ISO/IEC TR 24731-1)
The TR 24731-1 tmpnam_s()
function generates a string that is a valid file name and that is not the same as the name of an existing file [[ISO/IEC TR 24731-2006]]. The function is potentially capable of generating TMP_MAX_S
different strings, but any or all of them may already be in use by existing files and thus not be suitable return values. The lengths of these strings must be less than the value of the L_tmpnam_s
macro.
... FILE *file_ptr; char filename[L_tmpnam_s]; if (tmpnam_s(filename, L_tmpnam_s) != 0) { /* Handle Error */ } if (!fopen_s(&file_ptr, filename, "wb+")) { /* Handle Error */ } ...
This solution is also non-compliant because it violates [[FIO32-C]] and [[FI042-C]].
Non-Compliant Code Example: mktemp()/open()
(POSIX)
The POSIX function mktemp()
takes a given file name template and overwrites a portion of it to create a file name. The template may be any file name with some number of 'X's appended to it, for example /tmp/temp.XXXXXX
. The trailing 'X's are replaced with the current process number and/or a unique letter combination. The number of unique file names mktemp()
can return depends on the number of 'X's provided.
... int fd; char temp_name[] = "/tmp/temp-XXXXXX"; if (mktemp(temp_name) == NULL) { /* Handle Error */ } if ((fd = open(temp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) == -1) { /* Handle Error */ } ...
This solution is also non-compliant because it violates [[FIO32-C]] and [[FI042-C]].
Risk Assessment
A protected system file to which the symbolic link points can be overwritten when a vulnerable program is executed.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
FIO39-C |
2 (high) |
2 (probable) |
2 (medium) |
P8 |
L2 |
References
- ISO/IEC 9899-1999 Sections 7.19.4.3, "The tmpfile function", 7.19.4.4, "The tmpnam function"
- ISO/IEC TR 24731-2006 Section 6.5.1.1 The tmpfile_s function