I've attached a patch for cvs-1.11 which adds internal user and
authorization capability to CVS.  It is primarily based on this patch
for 1.10.3 from Corey Minyard:

   http://www.cvshome.org/cyclic/cvs/dev-access.txt

I have made the following modifications to Corey's original patch:

  * Changed src/setpass.c to use the builtin RCS functions instead of
    executing external binaries.

  * Changed auth code in src/perms.c to inherit directory owner and
    permissions from parent directories.

  * Because permissions are now inheritted from the parent when not
    explicitly set in the child, doing a "cvs add" on a directory no
    longer creates an empty perms file for that directory.  (It still
    sets the owner of the new directory, however.)

  * When doing a "cvs import" of a new directory, if the directory
    does not lie under a previously existing subdirectory of the CVS
    root, an empty perms file will be written.  (This prevents the
    perms from being inheritted from the root directory of the
    repository, so that using "ALL:c" in the root directory can allow
    any user to create a new hierarchy without opening up permissions
    in the new hierarchy itself.)

  * Fixed a bug in deluser() which caused the admin's account to be
    deleted instead of the user specified on the commandline.

  * Tweaked configure.in to allow --with-gssapi to work with MIT
    krb5-1.2.1.

  * Added very basic syslog() support.

  * Fixed "-D" flag to work with --allow-root option.

We've been using a slightly older version of this patch for 1.10.8 in
Production for several months now with no problems, but I'd appreciate
any feedback 

-- 
Mark D. Roth <[EMAIL PROTECTED]>
http://www.feep.net/~roth/
diff -urN cvs-1.11/ChangeLog cvs-1.11-auth/ChangeLog
--- cvs-1.11/ChangeLog  Fri Sep  8 13:28:02 2000
+++ cvs-1.11-auth/ChangeLog     Mon Nov  6 00:30:07 2000
@@ -205,6 +205,10 @@
        * TODO (195): Check in a few clarifications from Andrew Tridgell,
        the rsync author.
 
+1998-11-19  Corey Minyard  <[EMAIL PROTECTED]>
+
+        * Added code to handle permissions inside the CVS repository.
+
 1998-11-11  Jim Kingdon
 
        * HACKING: Change prep.ai.mit.edu to gnu.org.
diff -urN cvs-1.11/configure.in cvs-1.11-auth/configure.in
--- cvs-1.11/configure.in       Tue Aug  1 11:13:25 2000
+++ cvs-1.11-auth/configure.in  Mon Nov  6 00:30:02 2000
@@ -209,7 +209,13 @@
   if test "$ac_cv_header_gssapi_h" = "yes"; then
     LIBS="$LIBS -L$GSSAPI/lib -lgssapi -lkrb5 -lasn1 -ldes -lroken"
   else
-    LIBS="$LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err"
+    save_LIBS="$LIBS"
+    AC_CHECK_LIB(k5crypto, krb5_encrypt_data)
+    if test "$ac_cv_lib_k5crypto_krb5_encrypt_data" = "yes"; then
+      LIBS="$save_LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err"
+    else
+      LIBS="$save_LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err"
+    fi
   fi
   save_CPPFLAGS=$CPPFLAGS
   CPPFLAGS="-I$GSSAPI/include $CPPFLAGS"
diff -urN cvs-1.11/doc/cvs.texinfo cvs-1.11-auth/doc/cvs.texinfo
--- cvs-1.11/doc/cvs.texinfo    Tue Aug 29 20:16:32 2000
+++ cvs-1.11-auth/doc/cvs.texinfo       Mon Nov  6 00:30:07 2000
@@ -140,6 +140,7 @@
 @menu
 * Overview::                    An introduction to CVS
 * Repository::                  Where all your sources are stored
+* Security::                    Setting up local security for CVS
 * Starting a new project::      Starting a project with CVS
 * Revisions::                   Numeric and symbolic names for revisions
 * Branching and merging::       Diverging/rejoining branches of development
@@ -2039,6 +2040,7 @@
 * GSSAPI authenticated::        Direct connections using GSSAPI
 * Kerberos authenticated::      Direct connections with kerberos
 * Connecting via fork::         Using a forked @code{cvs server} to connect
+* Changing the base path::      Using @samp{-D} to change the base path
 @end menu
 
 @node Server requirements
@@ -2734,6 +2736,181 @@
 by default, or the value of the @code{CVS_SERVER}
 environment variable.
 
+@node Changing the base path
+@subsection Using @samp{-D} to change the base path
+
+The @samp{-D} option allows all the files to be based
+in some directory.  So, for instance, if the command
+was:
+
+@example
+/usr/local/bin/cvs -L -b /usr/local/bin -D /home/cvs pserver
+@end example
+
+then if the user had the following @code{CVSROOT} variable:
+
+@example
+:pserver:myname@@hostname:/ver1
+@end example
+
+then the repository would actually be in:
+
+@example
+/home/cvs/ver1
+@end example
+
+This is convenient because it allows the repository to
+be moved without affecting the user's environment
+variables.  It also makes the user's environment
+variables shorter and more convenient.
+
+@c ---------------------------------------------------------------------
+@node Security
+@chapter Security
+@cindex Security (intro)
+@cindex Security, example
+
+A remote @sc{cvs} @dfn{repository} can be set up to
+have its own security system outside of the standard
+security provided by the system.  This allows the
+administrator to provide @sc{cvs} accounts without
+having to set up accounts on the system.  It also
+provides some directory-level security.
+
+This feature has only been checked with pserver.  It
+may or may not work with kerberos.
+
+To enable this feature, add the @samp{-L} option to
+the invokation from the @code{inetd.conf} file.  It
+would be best to run the command as some user besides
+root that will own the repository.
+
+The provided security will only work at a directory
+level.  Files cannot be individually protected with
+this feature.
+
+@menu
+* Setting up security::         How to set up security
+* User maintenance::            How to add and delete users
+* Setting permissions::         Setting permissions for directories
+* Security files::              Files in the repository
+* Passwords::                   Setting and changing passwords
+@end menu
+
+@node Setting up security
+@section How to set up security
+
+Security is rather easy to set up.  First setup the
+@code{inetd.conf} file to invoke cvs with the @samp{-L}
+option.  Changing the base path as described in
+@pxref{Changing the base path} can be very convenient.
+The command should run as the user that owns the
+repository (not root).  With the @samp{-L} option,
+@sc{cvs} will run as the user that invokes it, not the
+user.  In fact, the system user names and passwords are
+never checked with this option.  The built-in password
+file is the only one used.
+
+Next, running as the user that will own the repository,
+create the repository.  Pick a user name to be the
+administrator.  In the @code{CVSROOT} directory, create
+a @file{passwd} file and add the administrator as
+described in @pxref{Password authentication server}.
+You have to figure out a clever way of setting the
+first password, such as using the @file{passwd} file on
+your system to get the first password in for the admin.
+
+Now create a file named @file{admin} and put the
+administrators name in it on the first line.  Now check
+both of these files in using RCS and add them to the
+@file{checkoutlist} file as described in that file's
+instructions.  Don't forget to check out the files
+again after they have been checked in.
+
+At this point, the administrator is ready to go.  Set
+the environment variable to the repository (using the
+pserver method) and log in.
+
+@node User maintenance
+@section How to add and delete users
+
+The @sc{cvs} setpass command can be user to add new
+users with the @samp{-a} option.  Only the
+administrator can do this.  If the setpass command is
+given a user name as a parameter and the user does not
+already exist, the user will be added with the password
+the setpass command prompts for.
+
+To delete a user, the administrator can use the
+@sc{cvs} deluser command.  Note that deleting a user
+does not remove them from any user permissions, so it
+is a relatively safe operation.
+
+@node Setting permissions
+@section Setting permissions for directories
+
+The owner of a file and the administrator can modify
+directory permissions.  Three permissions are available:
+
+@example
+c - create - create and delete files and directories
+r - read   - retrieve file and look at information
+w - write  - modify files and change information
+@end example
+
+The owner and administrator have all these
+capabilities.  Other users can be granted these
+capabilities using the @sc{cvs} setperm command.  It
+takes a user name and a set of permissions separated by
+a colon then a list of directories to grant permissions
+for.  So for instance:
+
+@example
+cvs setperm theuser:rw dir1 dir2 dir3
+@end example
+
+will grant the user named @code{theuser} read and write
+access to the three specified directories.  Specifying
+just the user or nothing after the colon will delete
+all the user's permissions.
+
+To view the current permissions the @sc{cvs} listperm
+command can be used.  It will show the owner and all
+the users that have permissions in the given
+directories.
+
+If the user name @code{ALL} is granted permissions,
+those permissions will be extended to all users of the
+repository.  This is an easy way to give everyone read
+access to a directory, for instance.  If a users is
+specifically in the permissions, the @code{ALL} will
+not be applied to them, just the permissions in their
+entry.
+
+The owner or a directory can be reassigned using the
+@sc{cvs} setowner command.
+
+@node Security files
+@section Files in the repository
+
+Every directory in the repository will contain two
+security files: the @file{owner} file and the
+@file{perms} file.  The @file{owner} file contains the
+name of the directory's owner.  The @file{perms} file
+contains the permissions for other users.  These files
+will not be checked out in any way, they stay internal
+to the repository.  The repository owner can modify
+these file if necessary; their format is
+straightforward.
+
+@node Passwords
+@section Setting and changing passwords
+
+Users can use the @sc{cvs} setpass command with no
+parameters to modify their passwords.  The
+administrator can specify a user on the command line
+to change their password.
+
 @c ---------------------------------------------------------------------
 @node Read-only access
 @section Read-only repository access
@@ -7470,6 +7647,11 @@
 * rdiff::                       'patch' format diffs between releases
 * release::                     Indicate that a directory is no longer in use
 * update::                      Bring work tree in sync with repository
+* setpass::                     Modify a user's password or create a user
+* deluser::                     Delete a user
+* setperm::                     Set a user's permissions for a directory
+* setowner::                    Change a directory's owner
+* listperm::                    Show a directory's permissions
 @end menu
 
 @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -9115,6 +9297,106 @@
 @example
 $ cvs diff -u | less
 @end example
+
+@node setpass
+@appendixsec Modify a user's password or create a user
+@cindex setpass (subcommand)
+
+@itemize @bullet
+@item
+setpass [-a] [username]
+@item
+Requires: repository
+@item
+Changes: password file
+@end itemize
+
+If invoked without a username, this will prompt the
+user for a new password and change the password to the
+new one specified by the user.
+
+If invoked by the administrator with a username, it
+allows another user's password to be modified.
+
+If invoked by the administrator with the @samp{-a}
+option, it allows addition of new users.
+
+This only works with remote pserver repositories.
+
+@node deluser
+@appendixsec Delete a user
+@cindex deluser (subcommand)
+
+@itemize @bullet
+@item
+deluser username
+@item
+Requires: repository
+@item
+Changes: password file
+@end itemize
+
+Allows the administrator to delete users from the
+repository.
+
+This only works with remote pserver repositories.
+
+@node setperm
+@appendixsec Set a user's permissions for a directory
+@cindex setperm (subcommand)
+
+@itemize @bullet
+@item
+setperm username:perm directory [directory ...]
+@item
+Requires: repository, directories
+@item
+Changes: directories' permission files
+@end itemize
+
+Allows the owner of a directory to allow or disallow
+access to a repository directory to other users of the
+repository.
+
+The @samp{perm} field can only contain three
+characters: c, r, and w.  These character mean:
+
+@example
+c - create - create and delete files and directories
+r - read   - retrieve file and look at information
+w - write  - modify files and change information
+@end example
+
+
+@node setowner
+@appendixsec Change a directory's owner
+@cindex setowner (subcommand)
+
+@itemize @bullet
+@item
+setowner username directory [directory ...]
+@item
+Requires: repository, directories
+@item
+Changes: directories' owner files
+@end itemize
+
+Allows the owner of a directory to assign a new owner
+to directories.
+
+@node listperm
+@appendixsec Show a directory's permissions
+@cindex listperm (subcommand)
+
+@itemize @bullet
+@item
+listperm directory [directory ...]
+@item
+Requires: repository, directories
+@end itemize
+
+Lists the owner and other users that have permissions
+in the directory.
 
 @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 @node export
diff -urN cvs-1.11/doc/cvsclient.texi cvs-1.11-auth/doc/cvsclient.texi
--- cvs-1.11/doc/cvsclient.texi Tue Aug 29 20:16:32 2000
+++ cvs-1.11-auth/doc/cvsclient.texi    Mon Nov  6 00:30:07 2000
@@ -1080,6 +1080,11 @@
 @itemx watchers \n
 @itemx editors \n
 @itemx annotate \n
+@itemx setowner \n
+@itemx setperm \n
+@itemx setpass \n
+@itemx listperm \n
+@itemx deluser \n
 Response expected: yes.  Actually do a cvs command.  This uses any
 previous @code{Argument}, @code{Directory}, @code{Entry}, or
 @code{Modified} requests, if they have been sent.  The
diff -urN cvs-1.11/src/Makefile.in cvs-1.11-auth/src/Makefile.in
--- cvs-1.11/src/Makefile.in    Fri Sep  8 13:28:03 2000
+++ cvs-1.11-auth/src/Makefile.in       Mon Nov  6 00:30:07 2000
@@ -43,7 +43,8 @@
 lock.c log.c login.c logmsg.c main.c mkmodules.c modules.c myndbm.c no_diff.c \
 parseinfo.c patch.c rcs.c rcscmds.c recurse.c release.c remove.c repos.c \
 root.c rtag.c scramble.c server.c status.c subr.c filesubr.c run.c \
-tag.c update.c watch.c wrapper.c vers_ts.c version.c zlib.c
+tag.c update.c watch.c wrapper.c vers_ts.c version.c zlib.c setowner.c \
+setperm.c perms.c listperm.c setpass.c
 
 OBJECTS = add.o admin.o buffer.o checkin.o checkout.o classify.o client.o \
 commit.o create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \
@@ -52,7 +53,8 @@
 parseinfo.o patch.o rcs.o rcscmds.o recurse.o release.o remove.o repos.o \
 root.o rtag.o scramble.o server.o status.o tag.o update.o \
 watch.o wrapper.o vers_ts.o \
-subr.o filesubr.o run.o version.o error.o zlib.o
+subr.o filesubr.o run.o version.o error.o zlib.o setowner.o setperm.o \
+perms.o listperm.o setpass.o
 
 HEADERS = buffer.h cvs.h rcs.h hardlink.h hash.h myndbm.h \
        update.h server.h client.h error.h fileattr.h edit.h watch.h
diff -urN cvs-1.11/src/add.c cvs-1.11-auth/src/add.c
--- cvs-1.11/src/add.c  Tue Jun 27 23:15:47 2000
+++ cvs-1.11-auth/src/add.c     Mon Nov  6 00:30:07 2000
@@ -333,6 +333,9 @@
                       CVSNULLREPOS) == 0)
            error (1, 0, "cannot add to %s", repository);
 
+        if (! verify_create (repository))
+           error (1, 0, "User '%s' cannot change %s", CVS_Username, repository);
+ 
        entries = Entries_Open (0, NULL);
 
        finfo.repository = repository;
@@ -769,6 +772,8 @@
                goto out;
            }
            (void) umask (omask);
+           if (local_security)
+              change_owner(rcsdir, CVS_Username);
        }
 
        /* Now set the default file attributes to the ones we inherited
diff -urN cvs-1.11/src/admin.c cvs-1.11-auth/src/admin.c
--- cvs-1.11/src/admin.c        Wed Jul 26 14:09:01 2000
+++ cvs-1.11-auth/src/admin.c   Mon Nov  6 00:30:07 2000
@@ -491,7 +491,7 @@
     err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
                           (DIRLEAVEPROC) NULL, (void *)&admin_data,
                           argc, argv, 0,
-                          W_LOCAL, 0, 0, (char *) NULL, 1);
+                          W_LOCAL, 0, 0, (char *) NULL, 1, verify_admin);
     Lock_Cleanup ();
 
  return_it:
@@ -891,5 +891,4 @@
 {
     if (!quiet)
        error (0, 0, "Administrating %s", update_dir);
-    return (R_PROCESS);
 }
diff -urN cvs-1.11/src/checkout.c cvs-1.11-auth/src/checkout.c
--- cvs-1.11/src/checkout.c     Tue Jun 27 23:15:47 2000
+++ cvs-1.11-auth/src/checkout.c        Mon Nov  6 00:30:07 2000
@@ -537,6 +537,12 @@
     Sanitize_Repository_Name (repository);
 
 
+    if (! verify_read(repository))
+    {
+       error (0, 0, "User %s cannot access %s", CVS_Username, argv[0]);
+       return (1);
+    }
+
     /* save the original value of preload_update_dir */
     if (preload_update_dir != NULL)
        oldupdate = xstrdup (preload_update_dir);
diff -urN cvs-1.11/src/client.c cvs-1.11-auth/src/client.c
--- cvs-1.11/src/client.c       Thu Jul  6 11:20:41 2000
+++ cvs-1.11-auth/src/client.c  Mon Nov  6 00:30:08 2000
@@ -5520,7 +5520,8 @@
     err = start_recursion
        (send_fileproc, send_filesdoneproc,
         send_dirent_proc, send_dirleave_proc, (void *) &args,
-        argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0);
+        argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0,
+         (PERMPROC) NULL);
     if (err)
        error_exit ();
     if (toplevel_repos == NULL)
diff -urN cvs-1.11/src/commit.c cvs-1.11-auth/src/commit.c
--- cvs-1.11/src/commit.c       Wed Jul 26 14:29:01 2000
+++ cvs-1.11-auth/src/commit.c  Mon Nov  6 00:30:08 2000
@@ -454,7 +454,7 @@
                               find_dirent_proc, (DIRLEAVEPROC) NULL,
                               (void *)&find_args,
                               argc, argv, local, W_LOCAL, 0, 0,
-                              (char *)NULL, 0);
+                              (char *)NULL, 0, (PERMPROC) NULL);
        if (err)
            error (1, 0, "correct above errors first!");
 
@@ -640,7 +640,8 @@
      */
     err = start_recursion (check_fileproc, check_filesdoneproc,
                           check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc,
-                          argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1);
+                          argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1,
+                          verify_write);
     if (err)
     {
        Lock_Cleanup ();
@@ -655,7 +656,7 @@
        err = start_recursion (commit_fileproc, commit_filesdoneproc,
                               commit_direntproc, commit_dirleaveproc, NULL,
                               argc, argv, local, W_LOCAL, aflag, 0,
-                              (char *) NULL, 1);
+                              (char *) NULL, 1, verify_write);
 
     /*
      * Unlock all the dirs and clean up
diff -urN cvs-1.11/src/cvs.h cvs-1.11-auth/src/cvs.h
--- cvs-1.11/src/cvs.h  Sat Jul  8 14:57:21 2000
+++ cvs-1.11-auth/src/cvs.h     Mon Nov  6 00:30:08 2000
@@ -383,6 +383,22 @@
 extern char *CVSroot_username; /* the username or NULL if method == local */
 extern char *CVSroot_hostname; /* the hostname or NULL if method == local */
 extern char *CVSroot_directory;        /* the directory name */
+extern char *CVSroot_prefix;    /* Prefix for CVSroot if supplied on cmdline */
+
+extern int local_security;     /* Only use the built-in CVS security system */
+
+/* Verification routines for local security, will always return 1
+   if local_security is false, otherwise will return 1 if the operations
+   is permitted and 0 if not. */
+int verify_admin PROTO(());
+int verify_owner PROTO((const char *));
+int verify_read PROTO((const char *));
+int verify_write PROTO((const char *));
+int verify_create PROTO((const char *));
+int change_owner PROTO((const char *, const char *));
+int change_perms PROTO((const char *, const char *, const char *));
+void list_owner PROTO((const char *));
+void list_perms PROTO((const char *));
 
 /* These variables keep track of all of the CVSROOT directories that
    have been seen by the client and the current one of those selected.  */
@@ -494,6 +510,11 @@
 int unlink_file PROTO((const char *f));
 int unlink_file_dir PROTO((const char *f));
 int update PROTO((int argc, char *argv[]));
+int setowner PROTO((int argc, char **argv));
+int setperm PROTO((int argc, char **argv));
+int listperm PROTO((int argc, char **argv));
+int setpass PROTO((int argc, char **argv));
+int deluser PROTO((int argc, char **argv));
 int xcmp PROTO((const char *file1, const char *file2));
 int yesno PROTO((void));
 void *valloc PROTO((size_t bytes));
@@ -622,6 +643,8 @@
 typedef        int (*DIRLEAVEPROC) PROTO ((void *callerdat, char *dir, int err,
                                    char *update_dir, List *entries));
 
+typedef int (*PERMPROC) PROTO ((const char *dir));
+
 extern int mkmodules PROTO ((char *dir));
 extern int init PROTO ((int argc, char **argv));
 
@@ -635,7 +658,7 @@
                     void *callerdat,
                     int argc, char *argv[], int local, int which,
                     int aflag, int readlock, char *update_preload,
-                    int dosrcs));
+                    int dosrcs, PERMPROC permproc));
 void SIG_beginCrSect PROTO((void));
 void SIG_endCrSect PROTO((void));
 int SIG_inCrSect PROTO((void));
diff -urN cvs-1.11/src/diff.c cvs-1.11-auth/src/diff.c
--- cvs-1.11/src/diff.c Wed Apr 14 19:12:26 1999
+++ cvs-1.11-auth/src/diff.c    Mon Nov  6 00:30:08 2000
@@ -393,7 +393,7 @@
     /* start the recursion processor */
     err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
                           diff_dirleaveproc, NULL, argc, argv, local,
-                          which, 0, 1, (char *) NULL, 1);
+                          which, 0, 1, (char *) NULL, 1, verify_read);
 
     /* clean up */
     free (options);
diff -urN cvs-1.11/src/edit.c cvs-1.11-auth/src/edit.c
--- cvs-1.11/src/edit.c Tue Jun 13 16:30:44 2000
+++ cvs-1.11-auth/src/edit.c    Mon Nov  6 00:30:08 2000
@@ -103,7 +103,7 @@
     err = start_recursion (onoff_fileproc, onoff_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          0);
+                          0, verify_write);
 
     Lock_Cleanup ();
     return err;
@@ -246,7 +246,7 @@
        err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL,
                                (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                                argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                               0);
+                               0, NULL);
 
        send_to_server ("noop\012", 0);
        if (strcmp (command_name, "release") == 0)
@@ -263,7 +263,7 @@
        err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL,
                                (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                                argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                               0);
+                               0, NULL);
        Lock_Cleanup ();
     }
     return err;
@@ -441,7 +441,7 @@
     err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          0);
+                          0, verify_write);
 
     err += send_notifications (argc, argv, local);
 
@@ -608,7 +608,7 @@
     err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          0);
+                          0, verify_write);
 
     err += send_notifications (argc, argv, local);
 
@@ -1131,5 +1131,5 @@
     return start_recursion (editors_fileproc, (FILESDONEPROC) NULL,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
-                           0);
+                           0, verify_write);
 }
diff -urN cvs-1.11/src/import.c cvs-1.11-auth/src/import.c
--- cvs-1.11/src/import.c       Tue Jul 11 15:32:02 2000
+++ cvs-1.11-auth/src/import.c  Mon Nov  6 00:30:08 2000
@@ -69,6 +69,7 @@
     List *ulist;
     Node *p;
     struct logfile_info *li;
+    int set_owner = 0, set_perms = 0;
 
     if (argc == -1)
        usage (import_usage);
@@ -282,12 +283,42 @@
        error (1, 0, "attempt to import the repository");
     }
 
+    if (! verify_create (repository))
+       error (1, 0, "User %s cannot create files in %s", CVS_Username, argv[0]);
+
     /*
      * Make all newly created directories writable.  Should really use a more
      * sophisticated security mechanism here.
      */
     (void) umask (cvsumask);
+
+    if (local_security)
+    {
+       char *tmp;
+
+       /* set the owner if we're creating a new directory */
+       if (! isdir (repository))
+          set_owner = 1;
+
+       /* set empty permissions if we're creating a new tree in CVSROOT */
+       tmp = xstrdup (repository);
+       if (cp = strchr (tmp + strlen (CVSroot_directory) + 1, '/'))
+          *cp = '\0';
+       if (! isdir (tmp))
+          set_perms = 1;
+       free (tmp);
+    }
+
     make_directories (repository);
+
+    if (local_security)
+    {
+       if (set_owner)
+          change_owner (repository, CVS_Username);
+
+       if (set_perms)
+          change_perms (repository, "", NULL);
+    }
 
     /* Create the logfile that will be logged upon completion */
     tmpfile = cvs_temp_name ();
diff -urN cvs-1.11/src/listperm.c cvs-1.11-auth/src/listperm.c
--- cvs-1.11/src/listperm.c     Wed Dec 31 18:00:00 1969
+++ cvs-1.11-auth/src/listperm.c        Mon Nov  6 00:30:08 2000
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ * 
+ * listperm
+ * 
+ * Shows the current permissions
+ */
+
+#include "cvs.h"
+
+static const char *const listperm_usage[] =
+{
+    "Usage: %s %s <directory1> [<directory2 ...]\n",
+    NULL
+};
+
+int
+listperm (argc, argv)
+   int argc;
+   char **argv;
+{
+   char *user;
+   int i;
+   char *repository;
+   int c;
+   int err = 0;
+   char *permptr;
+
+
+   if (argc == -1)
+      usage (listperm_usage);
+
+   wrap_setup ();
+
+   optind = 1;
+   while ((c = getopt (argc, argv, "")) != -1)
+   {
+      switch (c)
+      {
+      case '?':
+      default:
+        usage (listperm_usage);
+        break;
+      }
+   }
+   argc -= optind;
+   argv += optind;
+
+   if (argc <= 0)
+      usage (listperm_usage);
+
+   /* find the repository associated with our current dir */
+   repository = Name_Repository ((char *) NULL, (char *) NULL);
+
+#ifdef CLIENT_SUPPORT
+   if (client_active)
+   {
+      start_server ();
+      ign_setup ();
+
+      send_file_names (argc, argv, SEND_EXPAND_WILD);
+      send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
+      send_to_server ("listperm\012", 0);
+      return get_responses_and_close ();
+   }
+#endif
+
+   /* walk the arg list checking files/dirs */
+   for (i = 0; i < argc; i++)
+   {
+      char dname[PATH_MAX];
+      (void) sprintf (dname, "%s/%s", repository, argv[i]);
+      /* Sanitize_Repository_Name (dname); */
+      if (!isdir (dname))
+      {
+        error (0, 0, "`%s' is not a directory", dname);
+        err++;
+      }
+      else
+      {
+        if (!verify_read (dname))
+        {
+           error (0, 0, "'%s' cannot list '%s'\n", CVS_Username, argv[i]);
+           err++;
+        }
+        else
+        {
+           cvs_output ("File: ", 0);
+           cvs_output (argv[i], 0);
+           cvs_output ("\n", 0);
+           list_owner (dname);
+           list_perms (dname);
+        }
+      }
+   }
+
+   return (err);
+}
diff -urN cvs-1.11/src/lock.c cvs-1.11-auth/src/lock.c
--- cvs-1.11/src/lock.c Thu Jul 27 11:49:27 2000
+++ cvs-1.11-auth/src/lock.c    Mon Nov  6 00:30:08 2000
@@ -904,7 +904,8 @@
     lock_tree_list = getlist ();
     err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
-                          argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0);
+                          argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0,
+                          NULL);
     sortlist (lock_tree_list, fsortcmp);
     if (Writer_Lock (lock_tree_list) != 0)
        error (1, 0, "lock failed - giving up");
diff -urN cvs-1.11/src/log.c cvs-1.11-auth/src/log.c
--- cvs-1.11/src/log.c  Wed Jun 21 17:28:37 2000
+++ cvs-1.11-auth/src/log.c     Mon Nov  6 00:30:08 2000
@@ -365,7 +365,7 @@
                           (DIRLEAVEPROC) NULL, (void *) &log_data,
                           argc - optind, argv + optind, local,
                           W_LOCAL | W_REPOS | W_ATTIC, 0, 1,
-                          (char *) NULL, 1);
+                          (char *) NULL, 1, verify_read);
 
     while (log_data.revlist)
     {
diff -urN cvs-1.11/src/main.c cvs-1.11-auth/src/main.c
--- cvs-1.11/src/main.c Wed Sep  6 18:35:04 2000
+++ cvs-1.11-auth/src/main.c    Mon Nov  6 00:30:08 2000
@@ -136,6 +136,11 @@
     { "version",  "ve",       "ver",       version },
     { "watch",    NULL,              NULL,        watch },
     { "watchers", NULL,              NULL,        watchers },
+    { "setowner", NULL,       NULL,        setowner },
+    { "setperm",  NULL,       NULL,        setperm },
+    { "listperm", NULL,       NULL,        listperm },
+    { "setpass",  NULL,       NULL,        setpass },
+    { "deluser",  NULL,       NULL,        deluser },
     { NULL, NULL, NULL, NULL },
 };
 
@@ -224,6 +229,11 @@
     "        update       Bring work tree in sync with repository\n",
     "        watch        Set watches\n",
     "        watchers     See who is watching a file\n",
+    "        setowner     Change the owner of a file\n",
+    "        setperm      Set the permissions for another user\n",
+    "        listperm     List the directories permissions\n",
+    "        setpass      Set the user's password\n",
+    "        deluser      Delete a user\n",
     "(Specify the --help option for a list of other help options)\n",
     NULL,
 };
@@ -232,7 +242,9 @@
 {
     /* Omit -b because it is just for compatibility.  */
     "CVS global options (specified before the command name) are:\n",
+    "    -D           Adds a prefix to CVSROOT\n",
     "    -H           Displays usage information for command.\n",
+    "    -L           Use local CVS security\n",
     "    -Q           Cause CVS to be really quiet.\n",
     "    -q           Cause CVS to be somewhat quiet.\n",
     "    -r           Make checked-out files read-only.\n",
@@ -432,7 +444,7 @@
     int help = 0;              /* Has the user asked for help?  This
                                   lets us support the `cvs -H cmd'
                                   convention to give help for cmd. */
-    static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xa";
+    static const char short_options[] = "+Qqrwtnlvb:T:e:d:D:LHfz:s:xa";
     static struct option long_options[] =
     {
         {"help", 0, NULL, 'H'},
@@ -604,6 +616,12 @@
                CVSroot = xstrdup (optarg);
                free_CVSroot = 1;
                cvs_update_env = 1;     /* need to update environment */
+               break;
+           case 'D':
+               CVSroot_prefix = optarg;
+               break;
+           case 'L':
+               local_security = 1;
                break;
            case 'H':
                help = 1;
diff -urN cvs-1.11/src/mkmodules.c cvs-1.11-auth/src/mkmodules.c
--- cvs-1.11/src/mkmodules.c    Fri Sep  8 10:28:36 2000
+++ cvs-1.11-auth/src/mkmodules.c       Mon Nov  6 00:30:08 2000
@@ -858,6 +858,9 @@
     }
 #endif /* CLIENT_SUPPORT */
 
+    if (! verify_admin ())
+       error (1, 0, "Only the administrator can initialize new CVS stuff");
+
     /* Note: we do *not* create parent directories as needed like the
        old cvsinit.sh script did.  Few utilities do that, and a
        non-existent parent directory is as likely to be a typo as something
diff -urN cvs-1.11/src/patch.c cvs-1.11-auth/src/patch.c
--- cvs-1.11/src/patch.c        Thu Jul  6 12:30:42 2000
+++ cvs-1.11-auth/src/patch.c   Mon Nov  6 00:30:08 2000
@@ -326,6 +326,12 @@
        free (path);
     }
 
+    if (! verify_read(repository))
+    {
+       error (0, 0, "User %s cannot access %s", CVS_Username, repository);
+       return (1);
+    }
+
     /* cd to the starting repository */
     if ( CVS_CHDIR (repository) < 0)
     {
@@ -355,7 +361,7 @@
     err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
                           (DIRLEAVEPROC) NULL, NULL,
                           argc - 1, argv + 1, local,
-                          which, 0, 1, where, 1);
+                          which, 0, 1, where, 1, verify_read);
     free (where);
 
     return (err);
diff -urN cvs-1.11/src/perms.c cvs-1.11-auth/src/perms.c
--- cvs-1.11/src/perms.c        Wed Dec 31 18:00:00 1969
+++ cvs-1.11-auth/src/perms.c   Mon Nov  6 00:30:08 2000
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ * 
+ * Checking permissions for the current user in the specified
+ * directory.
+ * 
+ */
+
+#include "cvs.h"
+
+int local_security = 0;                /* Only use the built-in CVS security system */
+
+int
+verify_admin ()
+{
+   static int is_admin_set = 0;
+   static int is_the_admin = 0;
+
+   char *filename;
+   FILE *fp;
+   char *linebuf = NULL;
+   int  linebuf_len;
+   char *name;
+
+
+   if (!local_security)
+      return 1;
+
+   if (is_admin_set)
+   {
+      return (is_the_admin);
+   }
+   else
+   {
+      is_admin_set = 1;
+
+      filename = xmalloc (strlen (CVSroot_directory)
+                         + strlen ("/CVSROOT")
+                         + strlen ("/admin")
+                         + 1);
+
+      strcpy (filename, CVSroot_directory);
+      strcat (filename, "/CVSROOT");
+      strcat (filename, "/admin");
+
+      fp = CVS_FOPEN (filename, "r");
+      if (fp != NULL)
+      {
+        if (getline (&linebuf, &linebuf_len, fp) >= 0)
+        {
+           name = strtok (linebuf, "\n");
+           if (strcmp (CVS_Username, name) == 0)
+              is_the_admin = 1;
+           free (linebuf);
+        }
+        if (ferror (fp))
+           error (1, errno, "cannot read %s", filename);
+        if (fclose (fp) < 0)
+           error (0, errno, "cannot close %s", filename);
+      }
+   }
+
+   return (is_the_admin);
+}
+
+static FILE *
+open_lowest_file (dir, file, mode, realfile)
+   const char *dir, *file, *mode;
+   char **realfile;
+{
+   char *dirbuf, *filename;
+   FILE *fp;
+
+   dirbuf = xmalloc (strlen (dir) + 1);
+
+   *realfile = xmalloc (strlen (dir)
+                       + strlen (file)
+                       + 2);
+
+   for (strcpy (dirbuf, dir);
+        strlen (dirbuf) >= strlen (CVSroot_directory);
+        *(strrchr(dirbuf, '/')) = '\0') {
+
+      strcpy (*realfile, dirbuf);
+      strcat (*realfile, "/");
+      strcat (*realfile, file);
+
+      fp = CVS_FOPEN (*realfile, mode);
+
+      if (fp != NULL)
+      {
+         free(dirbuf);
+         return fp;
+      }
+
+      if (errno == ENOENT)
+         continue;
+
+      break;
+   }
+
+   free(dirbuf);
+
+   strcpy(*realfile, dir);
+   strcat(*realfile, "/");
+   strcat(*realfile, file);
+
+   return NULL;
+}
+
+
+int
+verify_owner (dir)
+   const char *dir;
+{
+   char *filename;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char *owner;
+   FILE *fp;
+   int retval = 0;
+
+   if (!local_security)
+      return 1;
+
+   if (verify_admin ())
+      return 1;
+
+   fp = open_lowest_file(dir, "owner", "r", &filename);
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s", filename);
+      retval = 0;
+   }
+   else
+   {
+      if (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+         owner = strtok(linebuf, "\n");
+         if (strcmp(owner, CVS_Username) == 0)
+           retval = 1;
+        else
+           retval = 0;
+        
+        free (linebuf);
+      }
+      else
+         retval = 0;
+
+      if (ferror (fp))
+         error (0, errno, "cannot read %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   free (filename);
+
+   return retval;
+}
+
+static int
+find_perms (dir, perm)
+   const char *dir;
+   char *perm;
+{
+   char *filename;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char *name;
+   char *permptr;
+   FILE *fp;
+   int retval = 1;
+   char all_perms[4];
+
+
+   all_perms[0] = '\0';
+
+   if (!local_security)
+   {
+      strcpy(perm, "rwc");
+      return 0;
+   }
+
+   if (verify_owner (dir))
+   {
+      strcpy(perm, "rwc");
+      return 0;
+   }
+
+   fp = open_lowest_file (dir, "perms", "r", &filename);
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s", filename);
+      retval = 1;
+   }
+   else
+   {
+      while (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+        name = strtok(linebuf, ":");
+        if (strcmp(name, CVS_Username) == 0)
+        {
+           permptr = strtok(NULL, "\n :");
+           if (permptr != NULL)
+           {
+              strncpy(perm, permptr, 4);
+              retval = 0;
+           }
+           free (linebuf);
+           break;
+        }
+        else if (strcmp (name, "ALL") == 0)
+        {
+           permptr = strtok(NULL, "\n :");
+           if (permptr != NULL)
+           {
+              strncpy(all_perms, permptr, 4);
+           }
+        }
+        
+        free (linebuf);
+        linebuf = NULL;
+      }
+
+      /* If we didn't find the user but found ALL, set it to ALL's perms */
+      if ((retval == 1) && (all_perms[0] != '\0'))
+      {
+        strcpy(perm, all_perms);
+        retval = 0;
+      }
+
+      if (ferror (fp))
+        error (0, errno, "cannot read %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   free (filename);
+
+   return retval;
+}
+
+int
+verify_read (dir)
+   const char *dir;
+{
+   char perm[4];
+
+   if (find_perms (dir, perm))
+      return 0;
+   else
+      if (strchr(perm, 'r') == NULL)
+        return 0;
+      else
+        return 1;
+}
+
+int
+verify_write (dir)
+   const char *dir;
+{
+   char perm[4];
+
+   if (find_perms (dir, perm))
+      return 0;
+   else
+      if (strchr(perm, 'w') == NULL)
+        return 0;
+      else
+        return 1;
+}
+
+int
+verify_create (dir)
+   const char *dir;
+{
+   char perm[4];
+
+   if (find_perms (dir, perm))
+      return 0;
+   else
+   {
+      if (strchr(perm, 'c') == NULL)
+        return 0;
+      else
+        return 1;
+   }
+}
+
+int
+change_owner (dir, user)
+   const char *dir;
+   const char *user;
+{
+   char *filename;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char owner;
+   FILE *fp;
+   int retval = 0;
+
+
+   filename = xmalloc (strlen (dir)
+                      + strlen ("/owner")
+                      + 1);
+
+   strcpy (filename, dir);
+   strcat (filename, "/owner");
+
+   fp = CVS_FOPEN (filename, "w");
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s for writing", filename);
+      retval = 0;
+   }
+   else
+   {
+      fprintf (fp, "%s\n", user);
+      if (ferror (fp))
+        error (0, errno, "cannot write %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   free (filename);
+
+   return retval;
+}
+
+static void
+node_deleted(p)
+   Node *p;
+{
+   if (p->key != NULL)
+      free (p->key);
+   if (p->data != NULL)
+      free (p->data);
+}
+
+static int
+write_node(p, v)
+   Node *p;
+   void *v;
+{
+   FILE *fp;
+
+   fp = v;
+   fprintf(fp, "%s:%s\n", p->key, p->data);
+   return 0;
+}
+
+int
+change_perms(dir, user, perm)
+   const char *dir;
+   const char *user;
+   const char *perm;
+{
+   char *filename;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char *name;
+   FILE *fp;
+   int retval = 1;
+   char all_perms[4];
+   List *userlist;
+   char *permptr;
+   Node *namenode;
+
+
+   filename = xmalloc (strlen (dir)
+                      + strlen ("/perms")
+                      + 1);
+
+   strcpy (filename, dir);
+   strcat (filename, "/perms");
+
+   userlist = getlist();
+
+   fp = CVS_FOPEN (filename, "r");
+   if (fp != NULL)
+   {
+      while (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+        name = strtok(linebuf, ":");
+        permptr = strtok(NULL, "\n :");
+        if ((permptr != NULL) && strlen(permptr) != 0)
+        {
+           namenode = getnode();
+           namenode->type = LIST;
+           namenode->key = xstrdup (name);
+           namenode->data = xstrdup (permptr);
+           namenode->delproc = node_deleted;
+           addnode(userlist, namenode);
+        }
+
+        free (linebuf);
+        linebuf = NULL;
+      }
+      if (ferror (fp))
+      {
+        error (1, errno, "cannot read %s", filename);
+      }
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   namenode = findnode(userlist, user);
+   if (namenode == NULL)
+   {
+      if (perm != NULL)
+      {
+        namenode = getnode();
+        namenode->type = LIST;
+        namenode->key = xstrdup (user);
+        namenode->data = xstrdup (perm);
+        namenode->delproc = node_deleted;
+        addnode(userlist, namenode);
+      }
+   }
+   else if (perm != NULL)
+   {
+      free (namenode->data);
+      namenode->data = xstrdup (perm);
+   }
+   else
+   {
+      delnode (namenode);
+   }
+   
+   fp = CVS_FOPEN (filename, "w");
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s for writing", filename);
+   }
+   else
+   {
+      walklist(userlist, write_node, fp);
+      retval = 0;
+   }
+
+   dellist(&userlist);
+
+   free (filename);
+
+   return retval;
+}
+
+void
+list_owner (dir)
+   const char *dir;
+{
+   char *filename, *cp;
+   char *dirbuf = NULL;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char *owner;
+   FILE *fp;
+
+   if (!local_security)
+   {
+      error (0, 0, "No owner");
+      return;
+   }
+
+   fp = open_lowest_file (dir, "owner", "r", &filename);
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s", filename);
+   }
+   else
+   {
+      if (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+        owner = strtok(linebuf, "\n");
+        cvs_output ("Owner: ", 0);
+        cvs_output (owner, 0);
+
+         if (dirbuf == NULL)
+         {
+            dirbuf = xmalloc (strlen (filename) + 1);
+            strcpy (dirbuf, filename);
+            cp = strrchr (dirbuf, '/');
+            if (cp != NULL)
+               *cp = '\0';
+         }
+
+         if (strcmp (dir, dirbuf) != 0)
+         {
+            cvs_output ("\t(inheritted from ", 0);
+            cvs_output (dirbuf, 0);
+           cvs_output (")", 0);
+         }
+
+        cvs_output ("\n", 0);
+        free (linebuf);
+      }
+      else
+        error (0, 0, "Error reading line in %s", filename);
+
+      if (ferror (fp))
+         error (0, errno, "cannot read %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   free (filename);
+   if (dirbuf != NULL)
+      free (dirbuf);
+}
+
+void
+list_perms (dir)
+   const char *dir;
+{
+   char *filename, *cp;
+   char *dirbuf = NULL;
+   char *linebuf = NULL;
+   int linebuf_len;
+   char *name;
+   char *permptr;
+   FILE *fp;
+   int retval = 1;
+
+
+   if (!local_security)
+   {
+      return;
+   }
+
+   fp = open_lowest_file (dir, "perms", "r", &filename);
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s", filename);
+   }
+   else
+   {
+      while (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+        permptr = strtok(linebuf, "\n");
+        cvs_output ("  ", 0);
+        cvs_output (permptr, 0);
+
+         if (dirbuf == NULL)
+         {
+            dirbuf = xmalloc (strlen (filename) + 1);
+            strcpy (dirbuf, filename);
+            cp = strrchr (dirbuf, '/');
+            if (cp != NULL)
+               *cp = '\0';
+         }
+
+         if (strcmp (dir, dirbuf) != 0)
+         {
+            cvs_output ("\t(inheritted from ", 0);
+            cvs_output (dirbuf, 0);
+            cvs_output (")", 0);
+         }
+
+        cvs_output ("\n", 0);
+        free (linebuf);
+        linebuf = NULL;
+      }
+
+      if (ferror (fp))
+        error (0, errno, "cannot read %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   free (filename);
+   if (dirbuf != NULL)
+      free (dirbuf);
+}
+
diff -urN cvs-1.11/src/rcs.c cvs-1.11-auth/src/rcs.c
--- cvs-1.11/src/rcs.c  Mon Aug 21 16:16:38 2000
+++ cvs-1.11-auth/src/rcs.c     Mon Nov  6 00:30:08 2000
@@ -8510,7 +8510,7 @@
     return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
-                           1);
+                           1, verify_read);
 }
 
 /*
diff -urN cvs-1.11/src/recurse.c cvs-1.11-auth/src/recurse.c
--- cvs-1.11/src/recurse.c      Wed Jun 21 17:28:37 2000
+++ cvs-1.11-auth/src/recurse.c Mon Nov  6 00:30:09 2000
@@ -35,6 +35,7 @@
     int aflag;
     int readlock;
     int dosrcs;
+    PERMPROC permproc;
 };
 
 static int do_recursion PROTO ((struct recursion_frame *frame));
@@ -59,6 +60,44 @@
 };
 
 
+static int
+verify_access (permproc, dir)
+    PERMPROC permproc;
+    char *dir;
+{
+    char *hostdir;
+    int  retval;
+
+    if (permproc == NULL)
+    {
+        return 1;
+    }
+
+    if (repository == NULL)
+    {
+       if (isdir (CVSADM))
+         hostdir = Name_Repository ((char *) NULL, dir);
+       else
+       {
+         hostdir = xmalloc (strlen (CVSroot_directory)
+                            + 1
+                            + strlen (dir)
+                            + 1);
+         strcpy (hostdir, CVSroot_directory);
+         strcat (hostdir, "/");
+         strcat (hostdir, dir);
+       }
+
+       retval = permproc (hostdir);
+       free (hostdir);
+    }
+    else
+    {
+       retval = permproc (repository);
+    }
+    return retval;
+}
+
 /* Start a recursive command.
 
    Command line arguments (ARGC, ARGV) dictate the directories and
@@ -67,7 +106,7 @@
 int
 start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
                 argc, argv, local, which, aflag, readlock,
-                update_preload, dosrcs)
+                update_preload, dosrcs, permproc)
     FILEPROC fileproc;
     FILESDONEPROC filesdoneproc;
     DIRENTPROC         direntproc;
@@ -105,6 +144,7 @@
     int readlock;
     char *update_preload;
     int dosrcs;
+    PERMPROC permproc;
 {
     int i, err = 0;
 #ifdef CLIENT_SUPPORT
@@ -123,6 +163,7 @@
     frame.aflag = aflag;
     frame.readlock = readlock;
     frame.dosrcs = dosrcs;
+    frame.permproc = permproc;
 
     expand_wild (argc, argv, &argc, &argv);
 
@@ -285,7 +326,17 @@
                file_to_try = xstrdup (argv[i]);
 
            if (isfile (file_to_try))
-               addfile (&files_by_dir, dir, comp);
+           {
+               if (! verify_access (frame.permproc, dir))
+               {
+                   error (0, 0, "User '%s' cannot access %s",
+                          CVS_Username, dir);
+               }
+               else
+               {
+                  addfile (&files_by_dir, dir, comp);
+               }
+           }
            else if (isdir (dir))
            {
                if ((which & W_LOCAL) && isdir (CVSADM)
@@ -623,6 +674,15 @@
     }
     srepository = repository;          /* remember what to free */
 
+    /*
+     * Do we have access to this directory?
+     */
+    if (! verify_access (frame->permproc, update_dir))
+    {
+       error (0, 0, "User '%s' cannot access %s", CVS_Username, update_dir);
+       return (1);
+    }
+
     fileattr_startdir (repository);
 
     /*
@@ -1163,6 +1223,7 @@
     List *save_dirlist;
     char *save_update_dir = NULL;
     struct saved_cwd cwd;
+
 
     /* if this dir was also an explicitly named argument, then skip
        it.  We'll catch it later when we do dirs. */
diff -urN cvs-1.11/src/remove.c cvs-1.11-auth/src/remove.c
--- cvs-1.11/src/remove.c       Wed Jun 21 17:28:37 2000
+++ cvs-1.11-auth/src/remove.c  Mon Nov  6 00:30:09 2000
@@ -90,7 +90,7 @@
                start_recursion (remove_force_fileproc, (FILESDONEPROC) NULL,
                                 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
                                 (void *) NULL, argc, argv, local, W_LOCAL,
-                                0, 0, (char *) NULL, 0);
+                                0, 0, (char *) NULL, 0, NULL);
            }
            /* else FIXME should probably act as if the file doesn't exist
               in doing the following checks.  */
@@ -113,7 +113,8 @@
     err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL,
                            remove_dirproc, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv,
-                           local, W_LOCAL, 0, 1, (char *) NULL, 1);
+                           local, W_LOCAL, 0, 1, (char *) NULL, 1,
+                          verify_create);
 
     if (removed_files)
        error (0, 0, "use '%s commit' to remove %s permanently", program_name,
diff -urN cvs-1.11/src/root.c cvs-1.11-auth/src/root.c
--- cvs-1.11/src/root.c Thu Jul 27 17:28:36 2000
+++ cvs-1.11-auth/src/root.c    Mon Nov  6 11:27:26 2000
@@ -288,6 +288,7 @@
 char *CVSroot_username;                /* the username or NULL if method == local */
 char *CVSroot_hostname;                /* the hostname or NULL if method == local */
 char *CVSroot_directory;       /* the directory name */
+char *CVSroot_prefix = NULL;    /* Prefix for CVSroot if supplied on cmdline */
 
 int
 parse_cvsroot (CVSroot)
@@ -521,7 +522,19 @@
     CVSroot_method = local_method;
     if (CVSroot_directory != NULL)
        free (CVSroot_directory);
-    CVSroot_directory = xstrdup(dir);
+    if (CVSroot_prefix == NULL)
+    {
+        CVSroot_directory = xstrdup (dir);
+    }
+    else
+    {
+        CVSroot_directory = malloc (strlen(dir) + strlen(CVSroot_prefix) + 1);
+        if (CVSroot_directory != NULL)
+        {
+            strcpy(CVSroot_directory, CVSroot_prefix);
+            strcat(CVSroot_directory, dir);
+        }
+    }
     if (CVSroot_username != NULL)
        free (CVSroot_username);
     CVSroot_username = NULL;
diff -urN cvs-1.11/src/rtag.c cvs-1.11-auth/src/rtag.c
--- cvs-1.11/src/rtag.c Wed Jun 14 14:38:15 2000
+++ cvs-1.11-auth/src/rtag.c    Mon Nov  6 00:30:09 2000
@@ -314,7 +314,7 @@
     err = start_recursion (check_fileproc, check_filesdoneproc,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc - 1, argv + 1, local, which, 0, 1,
-                           where, 1);
+                           where, 1, verify_write);
     
     if (err)
     {
@@ -325,7 +325,7 @@
     err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc,
                           (DIRLEAVEPROC) NULL, NULL,
                           argc - 1, argv + 1, local,
-                          which, 0, 0, where, 1);
+                          which, 0, 0, where, 1, verify_write);
     free (where);
     dellist(&mtlist);
 
diff -urN cvs-1.11/src/server.c cvs-1.11-auth/src/server.c
--- cvs-1.11/src/server.c       Fri Jul 28 15:18:40 2000
+++ cvs-1.11-auth/src/server.c  Mon Nov  6 11:28:24 2000
@@ -18,6 +18,9 @@
 
 #ifdef SERVER_SUPPORT
 
+#include <syslog.h>
+#include <sys/param.h>
+
 #ifdef HAVE_WINSOCK_H
 #include <winsock.h>
 #endif
@@ -749,7 +752,7 @@
 {
     char *env;
     char *path;
-    
+
     if (error_pending()) return;
 
     if (!isabsolute (arg))
@@ -870,23 +873,34 @@
 outside_root (repos)
     char *repos;
 {
-    size_t repos_len = strlen (repos);
+    char real_repos[MAXPATHLEN];
+    size_t repos_len;
     size_t root_len = strlen (CVSroot_directory);
 
+    if (CVSroot_prefix != NULL)
+    {
+        strcpy(real_repos, CVSroot_prefix);
+        strcat(real_repos, repos);
+    }
+    else
+        strcpy(real_repos, repos);
+
+    repos_len = strlen (real_repos);
+
     /* I think isabsolute (repos) should always be true, and that
        any RELATIVE_REPOS stuff should only be in CVS/Repository
        files, not the protocol (for compatibility), but I'm putting
        in the isabsolute check just in case.  */
-    if (!isabsolute (repos))
+    if (!isabsolute (real_repos))
     {
        if (alloc_pending (repos_len + 80))
            sprintf (pending_error_text, "\
-E protocol error: %s is not absolute", repos);
+E protocol error: %s is not absolute", real_repos);
        return 1;
     }
 
     if (repos_len < root_len
-       || strncmp (CVSroot_directory, repos, root_len) != 0)
+       || strncmp (CVSroot_directory, real_repos, root_len) != 0)
     {
     not_within:
        if (alloc_pending (strlen (CVSroot_directory)
@@ -894,14 +908,14 @@
                           + 80))
            sprintf (pending_error_text, "\
 E protocol error: directory '%s' not within root '%s'",
-                    repos, CVSroot_directory);
+                    real_repos, CVSroot_directory);
        return 1;
     }
     if (repos_len > root_len)
     {
-       if (repos[root_len] != '/')
+       if (real_repos[root_len] != '/')
            goto not_within;
-       if (pathname_levels (repos + root_len + 1) > 0)
+       if (pathname_levels (real_repos + root_len + 1) > 0)
            goto not_within;
     }
     return 0;
@@ -3539,6 +3553,41 @@
 }
 
 void
+serve_setowner (arg)
+   char *arg;
+{
+   do_cvs_command ("setowner", setowner);
+}
+
+void
+serve_setperm (arg)
+   char *arg;
+{
+   do_cvs_command ("setperm", setperm);
+}
+
+void
+serve_listperm (arg)
+   char *arg;
+{
+   do_cvs_command ("listperm", listperm);
+}
+
+void
+serve_setpass (arg)
+   char *arg;
+{
+   do_cvs_command ("setpass", setpass);
+}
+
+void
+serve_deluser (arg)
+   char *arg;
+{
+   do_cvs_command ("deluser", deluser);
+}
+
+void
 server_update_entries (file, update_dir, repository, updated)
     char *file;
     char *update_dir;
@@ -4688,6 +4737,11 @@
   REQ_LINE("expand-modules", serve_expand_modules, 0),
   REQ_LINE("ci", serve_ci, RQ_ESSENTIAL),
   REQ_LINE("co", serve_co, RQ_ESSENTIAL),
+  REQ_LINE("setowner", serve_setowner, 0),
+  REQ_LINE("setperm", serve_setperm, 0),
+  REQ_LINE("listperm", serve_listperm, 0),
+  REQ_LINE("setpass", serve_setpass, 0),
+  REQ_LINE("deluser", serve_deluser, 0),
   REQ_LINE("update", serve_update, RQ_ESSENTIAL),
   REQ_LINE("diff", serve_diff, 0),
   REQ_LINE("log", serve_log, 0),
@@ -4917,6 +4971,9 @@
     }
     /* Ignore argc and argv.  They might be from .cvsrc.  */
 
+    openlog("cvs-pserver", LOG_PID, LOG_LOCAL0);
+    syslog(LOG_DEBUG, "server started");
+
     buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0,
                                       outbuf_memory_error);
     buf_from_net = stdio_buffer_initialize (stdin, 1, outbuf_memory_error);
@@ -5144,7 +5201,10 @@
                                 "E Protocol error: Root request missing");
                }
                else
+               {
+                   syslog(LOG_DEBUG, "command: %s %s", rq->name, cmd);
                    (*rq->func) (cmd);
+               }
                break;
            }
        if (rq->name == NULL)
@@ -5583,6 +5643,7 @@
     char *descrambled_password;
 #endif /* AUTH_SERVER_SUPPORT */
     int verify_and_exit = 0;
+    char *real_repository = NULL;
 
     /* The Authentication Protocol.  Client sends:
      *
@@ -5706,7 +5767,27 @@
 
     /* We need the real cleartext before we hash it. */
     descrambled_password = descramble (password);
-    host_user = check_password (username, descrambled_password, repository);
+    if (CVSroot_prefix != NULL)
+    {
+        real_repository = malloc (strlen(CVSroot_prefix)
+                                 + strlen(repository)
+                                  + 1);
+        strcpy(real_repository, CVSroot_prefix);
+        strcat(real_repository, repository);
+    }
+    else
+    {
+        real_repository = repository;
+    }
+
+    host_user = check_password (username, descrambled_password,
+                                real_repository);
+
+    if (CVSroot_prefix != NULL)
+    {
+       free (real_repository);
+    }
+
     memset (descrambled_password, 0, strlen (descrambled_password));
     free (descrambled_password);
     if (host_user == NULL)
@@ -5741,7 +5822,8 @@
     strcpy (Pserver_Repos, repository);
 
     /* Switch to run as this user. */
-    switch_to_user (host_user);
+    if (! local_security)
+        switch_to_user (host_user);
     free (host_user);
     free (tmp);
     free (repository);
@@ -5829,7 +5911,8 @@
     }
 
     /* Switch to run as this user. */
-    switch_to_user (user);
+    if (! local_security)
+        switch_to_user (user);
 }
 #endif /* HAVE_KERBEROS */
 
@@ -5921,10 +6004,14 @@
                              &mechid) != GSS_S_COMPLETE
            || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0
            || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0
-           || krb5_kuserok (kc, p, buf) != TRUE)
+            || (!local_security && krb5_kuserok (kc, p, buf) != TRUE))
        {
            error (1, 0, "access denied");
        }
+        else
+       {
+                   CVS_Username = xstrdup(buf);
+       }
        krb5_free_principal (kc, p);
        krb5_free_context (kc);
     }
@@ -5941,7 +6028,8 @@
            error (1, errno, "fwrite failed");
     }
 
-    switch_to_user (buf);
+    if (! local_security)
+        switch_to_user (buf);
 
     printf ("I LOVE YOU\n");
     fflush (stdout);
diff -urN cvs-1.11/src/setowner.c cvs-1.11-auth/src/setowner.c
--- cvs-1.11/src/setowner.c     Wed Dec 31 18:00:00 1969
+++ cvs-1.11-auth/src/setowner.c        Mon Nov  6 00:30:09 2000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ * 
+ * Setowner
+ * 
+ * Changes the owner of a directory to the given name.
+ */
+
+#include "cvs.h"
+
+static const char *const setowner_usage[] =
+{
+    "Usage: %s %s user directory...\n",
+    NULL
+};
+
+int
+setowner (argc, argv)
+   int argc;
+   char **argv;
+{
+   char *user;
+   int i;
+   char *repository;
+   int c;
+   int err = 0;
+
+
+   if (argc == 1 || argc == -1)
+      usage (setowner_usage);
+
+   wrap_setup ();
+
+   optind = 1;
+   while ((c = getopt (argc, argv, "")) != -1)
+   {
+      switch (c)
+      {
+      case '?':
+      default:
+        usage (setowner_usage);
+        break;
+      }
+   }
+   argc -= optind;
+   argv += optind;
+
+   if (argc <= 1)
+      usage (setowner_usage);
+
+   /* find the repository associated with our current dir */
+   repository = Name_Repository ((char *) NULL, (char *) NULL);
+
+#ifdef CLIENT_SUPPORT
+   if (client_active)
+   {
+      start_server ();
+      ign_setup ();
+
+      send_arg (argv[0]); /* Send the user name */
+      argc--;
+      argv++;
+      send_file_names (argc, argv, SEND_EXPAND_WILD);
+      send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
+      send_to_server ("setowner\012", 0);
+      return get_responses_and_close ();
+   }
+#endif
+
+   user = argv[0];
+
+   /* walk the arg list checking files/dirs */
+   for (i = 1; i < argc; i++)
+   {
+      char dname[PATH_MAX];
+      (void) sprintf (dname, "%s/%s", repository, argv[i]);
+      if (!isdir (dname))
+      {
+        error (0, 0, "`%s' is not a directory", dname);
+        err++;
+      }
+      else
+      {
+        if (!verify_owner (dname))
+        {
+           error (0, 0, "'%s' does not own '%s'\n", CVS_Username, dname);
+           err++;
+        }
+        else
+        {
+           change_owner (dname, user);
+        }
+      }
+   }
+
+   return (err);
+}
diff -urN cvs-1.11/src/setpass.c cvs-1.11-auth/src/setpass.c
--- cvs-1.11/src/setpass.c      Wed Dec 31 18:00:00 1969
+++ cvs-1.11-auth/src/setpass.c Mon Nov  6 00:30:09 2000
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ * 
+ * setpass
+ * 
+ * Changes the password of the caller
+ */
+
+#include "cvs.h"
+
+extern char *crypt PROTO((const char *, const char *));
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+static const char *const setpass_usage[] =
+{
+    "Usage: %s %s [-a] [username]\n",
+    "\t-a\tAdd the user\n",
+    NULL
+};
+
+static void
+node_deleted(p)
+   Node *p;
+{
+   if (p->key != NULL)
+      free (p->key);
+   if (p->data != NULL)
+      free (p->data);
+}
+
+static int
+write_node (p, v)
+   Node *p;
+   void *v;
+{
+   FILE *fp;
+
+   fp = v;
+   fprintf (fp, "%s:%s\n", p->key, p->data);
+   return 0;
+}
+
+int
+setpass (argc, argv)
+   int argc;
+   char **argv;
+{
+   int c;
+   int err = 0;
+   char *typed_password, *typed_password2;
+   char *filename, *rcsfilename, *tmpfilename;
+   List *userlist;
+   Node *namenode;
+   char *rest;
+   char *oldpassword;
+   char *oldrest;
+   FILE *fp;
+   char *linebuf = NULL;
+   int  linebuf_len;
+   char *username;
+   int  adduser = 0;
+   RCSNode *rcsfile;
+
+
+   if (argc == -1)
+      usage (setpass_usage);
+
+   wrap_setup ();
+
+   optind = 1;
+   while ((c = getopt (argc, argv, "a")) != -1)
+   {
+      switch (c)
+      {
+      case 'a':
+        adduser = 1;
+        break;
+
+      case '?':
+      default:
+        usage (setpass_usage);
+        break;
+      }
+   }
+   argc -= optind;
+   argv += optind;
+
+#ifdef CLIENT_SUPPORT
+   if (client_active)
+   {
+      if (argc > 1)
+        usage (setpass_usage);
+
+      if (CVSroot_method != pserver_method)
+      {
+        error (0, 0, "can only use pserver method with `setpass' command");
+        error (1, 0, "CVSROOT: %s", CVSroot_original);
+      }
+    
+      if (! CVSroot_username)
+      {
+        error (0, 0, "CVSROOT \"%s\" is not fully-qualified.",
+               CVSroot_original);
+        error (1, 0, "Please make sure to specify \"user@host\"!");
+      }
+
+      start_server ();
+      ign_setup ();
+
+      printf ("Changing password for %s@%s\n",
+             (argc == 1) ? argv[0] : CVSroot_username, CVSroot_hostname);
+      fflush (stdout);
+
+      typed_password = getpass ("New password: ");
+      typed_password = xstrdup (typed_password);
+      if (strlen(typed_password) == 0)
+        error (1, 0, "A password must be supplied");
+
+      typed_password2 = getpass ("Verify password: ");
+      if (strcmp(typed_password, typed_password2) != 0)
+      {
+        memset (typed_password, 0, strlen (typed_password));
+        free (typed_password);
+        memset (typed_password2, 0, strlen (typed_password2));
+        error (1, 0, "Passwords do not match, try again");
+      }
+      memset (typed_password2, 0, strlen (typed_password2));
+      typed_password = scramble (typed_password);
+
+      if (adduser)
+        send_arg ("-a");
+
+      send_arg (typed_password); /* Send the new password */
+
+      if (argc == 1)
+        send_arg(argv[0]);
+
+      send_to_server ("setpass\012", 0);
+      memset (typed_password, 0, strlen (typed_password));
+      free (typed_password);
+      return get_responses_and_close ();
+   }
+#endif
+
+   if (!server_active)
+   {
+      error (1, 0, "Server is not active, not permitted interactively");
+   }
+
+#ifndef AUTH_SERVER_SUPPORT
+   error (1, 0, "Authentication not supported");
+#else
+   if ((argc != 1) && (argc != 2))
+      usage (setpass_usage);
+
+   if ((argc == 2) && (! verify_admin ()))
+      error (1, 0,
+            "Only the administrator can add or change another's password");
+
+   if (strlen(argv[0]) == 0)
+      error (1, 0, "A password must be supplied");
+
+   {
+      time_t tm;
+      char salt[2];
+      char *cryptstr;
+
+      argv[0] = descramble (argv[0]);
+
+      time(&tm);
+      salt[0] = bin_to_ascii(tm & 0x3f);
+      salt[1] = bin_to_ascii((tm >> 5) & 0x3f);
+      cryptstr = crypt(argv[0], salt);
+      memset (argv[0], 0, strlen (argv[0]));
+
+      filename = xmalloc (strlen (CVSroot_directory)
+                         + strlen ("/CVSROOT")
+                         + strlen ("/passwd")
+                         + 1);
+
+      strcpy (filename, CVSroot_directory);
+      strcat (filename, "/CVSROOT");
+      strcat (filename, "/passwd");
+
+      rcsfilename = xmalloc (strlen(filename) + 3);
+      strcpy (rcsfilename, filename);
+      strcat (rcsfilename, ",v");
+
+      rcsfile = RCS_parsercsfile (rcsfilename);
+      if (RCS_checkout(rcsfile, filename,
+                       NULL, NULL, NULL, NULL, NULL, NULL) != 0)
+        error (1, 0, "Error checking out password file");
+
+      userlist = getlist();
+
+      fp = CVS_FOPEN (filename, "r");
+      if (fp != NULL)
+      {
+        while (getline (&linebuf, &linebuf_len, fp) >= 0)
+        {
+           namenode = getnode();
+           namenode->type = LIST;
+           namenode->key = xstrdup (strtok (linebuf, ":\n"));
+           if ((namenode->key == NULL) || (strlen (namenode->key) == 0))
+           {
+              freenode (namenode);
+           }
+           else
+           {
+              namenode->data = xstrdup (strtok (NULL, "\n"));
+              namenode->delproc = node_deleted;
+              addnode(userlist, namenode);
+           }
+           
+           free (linebuf);
+           linebuf = NULL;
+        }
+        if (ferror (fp))
+           error (1, errno, "cannot read %s", filename);
+        if (fclose (fp) < 0)
+           error (0, errno, "cannot close %s", filename);
+      }
+
+      if (argc == 1)
+        username = CVS_Username;
+      else
+        username = argv[1];
+
+      namenode = findnode(userlist, username);
+      if (namenode == NULL)
+      {
+        if (argc == 1)
+           error (1, 0, "Could not find %s in password file", username);
+
+        if (!adduser)
+           error (1, 0, "Could not find %s in password file", username);
+
+        namenode = getnode();
+        namenode->type = LIST;
+        namenode->key = xstrdup (argv[1]);
+        namenode->delproc = node_deleted;
+        namenode->data = NULL;
+        addnode(userlist, namenode);
+      }
+
+      rest = namenode->data;
+      if (rest == NULL)
+      {
+        oldrest = NULL;
+        namenode->data = xmalloc (16);
+      }
+      else
+      {
+        namenode->data = xmalloc (strlen(rest) + 1);
+        oldpassword = strtok (rest, ":");
+        oldrest = strtok (NULL, "");
+      }
+
+      if (oldrest != NULL)
+      {
+        sprintf (namenode->data, "%s:%s", cryptstr, oldrest);
+        free (rest);
+      }
+      else
+        sprintf (namenode->data, "%s:", cryptstr);
+   
+      tmpfilename = cvs_temp_name ();
+
+      fp = CVS_FOPEN (tmpfilename, "w");
+      if (fp == NULL)
+      {
+        error (0, errno, "cannot open %s for writing", filename);
+      }
+      else
+      {
+        walklist(userlist, write_node, fp);
+        if (fclose (fp) < 0)
+           error (0, errno, "cannot close %s", filename);
+      }
+
+      dellist(&userlist);
+
+      if ((RCS_lock(rcsfile, NULL, 1) != 0) ||
+          (RCS_checkin(rcsfile, tmpfilename, NULL, NULL, RCS_FLAGS_QUIET) != 0))
+        error (0, 0, "Error checking in password file");
+
+      if (RCS_checkout(rcsfile, filename,
+                       NULL, NULL, NULL, NULL, NULL, NULL) != 0)
+        error (0, 0, "Error checking out password file");
+
+    }
+#endif
+   free (filename);
+
+   return (err);
+}
+
+static const char *const deluser_usage[] =
+{
+    "Usage: %s %s username\n",
+    NULL
+};
+
+int
+deluser (argc, argv)
+   int argc;
+   char **argv;
+{
+   int c;
+   int err = 0;
+   char *filename, *rcsfilename, *tmpfilename;
+   List *userlist;
+   Node *namenode;
+   char *rest;
+   char *oldpassword;
+   char *oldrest;
+   FILE *fp;
+   char *linebuf = NULL;
+   int  linebuf_len;
+   RCSNode *rcsfile;
+
+
+   if (argc == -1)
+      usage (deluser_usage);
+
+   wrap_setup ();
+
+   optind = 1;
+   while ((c = getopt (argc, argv, "")) != -1)
+   {
+      switch (c)
+      {
+      case '?':
+      default:
+        usage (deluser_usage);
+        break;
+      }
+   }
+   argc -= optind;
+   argv += optind;
+
+#ifdef CLIENT_SUPPORT
+   if (client_active)
+   {
+      if (argc != 1)
+        usage (deluser_usage);
+
+      if (CVSroot_method != pserver_method)
+      {
+        error (0, 0, "can only use pserver method with `deluser' command");
+        error (1, 0, "CVSROOT: %s", CVSroot_original);
+      }
+    
+      if (! CVSroot_username)
+      {
+        error (0, 0, "CVSROOT \"%s\" is not fully-qualified.",
+               CVSroot_original);
+        error (1, 0, "Please make sure to specify \"user@host\"!");
+      }
+
+      start_server ();
+      ign_setup ();
+
+      send_arg(argv[0]);
+
+      send_to_server ("deluser\012", 0);
+      return get_responses_and_close ();
+   }
+#endif
+
+   if (!server_active)
+   {
+      error (1, 0, "Server is not active, not permitted interactively");
+   }
+
+   if (argc != 1)
+      usage (deluser_usage);
+
+   if (! verify_admin ())
+      error (1, 0,
+            "Only the administrator can delete a user");
+
+   filename = xmalloc (strlen (CVSroot_directory)
+                      + strlen ("/CVSROOT")
+                      + strlen ("/passwd")
+                      + 1);
+   
+   strcpy (filename, CVSroot_directory);
+   strcat (filename, "/CVSROOT");
+   strcat (filename, "/passwd");
+
+   rcsfilename = xmalloc (strlen(filename) + 3);
+   strcpy (rcsfilename, filename);
+   strcat (rcsfilename, ",v");
+
+   rcsfile = RCS_parsercsfile (rcsfilename);
+   if (RCS_checkout(rcsfile, filename,
+                    NULL, NULL, NULL, NULL, NULL, NULL) != 0)
+      error (1, 0, "Error checking out password file");
+
+   userlist = getlist();
+   
+   fp = CVS_FOPEN (filename, "r");
+   if (fp != NULL)
+   {
+      while (getline (&linebuf, &linebuf_len, fp) >= 0)
+      {
+        namenode = getnode();
+        namenode->type = LIST;
+        namenode->key = xstrdup (strtok (linebuf, ":\n"));
+        if ((namenode->key == NULL) || (strlen (namenode->key) == 0))
+        {
+           freenode (namenode);
+        }
+        else
+        {
+           namenode->data = xstrdup (strtok (NULL, "\n"));
+           namenode->delproc = node_deleted;
+           addnode(userlist, namenode);
+        }
+           
+        free (linebuf);
+        linebuf = NULL;
+      }
+      if (ferror (fp))
+        error (1, errno, "cannot read %s", filename);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+
+   namenode = findnode(userlist, argv[0]);
+   if (namenode == NULL)
+   {
+      error (1, 0, "Could not find %s in password file", argv[0]);
+   }
+   else
+   {
+      delnode (namenode);
+   }
+   
+   tmpfilename = cvs_temp_name ();
+
+   fp = CVS_FOPEN (tmpfilename, "w");
+   if (fp == NULL)
+   {
+      error (0, errno, "cannot open %s for writing", filename);
+   }
+   else
+   {
+      walklist(userlist, write_node, fp);
+      if (fclose (fp) < 0)
+        error (0, errno, "cannot close %s", filename);
+   }
+   
+   dellist(&userlist);
+
+   if ((RCS_lock(rcsfile, NULL, 1) != 0) ||
+       (RCS_checkin(rcsfile, tmpfilename, NULL, NULL, RCS_FLAGS_QUIET) != 0))
+      error (0, 0, "Error checking in password file");
+
+   if (RCS_checkout(rcsfile, filename,
+                    NULL, NULL, NULL, NULL, NULL, NULL) != 0)
+      error (0, 0, "Error checking out password file");
+
+   free (filename);
+
+   return (err);
+}
+
diff -urN cvs-1.11/src/setperm.c cvs-1.11-auth/src/setperm.c
--- cvs-1.11/src/setperm.c      Wed Dec 31 18:00:00 1969
+++ cvs-1.11-auth/src/setperm.c Mon Nov  6 00:30:09 2000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ * 
+ * setperm
+ * 
+ * Sets the permission for the specified user for the directory
+ */
+
+#include "cvs.h"
+
+static const char *const setperm_usage[] =
+{
+    "Usage: %s %s user:perm directory...\n",
+    NULL
+};
+
+int
+check_perms(perm)
+   char *perm;
+{
+   int foundc, foundr, foundw;
+
+   if (strlen(perm) > 3)
+      return 0;
+
+   foundc = 0,
+   foundr = 0;
+   foundw = 0;
+   while (*perm != '\0')
+   {
+      switch (*perm)
+      {
+      case 'c':
+        if (foundc)
+           return 0;
+        foundc = 1;
+        break;
+
+      case 'r':
+        if (foundr)
+           return 0;
+        foundr = 1;
+        break;
+
+      case 'w':
+        if (foundw)
+           return 0;
+        foundw = 1;
+        break;
+
+      default:
+        return 0;
+      }
+      perm++;
+   }
+
+   return 1;
+}
+
+int
+setperm (argc, argv)
+   int argc;
+   char **argv;
+{
+   char *user;
+   int i;
+   char *repository;
+   int c;
+   int err = 0;
+   char *permptr;
+
+
+   if (argc == 1 || argc == -1)
+      usage (setperm_usage);
+
+   wrap_setup ();
+
+   optind = 1;
+   while ((c = getopt (argc, argv, "")) != -1)
+   {
+      switch (c)
+      {
+      case '?':
+      default:
+        usage (setperm_usage);
+        break;
+      }
+   }
+   argc -= optind;
+   argv += optind;
+
+   if (argc <= 1)
+      usage (setperm_usage);
+
+   /* find the repository associated with our current dir */
+   repository = Name_Repository ((char *) NULL, (char *) NULL);
+
+#ifdef CLIENT_SUPPORT
+   if (client_active)
+   {
+      start_server ();
+      ign_setup ();
+
+      send_arg (argv[0]); /* Send the user name and permissions */
+      argc--;
+      argv++;
+      send_file_names (argc, argv, SEND_EXPAND_WILD);
+      send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
+      send_to_server ("setperm\012", 0);
+      return get_responses_and_close ();
+   }
+#endif
+
+   user = strtok (argv[0], ":");
+   permptr = strtok (NULL, "");
+
+   if ((permptr != NULL) && (strlen(permptr) == 0))
+   {
+      permptr = NULL;
+   }
+
+   if (permptr == NULL)
+   {
+      /* Ok, nothing to do. */
+   }
+   else if  (! check_perms(permptr))
+   {
+      error(1, 0, "Invalid permissions: '%s', can only have r, w, and c",
+           permptr);
+   }
+
+   /* walk the arg list checking files/dirs */
+   for (i = 1; i < argc; i++)
+   {
+      char dname[PATH_MAX];
+      (void) sprintf (dname, "%s/%s", repository, argv[i]);
+      if (!isdir (dname))
+      {
+        error (0, 0, "`%s' is not a directory", dname);
+        err++;
+      }
+      else
+      {
+        if (!verify_owner (dname))
+        {
+           error (0, 0, "'%s' does not own '%s'\n", CVS_Username, argv[i]);
+           err++;
+        }
+        else
+        {
+           change_perms (dname, user, permptr);
+        }
+      }
+   }
+
+   return (err);
+}
diff -urN cvs-1.11/src/status.c cvs-1.11-auth/src/status.c
--- cvs-1.11/src/status.c       Tue Jun  1 16:36:33 1999
+++ cvs-1.11-auth/src/status.c  Mon Nov  6 00:30:09 2000
@@ -106,7 +106,7 @@
     err = start_recursion (status_fileproc, (FILESDONEPROC) NULL,
                           status_dirproc, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local,
-                          W_LOCAL, 0, 1, (char *) NULL, 1);
+                          W_LOCAL, 0, 1, (char *) NULL, 1, verify_read);
 
     return (err);
 }
diff -urN cvs-1.11/src/tag.c cvs-1.11-auth/src/tag.c
--- cvs-1.11/src/tag.c  Wed Jun 14 14:32:51 2000
+++ cvs-1.11-auth/src/tag.c     Mon Nov  6 00:30:09 2000
@@ -200,7 +200,7 @@
     err = start_recursion (check_fileproc, check_filesdoneproc,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, 1,
-                           (char *) NULL, 1);
+                           (char *) NULL, 1, verify_write);
     
     if (err)
     {
@@ -210,7 +210,7 @@
     /* start the recursion processor */
     err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc,
                           (DIRLEAVEPROC) NULL, NULL, argc, argv, local,
-                          W_LOCAL, 0, 0, (char *) NULL, 1);
+                          W_LOCAL, 0, 0, (char *) NULL, 1, verify_write);
     dellist(&mtlist);
     return (err);
 }
@@ -853,7 +853,7 @@
                           val_direntproc, (DIRLEAVEPROC) NULL,
                           (void *)&the_val_args,
                           argc, argv, local, which, aflag,
-                          1, NULL, 1);
+                          1, NULL, 1, verify_write);
     if (repository != NULL && repository[0] != '\0')
     {
        if (restore_cwd (&cwd, NULL))
diff -urN cvs-1.11/src/update.c cvs-1.11-auth/src/update.c
--- cvs-1.11/src/update.c       Wed Jul 26 14:29:01 2000
+++ cvs-1.11-auth/src/update.c  Mon Nov  6 00:30:09 2000
@@ -485,7 +485,7 @@
        err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
                               (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                               argc, argv, local, which, aflag, 1,
-                              preload_update_dir, 1);
+                              preload_update_dir, 1, NULL);
        if (err)
            return (err);
 
@@ -501,7 +501,7 @@
     err = start_recursion (update_fileproc, update_filesdone_proc,
                           update_dirent_proc, update_dirleave_proc, NULL,
                           argc, argv, local, which, aflag, 1,
-                          preload_update_dir, 1);
+                          preload_update_dir, 1, verify_read);
 
     /* see if we need to sleep before returning to avoid time-stamp races */
     if (last_register_time)
@@ -1368,7 +1368,8 @@
 
            if (cvswrite
                && !file_is_dead
-               && !fileattr_get (finfo->file, "_watched"))
+               && !fileattr_get (finfo->file, "_watched")
+               && verify_write (finfo->repository))
            {
                if (revbuf == NULL)
                    xchmod (finfo->file, 1);
@@ -1764,7 +1765,8 @@
                < 0)
                error (0, errno, "cannot change mode of file %s", finfo->file);
            if (cvswrite
-               && !fileattr_get (finfo->file, "_watched"))
+               && !fileattr_get (finfo->file, "_watched")
+               && verify_write (finfo->repository))
                xchmod (finfo->file, 1);
 
            /* Check the diff output to make sure patch will be handle it.  */
diff -urN cvs-1.11/src/watch.c cvs-1.11-auth/src/watch.c
--- cvs-1.11/src/watch.c        Tue Jun 13 16:30:44 2000
+++ cvs-1.11-auth/src/watch.c   Mon Nov  6 00:30:09 2000
@@ -353,7 +353,7 @@
     err = start_recursion (addremove_fileproc, addremove_filesdoneproc,
                           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                           argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
-                          1);
+                          1, verify_write);
 
     Lock_Cleanup ();
     return err;
@@ -526,5 +526,5 @@
     return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL,
                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
                            argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
-                           1);
+                           1, verify_write);
 }

Reply via email to