The somewhat surprising history of chroot()
The chroot()
system call is one of those that I think of as being
really old. Up until a while back, if you'd
pressed me I'd have guessed that it originated in V7. Then I dug
into it and in that tweet claimed it was from 4.2 BSD. This is
what you'd kind of expect and what it sort of looks like, but it
turns out to be wrong and the history of chroot()
seems to be
much more interesting than I expected.
The classic use of chroot()
is for safely confining network
daemons, and indeed this is what ftpd
uses it for in 4.2 BSD.
But there's another use of it in the 4.2 BSD tree lurking in a
file called /usr/src/games/compat/runcompat.c.
As a manpage
and a Readme
there make clear, this is a system to let you run PDP-11 V6 and V7
programs on your VAX. It uses chroot()
to optionally put those
programs inside a familiar V6 or V7 directory hierarchy. This
appears to go more or less all the way back to 32V, the first Unix version
to run on a VAX and thus the first one where this desire would come
up. But it's not where chroot(2)
was added.
Much to my surprise, it turns out that chroot(2)
is in V7 after
all. It's not obvious because there's no separate manpage for it;
instead it's part of the chdir(2)
manpage. I
don't know why it's there, apart from 'someone thought it was an
obvious feature'; tuhs.org has more or less complete V7 source, and
there's no programs in the source tree that use it that I can find.
Apparently it was added first and then people found various uses
for it later. What we now think of as its most prominent uses (for
things like confining anonymous ftp) are relatively latecomers to
the game, since they started years after chroot()
itself came
into existence.
(I don't believe that the V7 chroot()
was a security measure.)
Sidebar: why chroot is in the chdir manpage
V7 implements chdir(2)
and chroot(2)
with basically the same
code; you can see it in /usr/sys/sys/sys4.c.
It's pretty clever:
chdir() { chdirec(&u.u_cdir); } chroot() { if (suser()) chdirec(&u.u_rdir); } chdirec(ipp) register struct inode **ipp; { [...]
In other words, the only real difference between chdir and chroot is
what field in the u
user structure they act on. chdir acts on the
'current directory' field, chroot acts on the 'root directory' field.
The actual implementation of chroot requires a bit more kernel code than
this, because of course the u_rdir
field didn't exist before chroot
was added. But there turn out to be remarkably few places that deal
with u_rdir
in the V7 kernel. By the way, reading this code makes
me suspect that a V7 chroot was remarkably permeable; I don't see any
reason why you can't just 'cd ..
' your way right out of one (although
I may be missing something subtle in the code). That obviously changed
later.
(Note that the V7 manpage doesn't claim that chroot() confines
processes; it simply says that it changes what '/
' means to
them. Which is literally what the kernel code does.)
|
|