Bugtraq mailing list archives
Re: SUMMARY Security Info (root broken)
From: casper () fwi uva nl (Casper Dik)
Date: Tue, 04 Oct 1994 10:24:51 +0100
I think that Pat has highlighted here the problem with a lot of
packages that use a setuid root process to create a file in a
restricted directory (e.g, a 775 root.mail /var/spool/mail.)
I've looked at the 4.4BSD-lite (NetBSD uses this) mail.local.c and at
first, thought there was a potential race condition in the code
where it does an lstat check then an open, thinking there was a race
condition. Checking the man page for open() however, revealed the
following tidbits:
If path is a symbolic link and O_CREAT and O_EXCL are set,
the link is not followed.
(From Solaris 2.3, and the NetBSD-current man page says something
similar.)
These semntics were introduced at the same time that symlinks were introduced. However, some vendors did pick up symlinks but w/o the don't follow on O_CREAT|O_EXCL semantics. (IRIX 4.0.x, I think)
So, it seems that a standard (POSIX?) has explicitly given us a method to atomically create a file if it doesn't exist, whilst at the same time not getting fooled by a dangling symlink (which is a common way to exploit setuid race conditions, correct?)
POSIX doesn't specify symlinks (yet).
Now, I don't know if this helps people on systems where this behaviour doesn't exist (I'm not sure if Sunos 4 supports this, for example.)
SunOS 4 and all BSD/SVR4 derived systems should. SVR3 and earlier with symlinks need not.
It's the creating of the new file by a priviliged process that is the critical region that so often gets spoofed by a race condition. I have some (simple - thus easy to follow and assure is correct - I hope :) code at home that I was working on which should work without a race condition (using the atomic link()), so I'll post it tomorrow to get disected by those with more experience than I. If it does work the way I expect it to, I feel that a simpler, more effective, mail.local could be implemented that didn't rely upon the O_CREAT | O_EXCL feature of newer systems I described above...
Here's some code I wrote earlier that does much the same thing.
(it dates from May somewhere)
Casper
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: safecreate.c
# Wrapped by casper () fwi uva nl on Tue May 17 16:34:58 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'safecreate.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'safecreate.c'\"
else
echo shar: Extracting \"'safecreate.c'\" \(3836 characters\)
sed "s/^X//" >'safecreate.c' <<'END_OF_FILE'
X/*
X * Safe way to create/open a file in a sticky directory.
X * the file must belong to the executing user.
X *
X * Casper Dik (casper () fwi uva nl)
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <sys/stat.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#include <string.h>
X#include <errno.h>
X#include <pwd.h>
X
X#ifndef FILEMODE
X#define FILEMODE 0600
X#endif
X#ifndef FILEGROUP
X#define FILEGROUP -1
X#endif
X#define FIXMODE
X
XFILE *
Xopenmbox(uid_t, char *);
X
Xmain(int argc, char **argv)
X{
X struct passwd *pwd;
X FILE *mbox;
X int c;
X
X if (argc != 3) {
X fprintf(stderr,"Usage: %s file user\n", argv[0]);
X exit(1);
X }
X pwd = getpwnam(argv[2]);
X if (pwd == 0) {
X fprintf(stderr,"%s: %s: unknown user\n", argv[0], argv[1]);
X exit(1);
X }
X if ((mbox = openmbox(pwd->pw_uid, argv[1])) == 0) {
X fprintf(stderr,"Couldn't safely open %s\n", argv[1]);
X exit(1);
X }
X while((c = getchar()) != EOF)
X putc(c, mbox);
X exit(0);
X}
X
XFILE *
Xopenmbox(uid_t uid, char *file)
X{
X char *dir, *p;
X int fd;
X FILE *f;
X
X dir = strdup(file);
X p = strrchr(dir,'/');
X if (!p)
X dir = ".";
X else
X *p = '\0';
X
X /* First we'll try a normal open, no O_CREAT */
X fd = open(file, O_WRONLY|O_APPEND);
X if (fd == -1) {
X char *tmpf;
X
X /* If there's another error, we can't handle it */
X if (errno != ENOENT) {
X perror(file);
X return 0;
X }
X
X /* Now we must create the file; */
X tmpf = tempnam(dir, "psrz");
X if (tmpf == 0) {
X fprintf(stderr,"Can't generate unique filename\n");
X return 0;
X }
X fd = open(tmpf, O_WRONLY|O_APPEND|O_CREAT|O_EXCL, FILEMODE);
X if (fd < 0) {
X fprintf(stderr,"Can't create temporary file\n");
X (void) unlink(tmpf);
X free(tmpf);
X return 0;
X }
X /* Now we have a file in the spool directory, we're going to
X * rename it the old fashioned way because we want it to
X * fail if someone created the mailbox in the meantime */
X if (link(tmpf, file) == -1) {
X if (errno == EEXIST)
X fprintf(stderr,"%s created behind our back\n", file);
X else
X perror("link");
X (void) unlink(tmpf);
X free(tmpf);
X return 0;
X }
X (void) unlink(tmpf);
X free(tmpf);
X /*
X * At this point we have the file and an open filedescriptor
X * We can be sure we aren't overwriting another file because we've
X * created this file in the spool directory.
X * Only on systems where O_CREAT|O_EXCL creates files at the end
X * of symlinks we have to worry */
X } else {
X struct stat fdbuf, filebuf;
X
X /* the next two things should never happen */
X if (fstat(fd, &fdbuf) == -1) {
X perror("fstat");
X return 0;
X }
X if (lstat(file, &filebuf) == -1) {
X perror(file);
X return 0;
X }
X
X /* Now check that: file and fd reference the same file,
X file only has one link, file is plain file */
X if (fdbuf.st_dev != filebuf.st_dev ||
X fdbuf.st_ino != filebuf.st_ino ||
X fdbuf.st_nlink != 1 ||
X filebuf.st_nlink != 1 ||
X (fdbuf.st_mode & S_IFMT) != S_IFREG) {
X fprintf(stderr,"%s must be a plain file with one link\n", file);
X return 0;
X }
X /* we have a filedescriptor pointing to a file in the spool directory
X * how can we be sure it wasn't pointing at another file when we
X * opened it?
X * At the time of the lstat, the file in the spool directory was
X * a hardlink to the file we previously opened.
X * (st_dev and st_ino uniquely identify a file)
X * It was the only hardlink.
X * Could it have been a different file previously?
X * Yes, but only if the file existed in a writable directory and
X * no longer exists there now.
X */
X }
X /* Now we have an fd pointing to the file in the spool directory.
X * Now we're going to fix ownership and mode */
X#ifdef FIXMODE
X (void) fchmod(fd, FILEMODE);
X (void) fchown(fd, uid, FILEGROUP);
X#endif
X f = fdopen(fd, "a");
X if (f == 0)
X close(fd);
X return f;
X}
END_OF_FILE
if test 3836 -ne `wc -c <'safecreate.c'`; then
echo shar: \"'safecreate.c'\" unpacked with wrong size!
fi
# end of 'safecreate.c'
fi
echo shar: End of shell archive.
exit 0
Current thread:
- [Tim Newsham: IRIX Race Conditions] Tim Newsham (Oct 02)
- <Possible follow-ups>
- [Tim Newsham: IRIX Race Conditions] Tim Newsham (Oct 02)
- Re: your mail Joseph W. Stroup (Oct 02)
- SUMMARY Security Info (root broken) Pat Myrto (Oct 03)
- Re: SUMMARY Security Info (root broken) Luke Mewburn (Oct 03)
- Re: SUMMARY Security Info (root broken) Pat Myrto (Oct 03)
- Re: SUMMARY Security Info (root broken) Casper Dik (Oct 04)
- [Tim Newsham: IRIX Race Conditions] Tim Newsham (Oct 02)
- [Tim Newsham: IRIX Race Conditions] Tim Newsham (Oct 02)
- [Tim Newsham: IRIX Race Conditions] Brent Chapman (Oct 02)
