Versions Compared

Key

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

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.

Non-Compliant Code Example: 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.

Code Block
bgColor#FFCCCC

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()

Wiki Markup
Whenever working with temporary files, it is important to keep in mind \[[TMP33-C. Temporary files must be removed before the program exits]\] for implementations where the temporary file is not removed if the program terminates abnormally

Non-Compliant Code Example: 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.

Code Block
bgColor#FFCCCC

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()

Wiki Markup
The {{fopen()}} function does not indicate whether an existing file has been opened for writing or a new file has been created. However
Wiki Markup
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 as failsdefined whenin the file specified by {{file_name}} already exists. To prevent an existing file from being opened and truncated, include the flagsOpen 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 whenare calling {{used together, the {{open()}}.

Code Block
bgColor#FFCCCC

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.

Wiki Markup
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}}.

...

 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()}}.

Code Block
bgColor#FFCCCC

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.

Wiki Markup
TheCare C99 {{tmpnam()should be observed when using {{O_EXCL}} functionwith generatesremote afile stringsystems, thatas isit adoes validnot filenamework andwith thatNFS isversion not2. theNFS sameversion as3 theadded namesupport of an existing filefor {{O_EXCL}} mode in {{open()}}; see IETF RFC 1813 \[[ISO/IEC 9899-1999Callaghan 95|AA. C References#ISO/IEC 9899-1999References#Callaghan 95]\]., Filesin createdparticular using strings generated bythe {{EXCLUSIVE}} value to the {{tmpnam()mode}} functionargument 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.

Code Block
bgColor#FFCCCC

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 */
  }
}

Non-Compliant Code Example: tmpnam_s() (ISO/IEC TR 24731-1)

Wiki Markup
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:

of {{CREATE}}.

Non-Compliant Code Example: tmpnam()

Wiki Markup
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.

Code Block
bgColor#FFCCCC

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 */
  }
}

Non-Compliant Code Example: tmpnam_s() (ISO/IEC TR 24731-1)

Wiki Markup
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 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.

...

Code Block
bgColor#FFcccc
char filename[] = "/tmp/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.

Non-Compliant Code Example: 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.

Code Block
bgColor#FFCCCC
*/

if ((fpfd = tmpfile(open(filename, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) == NULL-1) {
  /* Handle Error */
}

...

The mktemp() function has been marked LEGACY in the Open Group Base Specifications Issue 6.

Non-Compliant Code Example: 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.

Code Block
bgColor#FFCCCC

if ((fp = tmpfile()) == NULL) {
  /* Handle Error */
}

...

Compliant Solution: mkstemp() (POSIX)

...