[ Team LiB ] Previous Section Next Section

2.11 Creating Files for Temporary Use

2.11.1 Problem

You need to create a file to use as scratch space that may contain sensitive data.

2.11.2 Solution

Generate a random filename and attempt to create the file, failing if the file already exists. If the file cannot be created because it already exists, repeat the process until it succeeds. If creating the file fails for any other reason, abort the process.

2.11.3 Discussion

When creating temporary files, you should consider using a known-safe directory to store them, as described in Recipe 2.4.

The need for temporary files is common. More often than not, other processes have no need to access the temporary files you create, and especially if the files contain sensitive data, it is best to do everything possible to ensure that other processes cannot access them. It is also important that temporary files do not remain on the filesystem any longer than necessary. If the program creating temporary files terminates unexpectedly before it cleans up the files, temporary directories often become littered with files of no interest or value to anyone or anything. Worse, if the temporary files contain sensitive data, they are suddenly both interesting and valuable to an attacker.

2.11.3.1 Temporary files on Unix

The best solution for creating a temporary file on Unix is to use the mkstemp( ) function in the standard C runtime library. This function generates a random filename,[2] attempts to create it, and repeats the whole process until it is successful, thus guaranteeing that a unique file is created. The file created by mkstemp( ) will be readable and writable by the owner, but not by anyone else.

[2] The filename may not be strongly random. An attacker might be able to predict the filename, but that is generally okay.

To help further ensure that the file cannot be accessed by any other process, and to be sure that the file will not be left behind by your program if it should terminate unexpectedly before being able to delete it, the file can be deleted by name while it is open immediately after mkstemp( ) returns. Even though the file has been deleted, you will still be able to read from and write to it because there is a valid descriptor for the file. No other process will be able to open the file because a name will no longer be associated with it. Once the last open descriptor to the file is closed, the file will no longer be accessible.

Between the time that a file is created with mkstemp( ) and the time that unlink( ) is called to delete the file, a window of opportunity exists where an attacker could open the file before it can be deleted.

The mkstemp( ) function works by specifying a template from which a random filename can be generated. From the end of the template, "X" characters are replaced with random characters. The template is modified in place, so the specified buffer must be writable. The return value from mkstemp( ) is -1 if an error occurs; otherwise, it is the file descriptor to the file that was created.

2.11.3.2 Temporary files on Windows

The Win32 API does not contain a functional equivalent of the standard C mkstemp( ) function. The Microsoft C Runtime implementation does not even provide support for the function, although it does provide an implementation of mktemp( ). However, we strongly advise against using that function on either Unix or Windows.

The Win32 API does provide a function, GetTempFileName( ), that will generate a temporary filename, but that is all that it does; it does not open the file for you. Further, if asked to generate a unique name itself, it will use the system time, which is highly predictable.

Instead, we recommend using GetTempPath( ) to obtain the current user's setting for the location to place temporary files, and generating your own random filename using CryptoAPI or some other cryptographically strong pseudo-random number generator. The code presented here uses the spc_rand_range( ) function from Recipe 11.11. Refer to Chapter 11 for possible implementations of random number generators.

The function SpcMakeTempFile( ) repeatedly generates a random temporary filename using a cryptographically strong pseudo-random number generator and attempts to create the file. The generated filename contains an absolute path specification to the user's temporary files directory. If successful, the file is created, inheriting access permissions from that directory, which ordinarily will prevent users other than the Administrator and the owner from gaining access to it. If SpcMakeTempFile( ) is unable to create the file, the process begins anew. SpcMakeTempFile( ) will not return until a file can be successfully created or some kind of fatal error occurs.

As arguments, SpcMakeTempFile( ) requires a preallocated writable buffer and the size of that buffer in characters. The buffer will contain the filename used to successfully create the temporary file, and the return value from the function will be a handle to the open file. If an error occurs, the return value will be INVALID_HANDLE_VALUE, and GetLastError( ) can be used to obtain more detailed error information.

#include <windows.h>
   
static LPTSTR lpszFilenameCharacters = TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
   
static BOOL MakeTempFilename(LPTSTR lpszBuffer, DWORD dwBuffer) {
  int   i;
  DWORD dwCharacterRange, dwTempPathLength;
  TCHAR cCharacter;
   
  dwTempPathLength = GetTempPath(dwBuffer, lpszBuffer);
  if (!dwTempPathLength) return FALSE;
  if (++dwTempPathLength > dwBuffer || dwBuffer - dwTempPathLength < 12) {
    SetLastError(ERROR_INSUFFICIENT_BUFFER);
    return FALSE;
  }
  dwCharacterRange = lstrlen(lpszFilenameCharacters) - 1;
  for (i = 0;  i < 8;  i++) {
    cCharacter = lpszFilenameCharacters[spc_rand_range(0, dwCharacterRange)];
    lpszBuffer[dwTempPathLength++ - 1] = cCharacter;
  }
  lpszBuffer[dwTempPathLength++ - 1] = '.';
  lpszBuffer[dwTempPathLength++ - 1] = 'T';
  lpszBuffer[dwTempPathLength++ - 1] = 'M';
  lpszBuffer[dwTempPathLength++ - 1] = 'P';
  lpszBuffer[dwTempPathLength++ - 1] = 0;
  return TRUE;
}
   
HANDLE SpcMakeTempFile(LPTSTR lpszBuffer, DWORD dwBuffer) {
  HANDLE hFile;
   
  do {
    if (!MakeTempFilename(lpszBuffer, dwBuffer)) {
      hFile = INVALID_HANDLE_VALUE;
      break;
    }
    hFile = CreateFile(lpszBuffer, GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                       0, CREATE_NEW,
                       FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0);
    if (hFile =  = INVALID_HANDLE_VALUE && GetLastError(  ) != ERROR_ALREADY_EXISTS)
      break;
  } while (hFile =  = INVALID_HANDLE_VALUE);
  
  return hFile;
}

2.11.4 See Also

Recipe 2.4, Recipe 11.11

    [ Team LiB ] Previous Section Next Section