Against /Tmp

Date:

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.

  • bad: mktemp, tempnam, tmpnam
  • ok: mkstemp, tmpfile
  • new: mkdtemp, mkostemp, mkstemp

There are so many dangerous API design problems in these 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:

  • Generate an unpredictable filename.

    It isn’t enough for the filename to be unique, it must contain
    sufficient secure randomness that an adversary can’t win a race
    and interfere.

  • Open the file with O_CREAT | O_EXCL.

    This ensures the file has not already been created by another
    friendly or malicious process, without any time-of-check /
    time-of-use vulnerabilities. And it ensures that the file isn’t a
    symlink pointing somewhere an attacker wants you to accidentally
    corrupt.

  • Retry if open fails with EEXIST.

    Together with the randomness in the name, this avoids
    denial-of-service vulnerabilites.

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 go, if not in /tmp?

There should be per-user temporary directories. In fact, on 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:

  • $HOME might be on NFS so a local $TMPDIR might be faster

  • $TMPDIR can be a way to get workspace beyond your disk quota

  • $HOME might be on a filesystem that doesn’t support named pipes,
    so your X11 and SSH agent sockets live in $TMPDIR instead

The fix, way back when, should have been for login(8) to create a
per-user temporary directory in a sensible place before it drops
privilege, and set $TMPDIR so the user’s shell and child processes
can find it.

Oh well, think of all the excitement we would have missed if insecure
temporary file vulnerabilites had not been a thing!

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Share post:

Subscribe

Popular

More like this
Related

Mancandy Mexico Spring 2025

For spring 2025, Mancandy’s Andrés Jiménez drew inspiration from...

Alfredo Martínez Mexico Spring 2025

The Mexican painter and poet Carmen Mondragón, who was...

Shop the Best Hoop Earrings—No Jewelry Box Is Complete Without a Pair

*All products featured on *Vogue are independently selected by...

Woman drops phone, gets stuck upside-down in rock crevice looking for it

We use cookies and data toDeliver and maintain Google...