When working with temporary files inside of shared directories, it is important to simultaneously address 3 issues
Programs that create files in shared directories may be exploited to overwrite protected files. For example, 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 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 file referenced by the symbolic link can be overwritten when the program is executed. To ensure that the name of the temporary file does not conflict with a preexisting file and that it cannot be guessed before the program is run, temporary files must be created with unique and unpredictable file names.
Exclusive access grants unrestricted file access to the locking process while denying access to all other processes and eliminates the potential for a race condition on the locked region (see \[[Seacord 05|AA. C References#Seacord 05]\] Chapter 7). |
Files, or regions of files, can be locked to prevent two processes from concurrent access. Windows supports file locking of two types: shared locks prohibit all write access to the locked file region while allowing concurrent read access to all processes; exclusive locks grant unrestricted file access to the locking process while denying access to all other processes. A call to LockFile() obtains shared access; exclusive access is accomplished via LockFileEx(). In either case the lock is removed by calling UnlockFile().
Both shared locks and exclusive locks eliminate the potential for a race condition on the locked region. The exclusive lock is similar to a mutual exclusion solution, and the shared lock eliminates race conditions by removing the potential for altering the state of the locked file region (one of the required properties for a race).
These Windows file-locking mechanisms are called mandatory locks because every process attempting access to a locked file region is subject to the restriction. Linux implements both mandatory locks and advisory locks. An advisory lock is not enforced by the operating system, which severely diminishes its value from a security perspective. Unfortunately, the mandatory file lock in Linux is also largely impractical for the following reasons: (a) mandatory locking works only on local file systems and does not extend to network file systems (NFS and AFS), (b) file systems must be mounted with support for mandatory locking, and this is disabled by default, and (c) locking relies on the group ID bit that can be turned off by another process (thereby defeating the lock).
It is important to remember to cleanup in order to allow filenames and other resources such as secondary storage to be recycled. In the case of abnormal termination, there is no sure method that can guarantee the removal of orphaned files. For this reason tmp cleaner utilities are widely used. These tmp cleaners are invoked manually by a system administrator or run as a cron daemon to sweep temporary directories and remove old files. These tmp cleaners are themselves vulnerable to file-based exploits, and often require the use of shared directories (see: TMP00-A. Do not create temporary files in shared directories). However, during normal operation, it is the responsibility of the program to ensure that temporary files are either removed explicitly, or through the use of library routines such as tmpfile_s which guarantee their removal upon program termination.
fopen()The following non-compliant code creates some_file in the /tmp directory. The name is hard coded and consequently is neither unique nor unpredictable.
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.
open()The {{fopen()}} function does not indicate whether 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|AA. C References#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 secure, but a temporary file is presumably still required. One approach that can be used with open() is to generate random filenames 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|AA. C References#Callaghan 95]\], in particular the {{EXCLUSIVE}} value to the {{mode}} argument of {{CREATE}}. |
tmpnam()The C99 {{tmpnam()}} function generates a string that is a valid filename and that is not the same as the name of an existing file \[[ISO/IEC 9899-1999|AA. C References#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|BB. Definitions#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. |
char filename[L_tmpnam];
if (tmpnam(filename)) {
/* temp_file_name may refer to an existing file */
t_file = fopen(filename,"wb+");
if (!t_file) {
/* Handle Error */
}
}
|
tmpnam_s() (ISO/IEC TR 24731-1)The TR 24731-1 {{tmpnam_s()}} function generates a string that is a valid filename and that is not the same as the name of an existing file \[[ISO/IEC TR 24731-1-2007|AA. C References#SO/IEC TR 24731-1-2007]\]. It is almost identical to the {{tmpnam}} function above except with an added {{maxsize}} argument for the supplied buffer. |
Non-normative text in TR 24731-1 also recommends the following:
Implementations should take care in choosing the patterns used for names returned by tmpnam_s. For example, making a thread id part of the names avoids the race condition and possible conflict when multiple programs run simultaneously by the same user generate the same temporary file names.
If implemented, this reduces the space for unique names and increases the predictability of the resulting names. But in general, TR 24731-1 does not establish any criteria for predictability of names.
char filename[L_tmpnam_s];
if (tmpnam_s(filename, L_tmpnam_s) != 0) {
/* Handle Error */
}
/* A TOCTOU race condition exists here */
if (!fopen_s(&fp, filename, "wb+")) {
/* Handle Error */
}
|
For Microsoft Visual Studio 2005, the name generated by tmpnam_s consists of a program-generated filename and, after the first call to tmpnam_s(), a file extension of sequential numbers in base 32 (.1-.1vvvvvu, when TMP_MAX_S in stdio.h is INT_MAX).
mktemp()/open() (POSIX)The POSIX function mktemp() takes a given filename template and overwrites a portion of it to create a filename. The template may be any filename with some number of Xs appended to it (for example, /tmp/temp.XXXXXX). The trailing Xs are replaced with the current process number and/or a unique letter combination. The number of unique filenames mktemp() can return depends on the number of Xs provided.
char filename[] = "temp-XXXXXX";
if (mktemp(filename) == NULL) {
/* Handle Error */
}
/* A TOCTOU race condition exists here */
if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) == -1) {
/* Handle Error */
}
|
The mktemp() function has been marked LEGACY in the Open Group Base Specifications Issue 6.
tmpfile()The C99 tmpfile() function creates a temporary binary file that is different from any other existing file and that is automatically removed when it is closed or at program termination.
It should be possible to open at least TMP_MAX temporary files during the lifetime of the program (this limit may be shared with tmpfile()). The value of the macro TMP_MAX is only required to be 25 by the C99 standard.
Most historic implementations provide only a limited number of possible temporary filenames (usually 26) before filenames are recycled.
if ((fp = tmpfile()) == NULL) {
/* Handle Error */
}
|
mkstemp() (POSIX)A reasonably secure solution for generating random file names is to use the mkstemp() function. The mkstemp() function is available on systems that support the Open Group Base Specifications Issue 4, Version 2 or later.
A call to mkstemp() replaces the six Xs in the template string with six randomly selected characters and returns a file descriptor for the file (opened for reading and writing):
char template[] = "temp-XXXXXX";
if ((fd = mkstemp(template)) == -1) {
/* Handle Error */
}
|
The mkstemp() algorithm for selecting filenames has proven to be immune to attacks.
char sfn[] = "temp-XXXXXX";
FILE *sfp;
int fd = -1;
if ((fd = mkstemp(sfn)) == -1) {
/* Handle Error */
}
/* We unlink immediately to the allow name to be recycled */
/* The race condition here is inconsequential if the file was created with exclusive permissions (glibc >= 2.0.7) */
unlink(sfn);
if ((sfp = fdopen(fd, "w+")) == NULL) {
close(fd);
/* Handle Error */
}
/* use temporary file */
fclose(sfp); /* also closes fd */
|
The Open Group Base Specification Issue 6 \[[Open Group 04|AA. C References#Open Group 04]\] does not specify the permissions the file is created with, so these are [implementation-defined|BB. Definitions#implementation-defined behavior]. However, Issue 7 (that is, POSIX.1-2008) specifies them as {{S_IRUSR\|S_IWUSR}} (0600). |
For glibc versions 2.0.6 and earlier, the file is created with permissions 0666; for glibc versions 2.0.7 and later, the file is created with permissions 0600. On NetBSD, the file is created with permissions 0600. This creates a security risk in that an attacker will have write access to the file immediately after creation. Consequently, programs need a private version of the mkstemp() function in which this issue is known to be fixed.
In many older [implementations|BB. Definitions#implementation], the name is a function of process ID and time, so it is possible for the attacker to predict the name and create a decoy in advance. FreeBSD has recently changed the {{mk*temp()}} family to eliminate the PID component of the filename and replace the entire field with base-62 encoded randomness. This raises the number of possible temporary files for the typical use of 6 Xs significantly, meaning that even {{mktemp()}} with 6 Xs is reasonably (probabilistically) secure against guessing, except under frequent usage \[[Kennaway 00|AA. C References#Kennaway 00]\] . |
tmpfile_s() (ISO/IEC TR 24731-1 )The ISO/IEC TR 24731-1 function tmpfile_s() creates a temporary binary file that is different from any other existing file and that is automatically removed when it is closed or at program termination. If the program terminates abnormally, whether an open temporary file is removed is implementation-defined.
The file is opened for update with "wb+" mode, which means "truncate to zero length or create binary file for update." To the extent that the underlying system supports the concepts, the file is opened with exclusive (non-shared) access and has a file permission that prevents other users on the system from accessing the file.
It should be possible to open at least TMP_MAX_S temporary files during the lifetime of the program (this limit may be shared with tmpnam_s()). The value of the macro TMP_MAX_S is only required to be 25 by ISO/IEC TR 24731-1.
The tmpfile_s() function is available on systems that support ISO/IEC TR 24731-1.
TR 24731-1 notes the following regarding the use of tmpfile_s() instead of tmpnam_s():
After a program obtains a file name using the
tmpnam_sfunction and before the program creates a file with that name, the possibility exists that someone else may create a file with that same name. To avoid this race condition, thetmpfile_sfunction should be used instead oftmpnam_swhen possible. One situation that requires the use of thetmpnam_sfunction is when the program needs to create a temporary directory rather than a temporary file.
if (tmpfile_s(&fp)) {
/* Handle Error */
}
|
Failure to create unique, unpredictable temporary file names can make it possible for an attacker to access or modify privileged files.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
|---|---|---|---|---|---|
TMP30-C |
3 (high) |
2 (probable) |
1 (high) |
P6 |
L2 |
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Sections 7.19.4.4, "The {{tmpnam}} function," 7.19.4.3, "The {{tmpfile}} function," and 7.19.5.3, "The {{fopen}} function"
\[[ISO/IEC PDTR 24772|AA. C References#ISO/IEC PDTR 24772]\] "EWR Path Traversal"
\[[ISO/IEC TR 24731-1-2007|AA. C References#ISO/IEC TR 24731-1-2007]\] Sections 6.5.1.2, "The {{tmpnam_s}} function," 6.5.1.1, "The {{tmpfile_s}} function," and 6.5.2.1, "The {{fopen_s}} function"
\[[Open Group 04|AA. C References#Open Group 04]\] [{{mktemp()}}|http://www.opengroup.org/onlinepubs/000095399/functions/mktemp.html], [{{mkstemp()}}|http://www.opengroup.org/onlinepubs/009695399/functions/mkstemp.html], [{{open()}}|http://www.opengroup.org/onlinepubs/009695399/functions/open.html]
\[[Seacord 05a|AA. C References#Seacord 05a]\] Chapter 3, "File I/O"
\[[Wheeler 03|AA. C References#Wheeler 03]\] [Chapter 7, "Structure Program Internals and Approach"|http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/avoid-race.html#TEMPORARY-FILES]
\[[Viega 03|AA. C References#Viega 03]\] Section 2.1, "Creating Files for Temporary Use"
\[[Kennaway 00|AA. C References#Kennaway 00]\]
\[[HP 03|AA. C References#HP 03]\] |
TMP00-A. Do not create temporary files in shared directories 10. Temporary Files (TMP) TMP32-C. Temporary files must be opened with exclusive access