[672] in linux-security and linux-alert archive

home help back first fref pref prev next nref lref last post

Re: [linux-security] Security problems in RedHat 3.0.3...

daemon@ATHENA.MIT.EDU (Zygo Blaxell)
Tue Apr 16 10:56:17 1996

From: Zygo Blaxell <zblaxell@myrus.com>
To: Andries.Brouwer@cwi.nl
Date: Mon, 15 Apr 1996 15:29:54 -0400 (EDT)
Cc: R.E.Wolff@et.tudelft.nl, zblaxell@myrus.com,
        linux-security@tarsier.cv.nrao.edu
In-Reply-To: <9604132013.AA03538=aeb@zeus.cwi.nl> from "Andries.Brouwer@cwi.nl" at Apr 13, 96 10:13:20 pm

Quoted from Andries.Brouwer@cwi.nl:
> What is the use of unverified rumours?
> What is `man'?

Whatever RedHat uses.  According to 'rpm':

	zblaxell@fludd:/var/lib/rpm$ rpm -q man
	man-1.4f-2

I'm using ftp://ftp.redhat.com/pub/current/i386/SRPMS/man-1.4f-2.src.rpm
as a reference.

Hmmm...I should have RTFMed...

	'man man' says 'make man setuid-man'
	the rpm file installs man setGid man

So there seems to be an installation error on RedHat's part (or
whoever maintains the man-1.4f-2.rpm file anyway).  (*) It should be
installed mode 4555, owner man.  

Write permission for the owner (man) doesn't matter--if a user can make
man overwrite arbitrary files as 'man', then attempting to modify
/usr/bin/man while it is executing will fail with EBUSY; if the user can
execute arbitrary commands as 'man', the user can chmod /usr/bin/man to
something more friendly; we lose either way.

> I know of some seven man programs in use under Linux, two in common use.
> I maintain one of these - man-1.4* - and am not aware of security-related
> bugs (although it is quite possible that some exist).
> If anything is wrong, point it out, and it will be corrected.

(!) There are some distressing things that still happen.  One is that
during man page creation I can get mode 666 files in the cache directory
sometimes.

(!) Currently, 'man' will use setgid privileges (if any) to manipulate
cache pages in non-standard locations.  I don't see any changes that
RedHat might have made to create this bug, other than to make man setgid
in the first place, so I assume the problem was already there.  This can
be fixed by the changes below.

(*) The file permissions should be set in the open() call
and/or by the umask before the open ever occurs--this will take care of
mode 666 files.  The open() call should also include O_EXCL, to
guarantee that symlinks in the cache directories will not be followed
while writing the cached man page.

(*) Suggestion:  Avoid the use of fopen(cache_page,"w").  Using fopen()
to create files is almost always evil, especially in programs that might
run with privileges in shared directories.  Use this instead:

FILE *safe_fopen_w(char *path) {	/* open path in "w" mode */
	int fd;
	FILE *fp;

	fd=open(path,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,0444);
	if (fd<0) return NULL;
	fp=fdopen(fd,"w");
	return fp;
}

(*) man.config should be used to explicitly specify where 'man'
privileges should be used to write cached pages; the user's original
privileges should be used in *all* other places.  This applies to both
setuid-man and setgid-man versions.  

Note:  Testing the permissions and ownership of the cache directory is
not sufficient, because a user can control path components leading up to
the cache directory, like this:

	MANPATH=/home/user/foo/bar/man/man1
	mkdir /home/user/foo/BAR/man/{man,cat}1
	ln -s /usr /home/user/foo/bar
	mv /home/user/evil-manpage.1 /home/user/foo/bar/man/man1/sh.1
	man sh

The user waits for 'man' to check the ownership of
'/home/user/foo/bar/man/cat1' and be satisfied with it, then replaces
'bar' with 'BAR' and makes 'cat1/sh.1.gz' a symlink to whatever file
she wants overwritten.  Now the user waits for 'man' to open
'/home/user/foo/bar/man/cat1/sh.1.gz' with 'man' privileges--note that
the directory name '/home/user/foo/bar/man/cat1/' points to a very
different directory now than it did before.

(*) An alternative to listing directories for cache pages in man.config is
to check every directory component of the cache page directory.  This
is somewhat more complicated to implement because unfortunately we're
stuck with Unix's security model.  Feel free to ignore everything up to
the next star in brackets.

If any directory component of a cache directory is writable by any user
other than root or man, drop all privileges.  If any directory component
is a symlink, chdir() to the pathname so far, replace that component and
all previous components with the result of getcwd(), and start over.

The actual writability test is:
	(owned by root OR owned by man OR on a list of approved users) AND
	(owned by group man OR not-group-writable) AND 
	not-world-writable 

Example:

	Assume symlink /usr/man -> /disk-three/usr/man
	For a man page in /usr/man/man1/sh.1, the cache directory is
	                  '/usr/man/cat1/'.
	To test this directory ("/usr/man/cat1"), do the following:
	lstat("/usr") - a directory writable only by 'man' or 'root'.  OK.
	lstat("/usr/man") - a symlink owned by 'man' or 'root'.  OK.
	chdir("/usr/man") - OK
	getcwd(buf,size) - get current directory - /disk-three/usr/man
	We followed a symlink, so start over with '/disk-three/usr/man/cat1'.
	lstat("/disk-three") - directory writable only... OK
	lstat("/disk-three/usr") - directory writable only... OK
	lstat("/disk-three/usr/man") - directory writable only... OK
	lstat("/disk-three/usr/man/cat1") - directory writable only... OK
		OK.
	End of test.  Still OK.

This test will correctly fail if a user has control over pathname
components.  If '/home/foo/man/usr-symlink/cat1' is passed to the test,
it will fail because '/home/foo' will fail the writability test.

Incidentally, if a cache directory derived from the user's MANPATH does
not begin with "/", then drop all privileges.  Also, man.config may have
to specify a list 'approved users', for sites that have a lot of software
maintained by users trusted to install software for the user community
but not to have root privileges.

(*) There's a robustness/reliability issue:  output for cached man pages
goes directly into a file opened O_CREAT|O_TRUNC.  If the system crashes
or the formatting program is interrupted by the user, this page will be
invalid but will have a newer timestamp than the original man page.
Ideally a temporary file would be created first, then rename()d to the
final cached man page name.  

(*) There's a chmod() call on the cached man page that is unnecessary.
The permissions can be set as described above with proper use of open()
or umask() and the chmod() call can be removed.

(*) It's possible for a user to attach a debugger to the man page
formatting program and take control of the man page formatting software
to produce arbitrary output for cached man pages.  This can be avoided by
carefully destroying the environment of the man page formatting software
(simply not passing 'envp' to execve() is enough), closing all open file
descriptors, resetting alarms (alarm(0)), resetting resource limits
(coredumpsize=0, datasize=unlimited, cputime=unlimited...), setting
signal handlers (ignore most of them), and running it as user 'man', out
of reach of the debugger.  I think I got everything that needs to be
done in that list.

-- 
Zygo Blaxell.  Former Unix/soft/hardware guru, U of Waterloo Computer Science 
Club.  Current sysadmin for Myrus Design, Inc.  10th place, ACM Intl Collegiate
Programming Contest Finals, 1994.  Administer Linux nets for food, clothing, 
and anime.  "I gave up $1000 to avoid working on windoze... *sigh*" - Amy Fong

home help back first fref pref prev next nref lref last post