[Very nice to see people working on access control. I haven't looked at it in any detail. However, here are a few reactions based on a quick glance. Looks to be nicely done in terms of cvs.texinfo documentation (also needs NEWS), coding standards (mostly - whitespace would need tidying), &c. Needs sanity.sh test cases. The new -L option should be a setting in CVSROOT/config instead. That would be easy to fix. The -D option is a separate feature from the access control stuff, but also strikes me as pretty sensible. Same caveat about having it in a config file (this time, the mythical global config file). Probably want to make it able to remap rather than just add an initial path (e.g. "Remap /home/cvs /home2/real-repos-location"). On 23 Nov 1998, Corey Minyard writes: It will definitely need some tweaks. It has some minor problems with recursive operations on the new commands, for instance, and checking in from a base directory you don't have write access to will fail. And you could do some pretty nice things with it, I think. -kingdon] To: bug-cvs@gnu.org Subject: Patch to add internal user and permissions to CVS From: Corey Minyard Date: 19 Nov 1998 02:23:27 -0600 Hello, I've been using this patch for a while and I thought you might want it. I'm not sure you really want something like this, but here it is if you do. This patch adds internal permissions to CVS, so that each directory in the repository has it's own owner file and permissions file that is used to allow or deny access. It works with remote password or Kerberos authentication only. It is activated with a command-line parameter, without that the behaviour is normal. With this you don't have to rely on unix file permissions (which don't work quite right) on remote servers. It also has a patch to allow the user's to change their own passwords and for an administrator to manipulate users in the password file. This is relative to 1.10.3. -- Corey Minyard Internet: minyard@acm.org Work: minyard@nortel.ca UUCP: minyard@wf-rch.cirr.com diff -urN cvs-1.10.3.old/doc/cvs.texinfo cvs-1.10.3/doc/cvs.texinfo --- cvs-1.10.3.old/doc/cvs.texinfo Mon Oct 5 13:18:01 1998 +++ cvs-1.10.3/doc/cvs.texinfo Thu Nov 19 01:26:09 1998 @@ -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 @@ -1925,6 +1926,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 @@ -2556,6 +2558,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 @@ -6924,6 +7101,11 @@ * rtag:: Add a tag to a module * tag:: Add a tag to checked out version * 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -8556,6 +8738,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.10.3.old/doc/cvsclient.texi cvs-1.10.3/doc/cvsclient.texi --- cvs-1.10.3.old/doc/cvsclient.texi Tue Sep 15 19:29:33 1998 +++ cvs-1.10.3/doc/cvsclient.texi Thu Nov 19 00:34:04 1998 @@ -1041,6 +1041,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.10.3.old/emx/Makefile cvs-1.10.3/emx/Makefile --- cvs-1.10.3.old/emx/Makefile Wed Sep 9 13:06:26 1998 +++ cvs-1.10.3/emx/Makefile Wed Nov 18 14:20:36 1998 @@ -11,10 +11,10 @@ install_dir = s:/gnu/util # srcdir is usually "." -srcdir = ../../work/ccvs/emx +srcdir = . # top_srcdir is usually ".." -top_srcdir = ../../work/ccvs +top_srcdir = .. lib_dir = ${top_srcdir}/lib cvs_srcdir = ${top_srcdir}/src diff -urN cvs-1.10.3.old/os2/Makefile cvs-1.10.3/os2/Makefile --- cvs-1.10.3.old/os2/Makefile Wed Sep 9 13:06:19 1998 +++ cvs-1.10.3/os2/Makefile Wed Nov 18 14:20:35 1998 @@ -16,13 +16,13 @@ install_dir = s:${SL}gnu${SL}util # srcdir is usually "." -srcdir = ../../work/ccvs/os2 +srcdir = . # top_srcdir is usually ".." -top_srcdir = ../../work/ccvs +top_srcdir = .. -lib_dir = ../../work/ccvs${SL}lib -cvs_srcdir = ../../work/ccvs${SL}src +lib_dir = ..${SL}lib +cvs_srcdir = ..${SL}src zlib_dir = ${top_srcdir}/zlib diff -urN cvs-1.10.3.old/src/Makefile.in cvs-1.10.3/src/Makefile.in --- cvs-1.10.3.old/src/Makefile.in Tue Oct 6 18:00:11 1998 +++ cvs-1.10.3/src/Makefile.in Thu Nov 19 00:34:05 1998 @@ -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.10.3.old/src/add.c cvs-1.10.3/src/add.c --- cvs-1.10.3.old/src/add.c Tue Sep 1 09:17:05 1998 +++ cvs-1.10.3/src/add.c Thu Nov 19 01:07:53 1998 @@ -289,6 +289,9 @@ /* Find the repository associated with our current dir. */ repository = Name_Repository (NULL, finfo.update_dir); + if (! verify_create (repository)) + error (1, 0, "User '%s' cannot change %s", CVS_Username, repository); + entries = Entries_Open (0, NULL); finfo.repository = repository; @@ -665,6 +668,11 @@ goto out; } (void) umask (omask); + if (local_security) + { + change_owner(rcsdir, CVS_Username); + change_perms(rcsdir, "", NULL); + } } /* Now set the default file attributes to the ones we inherited diff -urN cvs-1.10.3.old/src/admin.c cvs-1.10.3/src/admin.c --- cvs-1.10.3.old/src/admin.c Tue Sep 1 09:17:06 1998 +++ cvs-1.10.3/src/admin.c Thu Nov 19 01:27:55 1998 @@ -427,7 +427,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: @@ -841,5 +841,4 @@ { if (!quiet) error (0, 0, "Administrating %s", update_dir); - return (R_PROCESS); } diff -urN cvs-1.10.3.old/src/checkout.c cvs-1.10.3/src/checkout.c --- cvs-1.10.3.old/src/checkout.c Sun Jul 26 22:15:19 1998 +++ cvs-1.10.3/src/checkout.c Thu Nov 19 01:07:57 1998 @@ -532,6 +532,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.10.3.old/src/client.c cvs-1.10.3/src/client.c --- cvs-1.10.3.old/src/client.c Tue Oct 6 16:45:37 1998 +++ cvs-1.10.3/src/client.c Thu Nov 19 00:37:31 1998 @@ -5497,7 +5497,8 @@ err = start_recursion (send_fileproc, send_filesdoneproc, send_dirent_proc, (DIRLEAVEPROC)NULL, (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.10.3.old/src/commit.c cvs-1.10.3/src/commit.c --- cvs-1.10.3.old/src/commit.c Mon Oct 12 10:47:18 1998 +++ cvs-1.10.3/src/commit.c Thu Nov 19 00:34:06 1998 @@ -463,7 +463,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!"); @@ -644,7 +644,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 (); @@ -659,7 +660,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.10.3.old/src/cvs.h cvs-1.10.3/src/cvs.h --- cvs-1.10.3.old/src/cvs.h Fri Sep 25 14:12:48 1998 +++ cvs-1.10.3/src/cvs.h Thu Nov 19 01:09:09 1998 @@ -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. */ @@ -493,6 +509,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)); @@ -620,6 +641,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)); @@ -633,7 +656,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)); void read_cvsrc PROTO((int *argc, char ***argv, char *cmdname)); diff -urN cvs-1.10.3.old/src/diff.c cvs-1.10.3/src/diff.c --- cvs-1.10.3.old/src/diff.c Wed Oct 7 16:40:15 1998 +++ cvs-1.10.3/src/diff.c Thu Nov 19 00:34:06 1998 @@ -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.10.3.old/src/edit.c cvs-1.10.3/src/edit.c --- cvs-1.10.3.old/src/edit.c Sun Sep 13 18:00:04 1998 +++ cvs-1.10.3/src/edit.c Thu Nov 19 00:38:12 1998 @@ -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; @@ -432,7 +432,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); @@ -590,7 +590,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); @@ -1097,5 +1097,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.10.3.old/src/import.c cvs-1.10.3/src/import.c --- cvs-1.10.3.old/src/import.c Tue Sep 29 20:57:32 1998 +++ cvs-1.10.3/src/import.c Thu Nov 19 01:08:01 1998 @@ -269,6 +269,9 @@ } #endif + 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. diff -urN cvs-1.10.3.old/src/listperm.c cvs-1.10.3/src/listperm.c --- cvs-1.10.3.old/src/listperm.c Wed Dec 31 18:00:00 1969 +++ cvs-1.10.3/src/listperm.c Thu Nov 19 02:08:49 1998 @@ -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. + * + * listperm + * + * Shows the current permissions + */ + +#include "cvs.h" + +static const char *const listperm_usage[] = +{ + "Usage: %s %s [= 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); +} + +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; + + filename = xmalloc (strlen (dir) + + strlen ("/owner") + + 1); + + strcpy (filename, dir); + strcat (filename, "/owner"); + + fp = CVS_FOPEN (filename, "r"); + 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; + } + + filename = xmalloc (strlen (dir) + + strlen ("/perms") + + 1); + + strcpy (filename, dir); + strcat (filename, "/perms"); + + fp = CVS_FOPEN (filename, "r"); + 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; + char *linebuf = NULL; + int linebuf_len; + char *owner; + FILE *fp; + int retval = 0; + + + if (!local_security) + { + error (0, 0, "No owner"); + return; + } + + filename = xmalloc (strlen (dir) + + strlen ("/owner") + + 1); + + strcpy (filename, dir); + strcat (filename, "/owner"); + + fp = CVS_FOPEN (filename, "r"); + 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); + cvs_output ("\n", 0); + free (linebuf); + } + else + error (0, 0, "Error reading line in %d", filename); + + if (ferror (fp)) + error (0, errno, "cannot read %s", filename); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", filename); + } + + free (filename); +} + +void +list_perms (dir) + const char *dir; +{ + char *filename; + char *linebuf = NULL; + int linebuf_len; + char *name; + char *permptr; + FILE *fp; + int retval = 1; + + + if (!local_security) + { + return; + } + + filename = xmalloc (strlen (dir) + + strlen ("/perms") + + 1); + + strcpy (filename, dir); + strcat (filename, "/perms"); + + fp = CVS_FOPEN (filename, "r"); + 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); + 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); +} + diff -urN cvs-1.10.3.old/src/rcs.c cvs-1.10.3/src/rcs.c --- cvs-1.10.3.old/src/rcs.c Sat Oct 3 15:22:17 1998 +++ cvs-1.10.3/src/rcs.c Thu Nov 19 00:34:06 1998 @@ -8691,7 +8691,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.10.3.old/src/recurse.c cvs-1.10.3/src/recurse.c --- cvs-1.10.3.old/src/recurse.c Tue Sep 29 21:01:20 1998 +++ cvs-1.10.3/src/recurse.c Thu Nov 19 01:08:30 1998 @@ -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); @@ -261,7 +302,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) @@ -597,6 +648,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); /* @@ -1104,6 +1164,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.10.3.old/src/remove.c cvs-1.10.3/src/remove.c --- cvs-1.10.3.old/src/remove.c Tue Sep 1 09:17:20 1998 +++ cvs-1.10.3/src/remove.c Thu Nov 19 01:12:32 1998 @@ -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. */ @@ -112,7 +112,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.10.3.old/src/root.c cvs-1.10.3/src/root.c --- cvs-1.10.3.old/src/root.c Thu Sep 24 19:17:25 1998 +++ cvs-1.10.3/src/root.c Thu Nov 19 00:46:00 1998 @@ -291,6 +291,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) @@ -496,7 +497,19 @@ set_local_cvsroot (dir) char *dir; { - CVSroot_original = dir; + if (CVSroot_prefix == NULL) + { + CVSroot_original = xstrdup (dir); + } + else + { + CVSroot_original = malloc (strlen(dir) + strlen(CVSroot_prefix) + 1); + if (CVSroot_original != NULL) + { + strcpy(CVSroot_original, CVSroot_prefix); + strcat(CVSroot_original, dir); + } + } CVSroot_method = local_method; CVSroot_directory = CVSroot_original; CVSroot_username = NULL; diff -urN cvs-1.10.3.old/src/rtag.c cvs-1.10.3/src/rtag.c --- cvs-1.10.3.old/src/rtag.c Tue Jul 14 19:59:50 1998 +++ cvs-1.10.3/src/rtag.c Thu Nov 19 00:46:31 1998 @@ -316,7 +316,7 @@ err = start_recursion (check_fileproc, check_filesdoneproc, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, *pargc - 1, argv + 1, local, which, 0, 1, - where, 1); + where, 1, verify_write); if (err) { @@ -327,7 +327,7 @@ err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc, (DIRLEAVEPROC) NULL, NULL, *pargc - 1, argv + 1, local, - which, 0, 0, where, 1); + which, 0, 0, where, 1, verify_write); free (where); dellist(&mtlist); diff -urN cvs-1.10.3.old/src/server.c cvs-1.10.3/src/server.c --- cvs-1.10.3.old/src/server.c Thu Sep 24 19:17:31 1998 +++ cvs-1.10.3/src/server.c Thu Nov 19 02:50:22 1998 @@ -3314,6 +3314,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; @@ -4429,6 +4464,11 @@ REQ_LINE("expand-modules", serve_expand_modules, rq_optional), REQ_LINE("ci", serve_ci, rq_essential), REQ_LINE("co", serve_co, rq_essential), + REQ_LINE("setowner", serve_setowner, rq_optional), + REQ_LINE("setperm", serve_setperm, rq_optional), + REQ_LINE("listperm", serve_listperm, rq_optional), + REQ_LINE("setpass", serve_setpass, rq_optional), + REQ_LINE("deluser", serve_deluser, rq_optional), REQ_LINE("update", serve_update, rq_essential), REQ_LINE("diff", serve_diff, rq_optional), REQ_LINE("log", serve_log, rq_optional), @@ -5204,6 +5244,7 @@ char *descrambled_password; #endif /* AUTH_SERVER_SUPPORT */ int verify_and_exit = 0; + char *real_repository = NULL; /* The Authentication Protocol. Client sends: * @@ -5327,7 +5368,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) @@ -5371,7 +5432,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 (tmp); free (repository); free (username); @@ -5456,7 +5518,8 @@ } /* Switch to run as this user. */ - switch_to_user (user); + if (! local_security) + switch_to_user (user); } #endif /* HAVE_KERBEROS */ @@ -5548,10 +5611,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); } @@ -5568,7 +5635,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.10.3.old/src/setowner.c cvs-1.10.3/src/setowner.c --- cvs-1.10.3.old/src/setowner.c Wed Dec 31 18:00:00 1969 +++ cvs-1.10.3/src/setowner.c Thu Nov 19 01:15:15 1998 @@ -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.10.3.old/src/setpass.c cvs-1.10.3/src/setpass.c --- cvs-1.10.3.old/src/setpass.c Wed Dec 31 18:00:00 1969 +++ cvs-1.10.3/src/setpass.c Thu Nov 19 01:20:33 1998 @@ -0,0 +1,466 @@ +/* + * 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; + List *userlist; + Node *namenode; + char *rest; + char *oldpassword; + char *oldrest; + FILE *fp; + char *linebuf = NULL; + int linebuf_len; + char *username; + int adduser = 0; + + + 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; + char setup_str[1024]; + + 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"); + + sprintf (setup_str, "co -l %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 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); + + fp = CVS_FOPEN (filename, "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); + + sprintf (setup_str, "ci %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 0) + error (0, 0, "Error checking in password file"); + + sprintf (setup_str, "co %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 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; + List *userlist; + Node *namenode; + char *rest; + char *oldpassword; + char *oldrest; + FILE *fp; + char *linebuf = NULL; + int linebuf_len; + char setup_str[1024]; + + + 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"); + + sprintf (setup_str, "co -l %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 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, CVS_Username); + if (namenode == NULL) + { + error (1, 0, "Could not find %s in password file", CVS_Username); + } + 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); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", filename); + } + + dellist(&userlist); + + sprintf (setup_str, "ci %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 0) + error (0, 0, "Error checking in password file"); + + sprintf (setup_str, "co %s", filename); + run_setup (setup_str); + if (run_exec("/dev/null", "/dev/null", "/dev/null", RUN_REALLY) != 0) + error (0, 0, "Error checking out password file"); + + free (filename); + + return (err); +} diff -urN cvs-1.10.3.old/src/setperm.c cvs-1.10.3/src/setperm.c --- cvs-1.10.3.old/src/setperm.c Wed Dec 31 18:00:00 1969 +++ cvs-1.10.3/src/setperm.c Thu Nov 19 01:15:42 1998 @@ -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.10.3.old/src/status.c cvs-1.10.3/src/status.c --- cvs-1.10.3.old/src/status.c Tue Sep 1 09:17:41 1998 +++ cvs-1.10.3/src/status.c Thu Nov 19 00:34:07 1998 @@ -105,7 +105,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.10.3.old/src/tag.c cvs-1.10.3/src/tag.c --- cvs-1.10.3.old/src/tag.c Sun Sep 13 13:36:05 1998 +++ cvs-1.10.3/src/tag.c Thu Nov 19 00:34:07 1998 @@ -201,7 +201,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) { @@ -211,7 +211,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); } @@ -835,7 +835,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.10.3.old/src/update.c cvs-1.10.3/src/update.c --- cvs-1.10.3.old/src/update.c Fri Oct 9 10:49:22 1998 +++ cvs-1.10.3/src/update.c Thu Nov 19 01:13:44 1998 @@ -466,7 +466,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); @@ -482,7 +482,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 */ if (last_register_time) @@ -1323,7 +1323,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); @@ -1719,7 +1720,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.10.3.old/src/watch.c cvs-1.10.3/src/watch.c --- cvs-1.10.3.old/src/watch.c Sun Sep 13 18:07:10 1998 +++ cvs-1.10.3/src/watch.c Thu Nov 19 00:34:07 1998 @@ -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; @@ -525,5 +525,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); } --- cvs-1.10.3.old/ChangeLog Sat Oct 3 07:55:46 1998 +++ cvs-1.10.3/ChangeLog Thu Nov 19 03:11:13 1998 @@ -1,3 +1,7 @@ +1998-11-19 Corey Minyard + + * Added code to handle permissions inside the CVS repository. + 1998-10-03 Jim Kingdon * TODO (31): Mention the ,foo.c, and SIGINT issue.