...
This noncompliant code example creates a file with a hard-coded file_name
(presumably in a shared directory such as /tmp
or C:\Temp
).
Code Block |
---|
|
char file_name[] = /* hard coded string */;
FILE *fp = fopen(file_name, "wb+");
if (fp == NULL) {
/* Handle error */
}
|
...
Wiki Markup |
---|
The following noncompliant code example attempts to remedy the problem by generating the file name at runtime using {{tmpnam()}}. The C99 {{tmpnam()}} function generates a string that is a valid file name, and that is not the same as the name of an existing file \[[ISO/IEC 9899:1999|AA. Bibliography#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. |
Code Block |
---|
|
char file_name[L_tmpnam];
FILE* fp;
if (!tmpnam(file_name)) {
/* Handle error */
}
/* A TOCTOU race condition exists here */
fp = fopen(file_name, "wb+");
if (fp == NULL) {
/* Handle error */
}
|
...
Wiki Markup |
---|
This next noncompliant code example attempts to remedy the problem by using the POSIX {{open()}} function and providing a mechanism to indicate whether an existing file has been opened for writing or a new file has been created \[[Open Group 2004|AA. Bibliography#Open Group 04]\]. 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()}}. |
Code Block |
---|
|
char file_name[L_tmpnam];
int fd;
if (!(tmpnam(file_name))) {
/* Handle error */
}
/* A TOCTOU race condition exists here */
fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
/* Handle error */
}
|
...
Wiki Markup |
---|
The TR 24731-1 {{tmpnam_s()}} function generates a string that is a valid file name and that is not the same as the name of an existing file \[[ISO/IEC TR 24731-1:2007|AA. Bibliography#SO/IEC TR 24731-1-2007]\]. It is almost identical to the {{tmpnam()}} function, except for an added {{maxsize}} argument for the supplied buffer. |
Code Block |
---|
|
char file_name[L_tmpnam_s];
int fd;
if (tmpnam_s(file_name, L_tmpnam_s) != 0) {
/* Handle error */
}
/* A TOCTOU race condition exists here */
fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
/* Handle error */
}
|
...
The POSIX function mktemp()
takes a given file name template and overwrites a portion of it to create a file name. The template may be any file name with some number of X's appended to it (for example, /tmp/temp.XXXXXX
). The trailing X's are replaced with the current process number and/or a unique letter combination. The number of unique file names mktemp()
can return depends on the number of X's provided.
Code Block |
---|
|
char file_name[] = "tmp-XXXXXX";
int fd;
if (!mktemp(file_name)) {
/* Handle error */
}
/* A TOCTOU race condition exists here */
fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
/* Handle error */
}
|
...
Most historic implementations provide only a limited number of possible temporary file names (usually 26) before file names are recycled.
Code Block |
---|
|
FILE* fp = tmpfile();
if (fp == NULL) {
/* Handle error */
}
|
...
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 |
---|
|
if (tmpfile_s(&fp)) {
/* Handle error */
}
|
...
A call to mkstemp()
replaces the six X
's in the template string with six randomly selected characters and returns a file descriptor for the file (opened for reading and writing), as in this compliant solution.
Code Block |
---|
|
const char *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)
*/
if (unlink(sfn) == -1) {
/* Handle error */
}
sfp = fdopen(fd, "w+");
if (sfp == NULL) {
close(fd);
/* Handle error */
}
/* Use temporary file */
fclose(sfp); /* also closes fd */
|
...