diff -urN orig/cvs-1.11.1p1/ChangeLog cvs-1.11.1p1/ChangeLog --- orig/cvs-1.11.1p1/ChangeLog Thu Apr 26 13:12:56 2001 +++ cvs-1.11.1p1/ChangeLog Mon Jul 23 22:36:59 2001 @@ -1,3 +1,24 @@ + +2001-07-23 Corey Minyard + + * src/server.c: Fixed a bug in where the wrong function was + being called for chown. + + * src/perms.c: Added the ability to have user groups. + + * src/passwd.c: Removed reference to PATH_MAX and allow arbitrary + string sizes. + * src/chown.c: ditto + * src/chacl.c: ditto + * src/lsacl.c: ditto + +2001-07-10 Corey Minyard + + * Ported permissions code and prefix code forward to 1.11.1p1. + + * Added the ability for permissions to propigate down + directories. + 2001-04-26 Derek Price * cvs.spec.in: Don't include %{_infodir}/dir. @@ -130,6 +151,16 @@ * TODO: Add note about cvs_temp_file. +2001-02-11 Corey Minyard , Mark Ferrell (mferrell@mvista.com) + + * Renamed the permissions subcommands to more sane names: lsacl, + chacl, passwd, adduser, chown. + + * Allowed the lsacl, chacl, and chown commands to take no parameters + and default to the current directory. + + * Modified import to use the access control lists properly. + 2001-02-07 Derek Price * cvs.spec.in (build): Remove the info 'dir' file so it doesn't get @@ -588,6 +619,12 @@ * cvsnt.mak: Remove vasprintf. Plus of course the usual "because the IDE feels like it" changes. +1999-06-29 Mark Ferrell + + * Fixed some bugs in permission handling, for the most part the + handling of non-existent perms/owner files. Also fixed a minor problem + with setpass causing SIG11's; see src/ChangeLog for details + 1999-02-09 Jim Kingdon * configure.in (AC_REPLACE_FUNCS): Remove vasprintf; see @@ -629,6 +666,10 @@ * TODO (149): Update since -d doesn't rewrite CVS/Root any more. +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. diff -urN orig/cvs-1.11.1p1/doc/cvs.texinfo cvs-1.11.1p1/doc/cvs.texinfo --- orig/cvs-1.11.1p1/doc/cvs.texinfo Tue Apr 24 13:14:52 2001 +++ cvs-1.11.1p1/doc/cvs.texinfo Mon Jul 23 22:09:33 2001 @@ -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 @@ -2051,6 +2052,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 @@ -2762,6 +2764,221 @@ 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 +* Setting up groups:: Groups of users can be assigned permissions +* 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} adduser command can be used to add new +users. Only the +administrator can do this. If the adduser 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 adduser 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} chacl 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 chacl 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} lsacl +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. + +If the CVS server is started with the @samp{-P} option, +permissions will propigate down from directories to +subdirectories. So, for instance, if directory +@samp{a} has subdirectory @samp{b}, then any +permissions in the @samp{a} directory will +automatically be available in the @samp{b} directory. +This is not really recommended for groups that need +decent security, it's too easy to make a mistake and +you can't deny read access to anyone with this (since +everyone needs read access to the top-level directory). + +The owner or a directory can be reassigned using the +@sc{cvs} chown command. + +@node Setting up groups +@section Groups of users can be assigned permissions + +Sometime administrators find it easier to maintain permissions on +groups of users instead of on individual users. That way, if a group +of people have access to a directory, the group can be assigned rights +to the directories and the administrator only needs to modify the +members of the group to maintain the permissions. + +The @code{group} file in the @code{CVSROOT} directory holds a list of +groups. The file has two fields seperated by a colon, the first +is the group name, the second is a list of group members, separated +by white space, such as: + +@example +group1: user1 user2 user3 +group2: me you dognamedblue +group3: peter paul mary +@end example + +To set up groups, check out the @code{CVSROOT} +directory as the admin, create the @code{group} file, +add the groups, then check it in. Then set up the +permissions for the groups, commit the changes, and you +are done. It's that easy. You should always access +the group file by checking out CVSROOT, accessing it +directly on the server can cause problems. + +@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} passwd 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 @@ -7510,6 +7727,12 @@ * rdiff:: 'patch' format diffs between releases * release:: Indicate that a directory is no longer in use * update:: Bring work tree in sync with repository +* adduser:: Add a user to the repository +* passwd:: Modify a user's password +* deluser:: Delete a user +* chacl:: Set a user's permissions for a directory +* chown:: Change a directory's owner +* lsacl:: Show a directory's permissions @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -9150,6 +9373,120 @@ @example $ cvs diff -u | less @end example + +@node adduser +@appendixsec Add a user to the repository +@cindex adduser (subcommand) + +@itemize @bullet +@item +adduser username +@item +Requires: repository +@item +Changes: password file +@end itemize + +If invoked by the administrator, it adds a user. + +This only works with remote pserver repositories. + +@node passwd +@appendixsec Modify a user's password or create a user +@cindex passwd (subcommand) + +@itemize @bullet +@item +passwd [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. + +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 chacl +@appendixsec Set a user's permissions for a directory +@cindex chasl (subcommand) + +@itemize @bullet +@item +chacl 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 chown +@appendixsec Change a directory's owner +@cindex chown (subcommand) + +@itemize @bullet +@item +chown 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 lsacl +@appendixsec Show a directory's permissions +@cindex lsacl (subcommand) + +@itemize @bullet +@item +lsacl 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 orig/cvs-1.11.1p1/doc/cvsclient.texi cvs-1.11.1p1/doc/cvsclient.texi --- orig/cvs-1.11.1p1/doc/cvsclient.texi Tue Apr 24 13:14:52 2001 +++ cvs-1.11.1p1/doc/cvsclient.texi Mon Jul 9 19:11:28 2001 @@ -1082,6 +1082,11 @@ @itemx watchers \n @itemx editors \n @itemx annotate \n +@itemx chown (setowner) \n +@itemx chacl (setperm) \n +@itemx passwd (setpass) \n +@itemx lsacl (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 orig/cvs-1.11.1p1/src/ChangeLog cvs-1.11.1p1/src/ChangeLog --- orig/cvs-1.11.1p1/src/ChangeLog Fri Apr 27 14:57:23 2001 +++ cvs-1.11.1p1/src/ChangeLog Mon Jul 9 19:11:28 2001 @@ -2623,6 +2623,26 @@ * version.c: Version 1.10.5. +1999-06-29 Mark Ferrell + + * src/listperm.c: Modified output to correctly reflect that we are + listing permissions on a directory. + * src/perms.c: Fixed list_perm to correctly handle the lack of + existance of the perms file, to which it assumes no permissions have + been applied. list_owner fixed to correctly handle lack of an owner + file, to which it assumes no owner for that directory. As well, the + setowner command can set the owner to a zero length string via + 'cvs setowner "" ' ... this feature alwayz existed, but the + listperm command would fail when listing the owner, it now reports that + no owner is set. + * src/setpass.c: moved the call to free(filename); in in setpass() to + inside the #ifdef AUTH_SERVER_SUPPORT, also moved the declaration of + that char * filename to reside inside of that same ifdef as that's the + only time it is used. Moved the call to dellist(&userlist) inside of + same #ifdef to be called AFTER the call to free(filename) as something + in there is stepping on memory and was causing a SIG11. Problem is not + completely fixed, just avoided for the most part. + 1999-02-18 Jim Kingdon * sanity.sh (files): New test, for a relatively obscure spurious diff -urN orig/cvs-1.11.1p1/src/Makefile.in cvs-1.11.1p1/src/Makefile.in --- orig/cvs-1.11.1p1/src/Makefile.in Fri Apr 27 15:02:25 2001 +++ cvs-1.11.1p1/src/Makefile.in Mon Jul 9 19:15:02 2001 @@ -175,7 +175,12 @@ rcs.h \ server.h \ update.h \ - watch.h + watch.h \ + chown.c \ + chacl.c \ + perms.c \ + lsacl.c \ + passwd.c cvs_LDADD = \ ../diff/libdiff.a \ @@ -227,7 +232,9 @@ repos.$(OBJEXT) root.$(OBJEXT) run.$(OBJEXT) scramble.$(OBJEXT) \ server.$(OBJEXT) status.$(OBJEXT) subr.$(OBJEXT) tag.$(OBJEXT) \ update.$(OBJEXT) vers_ts.$(OBJEXT) watch.$(OBJEXT) \ - wrapper.$(OBJEXT) zlib.$(OBJEXT) + wrapper.$(OBJEXT) zlib.$(OBJEXT) chown.$(OBJEXT) chacl.$(OBJEXT) \ + perms.$(OBJEXT) lsacl.$(OBJEXT) passwd.$(OBJEXT) + cvs_OBJECTS = $(am_cvs_OBJECTS) cvs_DEPENDENCIES = ../diff/libdiff.a ../lib/libcvs.a ../zlib/libz.a \ version.o diff -urN orig/cvs-1.11.1p1/src/add.c cvs-1.11.1p1/src/add.c --- orig/cvs-1.11.1p1/src/add.c Thu Apr 19 14:45:31 2001 +++ cvs-1.11.1p1/src/add.c Tue Jul 24 13:56:22 2001 @@ -334,6 +334,11 @@ CVSNULLREPOS) == 0) error (1, 0, "cannot add to %s", repository); +#ifdef SERVER_SUPPORT + if (! verify_create (repository)) + error (1, 0, "User '%s' cannot change %s", CVS_Username, repository); +#endif + entries = Entries_Open (0, NULL); finfo.repository = repository; @@ -775,6 +780,13 @@ goto out; } (void) umask (omask); +#ifdef SERVER_SUPPORT + if (local_security) + { + change_owner(rcsdir, CVS_Username); + change_perms(rcsdir, "", NULL); + } +#endif } /* Now set the default file attributes to the ones we inherited diff -urN orig/cvs-1.11.1p1/src/admin.c cvs-1.11.1p1/src/admin.c --- orig/cvs-1.11.1p1/src/admin.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/admin.c Tue Jul 10 00:13:42 2001 @@ -509,7 +509,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_owner); Lock_Cleanup (); return_it: diff -urN orig/cvs-1.11.1p1/src/annotate.c cvs-1.11.1p1/src/annotate.c --- orig/cvs-1.11.1p1/src/annotate.c Fri Apr 20 11:13:12 2001 +++ cvs-1.11.1p1/src/annotate.c Mon Jul 9 19:57:51 2001 @@ -241,7 +241,7 @@ err = start_recursion (annotate_fileproc, (FILESDONEPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1, local, which, 0, 1, - where, 1); + where, 1, verify_write); return err; } diff -urN orig/cvs-1.11.1p1/src/chacl.c cvs-1.11.1p1/src/chacl.c --- orig/cvs-1.11.1p1/src/chacl.c Wed Dec 31 18:00:00 1969 +++ cvs-1.11.1p1/src/chacl.c Tue Jul 24 14:19:32 2001 @@ -0,0 +1,187 @@ +/* + * 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. + * + * chacl + * + * Sets the permission for the specified user for the directory + */ + +#include "cvs.h" + +static const char *const chacl_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 +chacl (argc, argv) + int argc; + char **argv; +{ + char *user; + int i; + char *repository; + int c; + int err = 0; + char *permptr; + char *new_argv[2]; /* Used if no filenames are specified. */ + + + if (argc == 1 || argc == -1) + usage (chacl_usage); + + wrap_setup (); + + optind = 1; + while ((c = getopt (argc, argv, "")) != -1) + { + switch (c) + { + case '?': + default: + usage (chacl_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (argc <= 0) + usage (chacl_usage); + + if (argc == 1) + { + /* We have no filenames specified, default to the current directory. */ + + new_argv[0] = argv[0]; + new_argv[1] = "."; + argv = new_argv; + argc++; + } + + /* find the repository associated with our current dir */ + repository = Name_Repository ((char *) NULL, (char *) NULL); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + 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 ("chacl\012", 0); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + if (!server_active) + { + error (1, 0, "Server is not active, not permitted interactively"); + } + + 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; + dname = xmalloc(strlen(repository) + + strlen(argv[i]) + + 2); + + (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); + } + } + free(dname); + } + + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} diff -urN orig/cvs-1.11.1p1/src/checkout.c cvs-1.11.1p1/src/checkout.c --- orig/cvs-1.11.1p1/src/checkout.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/checkout.c Tue Jul 24 13:57:03 2001 @@ -536,6 +536,13 @@ (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); Sanitize_Repository_Name (repository); +#ifdef SERVER_SUPPORT + if (! verify_read(repository)) + { + error (0, 0, "User %s cannot access %s", CVS_Username, argv[0]); + return (1); + } +#endif /* save the original value of preload_update_dir */ if (preload_update_dir != NULL) diff -urN orig/cvs-1.11.1p1/src/chown.c cvs-1.11.1p1/src/chown.c --- orig/cvs-1.11.1p1/src/chown.c Wed Dec 31 18:00:00 1969 +++ cvs-1.11.1p1/src/chown.c Tue Jul 24 14:17:16 2001 @@ -0,0 +1,202 @@ +/* + * 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. + * + * chown + * + * Changes the owner of a directory to the given name. + */ + +#include "cvs.h" + +static void +node_deleted(p) + Node *p; +{ + if (p->data != NULL) + free (p->data); + p->key = p->data = NULL; +} + +static const char *const chown_usage[] = +{ + "Usage: %s %s user directory...\n", + NULL +}; + +int +chowner (argc, argv) + int argc; + char **argv; +{ + char *user; + int i; + char *repository; + int c; + int err = 0; + char *new_argv[2]; /* Used if no filenames are specified. */ + + + if (argc == 1 || argc == -1) + usage (chown_usage); + + wrap_setup (); + + optind = 1; + while ((c = getopt (argc, argv, "")) != -1) + { + switch (c) + { + case '?': + default: + usage (chown_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (argc <= 0) + usage (chown_usage); + + if (argc == 1) + { + /* We have no filenames specified, default to the current directory. */ + + new_argv[0] = argv[0]; + new_argv[1] = "."; + argv = new_argv; + argc++; + } + + /* find the repository associated with our current dir */ + repository = Name_Repository ((char *) NULL, (char *) NULL); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + 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 ("chown\012", 0); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + if (!server_active) + { + error (1, 0, "Server is not active, not permitted interactively"); + } + + user = argv[0]; + + /* walk the arg list checking files/dirs */ + for (i = 1; i < argc; i++) + { + char *dname; + dname = xmalloc(strlen(repository) + + strlen(argv[i]) + + 2); + (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 + { + Node *namenode; + FILE *fp; + List *userlist; + char *filename; + char *linebuf = NULL; + int linebuf_len; + + filename = xmalloc(strlen(current_parsed_root->directory) + + strlen("/CVSROOT/passwd") + + 1); + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT/passwd"); + + checkout_file(filename); + + userlist = getlist(); + + fp = CVS_FOPEN (filename, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", filename); + free(dname); + free(filename); + return 0; + } + else + { + 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 = (char *) xstrdup (strtok (NULL, "\n")); + namenode->delproc = node_deleted; + addnode(userlist, namenode); + } + + free (linebuf); + linebuf = NULL; + } + if (ferror (fp)) { + checkin_file(filename); + 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) + { + error (0, 0, "User %s does not exist", user); + } + else + { + change_owner (dname, user); + } + + checkin_file(filename); + free(filename); + + dellist(&userlist); + } + } + free(dname); + } + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} diff -urN orig/cvs-1.11.1p1/src/client.c cvs-1.11.1p1/src/client.c --- orig/cvs-1.11.1p1/src/client.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/client.c Tue Jul 10 01:41:12 2001 @@ -5613,7 +5613,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 orig/cvs-1.11.1p1/src/commit.c cvs-1.11.1p1/src/commit.c --- orig/cvs-1.11.1p1/src/commit.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/commit.c Mon Jul 9 19:11:28 2001 @@ -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 orig/cvs-1.11.1p1/src/cvs.h cvs-1.11.1p1/src/cvs.h --- orig/cvs-1.11.1p1/src/cvs.h Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/cvs.h Tue Jul 24 14:04:58 2001 @@ -186,6 +186,7 @@ #define CVSROOTADM_READERS "readers" #define CVSROOTADM_WRITERS "writers" #define CVSROOTADM_PASSWD "passwd" +#define CVSROOTADM_GROUP "group" #define CVSROOTADM_CONFIG "config" #define CVSNULLREPOS "Emptydir" /* an empty directory */ @@ -385,11 +386,39 @@ #endif /* CLIENT_SUPPORT */ } cvsroot_t; +extern char *CVSroot_prefix; /* The directory prefix supplied on the + command line, or NULL if none + supplied. */ + /* This global variable holds the global -d option. It is NULL if -d was not used, which means that we must get the CVSroot information from the CVSROOT environment variable or from a CVS/Root file. */ extern char *CVSroot_cmdline; +extern int local_security; /* Only use the built-in CVS security system */ +int perms_propigate_down; /* If this is set, the code will move up + directories and check permission up + to (but not including) the root + directory. So if a user has + permissions in a directory, they will + have the same permissions in all + subdirectories. */ + +/* 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 *)); +#ifdef SERVER_SUPPORT +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 *)); +#endif + /* These variables keep track of all of the CVSROOT directories that have been seen by the client and the current one of those selected. */ extern List *root_directories; @@ -506,6 +535,12 @@ int unlink_file PROTO((const char *f)); int unlink_file_dir PROTO((const char *f)); int update PROTO((int argc, char *argv[])); +int chowner PROTO((int argc, char **argv)); +int chacl PROTO((int argc, char **argv)); +int lsacl PROTO((int argc, char **argv)); +int passwd PROTO((int argc, char **argv)); +int deluser PROTO((int argc, char **argv)); +int adduser PROTO((int argc, char **argv)); int xcmp PROTO((const char *file1, const char *file2)); int yesno PROTO((void)); void *valloc PROTO((size_t bytes)); @@ -634,6 +669,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)); @@ -648,7 +685,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 orig/cvs-1.11.1p1/src/diff.c cvs-1.11.1p1/src/diff.c --- orig/cvs-1.11.1p1/src/diff.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/diff.c Mon Jul 9 19:11:28 2001 @@ -371,7 +371,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 orig/cvs-1.11.1p1/src/edit.c cvs-1.11.1p1/src/edit.c --- orig/cvs-1.11.1p1/src/edit.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/edit.c Mon Jul 9 19:11:28 2001 @@ -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; @@ -443,7 +443,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); @@ -612,7 +612,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); @@ -1135,5 +1135,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 orig/cvs-1.11.1p1/src/filesubr.c cvs-1.11.1p1/src/filesubr.c --- orig/cvs-1.11.1p1/src/filesubr.c Thu Apr 19 14:45:32 2001 +++ cvs-1.11.1p1/src/filesubr.c Tue Jul 24 13:58:29 2001 @@ -305,7 +305,18 @@ if (noexec) return; - if (mkdir (name, 0777) == 0 || errno == EEXIST) + if (mkdir (name, 0777) == 0) + { +#ifdef SERVER_SUPPORT + if (local_security) + { + change_owner(name, CVS_Username); + change_perms(name, "", NULL); + } +#endif + return; + } + if (errno == EEXIST) return; if (! existence_error (errno)) { @@ -320,6 +331,13 @@ if (*cp == '\0') return; (void) mkdir (name, 0777); +#ifdef SERVER_SUPPORT + if (local_security) + { + change_owner(name, CVS_Username); + change_perms(name, "", NULL); + } +#endif } /* Create directory NAME if it does not already exist; fatal error for diff -urN orig/cvs-1.11.1p1/src/hash.c cvs-1.11.1p1/src/hash.c --- orig/cvs-1.11.1p1/src/hash.c Thu Apr 19 14:45:32 2001 +++ cvs-1.11.1p1/src/hash.c Mon Jul 9 19:11:28 2001 @@ -149,6 +149,10 @@ memset ((char *) p, 0, sizeof (Node)); p->type = NT_UNKNOWN; + /* to be safe, re-initialize these */ + p->key = p->data = (char *) NULL; + p->delproc = (void (*) ()) NULL; + return (p); } diff -urN orig/cvs-1.11.1p1/src/import.c cvs-1.11.1p1/src/import.c --- orig/cvs-1.11.1p1/src/import.c Thu Apr 19 14:45:32 2001 +++ cvs-1.11.1p1/src/import.c Tue Jul 24 13:59:11 2001 @@ -283,6 +283,11 @@ error (1, 0, "attempt to import the repository"); } +#ifdef SERVER_SUPPORT + if (! verify_create (repository)) + error (1, 0, "User %s cannot create files in %s", CVS_Username, argv[0]); +#endif + /* * Make all newly created directories writable. Should really use a more * sophisticated security mechanism here. @@ -1566,6 +1571,13 @@ repository = new; } +#ifdef SERVER_SUPPORT + /* Verify we have create access in this directory. */ + if (! verify_create (repository)) + error (1, 0, "User %s cannot create files in %s", + CVS_Username, repository); +#endif + #ifdef CLIENT_SUPPORT if (!quiet && !current_parsed_root->isremote) #else @@ -1609,6 +1621,13 @@ err = 1; goto out; } +#ifdef SERVER_SUPPORT + if (local_security) + { + change_owner(repository, CVS_Username); + change_perms(repository, "", NULL); + } +#endif } err = import_descend (message, vtag, targc, targv); out: diff -urN orig/cvs-1.11.1p1/src/lock.c cvs-1.11.1p1/src/lock.c --- orig/cvs-1.11.1p1/src/lock.c Thu Apr 19 14:45:32 2001 +++ cvs-1.11.1p1/src/lock.c Mon Jul 9 19:18:53 2001 @@ -906,7 +906,8 @@ lock_tree_list = getlist (); err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, - argv, local, which, aflag, 0, (char *) NULL, 0); + argv, local, which, 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 orig/cvs-1.11.1p1/src/log.c cvs-1.11.1p1/src/log.c --- orig/cvs-1.11.1p1/src/log.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/log.c Mon Jul 9 19:19:57 2001 @@ -540,7 +540,7 @@ err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc, (DIRLEAVEPROC) NULL, (void *) &log_data, argc - 1, argv + 1, local, which, 0, 1, - where, 1); + where, 1, verify_read); return err; } diff -urN orig/cvs-1.11.1p1/src/lsacl.c cvs-1.11.1p1/src/lsacl.c --- orig/cvs-1.11.1p1/src/lsacl.c Wed Dec 31 18:00:00 1969 +++ cvs-1.11.1p1/src/lsacl.c Tue Jul 24 14:20:01 2001 @@ -0,0 +1,129 @@ +/* + * 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. + * + * lsacl + * + * Shows the current permissions + */ + +#include "cvs.h" + +static const char *const lsacl_usage[] = +{ + "Usage: %s %s [isremote) + { + start_server (); + ign_setup (); + + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_files (argc, argv, 0, 0, SEND_NO_CONTENTS); + send_to_server ("lsacl\012", 0); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + if (!server_active) + { + error (1, 0, "Server is not active, not permitted interactively"); + } + + /* walk the arg list checking files/dirs */ + for (i = 0; i < argc; i++) + { + char *dname; + dname = xmalloc(strlen(repository) + + strlen(argv[i]) + + 2); + (void) sprintf (dname, "%s/%s", repository, argv[i]); + 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 + { + /* + * Changed it so that the output makes more sense ... permissions + * exist on Directories .. not files. + */ + cvs_output ("Directory: ", 0); + cvs_output (argv[i], 0); + cvs_output ("\n", 0); + list_owner (dname); + list_perms (dname); + } + } + free(dname); + } + + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} diff -urN orig/cvs-1.11.1p1/src/main.c cvs-1.11.1p1/src/main.c --- orig/cvs-1.11.1p1/src/main.c Fri Apr 27 14:57:23 2001 +++ cvs-1.11.1p1/src/main.c Tue Jul 24 14:09:40 2001 @@ -143,6 +143,12 @@ { "version", "ve", "ver", version, 0 }, { "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, { "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR }, + { "chown", "setowner", NULL, chowner, CVS_CMD_USES_WORK_DIR }, + { "chacl", "chattr", "setperm", chacl, CVS_CMD_USES_WORK_DIR }, + { "lsacl", "lsattr", "listperm", lsacl, CVS_CMD_USES_WORK_DIR }, + { "passwd", "password", "setpass", passwd, CVS_CMD_USES_WORK_DIR }, + { "deluser", "userdel", NULL, deluser, CVS_CMD_USES_WORK_DIR }, + { "adduser", "useradd", NULL, adduser, CVS_CMD_USES_WORK_DIR }, { NULL, NULL, NULL, NULL, 0 }, }; @@ -234,6 +240,12 @@ " version Show current CVS version(s)\n", " watch Set watches\n", " watchers See who is watching a file\n", + " chown Change the owner of a directory\n", + " chacl Change the Access Control List for a directory\n", + " lsacl List the directories Access Control List\n", + " passwd Set the user's password\n", + " deluser Delete a user (Admin Only)\n", + " adduser Add a user (Admin Only)\n", "(Specify the --help option for a list of other help options)\n", NULL, }; @@ -242,7 +254,12 @@ { /* Omit -b because it is just for compatibility. */ "CVS global options (specified before the command name) are:\n", + " -D prefix Adds a prefix to CVSROOT\n", " -H Displays usage information for command.\n", +#ifdef SERVER_SUPPORT + " -L Use local CVS security\n", + " -P Local security permissions propigate to subdirs.\n", +#endif " -Q Cause CVS to be really quiet.\n", " -q Cause CVS to be somewhat quiet.\n", " -r Make checked-out files read-only.\n", @@ -405,7 +422,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:LPHfz:s:xa"; static struct option long_options[] = { {"help", 0, NULL, 'H'}, @@ -580,6 +597,17 @@ free_CVSroot = 1; cvs_update_env = 1; /* need to update environment */ break; + case 'D': + CVSroot_prefix = optarg; + break; +#ifdef SERVER_SUPPORT + case 'L': + local_security = 1; + break; + case 'P': + perms_propigate_down = 1; + break; +#endif case 'H': help = 1; break; diff -urN orig/cvs-1.11.1p1/src/mkmodules.c cvs-1.11.1p1/src/mkmodules.c --- orig/cvs-1.11.1p1/src/mkmodules.c Thu Apr 19 14:45:32 2001 +++ cvs-1.11.1p1/src/mkmodules.c Mon Jul 23 21:54:37 2001 @@ -359,6 +359,9 @@ elsewhere, using a similar password, etc, and so saving old passwords, even hashed, is probably not a good idea. */ + {CVSROOTADM_GROUP, + "a %s file specifies user groups", + NULL}, {CVSROOTADM_CONFIG, "a %s file configures various behaviors", config_contents}, @@ -857,6 +860,9 @@ return get_responses_and_close (); } #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 diff -urN orig/cvs-1.11.1p1/src/passwd.c cvs-1.11.1p1/src/passwd.c --- orig/cvs-1.11.1p1/src/passwd.c Wed Dec 31 18:00:00 1969 +++ cvs-1.11.1p1/src/passwd.c Tue Jul 24 14:22:01 2001 @@ -0,0 +1,766 @@ +/* + * 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. + * + * passwd + * + * Changes the password of the caller + */ + +#include "cvs.h" + +#include "getline.h" + +RCSNode *checkout_file(); +void checkin_file(); + +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 passwd_usage[] = +{ + "Usage: %s %s [-a] [username]\n", + "\t-a\tAdd the user\n", + NULL +}; + +static void +node_deleted(p) + Node *p; +{ + if (p->data != NULL) + free (p->data); + p->key = p->data = NULL; +} + +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 +passwd (argc, argv) + int argc; + char **argv; +{ + int c; + int err = 0; + char *typed_password, *typed_password2; + 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 (passwd_usage); + + wrap_setup (); + + optind = 1; + while ((c = getopt (argc, argv, "a")) != -1) + { + switch (c) + { + case 'a': + adduser = 1; + break; + + case '?': + default: + usage (passwd_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + if (argc > 1) + usage (passwd_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use pserver method with `passwd' command"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + if (! current_parsed_root->username) + { + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + current_parsed_root->original); + error (1, 0, "Please make sure to specify \"user@host\"!"); + } + + start_server (); + ign_setup (); + + printf ("%s %s@%s\n", + (adduser)?"Changing password for":"Adding user", + (argc == 1) ? argv[0] : current_parsed_root->username, + current_parsed_root->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 ("passwd\012", 0); + memset (typed_password, 0, strlen (typed_password)); + free (typed_password); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + 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 (passwd_usage); + + if (argc == 1) + username = CVS_Username; + else + username = argv[1]; + + if ((argc == 2) && + (strcmp(username, CVS_Username) == 0) && + (! 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 *filename; + RCSNode *rcsfile; + + 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(current_parsed_root->directory) + + strlen("/CVSROOT/passwd") + + 1); + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT/passwd"); + + rcsfile = checkout_file(filename); + + 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)) + { + checkin_file(rcsfile, filename); + error (1, errno, "cannot read %s", filename); + } + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", filename); + } + + namenode = findnode(userlist, username); + if (namenode == NULL) + { + if (argc == 1) { + checkin_file(rcsfile, filename); + error (1, 0, "Could not find %s in password file", username); + } + + if (!adduser ) { + checkin_file(rcsfile, filename); + error (1, 0, "Could not find %s in password file", username); + } + + if (! verify_admin()) { + checkin_file(rcsfile, filename); + error (1, 0, "Only administrators can add users" ); + } + + 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); + sleep(10); + } + else + { + walklist(userlist, write_node, fp); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", filename); + } + + checkin_file(rcsfile, filename); + free(filename); + + dellist(&userlist); + } +#endif + + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} + +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; + Node *namenode; + FILE *fp; + char *linebuf = NULL; + int linebuf_len; + + 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 (current_parsed_root->isremote) + { + if (argc != 1) + usage (deluser_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use pserver method with `deluser' command"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + if (! current_parsed_root->username) + { + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + current_parsed_root->original); + error (1, 0, "Please make sure to specify \"user@host\"!"); + } + + start_server (); + ign_setup (); + + printf( "Deleting user %s@%s\n", argv[0], + current_parsed_root->hostname); + fflush(stdout); + + send_arg(argv[0]); + + send_to_server ("deluser\012", 0); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + 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) + usage (deluser_usage); + + if (! verify_admin ()) + error (1, 0, + "Only the administrator can delete a user"); + + { + List *userlist; + char *filename; + RCSNode *rcsfile; + + filename = xmalloc(strlen(current_parsed_root->directory) + + strlen("/CVSROOT/passwd") + + 1); + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT/passwd"); + + rcsfile = checkout_file(filename); + + userlist = getlist(); + + fp = CVS_FOPEN (filename, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", filename); + checkin_file(rcsfile, filename); + return 0; + } + else + { + + 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 = (char *) xstrdup (strtok (NULL, "\n")); + namenode->delproc = node_deleted; + addnode(userlist, namenode); + } + + free (linebuf); + linebuf = NULL; + } + if (ferror (fp)) { + checkin_file(rcsfile, filename); + 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) + { + checkin_file(rcsfile, filename); + error (1, 0, "Could not find %s in password file", argv[0]); + } + 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); + } + + checkin_file(rcsfile, filename); + free(filename); + + dellist(&userlist); + } +#endif + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} + +static const char *const adduser_usage[] = +{ + "Usage: %s %s username\n", + NULL +}; + +int +adduser (argc, argv) + int argc; + char **argv; +{ + int c; + int err = 0; + char *typed_password, *typed_password2; + List *userlist; + Node *namenode; + char *rest; + char *oldpassword; + char *oldrest; + FILE *fp; + char *linebuf = NULL; + int linebuf_len; + char *username; + + + if (argc == -1) + usage (adduser_usage); + + wrap_setup (); + + optind = 1; + while ((c = getopt (argc, argv, "")) != -1) + { + switch (c) + { + case '?': + default: + usage (adduser_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + if (argc != 1) + usage (adduser_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use pserver method with `adduser' command"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + if (! current_parsed_root->username) + { + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + current_parsed_root->original); + error (1, 0, "Please make sure to specify \"user@host\"!"); + } + + start_server (); + ign_setup (); + + printf ("Adding user %s@%s\n", argv[0], current_parsed_root->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); + + send_arg (typed_password); /* Send the new password */ + + if (argc == 1) + send_arg(argv[0]); + + send_to_server ("adduser\012", 0); + memset (typed_password, 0, strlen (typed_password)); + free (typed_password); + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + 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 != 2) + usage (adduser_usage); + + if (! verify_admin ()) + error (1, 0, + "Only the administrator can add a user"); + + if (strlen(argv[0]) == 0) + error (1, 0, "A password must be supplied"); + + { + time_t tm; + char salt[2]; + char *cryptstr; + char *filename; + RCSNode *rcsfile; + + 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 (current_parsed_root->directory) + + strlen ("/CVSROOT") + + strlen ("/passwd") + + 1); + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT/passwd"); + + rcsfile = checkout_file(filename); + + 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)) { + checkin_file(rcsfile, filename); + 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) + { + checkin_file(rcsfile, filename); + error (1, 0, "User %s already exists", username ); + } + else + { + 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); + } + + checkin_file(rcsfile, filename); + + free(filename); + + dellist(&userlist); + } +#endif + return (err); +#else + error (1, 0, "Server support is not available"); + return (0); +#endif /* SERVER_SUPPORT */ +} + +RCSNode * +checkout_file(filename) + char *filename; +{ + char *rcsfilename; + RCSNode *rcsfile; + + 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"); + + if (RCS_lock(rcsfile, NULL, 1) != 0) + error (1, 0, "Could not lock password file"); + + RCS_rewrite(rcsfile, NULL, NULL); + + chmod(filename, 0644); + + free(rcsfilename); + + return rcsfile; +} + +void +checkin_file(rcsfile, filename) + RCSNode *rcsfile; + char *filename; +{ + if (RCS_checkin(rcsfile, filename, 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"); + + freercsnode(&rcsfile); +} diff -urN orig/cvs-1.11.1p1/src/patch.c cvs-1.11.1p1/src/patch.c --- orig/cvs-1.11.1p1/src/patch.c Tue Apr 24 13:14:53 2001 +++ cvs-1.11.1p1/src/patch.c Tue Jul 24 13:59:52 2001 @@ -327,6 +327,14 @@ free (path); } +#ifdef SERVER_SUPPORT + if (! verify_read(repository)) + { + error (0, 0, "User %s cannot access %s", CVS_Username, repository); + return (1); + } +#endif + /* cd to the starting repository */ if ( CVS_CHDIR (repository) < 0) { @@ -356,7 +364,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 orig/cvs-1.11.1p1/src/perms.c cvs-1.11.1p1/src/perms.c --- orig/cvs-1.11.1p1/src/perms.c Wed Dec 31 18:00:00 1969 +++ cvs-1.11.1p1/src/perms.c Tue Jul 24 14:06:58 2001 @@ -0,0 +1,735 @@ +/* + * 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" + +#ifdef SERVER_SUPPORT + +#include + +int local_security = 0; /* Only use the built-in CVS security system */ +int perms_propigate_down = 0; /* If this is set, the code will move up + directories and check permission up + to (but not including) the root + directory. So if a user has + permissions in a directory, they will + have the same permissions in all + subdirectories. */ + +typedef struct valid_names_s +{ + struct valid_names_s *next; + char *name; +} valid_names_t; + +/* A list of all names that the user goes by, primarily groups. */ +static valid_names_t *valid_names = NULL; + +static void +add_valid_name(name) + char *name; +{ + valid_names_t *new_name = xmalloc(sizeof(*new_name)); + + new_name->name = xstrdup(name); + new_name->next = valid_names; + valid_names = new_name; +} + +/* Add the username to the list of valid names, and then find all the + groups the user is in and add them to the list of valid names. */ +static void +get_valid_names() +{ + char *filename; + char *names; + char *name; + char *group; + FILE *fp; + char *linebuf = NULL; + int linebuf_len; + + if (valid_names != NULL) + return; + + add_valid_name(CVS_Username); + + filename = xmalloc (strlen (current_parsed_root->directory) + + strlen ("/CVSROOT") + + strlen ("/group") + + 1); + + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT"); + strcat (filename, "/group"); + + fp = CVS_FOPEN (filename, "r"); + if (fp != NULL) { + while (getline (&linebuf, &linebuf_len, fp) >= 0) { + group = strtok(linebuf, ":\n"); + if (group == NULL) + continue; + names = strtok(NULL, ":\n"); + if (names == NULL) + continue; + + name = strtok(names, ", \t"); + while (name != NULL) { + if (!strcmp (CVS_Username, name)) { + add_valid_name(group); + break; + } + name = strtok(NULL, ", \t"); + } + + if( linebuf != NULL ) { + 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 we can't open the group file, we just don't do groups. */ + + free(filename); +} + +static int +verify_valid_name(name) + char *name; +{ + valid_names_t *names = valid_names; + + while (names != NULL) { + if (!strcmp(names->name, name)) { + return 1; + } + + names = names->next; + } + + return 0; +} + +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 (current_parsed_root->directory) + + strlen ("/CVSROOT") + + strlen ("/admin") + + 1); + + strcpy (filename, current_parsed_root->directory); + strcat (filename, "/CVSROOT"); + strcat (filename, "/admin"); + + fp = CVS_FOPEN (filename, "r"); + if (fp != NULL) + { + while (getline (&linebuf, &linebuf_len, fp) >= 0) + { + name = strtok (linebuf, "\n"); + if (!strcmp (CVS_Username, name)) + { + is_the_admin = 1; + if( linebuf != NULL ) { + free(linebuf); + linebuf = NULL; + } + break; + } + if( linebuf != NULL ) { + 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); + } + } + + 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; + + get_valid_names(); + + filename = xmalloc (strlen (dir) + + strlen ("/owner") + + 1); + + /* Search up the file patch to see if any directories above me have the + owner. Only do this if the 'perms_propigate_down' flag is set. */ + strcpy (filename, dir); + do { + char *newdir; + int i; + char *slashptr; + int done = 0; + + strcat (filename, "/owner"); + + fp = CVS_FOPEN (filename, "r"); + if (fp == NULL) + { + error (0, errno, "cannot find owner file in %s", dir); + } + else + { + if (getline (&linebuf, &linebuf_len, fp) >= 0) + { + owner = strtok(linebuf, "\n"); + if (verify_valid_name(owner)) { + retval = 1; + done = 1; + } + + 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); + } + + /* We only do this loop once if permissions don't propigate. */ + if (done || (! perms_propigate_down)) + break; + + /* Remove '/owner' from the end, then remove the last directory. */ + slashptr = strrchr(filename, '/'); + if (slashptr == NULL) + break; + *slashptr = '\0'; + slashptr = strrchr(filename, '/'); + if (slashptr == NULL) + break; + *slashptr = '\0'; + + } while (strlen(filename) > strlen(current_parsed_root->directory)); + + free (filename); + + return retval; +} + +/* Get the permissions from the directory for the given name. + Returns 1 if successful, 0 if it fails. */ +static int +find_perms (dir, perm, username) + const char *dir; + char *perm; + char *username; +{ + char *filename; + char *linebuf = NULL; + int linebuf_len; + char *name; + char *permptr; + FILE *fp; + int retval = 0; + char all_perms[4]; + + + all_perms[0] = '\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 = 0; + } + else + { + while (getline (&linebuf, &linebuf_len, fp) >= 0) + { + name = strtok(linebuf, ":"); + if (strcmp(name, username) == 0) + { + permptr = strtok(NULL, "\n :"); + if (permptr != NULL) + { + strncpy(perm, permptr, 3); + perm[3] = '\0'; + retval = 1; + } + free (linebuf); + break; + } + else if (strcmp (name, "ALL") == 0) + { + permptr = strtok(NULL, "\n :"); + if (permptr != NULL) + { + strncpy(all_perms, permptr, 3); + perm[3] = '\0'; + } + } + + free (linebuf); + linebuf = NULL; + } + + /* If we didn't find the user but found ALL, set it to ALL's perms */ + if ((retval == 0) && (all_perms[0] != '\0')) + { + strcpy(perm, all_perms); + retval = 1; + } + + 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 +verify_perm (dir, perm) + const char *dir; + const char perm; +{ + char perms[4]; + char *currdir; + int retval = 0; + + if (!local_security) + { + return 1; + } + + if (verify_owner (dir)) + { + /* Owners have all permissions. */ + return 1; + } + + get_valid_names(); + + currdir = xstrdup(dir); + + do { + char *slashptr; + valid_names_t *names = valid_names; + + while (names != NULL) { + if (find_perms (currdir, perms, names->name)) + if (strchr(perms, perm) != NULL) { + retval = 1; + goto name_found; + } + + names = names->next; + } + + /* We only do this loop once if permissions don't propigate. */ + if (! perms_propigate_down) + break; + + slashptr = strrchr(currdir, '/'); + if (slashptr == NULL) + break; + + *slashptr = '\0'; + + } while (strlen(currdir) > strlen(current_parsed_root->directory)); +name_found: + + free(currdir); + return retval; +} + +int +verify_read (dir) + const char *dir; +{ + return verify_perm(dir, 'r'); +} + +int +verify_write (dir) + const char *dir; +{ + return verify_perm(dir, 'w'); +} + +int +verify_create (dir) + const char *dir; +{ + return verify_perm(dir, 'c'); +} + +int +change_owner (dir, user) + const char *dir; + const char *user; +{ + char *filename; + 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->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; + 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; + + + 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"); + + /* + * Don't make noise about the file not existing as that + * only means that the owner has not been set + * M. Ferrell + */ + if( fp == NULL && existence_error(errno) ) + { + cvs_output( "Owner: \n", 0); + } + else if (fp == NULL) + { + error (0, errno, "cannot open %s", filename); + } + else + { + if (getline (&linebuf, &linebuf_len, fp) >= 0) + { + cvs_output ("Owner: ", 0); + if( strlen(linebuf) > 1 ) + { + owner = strtok(linebuf, "\n"); + cvs_output (owner , 0); + } + else + 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); +} + +void +list_perms (dir) + const char *dir; +{ + char *filename; + char *linebuf = NULL; + int linebuf_len; + char *permptr; + FILE *fp; + + + if (!local_security) + { + return; + } + + filename = xmalloc (strlen (dir) + + strlen ("/perms") + + 1); + + strcpy (filename, dir); + strcat (filename, "/perms"); + + fp = CVS_FOPEN (filename, "r"); + + /* + * Don't complain because the file doesn't exist + * M. Ferrell + */ + if (fp == NULL && !existence_error(errno) ) + { + error (0, errno, "cannot open %s", filename); + } + else if ( fp != NULL ) + { + 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); +} +#else /* SERVER_SUPPORT */ +int +verify_admin () +{ + return 1; +} + +int +verify_owner (name) + const char *name; +{ + return 1; +} + +int +verify_read (name) + const char *name; +{ + return 1; +} + +int +verify_write (name) + const char *name; +{ + return 1; +} + +int +verify_create (name) + const char *name; +{ + return 1; +} +#endif /* SERVER_SUPPORT */ diff -urN orig/cvs-1.11.1p1/src/recurse.c cvs-1.11.1p1/src/recurse.c --- orig/cvs-1.11.1p1/src/recurse.c Thu Apr 19 14:45:33 2001 +++ cvs-1.11.1p1/src/recurse.c Tue Jul 24 14:00:44 2001 @@ -35,6 +35,7 @@ int aflag; int readlock; int dosrcs; + PERMPROC permproc; }; static int do_recursion PROTO ((struct recursion_frame *frame)); @@ -59,6 +60,32 @@ }; +static int +verify_access (permproc, dir) + PERMPROC permproc; + char *dir; +{ + char *hostdir; + int retval; + + if (permproc == NULL) + { + return 1; + } + + if (repository == NULL) + { + hostdir = Name_Repository ((char *) NULL, 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 +94,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 +132,7 @@ int readlock; char *update_preload; int dosrcs; + PERMPROC permproc; { int i, err = 0; #ifdef CLIENT_SUPPORT @@ -123,6 +151,7 @@ frame.aflag = aflag; frame.readlock = readlock; frame.dosrcs = dosrcs; + frame.permproc = permproc; expand_wild (argc, argv, &argc, &argv); @@ -285,7 +314,19 @@ file_to_try = xstrdup (argv[i]); if (isfile (file_to_try)) - addfile (&files_by_dir, dir, comp); + { +#ifdef SERVER_SUPPORT + if (! verify_access (frame.permproc, dir)) + { + error (0, 0, "User '%s' cannot access %s", + CVS_Username, dir); + } + else +#endif + { + addfile (&files_by_dir, dir, comp); + } + } else if (isdir (dir)) { if ((which & W_LOCAL) && isdir (CVSADM) @@ -624,6 +665,16 @@ } srepository = repository; /* remember what to free */ +#ifdef SERVER_SUPPORT + /* + * Do we have access to this directory? + */ + if (! verify_access (frame->permproc, repository)) + { + error (0, 0, "User '%s' cannot access %s", CVS_Username, repository); + return (1); + } +#endif fileattr_startdir (repository); /* diff -urN orig/cvs-1.11.1p1/src/remove.c cvs-1.11.1p1/src/remove.c --- orig/cvs-1.11.1p1/src/remove.c Thu Apr 19 14:45:33 2001 +++ cvs-1.11.1p1/src/remove.c Mon Jul 9 19:11:29 2001 @@ -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 && !really_quiet) error (0, 0, "use '%s commit' to remove %s permanently", program_name, diff -urN orig/cvs-1.11.1p1/src/root.c cvs-1.11.1p1/src/root.c --- orig/cvs-1.11.1p1/src/root.c Thu Apr 19 14:45:33 2001 +++ cvs-1.11.1p1/src/root.c Tue Jul 10 00:21:51 2001 @@ -12,6 +12,8 @@ #include "cvs.h" #include "getline.h" +char *CVSroot_prefix = NULL; /* Prefix for CVSroot if supplied on cmdline */ + /* Printable names for things in the current_parsed_root->method enum variable. Watch out if the enum is changed in cvs.h! */ @@ -530,7 +532,23 @@ } /* parse the path for all methods */ - newroot->directory = xstrdup(cvsroot_copy); + if (CVSroot_prefix == NULL) + { + newroot->directory = xstrdup (cvsroot_copy); + } + else + { + /* If a prefix is supplied, then put it before the actual + directory. */ + newroot->directory = malloc (strlen(cvsroot_copy) + + strlen(CVSroot_prefix) + + 1); + if (newroot->directory != NULL) + { + strcpy(newroot->directory, CVSroot_prefix); + strcat(newroot->directory, cvsroot_copy); + } + } free (cvsroot_save); /* @@ -736,9 +754,27 @@ { cvsroot_t *newroot = new_cvsroot_t(); - newroot->original = xstrdup(dir); + newroot->original = xstrdup (dir); + newroot->method = local_method; - newroot->directory = xstrdup(dir); + + if (CVSroot_prefix == NULL) + { + newroot->directory = xstrdup (dir); + } + else + { + /* If a prefix is supplied, then put it before the actual + directory. */ + newroot->directory = malloc (strlen(dir) + + strlen(CVSroot_prefix) + + 1); + if (newroot->directory != NULL) + { + strcpy(newroot->directory, CVSroot_prefix); + strcat(newroot->directory, dir); + } + } return newroot; } diff -urN orig/cvs-1.11.1p1/src/server.c cvs-1.11.1p1/src/server.c --- orig/cvs-1.11.1p1/src/server.c Thu Apr 19 14:34:04 2001 +++ cvs-1.11.1p1/src/server.c Mon Jul 23 21:44:48 2001 @@ -734,7 +734,7 @@ if (error_pending()) return; - if (!isabsolute (arg)) + if ((!isabsolute (arg)) && (CVSroot_prefix == NULL)) { if (alloc_pending (80 + strlen (arg))) sprintf (pending_error_text, @@ -855,23 +855,39 @@ outside_root (repos) char *repos; { - size_t repos_len = strlen (repos); + char *real_repos = NULL; + char *full_repos; + size_t repos_len; size_t root_len = strlen (current_parsed_root->directory); + if (CVSroot_prefix != NULL) + { + real_repos = xmalloc(strlen(CVSroot_prefix) + + strlen(repos) + + 1); + strcpy(real_repos, CVSroot_prefix); + strcat(real_repos, repos); + full_repos = real_repos; + } + else + full_repos = repos; + + repos_len = strlen (full_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 (full_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", full_repos); return 1; } if (repos_len < root_len - || strncmp (current_parsed_root->directory, repos, root_len) != 0) + || strncmp (current_parsed_root->directory, full_repos, root_len) != 0) { not_within: if (alloc_pending (strlen (current_parsed_root->directory) @@ -884,11 +900,15 @@ } if (repos_len > root_len) { - if (repos[root_len] != '/') + if (full_repos[root_len] != '/') goto not_within; - if (pathname_levels (repos + root_len + 1) > 0) + if (pathname_levels (full_repos + root_len + 1) > 0) goto not_within; } + + if (real_repos) + free(real_repos); + return 0; } @@ -1068,6 +1088,19 @@ pending_error = save_errno; return; } + if (CVSroot_prefix != NULL) { + if (fprintf (f, "%s", CVSroot_prefix) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E error writing %s/%s", dir_name, CVSADM_REP); + pending_error = save_errno; + fclose (f); + return; + } + } + if (fprintf (f, "%s", repos) < 0) { int save_errno = errno; @@ -3346,6 +3379,11 @@ char *update_dir; char *repository; { + /* If we have a CVS root prefix set, don't transmit it back to the + client. */ + if (CVSroot_prefix != NULL) + repository += strlen(CVSroot_prefix); + if (server_dir != NULL) { buf_output0 (protocol, server_dir); @@ -3579,6 +3617,48 @@ } void +serve_chown (arg) + char *arg; +{ + do_cvs_command ("chown", chowner); +} + +void +serve_chacl (arg) + char *arg; +{ + do_cvs_command ("chacl", chacl); +} + +void +serve_lsacl (arg) + char *arg; +{ + do_cvs_command ("lsacl", lsacl); +} + +void +serve_passwd (arg) + char *arg; +{ + do_cvs_command ("passwd", passwd); +} + +void +serve_deluser (arg) + char *arg; +{ + do_cvs_command ("deluser", deluser); +} + +void +serve_adduser (arg) + char *arg; +{ + do_cvs_command ("adduser", adduser); +} + +void server_update_entries (file, update_dir, repository, updated) char *file; char *update_dir; @@ -4770,6 +4850,16 @@ REQ_LINE("expand-modules", serve_expand_modules, 0), REQ_LINE("ci", serve_ci, RQ_ESSENTIAL), REQ_LINE("co", serve_co, RQ_ESSENTIAL), + REQ_LINE("chown", serve_chown, 0), + REQ_LINE("setowner", serve_chown, 0), + REQ_LINE("setperm", serve_chacl, 0), + REQ_LINE("chacl", serve_chacl, 0), + REQ_LINE("listperm", serve_lsacl, 0), + REQ_LINE("lsacl", serve_lsacl, 0), + REQ_LINE("setpass", serve_passwd, 0), + REQ_LINE("passwd", serve_passwd, 0), + REQ_LINE("deluser", serve_deluser, 0), + REQ_LINE("adduser", serve_adduser, 0), REQ_LINE("update", serve_update, RQ_ESSENTIAL), REQ_LINE("diff", serve_diff, 0), REQ_LINE("log", serve_log, 0), @@ -5678,6 +5768,7 @@ char *descrambled_password; #endif /* AUTH_SERVER_SUPPORT */ int verify_and_exit = 0; + char *real_repository = NULL; /* The Authentication Protocol. Client sends: * @@ -5788,7 +5879,21 @@ { error (1, 0, "bad auth protocol end: %s", tmp); } - if (!root_allow_ok (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; + } + + if (!root_allow_ok (real_repository)) { printf ("error 0 %s: no such repository\n", repository); #ifdef HAVE_SYSLOG_H @@ -5802,11 +5907,18 @@ file, parse_config already printed an error. We keep going. Why? Because if we didn't, then there would be no way to check in a new CVSROOT/config file to fix the broken one! */ - parse_config (repository); + parse_config (real_repository); /* We need the real cleartext before we hash it. */ descrambled_password = descramble (password); - host_user = check_password (username, descrambled_password, 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) @@ -5848,7 +5960,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); @@ -5941,7 +6054,8 @@ } /* Switch to run as this user. */ - switch_to_user (user); + if (! local_security) + switch_to_user (user); } #endif /* HAVE_KERBEROS */ @@ -6033,10 +6147,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); } @@ -6053,7 +6171,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 orig/cvs-1.11.1p1/src/status.c cvs-1.11.1p1/src/status.c --- orig/cvs-1.11.1p1/src/status.c Thu Apr 19 14:34:04 2001 +++ cvs-1.11.1p1/src/status.c Mon Jul 9 19:11:42 2001 @@ -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 orig/cvs-1.11.1p1/src/tag.c cvs-1.11.1p1/src/tag.c --- orig/cvs-1.11.1p1/src/tag.c Tue Apr 24 12:04:59 2001 +++ cvs-1.11.1p1/src/tag.c Mon Jul 9 19:35:43 2001 @@ -373,7 +373,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) { @@ -393,7 +393,7 @@ err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc, (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1, - local, which, 0, 0, where, 1); + local, which, 0, 0, where, 1, verify_write); Lock_Cleanup (); dellist (&mtlist); if (where != NULL) @@ -1214,7 +1214,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 orig/cvs-1.11.1p1/src/update.c cvs-1.11.1p1/src/update.c --- orig/cvs-1.11.1p1/src/update.c Tue Apr 24 12:04:59 2001 +++ cvs-1.11.1p1/src/update.c Mon Jul 9 23:23:34 2001 @@ -489,7 +489,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); @@ -505,7 +505,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); #ifdef SERVER_SUPPORT if (server_active) @@ -1397,7 +1397,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); @@ -1793,7 +1794,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 orig/cvs-1.11.1p1/src/watch.c cvs-1.11.1p1/src/watch.c --- orig/cvs-1.11.1p1/src/watch.c Thu Apr 19 14:34:04 2001 +++ cvs-1.11.1p1/src/watch.c Mon Jul 9 19:11:42 2001 @@ -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); }