...
Code Block | ||
---|---|---|
| ||
char file_name[] = /* hard coded string */; FILE *fp = fopen(file_name, "wb+"); if (fp == NULL) { /* Handle Errorerror */ } |
Because the name is hard coded and consequently neither unique nor unpredictable, an attacker need only place a symbolic link in lieu of the file and the target file referenced by the link is opened and truncated.
...
Code Block | ||
---|---|---|
| ||
char file_name[L_tmpnam]; FILE* fp; if (!tmpnam(file_name)) { /* Handle Errorerror */ } /* A TOCTOU race condition exists here */ fp = fopen(file_name, "wb+"); if (fp == NULL) { /* Handle Errorerror */ } |
Because tmpnam()
does not guarantee a unique name and fopen()
does not provide a facility for an exclusive open, this code is still vulnerable.
...
Code Block | ||
---|---|---|
| ||
char file_name[L_tmpnam]; int fd; if (!(tmpnam(file_name))) { /* Handle Errorerror */ } /* A TOCTOU race condition exists here */ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); if (fd < 0) { /* Handle Errorerror */ } |
This call to open()
fails whenever file_name
already exists, including when it is a symbolic link. This is secure, but a temporary file is presumably still required. Unfortunately, the method used by tmpnam()
to generate file names is not guaranteed to be unpredictable, which leaves room for an attacker to guess the file name ahead of time.
...
Code Block | ||
---|---|---|
| ||
char file_name[L_tmpnam_s]; int fd; if (tmpnam_s(file_name, L_tmpnam_s) != 0) { /* Handle Errorerror */ } /* A TOCTOU race condition exists here */ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); if (fd < 0) { /* Handle Errorerror */ } |
Non-normative text in TR 24731-1 also recommends the following:
...
Code Block | ||
---|---|---|
| ||
char file_name[] = "tmp-XXXXXX"; int fd; if (!mktemp(file_name)) { /* Handle Errorerror */ } /* A TOCTOU race condition exists here */ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); if (fd < 0) { /* Handle Errorerror */ } |
The mktemp()
function has been marked "LEGACY" in the Open Group Base Specifications Issue 6. The man page for mktemp()
gives more detail:
...
Code Block | ||
---|---|---|
| ||
FILE* fp = tmpfile(); if (fp == NULL) { /* Handle Errorerror */ } |
Non-Compliant
...
Code Example: 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.
Wiki Markup |
---|
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 \[[ISO/IEC TR 24731-1:2007|AA. C References#ISO/IEC TR 24731-1-2007]\]. |
Wiki Markup |
---|
TR 24731-1 notes the following regarding the use of {{tmpfile_s()}} instead of {{tmpnam_s()}} \[[ISO/IEC TR 24731-1:2007|AA. C References#ISO/IEC TR 24731-1-2007]\]: |
After a program obtains a file name using the
tmpnam_s
function 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_s
function should be used instead oftmpnam_s
when possible. One situation that requires the use of thetmpnam_s
function is when the program needs to create a temporary directory rather than a temporary file.
Code Block | ||
---|---|---|
| ||
if (tmpfile_s(&fp)) { |
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):
Code Block |
---|
char template[] = "temp-XXXXXX";
fd = mkstemp(template);
if (fd == -1) {
/* Handle Error */
}
|
The mkstemp()
algorithm for selecting file names has proven to be immune to attacks. This solution is not serially reusable, however, because the mkstemp()
function replaces the "XXXXXX"
in template
the first it is invoked. This is not a problem, as long as template
is reinitialized before calling mkstemp()
again. If template
is not reinitialized, the mkstemp()
function will return -1
and leave template
unmodified because template
did not contain six X's.
Code Block | ||
---|---|---|
| ||
char sfn[] = "temp-XXXXXX"; FILE *sfp; int fd = mkstemp(sfn); if (fd == -1) { /* Handle Error */ } /* * Unlink immediately to hide the file name. * The race condition here is inconsequential if the file * is created with exclusive permissions (glibc >= 2.0.7) */ unlink(sfn); sfp = fdopen(fd, "w+"); if (sfp == NULL) { close(fd); /* Handle Errorerror */ } /* use temporary file */ fclose(sfp); /* also closes fd */ |
Wiki Markup |
---|
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 (POSIX.1-2008) specifies them as {{S_IRUSR\|S_IWUSR}} (0600) \[[Austin Group 08|AA. C References#Austin Group 08]\]. |
Implementation Details
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.
Wiki Markup |
---|
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 file name 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]\]. |
Compliant Solution: 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.
Wiki Markup |
---|
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 \[[ISO/IEC TR 24731-1:2007|AA. C References#ISO/IEC TR 24731-1-2007]\]. |
Wiki Markup |
---|
TR 24731-1 notes the following regarding the use of {{tmpfile_s()}} instead of {{tmpnam_s()}} \[[ISO/IEC TR 24731-1:2007|AA. C References#ISO/IEC TR 24731-1-2007]\]: |
After a program obtains a file name using the
tmpnam_s
function 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_s
function should be used instead oftmpnam_s
when possible. One situation that requires the use of thetmpnam_s
function is when the program needs to create a temporary directory rather than a temporary file.
Code Block | ||
---|---|---|
| ||
if (tmpfile_s(&fp)) {
/* Handle Error */
}
|
The TR24731-1 tmpfile_s()
function should not be used with implementations that create temporary files in shared directory such as /tmp
or C:
because the function does not allow the user to specify a directory in which the temporary file should be created (see FIO15-A. Do not create temporary files in shared directories).
Compliant Solution: 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):
Code Block |
---|
char template[] = "temp-XXXXXX";
fd = mkstemp(template);
if (fd == -1) {
/* Handle error */
}
|
The mkstemp()
algorithm for selecting file names has proven to be immune to attacks. This solution is not serially reusable, however, because the mkstemp()
function replaces the "XXXXXX"
in template
the first it is invoked. This is not a problem, as long as template
is reinitialized before calling mkstemp()
again. If template
is not reinitialized, the mkstemp()
function will return -1
and leave template
unmodified because template
did not contain six X's.
Code Block | ||
---|---|---|
| ||
char const *sdn = "/home/usr1/";
char sfn[] = "/home/usr1/temp-XXXXXX";
FILE *sfp;
if (!secure_dir(sdn)) {
/* Handle error */
}
int fd = mkstemp(sfn);
if (fd == -1) {
/* Handle error */
}
/*
* Unlink immediately to hide the file name.
* The race condition here is inconsequential if the file
* is created with exclusive permissions (glibc >= 2.0.7)
*/
unlink(sfn);
sfp = fdopen(fd, "w+");
if (sfp == NULL) {
close(fd);
/* Handle error */
}
/* use temporary file */
fclose(sfp); /* also closes fd */
|
Wiki Markup |
---|
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 (POSIX.1-2008) specifies them as {{S_IRUSR\|S_IWUSR}} (0600) \[[Austin Group 08|AA. C References#Austin Group 08]\]. |
This compliant solution invokes the an implementation secure_dir(
} function (such as the one defined in FIO17-A. Ensure that file operations are performed in a secure directory) to ensure the temporary file resides in a secure directory.
Implementation Details
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.
Wiki Markup |
---|
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 file name 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()
function should not be used with implementations that create temporary files in shared directory such as /tmp
or C:
because the function does not allow the user to specify a directory in which the temporary file should be created (see FIO15-A. Do not create temporary files in shared directories). |
Risk Assessment
Failure to create unique, unpredictable temporary file names can make it possible for an attacker to access or modify privileged files.
...