.@ Tony Finch – blog


I commented on Lobsters that /tmp is usually a bad idea, which caused some surprise. I suppose /tmp security bugs were common in the 1990s when I was learning Unix, but they are pretty rare now so I can see why less grizzled hackers might not be familiar with the problems.

I guess that’s some kind of success, but sadly the fixes have left behind a lot of scar tissue because they didn’t address the underlying problem: /tmp should not exist.

It’s a bad idea because it’s shared global mutable state that crosses security boundaries. There’s a ton of complexity at all levels of unix (filesystems, kernel APIs, libc, shell, admin scripts) that only exists as a workaround for the dangers caused by making /tmp shared.

sticky bit

I think the earliest and lowest-level workaround is the sticky bit.

The sticky bit is mode bit 01000 in unix file permissions. It is printed as the t instead of x in rwt.

    drwxrwxrwt  5 root  wheel  160 Oct 22 10:22 /tmp/

Originally in the 1970s the sticky bit was invented to speed up frequently-used programs such as the shell: the sticky bit indicated to the kernel that the executable file should stick in core. This functionality was made obsolete in the 1980s by filesystem page caches.

The sticky bit was re-used to mean something else on directories, to fix a security problem with /tmp.

On unix, permission to delete a file depends on write access to the directory containing the file, independent of the file’s own permissions.

So if a directory (such as /tmp) is world-writable, anyone can delete any file in it.

This means /tmp was vulnerable to all sorts of accidental or malicious trouble caused by users deleting each others’ files.

To fix this, a file in a sticky directory may only be removed or renamed by a user if the user has write permission for the directory and the user is the owner of the file, the owner of the directory, or the super-user.

tmp stupidity in C

POSIX used to provide 5 (five!) ways to create a temporary file, three of which (most of them!) you must never use and which have subsequently been deprecated – except the do-not-use footgun tmpnam which is still part of the C standard. Two more safe functions have subsequently been added to support more complicated situations.

There are so many dangerous API design problems in those old functions! I’m not going to waste time roasting them in detail because I can cover the important points by discussing …

mkstemp and mkdtemp

The purpose of all these functions is to create a temporary file (or directory) that doesn’t collide with other concurrent activity.

When you are creating a file in /tmp the risk is that another malicious user can make you open a file under their control. To avoid this vulnerability, you (or rather mkstemp) must:

This is intricate and not entirely obvious, as you can tell from all the previous failed attempts at functions that didn’t create temporary files safely.

mktemp in shell

The mktemp(1) command is a wrapper around mkstemp(3). (Slightly confusingly it isn’t a wrapper around mktemp(3) because that would be unsafe.) It was introduced by OpenBSD and it’s widely supported though it isn’t yet in POSIX. Its manual page includes a nice rationale:

The mktemp utility is provided to allow shell scripts to safely use temporary files. Traditionally, many shell scripts take the name of the program with the pid as a suffix and use that as a temporary file name. This kind of naming scheme is predictable and the race condition it creates is easy for an attacker to win. A safer, though still inferior, approach is to make a temporary directory using the same naming scheme. While this does allow one to guarantee that a temporary file will not be subverted, it still allows a simple denial of service attack. For these reasons it is suggested that mktemp be used instead.

Although shell scripts can’t avoid using the temporary file by name, mktemp(1) is safe because it creates the file securely and an attacker can’t interfere with it after that point. It’s OK to re-open a file that you know is yours.

tmp cleanup

The last item on my list of regrets is “admin scripts”, an oblique reference to /tmp cleanup jobs.

By its nature /tmp tends to accumulate junk, so it was common to have cron jobs that would delete old files. (Less common now that computers are much bigger.)

These scripts tended to have problems with time-of-check / time-of-use vulnerabilities, careless handling of symlinks, and pulling the rug out from under long-running programs that foolishly used /tmp. (Lots more reasons these scripts are now less common!)

tmp remedy

So I’ve spent dozens of paragraphs outlining bugs and complications related to /tmp. All of them could have been avoided if /tmp did not exist, and everything would have been simpler as a result.

So where should temporary files have gone, if not in /tmp?

There should have been per-user temporary directories in different per-user locations. In fact, on some modern systems there are per-user temporary directories! But this solution came several decades too late.

If you have per-user $TMPDIR then temporary filenames can safely be created using the simple mechanisms described in the mktemp(1) rationale or used by the old deprecated C functions. There’s no need to defend against an attacker who doesn’t have sufficient access to mount an attack! There’s no need for sticky directories because there aren’t any world-writable directories.

There’s a minor wrinkle that setuid programs would have to be more careful about how they create temporary files, but setuid programs have to be more careful about everything.

tmp rationale

So why wasn’t per-user $TMPDIR a thing back in the day?

Probably the main reason was path-dependence: /tmp was created and in wide use before its problems became apparent, at which point it was difficult to deprecate.

There are reasons 1990-ish-you didn’t want $TMPDIR to be in your home directory:

So some finesse is necessary when choosing where to put $TMPDIR, but that should not have been an insurmountable problem.

more tmp

edited to add…

Of course, we can’t go back in time to get rid of /tmp, so efforts continue to make it safer by making things more complicated.

For example, on Linux there are a couple of features that use filesystem namespaces to hide the global /tmp with a user-owned /tmp overlay: pam_namespace and systemd PrivateTmp.

less tmp

I don’t think there’s any way to get rid of the complications I’ve described in this post, as long as a /tmp directory exists – which will be forever. Unix programs have to assume /tmp is unsafe, so the security mechanisms in mkstemp() have to stay. We can’t remove sticky bit support from the kernel as long as there’s a global /tmp, even if it is sometimes hidden by an overlay.

Personally, when writing software I avoid using /tmp. I create a working directory somewhere that isn’t world writable and use that instead.