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

Reply via email to