Next: , Previous: Starting A Repository, Up: Repository Administration

The Password-Authenticating Server

Before running through the steps needed to set up the password server, let's examine how such connections work in the abstract. When a remote CVS client uses the :pserver: method to connect to a repository, the client is actually contacting a specific port number on the server machine – specifically, port number 2401 (which is 49 squared, if you like that sort of thing). Port 2401 is the designated default port for the CVS pserver, although one could arrange for a different port to be used as long as both client and server agree on it.

The CVS server is not actually waiting for connections at that port – the server won't get started up until a connection actually arrives. Instead, the Unix inetd (InterNET Daemon) program is listening on that port, and needs to know that when it receives a connection request there, it should start up the CVS server and connect it to the incoming client.

This is accomplished by modifying inetd's configuration files: /etc/services and /etc/inetd.conf. The services file maps raw port numbers to service names and then inetd.conf tells inetd what to do for a given service name.

First, put a line like this into /etc/services (after checking to make sure it isn't already there):

     cvspserver	2401/tcp

Then in /etc/inetd.conf, put this:

     cvspserver stream tcp nowait root /usr/local/bin/cvs cvs \
        --allow-root=/usr/local/newrepos pserver

(In the actual file, this should be all one long line, with no backslash.) If your system uses tcpwrappers, you may want to use something like this instead:

     cvspserver stream tcp nowait root /usr/sbin/tcpd /usr/local/bin/cvs \
        --allow-root=/usr/local/newrepos pserver

Now, restart inetd so it notices the changes to its configuration files (if you don't know how to restart the daemon, just reboot the machine – that will work too).

That's enough to permit connections, but you'll also want to set up special CVS passwords – separate from the users' regular login passwords – so people can access the repository without compromising overall system security.

The CVS password file is CVSROOT/passwd in the repository. It was not created by default when you ran cvs init, because CVS doesn't know for sure that you'll be using pserver. Even if the password file had been created, CVS would have no way of knowing what usernames and passwords to create. So, you'll have to create one yourself; here's a sample CVSRoot/passwd file:


The format is as simple as it looks. Each line is:


The extra colon followed by an optional system username tells CVS that connections authenticated with USERNAME should run as the system account SYSTEM_USERNAME – in other words, that CVS session would only be able to do things in the repository that someone logged in as SYSTEM_USERNAME could do.

If no system username is given, USERNAME must match an actual login account name on the system, and the session will run with that user's permissions. In either case, the encrypted password should not be the same as the user's actual login password. It should be an independent password used only for CVS pserver connections.

The password is encrypted using the same algorithm as the standard Unix system passwords stored in /etc/passwd. You may be wondering at this point, how does one acquire an encrypted version of a password? For Unix system passwords, the passwd command takes care of the encryption in /etc/passwd for you. Unfortunately, there is no corresponding cvs passwd command (it has been proposed several times, but no one's gotten around to writing it – perhaps you'll do it?).

This is an inconvenience, but only a slight one. If nothing else, you can always temporarily change a regular user's system password using passwd, cut and paste the encrypted text from /etc/passwd into CVSROOT/passwd, and then restore the old password (note that on some systems, the encrypted passwords are found in /etc/shadow and are readable only by root.)

That scheme is workable but rather cumbersome. It would be much easier to have a command-line utility that takes a plain text password as its argument and outputs the encrypted version. Here is such a tool, written in Perl:

     srand (time());
     my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
     my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
     my $plaintext = shift;
     my $crypttext = crypt ($plaintext, $salt);
     print "${crypttext}\n";

I keep the preceding script in /usr/local/bin/

     floss$ ls -l /usr/local/bin/
     -rwxr-xr-x   1   root   root   265  Jun 14 20:41 /usr/local/bin/
     floss$ "some text"

If I took the output of this example and used it to create the following entry in CVSROOT/passwd


then someone could connect to the repository with the following command:

     remote$ cvs -d login

They could then type some text as their password and thereafter be able to execute CVS commands with the same access privileges as the system user craig.

If someone attempts to authenticate with a username and password that don't appear in CVSROOT/passwd, CVS will check to see if that username and password are present in /etc/passwd. If they are (and if the password matches, of course), CVS will grant access. It behaves this way for the administrator's convenience, so that separate CVSROOT/passwd entries don't have to be set up for regular system users. However, this behavior is also a security hole, because it means that if one of those users does connect to the CVS server, her regular login password will have crossed over the network in cleartext, potentially vulnerable to the eyes of password sniffers. A bit further on, you'll learn how to turn off this "fallback" behavior, so that CVS consults only its own passwd file. Whether you leave it on or off, you should probably force any CVS users who also have login accounts to maintain different passwords for the two functions.

Although the passwd file authenticates for the whole repository, with a little extra work you can still use it to grant project-specific access. Here's one method:

Suppose you want to grant some remote developers access to project foo, and others access to project bar, and you don't want developers from one project to have commit access to the other. You can accomplish this by creating project-specific user accounts and groups on the system and then mapping to those accounts in the CVSROOT/passwd file.

Here's the relevant excerpt from /etc/passwd

     cvs-foo:*:600:600:Public CVS Account for Project Foo:/usr/local/cvs:/bin/false
     cvs-bar:*:601:601:Public CVS Account for Project Bar:/usr/local/cvs:/bin/false

and from /etc/group


and, finally, CVSROOT/passwd:


Some of the CVS usernames map onto the system user account cvs-foo and some onto cvs-bar. Because CVS runs under the user ID of the system account, you just have to make sure that the relevant parts of the repository are writeable only by the appropriate users and groups. If you just make sure that the user accounts are locked down pretty tight (no valid login password, /bin/false as the shell), then this system is reasonably secure (but see later in this chapter about CVSROOT permissions!). Also, CVS does record changes and log messages under the CVS username, not the system username, so you can still tell who is responsible for a given change.

Karl Fogel wrote this book. Buy a printed copy via his homepage at

copyright  ©  May 17 2021 sean dreilinger url: