main()
{
if (chroot("/usr/jfw") < 0)
exit(perror("/usr/jfw"));
setuid(getuid());
execl("sh","sh",0);
}
In my home directory, by the way, is a copy of /bin/sh.
This program is SUID root. What happens when I run it is the following:
I see a shell which believes that /usr/jfw is the root. I say "ls /"
and see my files (there is a copy of ls in /usr/jfw, too). BUT: If I
type:
cd ..
the kernel obligingly looks up the inode which is linked under the name ".."
and my shell lands there. Most people call it "/usr". I know not what
pwd would call it, but I certainly am no longer restricted to the /usr/jfw
heirarchy. I then type "cd .." again and land in the root, which is on
another device.
As far as 4.1BSD goes: The code for chroot(), chdir(), and chdirec() (the
underlying code of both) is character for character the same: ERGO, 4.1
does exactly the same. If you tried it and got the results you claim, then
you are running something which isn't standard. If you haven't tried these
cases (nor thought about them), then I think you have overlooked something.
Summary: REMEMBER that UNIX heirarchy reverse-links are implemented with
pointers that turn the directed acyclic graph you think you see into a
heavily-cyclic graph. If you want to do this, I suppose that you could remove
the ".." pointer in /usr/guests, but a lot of UNIX utilities are going to
throw up their hands at that.
The only truly secure system is a personal computer locked in a room -- with
the power cord unplugged. Undo any of those, and you've got to accept
compromises.
John Woods, jfw%mit-ccc@Mit-MC
You're both right! The concept of each process having is own pointer
to a "root directory" introduced in UNIX Version 7 was not implemented
in a completely "secure" manner. As you have shown, on a V7 system it
is possible to to access ".." above your root directory. On System III,
BSD 4.1, and BSD 4.2 UNIX systems the kernel actually checks to see if
you're accessing the name ".." in the root directory, if so it just remaps
".." to be your root directory. Therefore "/.." will always be the same
as "/" on one of these more modern systems.
-Bob Miles, rsm@BRL
I believe that .. can be unlinked by the super user using the unlink() call.
At that point, a link of .. to . would leave a closed subtree.
This subtree could then be used safely... (I think).
This should probably be done only in a newly created directory, since
some code may make assumptions about the location of .. in
the directory (first block).
RSVP
- Matt
As far as 4.1BSD goes: The code for chroot(), chdir(), and chdirec()
(the underlying code of both) is character for character the same:
ERGO, 4.1 does exactly the same.
But the code in namei() is different, and THAT is the code that interprets
pathnames. All chdir(), chroot(), and chdirec() do is set an inode pointer
in your u-page; namei() is the routine that is affected by changing that
pointer.
Summary: REMEMBER that UNIX heirarchy reverse-links are implemented
with pointers that turn the directed acyclic graph you think you see
into a heavily-cyclic graph. If you want to do this, I suppose that
you could remove the ".." pointer in /usr/guests, but a lot of UNIX
utilities are going to throw up their hands at that.
The fix in 4.1BSD and System III causes a dynamic reinterpretation of the ".."
link, so that the tree looks different to different processes. The ".." link
should NOT be removed, as it will upset those utilities; the fix should be put
into namei() to reinterpret that link if it points above the fake root
directory.
Conclusion: on 4.1BSD and System III (and all later releases), feel free to
use chroot(). On V7, put in the fix I posted, and then feel free to use
chroot(). If you're running a UNIX-lookalike, make sure if it has chroot()
that the hole has been plugged. If you wrote a UNIX-lookalike with chroot(),
plug the hole.
Guy Harris
{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy
A "pwd" in "/usr/guest" after chroot("/usr/guest") and chdir("/") prints "/" on
4.1BSD. The only significant difference between the 4.1BSD "pwd" command and
the System III "pwd" command is that the 4.1BSD command does a stat("/") and
saves the dev/inumber pair of "/", and it stops backing up the directory tree
whenever it finds a directory with that dev/inumber pair. Both versions stop
when they find a directory in which a chdir("..") leaves you in the same
directory. I suspect the System III version will also print "/" after the
chroot().
Guy Harris
{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy
Moral: ID 0 is very special to the operating system, and cannot be trusted
to someone that needs chroot'ed.
John Haller
If there was ever a better reason for keeping this discussion public than
the current discussion, I don't know what it is.
Bob English brings up a good point which shouldn't be dismissed,
however, the solution may not be quite as complex as he stated. I am
willing to admit that there are some difficulties with the example
I gave IF one uses the Bourne shell. I specifically used the C-shell
because it explicitly handles the cases of "/", "./", and ".." . I
still maintain that if the shell that I listed (the C-shell) is called
from a process which invokes chroot(), you can create a secure shell
UNLESS you allow users to call programs within that heirarchy which are
linked to programs outside that heirarchy. Furthermore, simple attempts
such as writing a program which calls "chdir" won't work, even if you are
the super user. Of course, I don't expect that users of the restricted
shell will be able to do everything a non-restricted user can. But
then I was speaking on the case of the casual user, such as student
running Lisp or (God forbid), FORTRAN, who doesn't need to use programs
which setuid 0 or read kmem. I'd be the first to admit you can't have
your cake and eat it too. And besides, I wanted to talk about something
besides passwords.
[Bring on the lions!]
Sean
-------
Jack Jansen, VU Amsterdam.
(mcvax!vu44!jack)