info-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

CVS passwd patch


From: David Fuller
Subject: CVS passwd patch
Date: Thu, 16 Aug 2001 08:50:32 -0400

I've updated a previously available patch to the 1.11.1p1 release (was
against 1.10.8).  

Summary:
Provides improved security and convenience by organizing CVS passwords
into one file '/etc/cvs.passwd'.  It also solves the problems caused by
running multiple cvs roots (such as having to make a wrapper script).

Description:
The patch (see attached) looks for a file '/etc/cvs.passwd' when running
as pserver.  This file specifies the available repositories (deprecates
--allow-root) and passwords within each repository.  This file should be
owned and readable only by root for best security.

Example:
# The main CVS repository:
/usr/src/cvsroot
anonymous:
bach:ULtgRLXo7NRxs
spwang:1sOp854gDF3DY
melissa:tGX1fS8sun6rY:pubcvs
qproj:XR4EZcEs0szik:pubcvs

# We use a different repository for `project':
/usr/local/project/cvs
anoncvs:s2HgRE.D/a7Xs:rob
robby:zVCvOmrpDiPBE:rob


I must apologize to the original developer, I lost your info and am
having trouble finding you again.

-- David F.
===================================
David Fuller
Software Design Engineer
TechSight, a business unit of GDDS
address@hidden
===================================
diff -rc cvs-1.11.1p1-patched/doc/cvs.texinfo cvs-1.10.8-patched/doc/cvs.texinfo
*** cvs-1.11.1p1-patched/doc/cvs.texinfo        Tue Apr 24 14:14:52 2001
--- cvs-1.10.8-patched/doc/cvs.texinfo  Wed Aug 15 11:13:20 2001
***************
*** 2336,2355 ****
  file is generally used, so people don't compromise
  their regular passwords when they access the
  repository.  This file is
! @file{$CVSROOT/CVSROOT/passwd} (@pxref{Intro
  administrative files}).  It uses a colon-separated
  format, similar to @file{/etc/passwd} on Unix systems,
  except that it has fewer fields: @sc{cvs} username,
  optional password, and an optional system username for
! @sc{cvs} to run as if authentication succeeds.  Here is
! an example @file{passwd} file with five entries:
  
  @example
  anonymous:
  bach:ULtgRLXo7NRxs
  spwang:1sOp854gDF3DY
  melissa:tGX1fS8sun6rY:pubcvs
  qproj:XR4EZcEs0szik:pubcvs
  @end example
  
  (The passwords are encrypted according to the standard
--- 2336,2364 ----
  file is generally used, so people don't compromise
  their regular passwords when they access the
  repository.  This file is
! @file{/etc/cvs.passwd} (@pxref{Intro
  administrative files}).  It uses a colon-separated
  format, similar to @file{/etc/passwd} on Unix systems,
  except that it has fewer fields: @sc{cvs} username,
  optional password, and an optional system username for
! @sc{cvs} to run as if authentication succeeds.  For each
! allowed root a seperate list of user/passwords must be
! provided, prepended with the root path.  Here is an
! example @file{passwd} file with five entries:
  
  @example
+ # The main CVS repository:
+ /usr/src/cvsroot
  anonymous:
  bach:ULtgRLXo7NRxs
  spwang:1sOp854gDF3DY
  melissa:tGX1fS8sun6rY:pubcvs
  qproj:XR4EZcEs0szik:pubcvs
+ 
+ # We use a different repository for `project':
+ /usr/local/project/cvs
+ anoncvs:s2HgRE.D/a7Xs:rob
+ robby:zVCvOmrpDiPBE:rob
  @end example
  
  (The passwords are encrypted according to the standard
***************
*** 2424,2433 ****
  
  @sc{cvs} can also fall back to use system authentication.
  When authenticating a password, the server first checks
! for the user in the @file{$CVSROOT/CVSROOT/passwd}
  file.  If it finds the user, it will use that entry for
  authentication as described above.  But if it does not
! find the user, or if the @sc{cvs} @file{passwd} file
  does not exist, then the server can try to authenticate
  the username and password using the operating system's
  user-lookup routines (this "fallback" behavior can be
--- 2433,2442 ----
  
  @sc{cvs} can also fall back to use system authentication.
  When authenticating a password, the server first checks
! for the user in the @file{/etc/cvs.passwd}
  file.  If it finds the user, it will use that entry for
  authentication as described above.  But if it does not
! find the user, or if the @sc{cvs} @file{cvs.passwd} file
  does not exist, then the server can try to authenticate
  the username and password using the operating system's
  user-lookup routines (this "fallback" behavior can be
***************
*** 2441,2458 ****
  authentication security} for more on this.
  
  Right now, the only way to put a password in the
! @sc{cvs} @file{passwd} file is to paste it there from
  somewhere else.  Someday, there may be a @code{cvs
  passwd} command.
  
- Unlike many of the files in @file{$CVSROOT/CVSROOT}, it
- is normal to edit the @file{passwd} file in-place,
- rather than via @sc{cvs}.  This is because of the
- possible security risks of having the @file{passwd}
- file checked out to people's working copies.  If you do
- want to include the @file{passwd} file in checkouts of
- @file{$CVSROOT/CVSROOT}, see @ref{checkoutlist}.
- 
  @c We might also suggest using the @code{htpasswd} command
  @c from freely available web servers as well, but that
  @c would open up a can of worms in that the users next
--- 2450,2459 ----
  authentication security} for more on this.
  
  Right now, the only way to put a password in the
! @sc{cvs} @file{cvs.passwd} file is to paste it there from
  somewhere else.  Someday, there may be a @code{cvs
  passwd} command.
  
  @c We might also suggest using the @code{htpasswd} command
  @c from freely available web servers as well, but that
  @c would open up a can of worms in that the users next
***************
*** 2594,2626 ****
  @c *very* easy to accidentally screw up a setup of this
  @c type).
  
! Note that because the @file{$CVSROOT/CVSROOT} directory
! contains @file{passwd} and other files which are used
! to check security, you must control the permissions on
! this directory as tightly as the permissions on
! @file{/etc}.  The same applies to the @file{$CVSROOT}
! directory itself and any directory
! above it in the tree.  Anyone who has write access to
! such a directory will have the ability to become any
! user on the system.  Note that these permissions are
! typically tighter than you would use if you are not
! using pserver.
! @c TODO: Would be really nice to document/implement a
! @c scheme where the CVS server can run as some non-root
! @c user, e.g. "cvs".  CVSROOT/passwd would contain a
! @c bunch of entries of the form foo:xxx:cvs (or the "cvs"
! @c would be implicit).  This would greatly reduce
! @c security risks such as those hinted at in the
! @c previous paragraph.  I think minor changes to CVS
! @c might be required but mostly this would just need
! @c someone who wants to play with it, document it, &c.
! 
! In summary, anyone who gets the password gets
! repository access (which may imply some measure of general system
! access as well).  The password is available to anyone
! who can sniff network packets or read a protected
! (i.e., user read-only) file.  If you want real
! security, get Kerberos.
  
  @node GSSAPI authenticated
  @subsection Direct connection with GSSAPI
--- 2595,2612 ----
  @c *very* easy to accidentally screw up a setup of this
  @c type).
  
! Note that because the @file{/etc/cvs.passwd} file contains
! DES encrypted passwords, you must make it only readable
! root.
! 
! Anyone who gets the password gets repository access, and
! some measure of general system access as well.
! The password is available to anyone who can sniff network
! packets or read a protected (i.e., user read-only) file.
! It is therefore desirable to always use different passwords
! for users in /etc/cvs.passwd and disable write access to
! anyone else.
! If you want real security, get Kerberos.
  
  @node GSSAPI authenticated
  @subsection Direct connection with GSSAPI
***************
*** 2823,2833 ****
  those users listed in it have write access, and
  everyone else has read-only access (of course, even the
  read-only users still need to be listed in the
! @sc{cvs} @file{passwd} file).  The
  @file{writers} file has the same format as the
  @file{readers} file.
  
!         Note: if your @sc{cvs} @file{passwd}
  file maps cvs users onto system users (@pxref{Password
  authentication server}), make sure you deny or grant
  read-only access using the @emph{cvs} usernames, not
--- 2809,2819 ----
  those users listed in it have write access, and
  everyone else has read-only access (of course, even the
  read-only users still need to be listed in the
! @sc{cvs} @file{cvs.passwd} file).  The
  @file{writers} file has the same format as the
  @file{readers} file.
  
!         Note: if your @sc{cvs} @file{cvs.passwd}
  file maps cvs users onto system users (@pxref{Password
  authentication server}), make sure you deny or grant
  read-only access using the @emph{cvs} usernames, not
diff -rc cvs-1.11.1p1/src/cvs.h cvs-1.10.8-patched/src/cvs.h
*** cvs-1.11.1p1/src/cvs.h      Tue Apr 24 14:14:53 2001
--- cvs-1.10.8-patched/src/cvs.h        Wed Aug 15 11:37:54 2001
***************
*** 192,197 ****
--- 192,202 ----
  
  /* Other CVS file names */
  
+ /* The use of CVSROOTADM_PASSWD is a security risk.  Instead the passwd
+    file should be put in /etc.  This file has a slightly different format:
+    It allows to list multiple repositories and their password lists. */
+ #define CVS_PASSWD            "/etc/cvs.passwd"
+ 
  /* Files go in the attic if the head main branch revision is dead,
     otherwise they go in the regular repository directories.  The whole
     concept of having an attic is sort of a relic from before death
diff -rc cvs-1.11.1p1/src/root.c cvs-1.10.8-patched/src/root.c
*** cvs-1.11.1p1/src/root.c     Thu Apr 19 15:45:33 2001
--- cvs-1.10.8-patched/src/root.c       Wed Aug 15 11:42:52 2001
***************
*** 237,261 ****
      root_allow_size = 0;
  }
  
  int
  root_allow_ok (arg)
      char *arg;
  {
!     int i;
  
      if (root_allow_count == 0)
      {
!       /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10
!          or later without reading the documentation about
!          --allow-root.  Printing an error here doesn't disclose any
           particularly useful information to an attacker because a
           CVS server configured in this way won't let *anyone* in.  */
  
        /* Note that we are called from a context where we can spit
           back "error" rather than waiting for the next request which
           expects responses.  */
!       printf ("\
! error 0 Server configuration missing --allow-root in inetd.conf\n");
        error_exit ();
      }
  
--- 237,349 ----
      root_allow_size = 0;
  }
  
+ static int
+ parse_cvs_passwd_roots ()
+ {
+     FILE *fp_passwd;
+     char *line;
+     size_t line_allocated;
+     int line_number, blank_line;
+ 
+     fp_passwd = CVS_FOPEN (CVS_PASSWD, "r");
+ 
+     /* For backwards compatibility we check if /etc/cvs.passwd exists,
+      * and if not, we continue as before. */
+     if (fp_passwd == NULL)
+         return 0;
+ 
+     /* It is not possible to give a warning here because that confuses
+      * the clients:
+      * jolan% cvs commit CVSROOT
+      * cvs [commit aborted]: unrecognized auth response from jolan: cvs 
pserver: warning: --allow-root is depricated/ignored
+      * jolan%
+      */
+ #if 0
+     /* If /etc/cvs.passwd exists, then we check if --allow-root was
+      * used and give a warning when it was. */
+     if (root_allow_size > 0)
+         error (0, 0, "warning: --allow-root is depricated/ignored");
+ #endif
+ 
+     /* Next allow_root_vector is emptied in order to ignore these possible
+      * --allow-root commandline arguments. */
+     root_allow_free ();
+ 
+     if (trace)
+         (void) fprintf (stderr, " -> parse_cvs_passwd()\n");
+ 
+     /* Finally, /etc/cvs.passwd is parsed, looking for allowed roots. */
+     line = NULL;
+     line_allocated = 0;
+     line_number = 0;
+     blank_line = 1;
+     while (getline (&line, &line_allocated, fp_passwd) >= 0)
+     {
+         line_number++;
+ 
+         /* skip lines starting with # */
+         if (line[0] == '#')
+             continue;
+ 
+       /* If the preceding line was blank and this one starts with a '/',
+        * then interpret it as a root path */
+       if (blank_line && line[0] == '/')
+       {
+           char *cp;
+           /* find end of repository path */
+           for (cp = line; *cp && !isspace (*cp) && isprint (*cp); cp++)
+               ;
+           /* Allow path to be terminated by a space */
+           if (*cp == '\0' || isspace (*cp))
+           {
+               *cp = '\0';
+               root_allow_add (line);
+           }
+           blank_line = 0;
+       }
+       else
+       {
+           char *cp;
+           /* skip whitespace at beginning of line */
+           for (cp = line; *cp && isspace (*cp); cp++)
+               ;
+             /* if *cp is null, the whole line was blank */
+             blank_line = (*cp == '\0');
+             if (blank_line)
+               continue;
+       }
+     }
+     return 1;
+ }
+ 
  int
  root_allow_ok (arg)
      char *arg;
  {
!     static int cvs_passwd_parsed = 0, cvs_passwd_exists = 0;
!     unsigned int i;
! 
!     if (!cvs_passwd_parsed)
!     {
!       cvs_passwd_exists = parse_cvs_passwd_roots();
!       cvs_passwd_parsed = 1;
!     }
  
      if (root_allow_count == 0)
      {
!       /* Possibly someone upgraded from CVS before 1.9.10 to 1.9.10
!          or later without reading the documentation about --allow-root
!          or /etc/cvs.passwd.  Printing an error here doesn't disclose any
           particularly useful information to an attacker because a
           CVS server configured in this way won't let *anyone* in.  */
  
        /* Note that we are called from a context where we can spit
           back "error" rather than waiting for the next request which
           expects responses.  */
!       printf ("error 0 Server configuration missing %s\n",
!             cvs_passwd_exists
!           ? "repositories in cvs.passwd"
!           : "--allow-root in inetd.conf");
        error_exit ();
      }
  
diff -rc cvs-1.11.1p1/src/server.c cvs-1.10.8-patched/src/server.c
*** cvs-1.11.1p1/src/server.c   Thu Apr 19 15:34:04 2001
--- cvs-1.10.8-patched/src/server.c     Wed Aug 15 11:13:24 2001
***************
*** 5374,5380 ****
  
  
  /* 
!  * 0 means no entry found for this user.
   * 1 means entry found and password matches (or found password is empty)
   * 2 means entry found, but password does not match.
   *
--- 5374,5380 ----
  
  
  /* 
!  * 0 means no entry found for this user/repository.
   * 1 means entry found and password matches (or found password is empty)
   * 2 means entry found, but password does not match.
   *
***************
*** 5397,5402 ****
--- 5397,5416 ----
      int found_it = 0;
      int namelen;
  
+     /* First we try /etc/cvs.passwd because if it exists, we should
+      * ignore CVSROOT/passwd. */
+ 
+     filename = xmalloc (strlen (CVS_PASSWD) + 1);
+     strcpy(filename, CVS_PASSWD);
+ 
+     fp = CVS_FOPEN (filename, "r");
+     if (fp == NULL)
+     {
+ 
+         /* Now try if the user is still using the depricated
+          * CVSROOTADM_PASSWD method of authenticating. */
+ /* NOT INDENTED YET: */
+ 
      /* We don't use current_parsed_root->directory because it hasn't been set 
yet
       * -- our `repository' argument came from the authentication
       * protocol, not the regular CVS protocol.
***************
*** 5415,5429 ****
      fp = CVS_FOPEN (filename, "r");
      if (fp == NULL)
      {
        if (!existence_error (errno))
!           error (0, errno, "cannot open %s", filename);
        return 0;
      }
  
      /* Look for a relevant line -- one with this user's name. */
      namelen = strlen (username);
      while (getline (&linebuf, &linebuf_len, fp) >= 0)
      {
        if ((strncmp (linebuf, username, namelen) == 0)
            && (linebuf[namelen] == ':'))
          {
--- 5429,5484 ----
      fp = CVS_FOPEN (filename, "r");
      if (fp == NULL)
      {
+         /* Complain about CVS_PASSWD only: CVSROOTADM_PASSWD is depricated. */
        if (!existence_error (errno))
!           error (0, errno, "cannot open %s", CVS_PASSWD);
        return 0;
      }
+     }  /* END OF MISSING INDENTATION */
+     else
+     {
+         /* Now skip to the correct cvs root path, if any. */
+ 
+         int found = 0;
+         while (getline (&linebuf, &linebuf_len, fp) >= 0)
+       {
+           /* Skip comments */
+           if (linebuf[0] == '#')
+               continue;
+ 
+             if (linebuf[0] == '/')
+           {
+               char *cp;
+ 
+               /* Repository path can end on a space in the file */
+               for (cp = linebuf; *cp && !isspace(*cp); cp++)
+                   ;
+               *cp = '\0';
+ 
+               if (strcmp(repository, linebuf) == 0)
+               {
+                   found = 1;
+                   break;
+               }
+           }
+       }
+       if (!found)
+           goto repository_not_found;          /* Don't try to read more */
+     }
  
      /* Look for a relevant line -- one with this user's name. */
      namelen = strlen (username);
      while (getline (&linebuf, &linebuf_len, fp) >= 0)
      {
+         /* Skip comments */
+       if (linebuf[0] == '#')
+           continue;
+ 
+         /* If the line starts with a '/', then we bumped
+        * into the next root path. */
+       if (linebuf[0] == '/')
+           break;
+ 
        if ((strncmp (linebuf, username, namelen) == 0)
            && (linebuf[namelen] == ':'))
          {
***************
*** 5431,5436 ****
--- 5486,5492 ----
            break;
          }
      }
+ repository_not_found:
      if (ferror (fp))
        error (0, errno, "cannot read %s", filename);
      if (fclose (fp) < 0)
***************
*** 5621,5627 ****
           letting you in if it won't say why, and I am not convinced
           that the potential information disclosure to an attacker
           outweighs this.  */
!       printf ("error 0 no such user %s in CVSROOT/passwd\n", username);
  
        /* I'm doing this manually rather than via error_exit ()
           because I'm not sure whether we want to call server_cleanup.
--- 5677,5686 ----
           letting you in if it won't say why, and I am not convinced
           that the potential information disclosure to an attacker
           outweighs this.  */
!       /* If the deprecated CVSROOT/passwd is being used, we still print
!          this error about CVS_PASSWD; we don't want the user to think
!          it is ok to continue to use CVSROOT/passwd. */
!       printf ("error 0 no such user %s in %s\n", username, CVS_PASSWD);
  
        /* I'm doing this manually rather than via error_exit ()
           because I'm not sure whether we want to call server_cleanup.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]