From: Nick Craig-Wood (ncw1_at_axis.demon.co.uk)
Date: Wed 04 Dec 2002 - 11:06:38 GMT
On Tue, Dec 03, 2002 at 05:56:01PM -0500, Jacques Gelinas wrote:
> On Tue, 3 Dec 2002 21:13:03 -0500, Nick Craig-Wood wrote
> > I don't think that AC (or any other kernel maintainer) would ever
> > accept the chmod 000 hack to stop chroot escapes. Its just horrid!
>
> But so simple
Like all the best bodges ;-)
> > AC has expressed strong opinions on not modifying the current
> > semantics of chroot to "fix" them too as it breaks current
> > applications.
> >
> > I think that this needs careful thought before vserver goes for the
> > mainline kernel. Perhaps a new system call is needed - one based on
> > the BSD chroot maybe but called something else?
>
> What about a new syscall working like chroot but with the following extra
>
> The syscall fails if
>
> -There are opened file handles which are directories
> -The current working directory is not the same as the argument
These two requirements will stop the chroot breakage.
> Or we can drop the second requierement and use the current working directory
> as the new root, so the system call would be parameter-less and even simpler
> since we will simply copy one pointer over an other, no parameter validation at
> all.
This is better, though it might be best to keep compatibility with the
original chroot syscall so it could maybe be a compile time option as
to whether the library (glibc) chooses chroot() or chrootsafe() to
implement the chroot library routine.
> We need a good name for the system call. chrootsafe() ?
Sounds good!
> The system call would have another effect. It will record the new root
> in two places in the process. One is the current root of the process and the
> other is the unbreakable root. The idea is that chroot may still be used
> inside a chrootsafe() world and the weakness in chroot() may be used to
> break chrootsafe() as well.
Yes this is true..
> So this unbreakable root would be used as a test where the horrid
> 000 test is currently done.
;-)
...
Alternatively if you have called chrootsafe() then the kernel could
transmute all chroot() calls into chrootsafe() calls. Once you've
called chrootsafe() then all bets are off concerning chroot()
compatibility. This would seem to be a cleaner idea!
You might want to change condition 2) to be
chdir(argument)
otherwise this will break applications (for example postfix) - see
below.
...
This leads me to explore going down the road chosen by the BSD
architects :-
Depending on the setting of the `kern.chroot_allow_open_directories'
sysctl variable, open filedescriptors which reference directories will
make the chroot() fail as follows:
If `kern.chroot_allow_open_directories' is set to zero, chroot() will
always fail with EPERM if there are any directories open.
If `kern.chroot_allow_open_directories' is set to one (the default),
chroot() will fail with EPERM if there are any directories open and the
process is already subject to a chroot() call.
Any other value for `kern.chroot_allow_open_directories' will bypass the
check for open directories
So another alternative would be to make a kernel tweakable which
defaults to 0, say /proc/sys/kernel/safechroot. 0 would mean chroot()
works exactly like it normally does. 1 would mean :-
chroot() fails if
1) There are opened file handles which are directories
2) The current working directory is not the same as the argument
Applications which chroot safely either do
A) chroot("/jail");
chdir("/");
(as used by postfix)
or
B) chdir("/jail");
chroot(".");
(as used by djbdns)
or possibly
C) chdir("/jail/cubicle");
chroot("/jail");
all of which work and currently have the correct effect. Condition 2)
would break A) and C) which is undesirable.
It might be better to make condition 2)
2b) chdir(argument)
This would make A) and B) work just fine only breaking the bizarre C)
which I haven't seen in the wild. C) could be made to work by not
doing the chdir in 2b) if the cwd is below the argument.
...
So options presented so far are
i) chrootsafe() syscall with unbreakable root for chroot() calls
ii) chrootsafe() syscall with chroot() -> chrootsafe() transmutation
iii) sysctl to control chrootsafe() like behaviour of chroot
Where chrootsafe() is defined as chroot() doing this first :-
1) Fail if there are opened file handles which are directories
2a) Fail if the current working directory is not the same as the argument
-or-
2b) chdir(argument)
My feelings are i) will still require the 000 hack (and hence be
unacceptable in a similar way) but will provide perfect chroot()
compatibility. ii) is good and iii) has a precedent from BSD and
doesn't require another syscall.
2b) breaks less apps than 2a), but it violates POSIX chroot()
semantics "chroot does not change the cwd of the process".
-- Nick Craig-Wood ncw1_at_axis.demon.co.uk