Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
bgColor#FFCCCC
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
bgColor#FFCCCC
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
bgColor#FFCCCC
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
bgColor#FFCCCC
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
bgColor#FFCCCC
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
bgColor#FFCCCC
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, the tmpfile_s function should be used instead of tmpnam_s when possible. One situation that requires the use of the tmpnam_s function is when the program needs to create a temporary directory rather than a temporary file.

Code Block
bgColor#FFCCCC

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
bgColor#ccccff

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, the tmpfile_s function should be used instead of tmpnam_s when possible. One situation that requires the use of the tmpnam_s function is when the program needs to create a temporary directory rather than a temporary file.

Code Block
bgColor#ccccff

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
bgColor#ccccff

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]\]
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)
.

Risk Assessment

Failure to create unique, unpredictable temporary file names can make it possible for an attacker to access or modify privileged files.

...