I've patched CVS 1.10.8 so that it supports a new command line option:
cvs --chroot /some/chroot/root/
The patch is attached to this email. I hereby grant permission to everyone
in the entire world to use this patch in whatever way they like for whatever
purpose they like. I assign copyright to it to the Free Software Foundation.
Let me know what else I need to say--I'd like to see something like this
incorporated back into the main CVS tree so I don't have to keep patching
my copy of CVS.
This has two effects:
1) CVS will attempt to chroot to that base directory, if CVS is not
running as root it will fail with an error message.
2) CVS will fail with an error message unless it is running as a
non-root user by the end of the authentication process. In other
words is *has* to succeed in doing a setuid to an authenticated user.
So that makes the option pretty much useless, except for with pserver, or
some other form of authentication.
In order to make that work, you have to set up the directory which you
have to create a skeleton filesystem in the chroot area, like this:
dev/null # a read-only empty text file to fool CVS into working
etc/group # a file listing the group id's CVS should setguid to
etc/passwd # a file containing the user id's CVS should setuid to
etc/shadow # matching etc/passwd
tmp/ # a tmp directory for CVS users to write to
That's a minimal setup for Debian. On FreeBSD you need to create password
databases inside the chroot area. If you want CVS to run any external
programs you have to supply those inside the chrooted are as well, which
may mean you need to stick a few things in a lib/ directory to support them.
So, it will take you a little bit of work to set this up--but I think it's
well worth it. You wind up with CVS guaranteed to be running running non-root
in a chrooted area. If some attacker managed to find a way to get CVS to
read or write an arbitrary file, they would be restricted to attacking
things that were assigned to CVS.
I have my CVS run like this out of xinetd:
service cvspserver
{
socket_type = stream
protocol = tcp
wait = no
user = root
group = wheel
instances = 3
server = /local/bin/cvs
server_args = --chroot /local/cvs --allow-root=/root pserver
}
that would look something like this for inetd.conf
cvspserver stream tcp nowait root /local/bin/cvs --chroot /local/cvs
--allow-root=/root pserver
Where '/root' corresponds to /local/cvs/root due to the chroot to /local/cvs.
Justin
*** cvs-1.10.8/src/main.c Tue Aug 17 13:16:25 1999
--- main.c Fri Aug 4 11:24:39 2000
***************
*** 415,418 ****
--- 415,419 ----
int free_Editor = 0;
int free_Tmpdir = 0;
+ int do_chroot = 0;
int help = 0; /* Has the user asked for help? This
***************
*** 427,430 ****
--- 428,432 ----
{"help-options", 0, NULL, 4},
{"allow-root", required_argument, NULL, 3},
+ {"chroot", required_argument, NULL, 5},
{0, 0, 0, 0}
};
***************
*** 531,534 ****
--- 533,553 ----
root_allow_add (optarg);
break;
+ case 5:
+ /* --chroot */
+ do_chroot = 1;
+ if (chdir(optarg)) {
+ fputs("failed to chdir to chroot base: ", stdout);
+ fputs(optarg, stdout);
+ fputs("\n", stdout);
+ exit (0);
+ }
+ if (chroot(optarg)) {
+ fputs("failed to chroot to chroot base: ", stdout);
+ fputs(optarg, stdout);
+ fputs("\n", stdout);
+ exit (0);
+ }
+ break;
+
case 'Q':
really_quiet = 1;
***************
*** 729,732 ****
--- 748,757 ----
}
#endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */
+
+ /* Make sure we aren't still running as the root user */
+ if (do_chroot && !getuid() ) {
+ fputs("SECURITY VIOLATION: cvs tried to chroot but failed to drop root
+privileges during authentication.\n", stdout);
+ exit(0);
+ }
#ifdef SERVER_SUPPORT