Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: merging in TMP32 bit by bit

...

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.

Non-Compliant Code Example: fopen()/open()

...

with tmpnam()

The following non-compliant code creates a file with some hard coded file_name (presumably in a shared directory such as /tmp or C:\Temp).

Code Block
bgColor#FFCCCC
char * file_name[] = /* hard coded string */;
FILE *fp;

if(!(fp = fopen(file_name, "w");
wb+"))) {
  /* Handle Error */
}

Since Clearly, since 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)) =={
 NULL || (fp = fopen(file/* Handle Error */
}

/* A TOCTOU race condition exists here */

if(!(fp = fopen(file_name, "wb+")) == NULL) {
   /* Handle Error */
}

Since neither does tmpnam() guarantee a unique name, nor does fopen() provide a facility for an exclusive open, this code is still vulnerable to the exploit above.

...

Code Block
bgColor#FFCCCC
char *file_name[L_tmpnam];
int fd;

if (!(tmpnam(file_name))) =={
 NULL || (fd = open(file_name, /* Handle Error */
}

/* A TOCTOU race condition exists here */

if((fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) < 0) {
   /* Handle Error */
}

...

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

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

Wiki Markup
TheMoreover, TRthe 24731-1 {{tmpnam_sopen()}} function generatesas 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-2007specified by the Open Group Base Specifications Issue 6 \[[Open Group 04|AA. C References#SO/IECReferences#Open TR 24731-1-2007Group 04]\]. Itdoes isnot almostinclude identicalsupport tofor 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.

Code Block
bgColor#FFCCCC

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

Implementation Details

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

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

shared or exclusive locks.

BSD systems support two additional flags that allow you to obtain a shared or exclusive lock:

  • O_SHLOCK Atomically obtain a shared lock.
  • O_EXLOCK Atomically obtain an exclusive lock.

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 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 the predictability of names.

Code Block
bgColor#FFCCCC

char file_name[L_tmpnam_s];
int fd;

if (tmpnam_s(file_name, L_tmpnam_s) != 0
Code Block
bgColor#FFcccc

char filename[] = "temp-XXXXXX";

if (mktemp(filename) == NULL) {
  /* Handle Error */
}

/* A TOCTOU race condition exists here */

if ((fd = open(filenamefile_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) ==< -10) {
   /* Handle Error */
}

...

 */
}

Implementation Details

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

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

Code Block
bgColor#FFCCCC

char file_name[] = "tmp-XXXXXX";
int fd;

if (!mktemp(file_name)) {
  /* Handle Error */
}

/* A TOCTOU race condition exists here */

if ((fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600)) < 0) {
  /* Handle Error */
}

The mktemp() function has been marked LEGACY in the Open Group Base Specifications Issue 6. The man page for mktemp() gives more detail:

Never use mktemp(). Some implementations follow BSD 4.3 and replace XXXXXX by the current process id and a single letter, so that at most 26 different names can be returned. Since on the one hand the names are easy to guess, and on the other hand there is a race between testing whether the name exists and opening the file, every use of mktemp() is a security risk. The race is avoided by mkstemp(3).

Non-Compliant Code Example: tmpfile()

...

Most historic implementations provide only a limited number of possible temporary filenames (usually 26) before filenames are recycled.

Code Block
bgColor#FFCCCC
FILE* fp;

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

Compliant Solution: mkstemp() (POSIX)

...

Code Block
bgColor#ccccff
char sfn[] = "temp-XXXXXX";
FILE *sfp;
int fd = -1;

if ((fd = mkstemp(sfn)) == -1) {
  /* Handle Error */
}

/* We unlink immediately to allow 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 */

...

Code Block
bgColor#ccccff
if (tmpfile_s(&fp)) {
  /* Handle Error */
}

Risk Assessment

RULE HAS UNDERGONE MAJOR REVISION, PLEASE UPDATE RISK ASSESSMENT

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

...