Both applications described above face the same problem. There's an object in a hostile directory they want to access, but they need to make sure they cannot be fooled into following symlinks, hard links, or talking to a process they shouldn't be trusting.
The best cure I know is to check the file's owner using lstat. Consider the following code:
snprintf(filename, sizeof(filename),
"/tmp/krb5cc_%u", getuid());
if (lstat(filename, &stb) >= 0) {
/* Bail out if it's ... */
if (!S_ISREG(stb.st_mode) /* ... not a regular file */
|| stb.st_uid != getuid() /* ... not owned by us */
|| stb.st_nlink != 1) /* or a hard link */
fatal("Argh, spoofed temp file!");
fd = open(filename, O_RDWR);
} else {
fd = open(filename, O_RDWR|O_CREAT|O_EXCL, 0600);
}
if (fd < 0)
fatal("Cannot open %s: %s", filename, strerror(errno));
At first glance, this looks like the typical race condition I was ranting about above. Of course, many things can happen between the lstat call and the open call. But in this case, the attacker will not be able to replace the file with something else because of the sticky bit on /tmp! If the information returned by lstat tells us the file is owned by us, we know we're safe because the attacker cannot remove whatever we just looked at.
Note that the code snippet above works for everything except setuid applications; in a setuid application getuid will return the user ID of the user who invoked the program, i.e. the (potential) attacker. In this case, you should make sure that the file was created with the effective uid of the program.
Another solution is to create a subdirectory of /tmp, which we will discuss in the next section.