diff -urN cvs-1.11.19.orig/INSTALL.cvsacl cvs-1.11.19/INSTALL.cvsacl --- cvs-1.11.19.orig/INSTALL.cvsacl 1970-01-01 01:00:00.000000000 +0100 +++ cvs-1.11.19/INSTALL.cvsacl 2004-11-26 13:48:57.000000000 +0100 @@ -0,0 +1,23 @@ + +Installation + +- copy the file acl.c under src directory of CVS source distribution. + "cp acl.c /path/to/cvs-1.11.18/src/" +- copy the patch file cvsacl-patch-1.2.2 under CVS source distribution + directory. + "cp cvsacl-patch-1.2.2 /path/to/cvs-1.11.18/" +- cd to CVS source directory. + "cd /path/to/cvs-1.11.18/" +- apply the patch. + "patch -p0 < cvsacl-patch-1.2.2" +- if you are initializing the repository after applying patch, related + config files will be created with init command. + "cvs -d /path/to/repository init" +- if you already have a repository, you have to add the aclconfig file + to your $CVSROOT/CVSROOT/. aclconfig.default is the default configuration + file, you can rename it to aclconfig, and use it . +- modify aclconfig file, if you need to change some options. +- as the last step, you have to define yourself as acl administrator. + "cvs -d /path/to/repository racl yourname:p -r ALL -d ALL" + this command gives p (acl admin) rights to user (yourname), + on all repository and tags/branches. diff -urN cvs-1.11.19.orig/README.cvsacl cvs-1.11.19/README.cvsacl --- cvs-1.11.19.orig/README.cvsacl 1970-01-01 01:00:00.000000000 +0100 +++ cvs-1.11.19/README.cvsacl 2004-11-26 13:47:23.000000000 +0100 @@ -0,0 +1,282 @@ + +CVS Access Control List Extension Patch + +http://cvsacl.sourceforge.net/ +sbaris@users.sourceforge.net + +CVSACL is a patch for CVS. It adds two new subcommands +(acl & racl) to cvs for access control list management. It +provides advanced ACL definitions per modules, directories, +and files on branch/tag for remote cvs repository connections. +Execution of all CVS subcommands can be controlled with eight +different permissions. +ACL definitions works for only remote connections, local users +can access and modify repository, if unix file system permissions +allow. If you want all users to make remote connections to +repository, and not allow local users to access repository, you +have to set CVSServerRunAsUser keyword in aclconfig file +(explained below). +Still local users can use acl and racl subcommands to set +permissions on directories or files if they have acl admin rights (p) +on related directories or files. +So, in order to control all access to repository with this ACL +extension, you should use CVSServerRunAsUser keyword and force all +users to make remote connections. CVS repository administrator or +project managers have to use acl and racl subcommands to manage +permissions. But there is no gui client supporting these subcommands, +so you have to use cvs client itself either locally or remotely. + + + + +Permission Types + +- no access + Command line character: n + If a user given n permission, it is not allowed for any action on repository. +- read + Command line character: r + r permission gives only read access on repository. + With r permission you are allowed to run cvs subcommands: annotate, + checkout, diff, export, log, rannotate, rdiff, rlog, status. +- write + Command line character: w + w permission allows only cvs commit/checkin action. + With w permission, you are not allowed to add/remove any file to/from + repository, other permissions should be defines for that. +- tag + Command line character: t + t permission allows cvs tag and rtag subcommands to run, so you may + control tagging and untagging operations. t permission includes r + permission, since without reading you can not tag/untag a file. + However t permission does not include write permission, you can not + commit a file with only t permission. +- create + Command line character: c + c permission allows cvs add and import subcommands to run. To add or + import a file/directory to repository, you have to given a c + permission. Again, c permission does not include write permission, + thus you may only add or import files, but you can not modify any + existing file. After issuing add subcommand, you have to commit the file + to complete adding. This commit subcommand is allowed because you are + adding file and not modifying existing one. +- delete + Command line character: d + d permission allows cvs remove command to run. To remove a file/directory + from repository, d permission have to set. It does not include write + permission, so you can not modify contents of an existing file on repository. +- full access except admin rights + Command line character: a + a permission gives all access (above permissions) to repository, but it + can not modify permissions. Only acl admins may modify the acl definitions. +- acl admin + Command line character: p + p permission means that user is an acl admin, so it is allowed to make anything on repository. + + +ACL Config Keywords +The administrative file aclconfig contains miscellaneous settings which +affect the behaviour of ACL extension. Currently defined keywords are: + +UseCVSACL=value +Use ACL definitions if set to yes. If you do not want to use ACLs for +some repositories in a patched CVS server, set this keyword to no. The default is no. + +UseCVSACLDefaultPermissions=value +Value can be any combination of valid permission types (w,r,t,c,d,t,a,p). +if there is no defined ACL and default permission in access file, or no +access file at all, this permissions are used. The default is p (admin rights), +if aclconfig file is created with cvs init. + +UseCVSGroups=value +CVS does not have a CVSROOT/passwd file. However it can be created manually +(format should be same as /etc/group). If value set to yes, CVS checks for +groups in file $CVSROOT/CVSROOT/group The default value is no. + +UseSystemGroups=value +Group memberships for users are checked in file /etc/group, if value is set +to yes. The default value is no. + +CVSACLFileLocation=value +Originally access file is put under CVSROOT/CVSROOT, if you want a different +location, set value to a valid path. The default value is $CVSROOT/CVSROOT/access. + +CVSGroupsFileLocation=value +IF UseCVSGroups is set to yes, CVS looks for a group file under $CVSROOT/CVSROOT. +To use a different location for group file set value to a valid path to group. +The default value is $CVSROOT/CVSROOT/group. + +UseSeparateACLFileForEachDir=value +If value is set to yes, a separate ACL file (access) is created for each +directory in repository. If you have a really big repository +(directories>10,000 and files>100,000), performance may drop due to a big +acl file, access. Setting the value to yes, may increase performance. Normally, +you will not need this. The default value is no. + +StopAtFirstPermissionDenied=value +If StopAtFirstPermissionDenied is set to yes +operation will stop at first permission denied message. +e.g. when you send commit command for a directory, if you dont +have write permission for just one file under the directory, +by default you will have a warning and commit will continue +on the other files. If you set this keyword to 'no', then +commit operation will be stopped when inaccassable file found. +Default is no. + +CVSServerRunAsUser=value +Set CVSServerRunAsUser keyword to a valid system user. +When a user make a remote connection to CVS, after successfull authentication +cvs process switch to run as that user, or defined system user in +$CVSROOT/CVSROOT/passwd. So, you also have to set unix file permissions accordingly. +A better solution: +Add a user and group such as both cvsadm. +Set CVSServerRunAsUser keyword to cvsadm. +Change unix file system permissions for your repository, +make cvsadm user and group owner, and read,write,execute permissions and setgid. +(chown cvsadm -R /path/to/your/repository) +(chgrp cvsadm -R /path/to/your/repository) +(chmod 2770 -R /path/to/your/repository) +Add yourself to cvsadm group (since you are ACL administrator). +Therefore, only users making remote connections will have access to repository +if you give rights. Local users can not access to repository via a cvs client or directly. + + +Command Line Usage Information +acl command is used on checked out files or directories. racl command is +used on repository without a working copy. Usage information can be obtained +with standard cvs --help command. +Output of cvs --help acl and cvs --help racl: + +Usage: cvs racl [user||group:permissions] [-Rl] [-r tag] + -d [directory] -f [file] + -R Process directories recursively. + -r rev Existing revision/tag. + -l List defined ACLs. + -d dir Process on given directory. + -f file Process on given file. + +Usage: cvs acl [user||group:permissions] [-Rl] [-r tag] + -d [directory] -f [file] + -R Process directories recursively. + -r rev Existing revision/tag. + -l List defined ACLs. + -d dir Process on given directory. + -f file Process on given file. + +You may directly set permissions for a user or group or add/remove +permissions with + and - signs to/from existing permissions. +If you do not give the branch/tag information, default value of HEAD +(main branch) will be used. You have to give branch/tag name with -r option. +You may type ALL for branch/tag field. + +While checking for permissions, it goes thorough the list below. So the highest +significant permission is the first item in list. + +- permissions assigned to username for specific directory or file. +- permissions assigned to group name for specific directory or file. +- permissions as defaults for specific directory or file. +- permissions assigned to parent folders (inherits from the first parent + which permissions are assigned). +- permissions as repository defaults. +- permissions in aclconfig file. + + + + +Examples + /cvs/ + | + | + +--projectA/ + | | + | +---CVSROOT/ + | | + | +---lib/ + | | | + | | +---gnulib/ + | | | + | | +---zlib/ + | | + | +---src/ + | | | + | | +---main.c + | | | + | | +---server.c + | | | + | | +---client.c + | | + | +---gui/ + | + +--projectB/ +We have above directory structure for a cvs repository, and no defined permissions. + +Setting main default permissions: + +$ cvs -d /cvs/projectA racl cvsadmin:p -r ALL -d ALL +$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL +User cvsadmin will be an acl admin, and all other users will have only read +rights on all branches/tags in projectA repository. This is the default acl +definition and it overwrites default permissions in $CVSROOT/CVSROOT/aclconfig file. + +$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL +$ cvs -d /cvs/projectA racl ALL:n -r ALL -d gui +After executing these two commands, all users will have read access on all +directories and files except gui directory. Everyone will be denied to access to gui +directory becase no access, n, permissions is set. + +Setting permissions directly on a file or directory: + +$ cvs -d /cvs/projectA racl userX:wcd -d lib +$ cvs -d /cvs/projectA racl group1:w -d lib +First command will set write, create, and delete permissions for userX on directory +lib with branch HEAD (since no branch/tag information given, branch defaults to HEAD). +Second command will set only write permission for group1 on directory lib with branch HEAD. +Members of group1 will have only commit rights on lib directory, branch HEAD, they can +not add or remove any file, just modify existing files. +If userX is also a member of group1, userX will have write, create, and delete permissions +because it is specifically given these permissions. + +$ cvs -d /cvs/projectA racl userY:wcd -r develStream -d lib +$ cvs -d /cvs/projectA racl userY:r -r integStream -d lib +These commands will give wcd permissions to userY on lib directory with tag develstream, +and r permissions on lib directory with tag integStream. + +$ cvs -d /cvs/projectA racl userZ:wcd -d src +$ cvs -d /cvs/projectA racl userZ:r -f src/main.c +First command will give wcd permissions to userZ on src directory, but only read +permission on file main.c in src directory. + +Using + and - signs to set permissions on a file or directory: + +$ cvs -d /cvs/projectA racl userZ:+t -d src +$ cvs -d /cvs/projectA racl userZ:-cd -d src +$ cvs -d /cvs/projectA racl userZ:-wt -d src +Before the first command, userZ has wcd permissions on src directory, after issuing +command it will have wcdt permissions. Tag permission will be added. UserZ has wcdt +permissions, and we execute the second command to remove create and delete permissions. +So userZ has wt permissions. In the last command we also remove wt permissions, finally +userZ has no defined permissions left, and it will use the default permissions if set. + +Listing permissions on a file or directory: + +$ cvs -d /cvs/projectA racl -l -d src +$ cvs -d /cvs/projectA racl -l -f src +$ cvs -d /cvs/projectA racl -l -f src/main.c + +First command will list the permissions for src directory. +Example output: +d src HEAD | userX:wcd group1:r | defaults:r +userX and group1 has assigned permissions, all other users will have default +permissions, which is only read. + +Second command will list the permissions for files in src directory. +Example output: +f src/main.c HEAD | userX:wcd group1:r | defaults:r +f src/server.c HEAD | userX:wcd group1:r | defaults:r +f src/client.c HEAD | userX:wcd group1:r | defaults:r + +Third command will list the permissions for main.c file in src directory. +Example output: +f src/main.c HEAD | userX:wcd group1:r | defaults:r + + diff -urN cvs-1.11.19.orig/aclconfig.default cvs-1.11.19/aclconfig.default --- cvs-1.11.19.orig/aclconfig.default 1970-01-01 01:00:00.000000000 +0100 +++ cvs-1.11.19/aclconfig.default 2004-11-26 13:47:34.000000000 +0100 @@ -0,0 +1,33 @@ +# Set `UseCVSACL' to yes to use CVS ACL feature. +UseCVSACL=yes + +# Default CVS ACL Permission are to use. +#CVSACLDefaultPermissions=a + +# Default file location for CVS ACL file (access) is CVSROOT/access. +# If you want to use a different location, define it below. +#CVSACLFileLocation=/path/to/cvs/access + +# Set `UseSystemGroups' to yes to use system group definitions (/etc/group). +UseSystemGroups=yes + +# Set `UseCVSGroups' to yes to use another group file. +#UseCVSGroups=yes + +# Default file location for CVS groups file is CVSROOT/group. +# If you want to use a different location, define it below. +#CVSGroupsFileLocation=/path/to/cvs/group + +# Set UseSeparateACLFileForEachDir to yes in order to use a +# separate 'access' file for each directory. +# This increased the performance if you have really big repository. +#UseSeparateACLFileForEachDir=no + +# If StopAtFirstPermissionDenied is set to yes +# operation will stop at first permission denied message. +# Default is no. +#StopAtFirstPermissionDenied=no + +# Set CVSServerRunAsUser to a system user, in order CVS server +# to run as. +#CVSServerRunAsUser=runascvsuser diff -urN cvs-1.11.19.orig/src/Makefile.am cvs-1.11.19/src/Makefile.am --- cvs-1.11.19.orig/src/Makefile.am 2005-03-14 19:49:29.000000000 +0100 +++ cvs-1.11.19/src/Makefile.am 2005-03-14 19:55:09.000000000 +0100 @@ -31,6 +31,7 @@ # The cvs executable cvs_SOURCES = \ + acl.c \ add.c \ admin.c \ annotate.c \ diff -urN cvs-1.11.19.orig/src/Makefile.in cvs-1.11.19/src/Makefile.in --- cvs-1.11.19.orig/src/Makefile.in 2005-02-01 00:11:15.000000000 +0100 +++ cvs-1.11.19/src/Makefile.in 2005-03-14 19:55:09.000000000 +0100 @@ -69,20 +69,20 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(bin_PROGRAMS) -am_cvs_OBJECTS = add.$(OBJEXT) admin.$(OBJEXT) annotate.$(OBJEXT) \ - buffer.$(OBJEXT) checkin.$(OBJEXT) checkout.$(OBJEXT) \ - classify.$(OBJEXT) client.$(OBJEXT) commit.$(OBJEXT) \ - create_adm.$(OBJEXT) cvsrc.$(OBJEXT) diff.$(OBJEXT) \ - edit.$(OBJEXT) entries.$(OBJEXT) error.$(OBJEXT) \ - expand_path.$(OBJEXT) fileattr.$(OBJEXT) filesubr.$(OBJEXT) \ - find_names.$(OBJEXT) hardlink.$(OBJEXT) hash.$(OBJEXT) \ - history.$(OBJEXT) ignore.$(OBJEXT) import.$(OBJEXT) \ - lock.$(OBJEXT) log.$(OBJEXT) login.$(OBJEXT) logmsg.$(OBJEXT) \ - main.$(OBJEXT) mkmodules.$(OBJEXT) modules.$(OBJEXT) \ - myndbm.$(OBJEXT) no_diff.$(OBJEXT) parseinfo.$(OBJEXT) \ - patch.$(OBJEXT) rcs.$(OBJEXT) rcscmds.$(OBJEXT) \ - recurse.$(OBJEXT) release.$(OBJEXT) remove.$(OBJEXT) \ - repos.$(OBJEXT) root.$(OBJEXT) run.$(OBJEXT) \ +am_cvs_OBJECTS = acl.$(OBJEXT) add.$(OBJEXT) admin.$(OBJEXT) \ + annotate.$(OBJEXT) buffer.$(OBJEXT) checkin.$(OBJEXT) \ + checkout.$(OBJEXT) classify.$(OBJEXT) client.$(OBJEXT) \ + commit.$(OBJEXT) create_adm.$(OBJEXT) cvsrc.$(OBJEXT) \ + diff.$(OBJEXT) edit.$(OBJEXT) entries.$(OBJEXT) \ + error.$(OBJEXT) expand_path.$(OBJEXT) fileattr.$(OBJEXT) \ + filesubr.$(OBJEXT) find_names.$(OBJEXT) hardlink.$(OBJEXT) \ + hash.$(OBJEXT) history.$(OBJEXT) ignore.$(OBJEXT) \ + import.$(OBJEXT) lock.$(OBJEXT) log.$(OBJEXT) login.$(OBJEXT) \ + logmsg.$(OBJEXT) main.$(OBJEXT) mkmodules.$(OBJEXT) \ + modules.$(OBJEXT) myndbm.$(OBJEXT) no_diff.$(OBJEXT) \ + parseinfo.$(OBJEXT) patch.$(OBJEXT) rcs.$(OBJEXT) \ + rcscmds.$(OBJEXT) recurse.$(OBJEXT) release.$(OBJEXT) \ + remove.$(OBJEXT) repos.$(OBJEXT) root.$(OBJEXT) run.$(OBJEXT) \ scramble.$(OBJEXT) server.$(OBJEXT) stack.$(OBJEXT) \ status.$(OBJEXT) subr.$(OBJEXT) tag.$(OBJEXT) update.$(OBJEXT) \ version.$(OBJEXT) vers_ts.$(OBJEXT) watch.$(OBJEXT) \ @@ -206,6 +206,7 @@ # The cvs executable cvs_SOURCES = \ + acl.c \ add.c \ admin.c \ annotate.c \ @@ -384,6 +385,7 @@ distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/admin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/annotate.Po@am__quote@ diff -urN cvs-1.11.19.orig/src/acl.c cvs-1.11.19/src/acl.c --- cvs-1.11.19.orig/src/acl.c 1970-01-01 01:00:00.000000000 +0100 +++ cvs-1.11.19/src/acl.c 2004-11-26 13:50:48.000000000 +0100 @@ -0,0 +1,2032 @@ +/* + * + * CVS ACCESS CONTROL LIST EXTENSION + * + * sbaris@users.sourceforge.net + * + * http://cvsacl.sourceforge.net/ + * + * CVSACL is a patch for CVS versions + * - cvs-1.11.18 + * + * It adds two new subcommands (acl & racl) to cvs for access control + * list management. + * It provides advanced ACL definitions per modules, directories, + * and files on branch/tag for remote cvs repository connections. + * Execution of all CVS subcommands can be controlled with eight + * different permissions. + * ACL definitions works for only remote connections, local users can + * access and modify repository, if unix file system permissions allow. + * If you want all users to make remote connections to repository, + * and not allow local users to access repository, you have to set + * CVSServerRunAsUser keyword in aclconfig file (explained below). + * Still local users can use acl and racl subcommands to set permissions + * on directories or files if they have acl admin rights (p) on related + * directories or files. + * So, in order to control all access to repository with this ACL extension, + * you should use CVSServerRunAsUser keyword and force all users to make + * remote connections. + * CVS repository administrator or project managers have to use acl and racl + * subcommands to manage permissions. But there is no gui client supporting + * these subcommands, so you have to use cvs client itself either + * locally or remotely. + * + * + * Permission Types: + * - no permission (n) (1) + * - all permissions (a) (2) + * - write permission (w) (3) + * - tag permission (t) (4) + * - read permission (r) (5) + * - add permission (c) (6) + * - remove permission (d) (7) + * - permission change (p) (8) + * + * + * + * + * ******************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ******************************************************************* + * + */ + +#include "cvs.h" +#include "getline.h" +#include + +static int acl_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static Dtype acl_dirproc PROTO ((void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries)); + +static int acllist_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static Dtype acllist_dirproc PROTO ((void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries)); + +static void acllist_print PROTO ((char *line, const char *obj)); + +static int racl_proc PROTO((int argc, char **argv, char *xwhere, + char *mwhere, char *mfile, int shorten, + int local_specified, char *mname, char *msg)); + +FILE *open_accessfile (char *xmode, const char *repos, char **fname); +FILE *open_groupfile (char *xmode); + +char *get_perms (char *xperms); +char *make_perms (char *xperms, char *xfounduserpart, char **xerrmsg); + +static char *cache_repository; +static int cache_retval; +static int founddeniedfile; +static int cache_perm; + +static int is_racl; + +int use_cvs_acl = 0; +char *cvs_acl_default_permissions; +int use_cvs_groups = 0; +int use_system_groups = 0; +int use_separate_acl_file_for_each_dir = 0; +char *cvs_acl_file_location = NULL; +char *cvs_groups_file_location = NULL; +char *cvs_server_run_as = NULL; +int stop_at_first_permission_denied = 0; + +char *tag = NULL; + +char *muser; +char *mperms; +static int defaultperms; + +static char *default_perms_object; +char *default_part_perms_accessfile; + +int acldir = 0; +int aclfile = 0; +int listacl = 0; + +int userfound; +int groupfound; + +char *dirs[25]; + +int aclconfig_default_used; + +static const char *const acl_usage[] = + { + "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n", + "\t-R\tProcess directories recursively.\n", + "\t-r rev\tExisting revision/tag.\n", + "\t-l\tList defined ACLs.\n", + "\t-d dir\tProcess on given directory.\n", + "\t-f file\tProcess on given file.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL + }; + +static const char *const racl_usage[] = + { + "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n", + "\t-R\tProcess directories recursively.\n", + "\t-r rev\tExisting revision/tag.\n", + "\t-l\tList defined ACLs.\n", + "\t-d dir\tProcess on given directory.\n", + "\t-f file\tProcess on given file.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL + }; + + +int +access_allowed (file, repos, tag, perm, mline, mpos, usecache) +char *file; +char *repos; +char *tag; +int perm; +char **mline; +int *mpos; +int usecache; +{ + int retval = 0; + int foundline = 0; + FILE *accessfp; + + char *line = NULL; + size_t line_allocated = 0; + + char *part_type = NULL; + char *part_object = NULL; + char *part_tag = NULL; + char *part_perms = NULL; + char *xline; + int x; + + char *iline; + + char *tempv; + char *tempc; + size_t tempsize; + int intcount; + + int oneaccessfile = 0; + int accessfilecount; + + int signlevel = -1; + + int dadmin = 0; + + const char *repository; + char *filefullname = NULL; + + + userfound = 0; + groupfound = 0; + + + if (defaultperms) + { + repository = xstrdup ("ALL"); + } + else + repository = Short_Repository (repos); + + /* cache */ + if (usecache + && cache_repository != NULL + && strcmp (cache_repository, repository) == 0 + && !founddeniedfile + && perm == cache_perm) + return (cache_retval); + else + { + free(cache_repository); + cache_repository = xstrdup(repository); + cache_perm = perm; + } + + if (file != NULL) + { + filefullname = xmalloc (strlen (repository) + + strlen (file) + + 2); + + strcpy (filefullname, repository); + strcat (filefullname, "/"); + strcat (filefullname, file); + } + + + iline = xstrdup(repository); + + tempv = strtok(iline, "/\t"); + tempc = xstrdup(tempv); + tempsize = strlen(tempc); + + intcount = 0; + + dirs[intcount] = xstrdup(tempc); + + while ((tempv = strtok(NULL, "/\t")) != NULL) + { + intcount++; + + xrealloc_and_strcat(&tempc, &tempsize, "/"); + xrealloc_and_strcat(&tempc, &tempsize, tempv); + + dirs[intcount] = xstrdup(tempc); + } + + accessfilecount = intcount; + + if (file != NULL) + { + intcount++; + dirs[intcount] = xstrdup(filefullname); + } + + for (accessfilecount; accessfilecount >= 0 && !oneaccessfile; accessfilecount--) + { + if (!use_separate_acl_file_for_each_dir) + oneaccessfile = 1; + else if (use_separate_acl_file_for_each_dir) + oneaccessfile = 0; + + if (acldir || aclfile) + oneaccessfile = 1; + + if (oneaccessfile) + accessfp = open_accessfile ("r", repository, NULL); + else + accessfp = open_accessfile ("r", dirs[accessfilecount], NULL); + + if (accessfp != NULL) + { + while (getline (&line, &line_allocated, accessfp) >= 0) + { + + if (line[0] == '#' || line[0] == '\0' || line[0] == '\n') + continue; + + xline = xstrdup (line); + part_type = strtok (line, ":\t"); + part_object = strtok (NULL, ":\t"); + part_tag = strtok (NULL, ":\t"); + part_perms = strtok (NULL, ":\t"); + + for (x = intcount; x >= signlevel && x != -1; x--) + { + if (strcmp (dirs[x], part_object) == 0) + { + if (valid_tag (part_tag, tag)) + { + foundline = 1; + + if (listacl || acldir || aclfile) + { + *mline = xstrdup (xline); + *mpos = ftell (accessfp); + } + + if (valid_perm (part_perms, perm)) + { + if (signlevel == x) + { + if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used) + retval = 1; + } + else if (!aclconfig_default_used) + { + signlevel = x; + retval = 1; + } + else { + } + } + else + { + if (signlevel == x) + { + if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used) + retval = 0; + } + else if (!aclconfig_default_used) + { + signlevel = x; + retval = 0; + + if (strncmp (part_type, "f", 1) == 0) + founddeniedfile = 1; + } + else { + } + } + } + } + } + if (strncmp (xline, "d:ALL", 5) == 0 && (!groupfound && !userfound || listacl)) + { + /* a default found */ + if (valid_tag (part_tag, tag)) + { + foundline = 1; + + default_part_perms_accessfile = xstrdup (part_perms); + + if (valid_perm (part_perms, perm)) + { + retval = 1; + + if (perm == 8) + dadmin = 1; + } + else + retval = 0; + } + } + + } + if (fclose (accessfp) == EOF) + error (1, errno, "cannot close 'access' file"); + + } + } + + if (!foundline) + { + /* DEFAULT */ + if (valid_perm (NULL, perm)) + retval = 1; + else + retval = 0; + } + + if (dadmin) + { + retval = dadmin; + } + + cache_retval = retval; + + free (filefullname); + + return (retval); +} + +/* Returns 1 if successful, 0 if not. */ +int +valid_tag (part_tag, tag) +char *part_tag; +char *tag; +{ + if (acldir || aclfile) + return (0); + + if (tag == NULL) + tag = xstrdup ("HEAD"); + + if (strcmp (part_tag, "ALL") == 0) + return (1); + + if (strcmp (tag, part_tag) == 0) + return (1); + else + return (0); +} + +/* Returns 1 if successful, 0 if not. */ +int +valid_perm (part_perms, perm) +char *part_perms; +int perm; +{ + char *perms; + int retval = 0; + + perms = get_perms (part_perms); + + /* Allow, if nothing found. */ + if (perms[0] == '\0') + return (1); + + + if (strstr (perms, "n")) + retval = 0; /* no access allowed, exit */ + if (strstr (perms, "p")) + retval = 1; /* admin rights */ + else if (strstr (perms, "a") && perm != 8) + retval = 1; /* all access allowed, exit */ + else + switch (perm) + { + case 3: /* write permission */ + if (strstr (perms, "w")) + retval = 1; + break; + case 4: /* tag permission */ + if (strstr (perms, "t")) + retval = 1; + break; + case 5: /* read permission */ + if (strstr (perms, "w") || strstr (perms, "t") || strstr (perms, "c") || + strstr (perms, "d") || strstr (perms, "r")) + retval = 1; + break; + case 6: /* create permission */ + if (strstr (perms, "c")) + retval = 1; + break; + case 7: /* delete permission */ + if (strstr (perms, "d")) + retval = 1; + break; + case 8: /* permission change */ + if (strstr (perms, "p")) + retval = 1; + break; + default: + retval = 0; /* never reached */ + break; + } + + return (retval); +} + +char * +get_perms (part_perms) +char *part_perms; + +{ + char *username; + char *xperms; + size_t xperms_len = 1; + + FILE *groupfp; + + char *usr; + char *per; + char *founduser = NULL; + char *foundall = NULL; + int default_checked = 0; + + aclconfig_default_used = 0; + + xperms = xmalloc (xperms_len); + xperms[0] = '\0'; + + username = getcaller (); + + /* no defined acl, no default acl in access file, + or no access file at all */ + if (part_perms == NULL) + if (cvs_acl_default_permissions) + { + aclconfig_default_used = 1; + return (cvs_acl_default_permissions); + } + else + return (xperms); + +check_default: + founduser = strstr (part_perms, username); + foundall = strstr (part_perms, "ALL!"); + + if (founduser) + { + usr = strtok (founduser, "!\t"); + per = strtok (NULL, ",\t"); + + if (strcmp (usr, username) == 0) + { + xperms = xstrdup (per); + xperms_len = strlen (xperms); + + userfound = 1; + } + } + else + { + if (use_system_groups) { + struct group *griter; + setgrent (); + while (griter = getgrent ()) { + char **users=griter->gr_mem; + int index = 0; + char *userchk = users [index++]; + while(userchk != NULL) { + if(strcmp (userchk, username) == 0) + break; + userchk = users[index++]; + } + if (userchk != NULL) { + char *grp; + if ((grp = strstr (part_perms, griter->gr_name)) && grp[strlen (griter->gr_name)] == '!') { + char *gperm = strtok (grp, "!\t"); + gperm = strtok (NULL, ",\t"); + xrealloc_and_strcat (&xperms, &xperms_len, gperm); + + groupfound = 1; + } + } + } + endgrent (); + } + else if (use_cvs_groups) { + groupfp = open_groupfile ("r"); + if (groupfp != NULL) + { + char *line = NULL; + size_t line_allocated = 0; + + while (getline (&line, &line_allocated, groupfp) >= 0) + { + if (strstr (line, username)) + { + char *temp; + temp = strstr (line, username); + + if (temp[strlen (username)] == ',' + || temp[strlen (username)] == ' ' + || temp[strlen (username)] == '\n') + { + char *tmp; + tmp = strtok (line, ":\t"); + if (strcmp (tmp, username) != 0) + { + char *grp; + if ((grp = strstr (part_perms, tmp))) + if (grp[strlen (tmp)] == '!') + { + char *gperm; + gperm = strtok (grp, "!\t"); + gperm = strtok (NULL, ",\t"); + + xrealloc_and_strcat (&xperms, &xperms_len, gperm); + + groupfound = 1; + } + } + } + } + } + if (fclose (groupfp) == EOF) + error (1, errno, "cannot close 'group' file"); + } + } + } + + if (foundall && (!groupfound && !userfound)) + { + usr = strtok (strstr (part_perms, "ALL!"), "!\t"); + per = strtok (NULL, ",\t"); + + if (!default_checked) + default_perms_object = xstrdup (per); + + if (xperms[0] == '\0') + { + xperms = xstrdup (per); + xperms_len = strlen (xperms); + } + } + else if (xperms[0] == '\0' && !default_checked && default_part_perms_accessfile) + { + part_perms = xstrdup (default_part_perms_accessfile); + default_checked = 1; + + goto check_default; + } + + if (xperms[0] != '\0' && strcmp (xperms, "x") == 0) + { + if (default_perms_object) + xperms = xstrdup (default_perms_object); + else if (default_part_perms_accessfile) + { + part_perms = default_part_perms_accessfile; + default_checked = 1; + goto check_default; + } + else if (cvs_acl_default_permissions) + { + aclconfig_default_used = 1; + xperms = xstrdup (cvs_acl_default_permissions); + } + } + + if (xperms[0] == '\0' && cvs_acl_default_permissions) + { + aclconfig_default_used = 1; + xperms = xstrdup (cvs_acl_default_permissions); + } + + return (xperms); +} + + +int +cvsacl (argc, argv) +int argc; +char **argv; +{ + char *chdirrepository; + int c; + int err = 0; + int usetag = 0; + int recursive = 0; + + int k; + + int which; + char *where; + + is_racl = (strcmp (cvs_cmd_name, "racl") == 0); + + if (argc == -1) + usage (is_racl ? racl_usage : acl_usage); + + /* parse the args */ + optind = 0; + + while ((c = getopt (argc, argv, "Rr:dfl")) != -1) + { + switch (c) + { + case 'R': + recursive = 1; + break; + case 'r': + tag = xstrdup (optarg); + break; + case 'd': + acldir = 1; + break; + case 'f': + aclfile = 1; + break; + case 'l': + listacl = 1; + break; + case '?': + default: + usage (is_racl ? racl_usage : acl_usage); + break; + } + } + + argc -= optind; + argv += optind; + + if (!acldir && !aclfile) + usage (is_racl ? racl_usage : acl_usage); + if (acldir && aclfile) + usage (is_racl ? racl_usage : acl_usage); + + if (listacl) + if (strstr (argv[0], ":")) + usage (is_racl ? racl_usage : acl_usage); + if (!listacl) + if (!strstr (argv[0], ":")) + usage (is_racl ? racl_usage : acl_usage); + + if (argc < (is_racl ? 1 : 1)) + usage (is_racl ? racl_usage : acl_usage); + +#ifdef CLIENT_SUPPORT + + if (current_parsed_root->isremote) + { + start_server (); + ign_setup (); + + if(recursive) + send_arg ("-R"); + + if (acldir) + send_arg ("-d"); + + if (aclfile) + send_arg ("-f"); + + if (listacl) + send_arg ("-l"); + + if(tag) + { + send_arg ("-r"); + send_arg (tag); + } + + send_arg ("--"); + + if (!listacl) + { + send_arg (argv[0]); + + argc--; + argv++; + } + + if (is_racl) + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + + send_to_server ("racl\012",0); + } + else + { + send_files (argc, argv, recursive, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("acl\012", 0); + } + + return get_responses_and_close (); + } +#endif + +#ifdef SERVER_SUPPORT + + if (!listacl) + { + muser = strtok (argv[0], ":\t"); + mperms = strtok (NULL, ":\t"); + + /* if set to 'default' */ + if ((strlen (mperms) == 7) && (strncmp (mperms, "default", 7) == 0)) + mperms = xstrdup ("x"); + + /* Check that the given permissions are valid. */ + if (!given_perms_valid (mperms)) + error (1,0,"Invalid permissions: '%s'", mperms); + + argc--; + argv++; + } + + + if (!tag) + tag = xstrdup ("HEAD"); + + if (!cvs_casecmp (argv[0], "ALL")) + { + argv[0] = xstrdup ("."); + defaultperms = 1; + if (!use_separate_acl_file_for_each_dir) + { + recursive = 0; + } + + } + + if (is_racl) + { + DBM *db; + int i; + db = open_module (); + for (i = 0; i < argc; i++) + { + err += do_module (db, argv[i], MISC, "ACL ing: ", + racl_proc, (char *) NULL, 0, !recursive, 0, + 0, NULL); + } + close_module (db); + } + else + { + err = racl_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, !recursive, NULL, + NULL); + } + if (err) + error (1, 0, "an error accured"); + + return (err); + +#endif +} + +static int +racl_proc (argc, argv, xwhere, mwhere, mfile, shorten, local, mname, msg) +int argc; +char **argv; +char *xwhere; +char *mwhere; +char *mfile; +int shorten; +int local; +char *mname; +char *msg; +{ + char *myargv[2]; + int err = 0; + int which; + char *repository; + char *where; + + if (is_racl) + { + repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); + + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); + where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (void) strcpy (where, argv[0]); + + /* if mfile isn't null, we need to set up to do only part of the module */ + if (mfile != NULL) + { + char *cp; + char *path; + + /* if the portion of the module is a path, put the dir part on repos */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void) sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void) strcpy (repository, path); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + } + else + { + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if ( CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + return (1); + } + + /* End section which is identical to patch_proc. */ + + which = W_REPOS | W_ATTIC; + } + else + { + where = NULL; + which = W_LOCAL | W_REPOS | W_ATTIC; + } + if (listacl) + err = start_recursion (acllist_fileproc, NULL, acllist_dirproc, NULL, NULL, + argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1, + repository); + else + err = start_recursion (acl_fileproc, NULL, acl_dirproc, NULL, NULL, + argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1, + repository); + + return (err); +} + + +static int +acl_fileproc (callerdat, finfo) +void *callerdat; +struct file_info *finfo; +{ + Vers_TS *vers; + + FILE *accessfp; + + char *filefullname; + + char *founduserpart = NULL; + char *newuserpart = NULL; + char *otheruserparts = NULL; + size_t otherslen = 0; + + const char *frepository; + int foundline = 0; + + char *line = NULL; + size_t line_allocated = 0; + int linelen; + + char *part_type = NULL; + char *part_object = NULL; + char *part_tag = NULL; + char *part_perms = NULL; + char *wperms; + char *userpart; + + char *errmsg; + + int pos; + + if (!aclfile) + return (0); + + frepository = Short_Repository (finfo->repository); + + filefullname = xmalloc (strlen (frepository) + + strlen (finfo->file) + + 2); + strcpy (filefullname, frepository); + strcat (filefullname, "/"); + strcat (filefullname, finfo->file); + + if (!access_allowed (finfo->file, finfo->repository, tag, 8, &line, &pos, 0)) + error (1,0,"You do not have acl admin rights on '%s'", frepository); + + if (line != NULL) + { + part_type = strtok (line, ":\t"); + part_object = strtok (NULL, ":\t"); + part_tag = strtok (NULL, ":\t"); + part_perms = strtok (NULL, ":\t"); + + foundline = 1; + userpart = strtok (part_perms, ",\t"); + + if (strstr (userpart, muser)) + founduserpart = xstrdup (userpart); + else + { + otheruserparts = xstrdup (userpart); + otherslen = strlen (otheruserparts); + } + + while ((userpart = strtok (NULL, ",\t")) != NULL) + { + if (strncmp (userpart, muser, strlen (muser)) == 0) + founduserpart = xstrdup (userpart); + else + { + if (otheruserparts != NULL) + { + xrealloc_and_strcat (&otheruserparts, &otherslen, ","); + xrealloc_and_strcat (&otheruserparts, &otherslen, userpart); + } + else + { + otheruserparts = xstrdup (userpart); + otherslen = strlen (otheruserparts); + } + } + } + } + + wperms = make_perms (mperms, founduserpart, &errmsg); + if (wperms == NULL) + { + if (errmsg) + error (0, 0, "%s %s", filefullname, errmsg); + + return (0); + } + else + { + cvs_output ("X ", 0); + cvs_output (filefullname, 0); + cvs_output ("\n", 0); + + write_perms (muser, wperms, founduserpart, foundline, + otheruserparts, "f", filefullname, tag, pos, finfo->repository); + } + + return (0); +} + +static Dtype +acl_dirproc (callerdat, dir, repos, update_dir, entries) +void *callerdat; +const char *dir; +const char *repos; +const char *update_dir; +List *entries; +{ + const char *drepository; + char *founduserpart = NULL; + char *newuserpart = NULL; + char *otheruserparts = NULL; + size_t otherslen = 0; + int foundline = 0; + + char *line = NULL; + size_t line_allocated = 0; + int linelen; + + FILE *accessfp; + char *part_type = NULL; + char *part_object = NULL; + char *part_tag = NULL; + char *part_perms = NULL; + char *wperms; + int i = 0; + char *userpart; + int pos; + + char *errmsg; + + if (repos[0] == '\0') + repos = Name_Repository (dir, NULL); + + if (!acldir) + return (0); + + if (!access_allowed (NULL, repos, tag, 8, &line, &pos, 0)) + error (1,0,"You do not have admin rights on '%s'", Short_Repository (repos)); + + drepository = Short_Repository (repos); + + if (line != NULL) + { + part_type = strtok (line, ":\t"); + part_object = strtok (NULL, ":\t"); + part_tag = strtok (NULL, ":\t"); + part_perms = strtok (NULL, ":\t"); + + foundline = 1; + userpart = strtok (part_perms, ",\t"); + + if (strstr (userpart, muser)) + founduserpart = xstrdup (userpart); + else + { + otheruserparts = xstrdup (userpart); + otherslen = strlen (otheruserparts); + } + + while ((userpart = strtok (NULL, ",\t")) != NULL) + { + if (strncmp (userpart, muser, strlen (muser)) == 0) + founduserpart = xstrdup (userpart); + else + { + if (otheruserparts != NULL) + { + xrealloc_and_strcat (&otheruserparts, &otherslen, ","); + xrealloc_and_strcat (&otheruserparts, &otherslen, userpart); + } + else + { + otheruserparts = xstrdup (userpart); + otherslen = strlen (otheruserparts); + } + } + } + } + + wperms = make_perms (mperms, founduserpart, &errmsg); + if (wperms == NULL) + { + if (errmsg) + error (0, 0, "%s %s", drepository, errmsg); + + return (0); + } + else + { + if (defaultperms) + { + cvs_output ("X ", 0); + cvs_output ("ALL", 0); + cvs_output ("\n", 0); + write_perms (muser, wperms, founduserpart, foundline, + otheruserparts, "d", "ALL", tag, pos, drepository);//baris + + } + else + { + cvs_output ("X ", 0); + cvs_output (drepository, 0); + cvs_output ("\n", 0); + write_perms (muser, wperms, founduserpart, foundline, + otheruserparts, "d", drepository, tag, pos, drepository);//baris `repos` + } + } + + return (0); +} + +/* Open CVSROOT/access or defined CVSACLFileLocation file. */ +FILE * +open_accessfile (fmode, adir, fname) +char *fmode; +const char *adir; +char **fname; +{ + char *accessfile; + FILE *accessfp; + + if (!use_separate_acl_file_for_each_dir) + { + if (cvs_acl_file_location == NULL) + { + accessfile = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_ACCESS) + + 3); + + strcpy (accessfile, current_parsed_root->directory); + strcat (accessfile, "/"); + strcat (accessfile, CVSROOTADM); + strcat (accessfile, "/"); + strcat (accessfile, CVSROOTADM_ACCESS); + } + else + { + accessfile = xmalloc (strlen (cvs_acl_file_location)); + strcpy (accessfile, cvs_acl_file_location); + } + } + else + { + accessfile = xmalloc (strlen (current_parsed_root->directory) + + strlen (adir) + + strlen ("access") + + 3); + + strcpy (accessfile, current_parsed_root->directory); + strcat (accessfile, "/"); + strcat (accessfile, adir); + strcat (accessfile, "/"); + strcat (accessfile, "access"); + } + + accessfp = CVS_FOPEN (accessfile, fmode); + + if (accessfp == NULL) + error (0, 0, "cannot open file: %s", accessfile); + + if (fname != NULL) + *fname = xstrdup (accessfile); + + free (accessfile); + + return (accessfp); +} + +/* Open /etc/group file if UseSystemGroups=yes in config file. */ +/* Open CVSROOT/group file if UseCVSGroups=yes in config file. */ +FILE * +open_groupfile (fmode) +char *fmode; +{ + char *groupfile; + FILE *groupfp; + + if (use_cvs_groups) + { + if (cvs_groups_file_location != NULL) + { + groupfile = xmalloc (strlen (cvs_groups_file_location)); + strcpy (groupfile, cvs_groups_file_location); + } + else + { + groupfile = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_GROUP) + + 3); + + strcpy (groupfile, current_parsed_root->directory); + strcat (groupfile, "/"); + strcat (groupfile, CVSROOTADM); + strcat (groupfile, "/"); + strcat (groupfile, CVSROOTADM_GROUP); + } + } + + else + { + return (NULL); + } + + groupfp = CVS_FOPEN (groupfile, "r"); + + if (groupfp == NULL) + error (0, 0, "cannot open file: %s", groupfile); + + free (groupfile); + + return (groupfp); +} + + +/* Check whether given permissions are valid or not. */ +/* Returns 1 if permissions are valid. */ +/* Returns 0 if permissions are NOT valid. */ +int +given_perms_valid (cperms) +char *cperms; +{ + int cperms_len; + int retval; + int index, i; + + if (cperms[0] == '+' || cperms[0] == '-') + index = 1; + else + index = 0; + + cperms_len = strlen (cperms); + + switch (cperms[index]) + { + case 'x': + if ((cperms_len - index) == 1 && cperms_len == 1) + retval = 1; + else + retval = 0; + break; + case 'n': + if ((cperms_len - index) == 1 && cperms_len == 1) + retval = 1; + else + retval = 0; + break; + case 'p': + if ((cperms_len - index) == 1) + retval = 1; + else + retval = 0; + break; + case 'a': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 'p') + retval = 1; + else + retval = 0; + break; + case 'r': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd') + retval = 1; + else + retval = 0; + break; + case 'w': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd') + retval = 1; + else + retval = 0; + break; + case 't': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 'w' || cperms[i] == 'c' || cperms[i] == 'd') + retval = 1; + else + retval = 0; + break; + case 'c': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 't' || cperms[i] == 'w' || cperms[i] == 'd') + retval = 1; + else + retval = 0; + break; + case 'd': + if ((cperms_len - index) == 1) + retval = 1; + else + for (i = index + 1; i < cperms_len; i++) + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'w') + retval = 1; + else + retval = 0; + break; + default: + retval = 0; + break; + } + return (retval); +} + +char * +make_perms (perms, founduserpart, xerrmsg) +char *perms; +char *founduserpart; +char **xerrmsg; +{ + char *fperms; + size_t perms_len; + size_t fperms_len; + char *retperms = NULL; + char *xperms; + int i, j; + int err = 0; + char *errmsg = NULL; + size_t retperms_len = 1; + + retperms = xmalloc (retperms_len); + retperms[0] = '\0'; + + perms_len = strlen (perms); + + if (perms[0] == '+' || perms[0] == '-') + { + if (founduserpart) + { + char *temp; + temp = strtok (founduserpart, "!\t"); + fperms = strtok (NULL, "!\t"); + fperms_len = strlen (fperms); + + if (strncmp (fperms, "x", 1) == 0) + { + err = 1; + if (perms[0] == '+') + *xerrmsg = xstrdup ("cannot add default permission 'x'"); + else + *xerrmsg = xstrdup ("cannot remove default permission 'x'"); + } + + for (i = 1; i < perms_len && !err; i++) + { + switch (perms[i]) + { + case 'n': + err = 1; + break; + case 'p': + if (perms[0] == '+') + fperms = xstrdup ("p"); + else if (perms[0] == '-') + { + fperms_len = 1; + fperms = xmalloc (fperms_len); + fperms[0] = '\0'; + } + break; + case 'a': + for (j = 0; j < fperms_len; j++) + { + if (fperms[j] == 'p') + { + err = 1; + *xerrmsg = xstrdup ("user has admin rights, cannot use +/- permissions"); + } + else if (fperms[j] == 'a' && perms[0] == '+') + { + err = 1; + *xerrmsg = xstrdup ("user already has all ('a') permission"); + } + else if (fperms[j] != 'a' && perms[0] == '-') + { + err = 1; + *xerrmsg = xstrdup ("user does not have all ('a') permission"); + } + } + if (perms[0] == '+') + { + fperms = xstrdup ("a"); + fperms_len = strlen (fperms); + } + else if (perms[0] == '-') + { + fperms_len = 1; + fperms = xmalloc (fperms_len); + fperms[0] = '\0'; + } + + break; + case 'r': + for (i = 0; i < fperms_len; i++) + { + if (fperms[i] == 'n' && perms[0] == '+') + { + fperms = xstrdup ("r"); + fperms_len = strlen (fperms); + } + else if (fperms[i] == 'r' && perms[0] == '-') + { + fperms_len = 1; + fperms = xmalloc (fperms_len); + fperms[0] = '\0'; + } + else if (perms[0] == '-') + { + err = 1; + *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission"); + } + else + { + err = 1; + *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission"); + } + } + break; + case 'w': + { + char *tempfperms; + size_t tempfperms_len = 1; + + tempfperms = xmalloc (tempfperms_len); + tempfperms[0] = '\0'; + + for (j = 0; j < fperms_len; j++) + { + if (fperms[j] == 't' || fperms[j] == 'c' || fperms[j] == 'd') + { + char *temp; + temp = xmalloc (2); + temp[0] = fperms[j]; + temp[1] = '\0'; + + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp); + free (temp); + } + else if (fperms[j] == 'a' || fperms[j] == 'p') + { + err = 1; + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- write permissions"); + } + else if (fperms[j] == 'n' || fperms[j] == 'r') + { + if (perms[0] == '-') + { + err = 1; + *xerrmsg = xstrdup ("user does not have write ('w') permission"); + } + } + } + + fperms = xstrdup (tempfperms); + fperms_len = strlen (fperms); + free (tempfperms); + + if (perms[0] == '+') + { + xrealloc_and_strcat (&fperms, &fperms_len, "w"); + } + } + break; + case 't': + { + char *tempfperms; + size_t tempfperms_len = 1; + + tempfperms = xmalloc (tempfperms_len); + tempfperms[0] = '\0'; + + for (j = 0; j < fperms_len; j++) + { + if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 'd') + { + char *temp; + temp = xmalloc (2); + temp[0] = fperms[j]; + temp[1] = '\0'; + + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp); + free (temp); + } + else if (fperms[j] == 'a' || fperms[j] == 'p') + { + err = 1; + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- tag permissions"); + } + else if (fperms[i] == 'n' || fperms[i] == 'r') + { + if (perms[0] == '-') + *xerrmsg = xstrdup ("user does not have tag ('t') permission"); + } + } + + fperms = xstrdup (tempfperms); + fperms_len = strlen (fperms); + free (tempfperms); + + if (perms[0] == '+') + { + xrealloc_and_strcat (&fperms, &fperms_len, "t"); + } + } + break; + case 'c': + { + char *tempfperms; + size_t tempfperms_len = 1; + + tempfperms = xmalloc (tempfperms_len); + tempfperms[0] = '\0'; + + for (j = 0; j < fperms_len; j++) + { + if (fperms[j] == 'w' || fperms[j] == 't' || fperms[j] == 'd') + { + char *temp; + temp = xmalloc (2); + temp[0] = fperms[j]; + temp[1] = '\0'; + + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp); + free (temp); + } + else if (fperms[j] == 'a' || fperms[j] == 'p') + { + err = 1; + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- create permissions"); + } + else if (fperms[i] == 'n' || fperms[i] == 'r') + { + if (perms[0] == '-') + err = 1; + *xerrmsg = xstrdup ("user does not have create ('c') permission"); + } + + } + + fperms = xstrdup (tempfperms); + fperms_len = strlen (fperms); + free (tempfperms); + + if (perms[0] == '+') + { + xrealloc_and_strcat (&fperms, &fperms_len, "c"); + } + } + break; + case 'd': + { + char *tempfperms; + size_t tempfperms_len = 1; + + tempfperms = xmalloc (tempfperms_len); + tempfperms[0] = '\0'; + + for (j = 0; j < fperms_len; j++) + { + if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 't') + { + char *temp; + temp = xmalloc (2); + temp[0] = fperms[j]; + temp[1] = '\0'; + + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp); + free (temp); + } + else if (fperms[j] == 'a' || fperms[j] == 'p') + { + err = 1; + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- delete permissions"); + } + else if (fperms[i] == 'n' || fperms[i] == 'r') + { + if (perms[0] == '-') + err = 1; + *xerrmsg = xstrdup ("user does not have delete ('d') permission"); + } + } + + fperms = xstrdup (tempfperms); + fperms_len = strlen (fperms); + free (tempfperms); + + if (perms[0] == '+') + { + xrealloc_and_strcat (&fperms, &fperms_len, "d"); + } + } + break; + default: + err = 1; + *xerrmsg = xstrdup ("error in 'access' file format"); + break; + } + if (fperms[0] == '\0') + retperms = xstrdup ("none"); + else + retperms = xstrdup (fperms); + } + } + else + { + err = 1; + *xerrmsg = xstrdup("user is not given any permissions to remove/add"); + } + } + + else + { + retperms = xstrdup (perms); + } + + if (err) + return (NULL); + else + return (retperms); +} + +int +write_perms (user, perms, founduserpart, foundline, otheruserparts, + part_type, part_object, part_tag, pos, arepos) +char *user; +char *perms; +char *founduserpart; +int foundline; +char *otheruserparts; +char *part_type; +char *part_object; +char *part_tag; +int pos; +char *arepos; +{ + char *accessfile; + char *tmpaccessout; + FILE *accessfpin; + FILE *accessfpout; + + char *newline = NULL; + size_t newlinelen = 1; + + char *line = NULL; + size_t line_allocated = 0; + + newline = xmalloc (newlinelen); + newline[0] = '\0'; + + if (!cvs_casecmp (part_tag, "ALL")) + part_tag = xstrdup ("ALL"); + + xrealloc_and_strcat (&newline, &newlinelen, part_type); + xrealloc_and_strcat (&newline, &newlinelen, ":"); + xrealloc_and_strcat (&newline, &newlinelen, part_object); + xrealloc_and_strcat (&newline, &newlinelen, ":"); + xrealloc_and_strcat (&newline, &newlinelen, part_tag); + xrealloc_and_strcat (&newline, &newlinelen, ":"); + + if (strncmp (perms, "none", 4) != 0) + { + xrealloc_and_strcat (&newline, &newlinelen, user); + xrealloc_and_strcat (&newline, &newlinelen, "!"); + xrealloc_and_strcat (&newline, &newlinelen, perms); + if (otheruserparts != NULL) + xrealloc_and_strcat (&newline, &newlinelen, ","); + } + + if (otheruserparts != NULL) + { + if (otheruserparts[strlen (otheruserparts) - 1] == '\n') + otheruserparts[strlen (otheruserparts) - 1] = '\0'; + + xrealloc_and_strcat (&newline, &newlinelen, otheruserparts); + } + + xrealloc_and_strcat (&newline, &newlinelen, ":"); + + if (foundline) + { + accessfpout = cvs_temp_file (&tmpaccessout); + accessfpin = open_accessfile ("r", arepos, &accessfile); + + while (getline (&line, &line_allocated, accessfpin) >= 0) + { + if (pos != ftell (accessfpin)) + { + if (fprintf (accessfpout, line) < 0) + error (1, errno, "writing temporary file: %s", tmpaccessout); + } + else + { + if (fprintf (accessfpout, "%s\n", newline) < 0) + error (1, errno, "writing temporary file: %s", tmpaccessout); + } + + } + if (fclose (accessfpin) == EOF) + error (1, errno, "cannot close access file: %s", accessfile); + + if (fclose (accessfpout) == EOF) + error (1, errno, "cannot close temporary file: %s", tmpaccessout); + + if (CVS_UNLINK (accessfile) < 0) + error (0, errno, "cannot remove %s", accessfile); + + copy_file (tmpaccessout, accessfile); + + if (CVS_UNLINK (tmpaccessout) < 0) + error (0, errno, "cannot remove temporary file: %s", tmpaccessout); + } + else + { + accessfpout = open_accessfile ("r+", arepos, &accessfile); + + if (accessfpout == NULL) + { + if (existence_error (errno)) + { + accessfpout = open_accessfile ("w+", arepos, &accessfile); + } + } + else { + if (fseek (accessfpout, 0, 2) != 0) + error (1, errno, "cannot fseek access file: %s", accessfile); + } + + if (fprintf (accessfpout, "%s\n", newline) < 0) + error (1, errno, "writing access file: %s", accessfile); + + if (fclose (accessfpout) == EOF) + error (1, errno, "cannot close access file: %s", accessfile); + } + + free (newline); + + chmod(accessfile, 0644); + + return (0); +} + + +static int +acllist_fileproc (callerdat, finfo) +void *callerdat; +struct file_info *finfo; +{ + + char *filefullname; + const char *frepository; + char *line = NULL; + int pos; + + if (!aclfile) + return (0); + + frepository = Short_Repository (finfo->repository); + + filefullname = xmalloc (strlen (frepository) + + strlen (finfo->file) + + 2); + strcpy (filefullname, frepository); + strcat (filefullname, "/"); + strcat (filefullname, finfo->file); + + if (!access_allowed (finfo->file, finfo->repository, tag, 5, &line, &pos, 0)) + error (1,0,"You do not have admin rights on '%s'", frepository); + + acllist_print (line, filefullname); + + return (0); +} + +static Dtype +acllist_dirproc (callerdat, dir, repos, update_dir, entries) +void *callerdat; +const char *dir; +const char *repos; +const char *update_dir; +List *entries; +{ + + char *line = NULL; + const char *drepository; + int pos; + + if (repos[0] == '\0') + repos = Name_Repository (dir, NULL); + + if (!acldir) + return (0); + + drepository = Short_Repository (repos); + + if (!access_allowed (NULL, repos, tag, 5, &line, &pos, 0)) + error (1, 0, "You do not have admin rights on '%s'", drepository); + + acllist_print (line, drepository); + + return (0); +} + + +void +acllist_print (line, obj) +char *line; +const char *obj; +{ + char *temp; + char *temp2; + int x; + int c = 0; + + char *printedusers[255]; + printedusers[0] = NULL; + + if (line != NULL) + { + temp = strtok (line, ":\t"); + + if (acldir) + cvs_output ("d ", 0); + else if (aclfile) + cvs_output ("f ", 0); + + temp = strtok (NULL, ":\t"); + + cvs_output(obj, 0); + cvs_output (" | ", 0); + + temp = strtok (NULL, ":\t"); + cvs_output (temp, 0); + cvs_output (" | ", 0); + + while ((temp = strtok (NULL, "!\t")) != NULL) + { + if (strncmp (temp, ":", 1) == 0) + break; + + if (strcmp (temp, "ALL") == 0) + { + temp = strtok (NULL, ",\t"); + continue; + } + + cvs_output (temp, 0); + cvs_output (":", 0); + + while (printedusers[c] != NULL) + c++; + + printedusers[c] = xstrdup (temp); + c++; + printedusers[c] = NULL; + + temp = strtok (NULL, ",\t"); + + if (temp != NULL && temp[strlen (temp) - 2] == ':') + temp[strlen (temp) - 2] = '\0'; + + cvs_output (temp, 0); + cvs_output (" ", 0); + } + + if (default_perms_object) + { + cvs_output ("| defaults ", 0); + cvs_output ("ALL:", 0); + cvs_output (default_perms_object, 0); + } + else if (default_part_perms_accessfile) + { + size_t i; + i = strlen (default_part_perms_accessfile); + xrealloc_and_strcat (&default_part_perms_accessfile, &i, ","); + + free(line); + line = xstrdup(default_part_perms_accessfile); + + cvs_output ("| defaults ", 0); + + temp = strtok (line, "!\t"); + cvs_output (temp, 0); + cvs_output (":", 0); + + temp = strtok (NULL, ",\t"); + + cvs_output (temp, 0); + cvs_output (" ", 0); + + while ((temp = strtok (NULL, "!\t")) != NULL) + { + int printed = 0; + int c2 = 0; + while (printedusers[c2] != NULL && printed == 0) + { + if (strcmp (printedusers[c2], temp) == 0) + { + printed = 1; + break; + } + c2++; + } + + if (printed == 0) + { + cvs_output (temp, 0); + cvs_output (":", 0); + } + + temp = strtok (NULL, ",\t"); + + if (temp[strlen (temp) - 2] == ':') + temp[strlen (temp) - 2] = '\0'; + + if (printed == 0) + { + cvs_output (temp, 0); + cvs_output (" ", 0); + } + } + } + else if (cvs_acl_default_permissions) + { + cvs_output ("| defaults ", 0); + cvs_output ("ALL: ", 0); + cvs_output (cvs_acl_default_permissions, 0); + } + + cvs_output ("\n", 0); + + } + else + { + if (acldir) + cvs_output ("d ", 0); + else if (aclfile) + cvs_output ("f ", 0); + cvs_output (obj, 0); + cvs_output (" | ", 0); + cvs_output (tag, 0); + cvs_output (" | ", 0); + + if (default_perms_object) + { + cvs_output ("| defaults ", 0); + cvs_output ("ALL:", 0); + cvs_output (default_perms_object, 0); + cvs_output ("\n", 0); + } + else if (default_part_perms_accessfile) + { + free(line); + line = xstrdup(default_part_perms_accessfile); + + cvs_output ("| defaults ", 0); + + temp = strtok (line, "!\t"); + cvs_output (temp, 0); + cvs_output (":", 0); + + temp = strtok (NULL, ",\t"); + + if (temp[strlen (temp) - 2] == ':') + temp[strlen (temp) - 2] = '\0'; + + cvs_output (temp, 0); + cvs_output (" ", 0); + + while ((temp = strtok (NULL, "!\t")) != NULL) + { + cvs_output (temp, 0); + cvs_output (":", 0); + + temp = strtok (NULL, ",\t"); + + if (temp[strlen (temp) - 2] == ':') + temp[strlen (temp) - 2] = '\0'; + + cvs_output (temp, 0); + cvs_output (" ", 0); + } + cvs_output ("\n", 0); + } + else if (cvs_acl_default_permissions) + { + cvs_output ("| defaults ", 0); + cvs_output ("ALL: ", 0); + cvs_output (cvs_acl_default_permissions, 0); + cvs_output ("\n", 0); + } + else + cvs_output ("default:p (no perms)\n", 0); + } + +} diff -urN cvs-1.11.19.orig/src/add.c cvs-1.11.19/src/add.c --- cvs-1.11.19.orig/src/add.c 2005-01-31 23:09:18.000000000 +0100 +++ cvs-1.11.19/src/add.c 2005-03-14 19:55:09.000000000 +0100 @@ -405,6 +405,24 @@ } else { +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo.file, repository, vers->tag, 6, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo.repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo.repository), finfo.file); + + return (0); + } + } +#endif /* There is a user file, so build the entry for it */ if (build_entry (repository, finfo.file, vers->options, message, entries, vers->tag) != 0) @@ -679,6 +697,26 @@ && isdir (finfo.file) && !wrap_name_has (finfo.file, WRAP_TOCVS)) { + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (NULL, repository, NULL, 6, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo.repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo.repository), finfo.file); + + return (0); + } + } +#endif + err += add_directory (&finfo); } else diff -urN cvs-1.11.19.orig/src/admin.c cvs-1.11.19/src/admin.c --- cvs-1.11.19.orig/src/admin.c 2005-01-31 23:09:57.000000000 +0100 +++ cvs-1.11.19/src/admin.c 2005-03-14 19:55:09.000000000 +0100 @@ -566,6 +566,25 @@ vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, NULL, 2, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + version = vers->vn_user; if (version != NULL && strcmp (version, "0") == 0) { diff -urN cvs-1.11.19.orig/src/annotate.c cvs-1.11.19/src/annotate.c --- cvs-1.11.19.orig/src/annotate.c 2005-01-31 23:10:09.000000000 +0100 +++ cvs-1.11.19/src/annotate.c 2005-03-14 19:55:09.000000000 +0100 @@ -281,6 +281,25 @@ if (version == NULL) return 0; +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, version, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + /* Distinguish output for various files if we are processing several files. */ cvs_outerr ("\nAnnotations for ", 0); diff -urN cvs-1.11.19.orig/src/commit.c cvs-1.11.19/src/commit.c --- cvs-1.11.19.orig/src/commit.c 2005-01-31 23:11:19.000000000 +0100 +++ cvs-1.11.19/src/commit.c 2005-03-14 19:55:09.000000000 +0100 @@ -1304,6 +1304,34 @@ return 0; ci = p->data; + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + int whichperm; + if (ci->status == T_MODIFIED) + whichperm = 3; + else if (ci->status == T_ADDED) + whichperm = 6; + else if (ci->status == T_REMOVED) + whichperm = 7; + + if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + if (ci->status == T_MODIFIED) { if (finfo->rcs == NULL) diff -urN cvs-1.11.19.orig/src/cvs.h cvs-1.11.19/src/cvs.h --- cvs-1.11.19.orig/src/cvs.h 2005-03-14 19:49:29.000000000 +0100 +++ cvs-1.11.19/src/cvs.h 2005-03-14 19:55:09.000000000 +0100 @@ -200,6 +200,11 @@ #define CVSROOTADM_PASSWD "passwd" #define CVSROOTADM_CONFIG "config" +/* cvsacl patch */ +#define CVSROOTADM_ACLCONFIG "aclconfig" +#define CVSROOTADM_ACCESS "access" +#define CVSROOTADM_GROUP "group" + #define CVSNULLREPOS "Emptydir" /* an empty directory */ /* Other CVS file names */ @@ -572,6 +577,18 @@ /* LockDir setting from CVSROOT/config. */ extern char *lock_dir; +/* cvsacl patch */ +/* ACL Patch settings from CVSROOT/config */ +extern int use_cvs_acl; +extern char *cvs_acl_default_permissions; +extern int use_cvs_groups; +extern int use_system_groups; +extern int use_separate_acl_file_for_each_dir; +extern char *cvs_acl_file_location; +extern char *cvs_groups_file_location; +extern char *cvs_server_run_as; +extern int stop_at_first_permission_denied; + void Scratch_Entry PROTO((List * list, const char *fname)); void ParseTag PROTO((char **tagp, char **datep, int *nonbranchp)); void WriteTag PROTO ((const char *dir, const char *tag, const char *date, @@ -867,6 +884,10 @@ int editors PROTO ((int argc, char **argv)); int watchers PROTO ((int argc, char **argv)); extern int annotate PROTO ((int argc, char **argv)); + +/* cvsacl patch */ +extern int cvsacl PROTO ((int argc, char **argv)); + extern int add PROTO ((int argc, char **argv)); extern int admin PROTO ((int argc, char **argv)); extern int checkout PROTO ((int argc, char **argv)); diff -urN cvs-1.11.19.orig/src/diff.c cvs-1.11.19/src/diff.c --- cvs-1.11.19.orig/src/diff.c 2005-01-31 23:12:09.000000000 +0100 +++ cvs-1.11.19/src/diff.c 2005-03-14 19:55:09.000000000 +0100 @@ -479,6 +479,43 @@ { /* Skip all the following checks regarding the user file; we're not using it. */ + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (diff_rev1) + { + if (!access_allowed (NULL, finfo->repository, diff_rev1, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } + if (diff_rev2) + { + if (!access_allowed (NULL, finfo->repository, diff_rev2, 5)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } + } +#endif + } else if (vers->vn_user == NULL) { @@ -832,6 +869,42 @@ if (!isdir (dir)) return (R_SKIP_ALL); +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (diff_rev1) + { + if (!access_allowed (NULL, update_dir, diff_rev1, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (update_dir)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (update_dir), update_dir); + + return (0); + } + } + if (diff_rev2) + { + if (!access_allowed (NULL, update_dir, diff_rev2, 5)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (update_dir)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (update_dir), update_dir); + + return (0); + } + } + } +#endif + if (!quiet) error (0, 0, "Diffing %s", update_dir); return (R_PROCESS); diff -urN cvs-1.11.19.orig/src/import.c cvs-1.11.19/src/import.c --- cvs-1.11.19.orig/src/import.c 2005-01-31 23:13:00.000000000 +0100 +++ cvs-1.11.19/src/import.c 2005-03-14 19:55:09.000000000 +0100 @@ -319,6 +319,20 @@ error (1, 0, "attempt to import the repository"); } +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (NULL, repository, argv[1], 6, NULL, NULL, 1)) + { + error (stop_at_first_permission_denied, 0, "permission denied for %s", + Short_Repository (repository)); + + return (0); + } + } +#endif + /* * Make all newly created directories writable. Should really use a more * sophisticated security mechanism here. diff -urN cvs-1.11.19.orig/src/log.c cvs-1.11.19/src/log.c --- cvs-1.11.19.orig/src/log.c 2005-02-03 15:50:51.000000000 +0100 +++ cvs-1.11.19/src/log.c 2005-03-14 19:55:09.000000000 +0100 @@ -853,6 +853,25 @@ return 1; } +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, NULL, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + if (log_data->sup_header || !log_data->nameonly) { diff -urN cvs-1.11.19.orig/src/main.c cvs-1.11.19/src/main.c --- cvs-1.11.19.orig/src/main.c 2005-03-14 19:49:29.000000000 +0100 +++ cvs-1.11.19/src/main.c 2005-03-14 19:55:09.000000000 +0100 @@ -102,6 +102,10 @@ } cmds[] = { + /* cvsacl patch */ + { "acl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "racl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR }, @@ -968,6 +972,9 @@ 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 (current_parsed_root->directory); + + /* cvsacl patch */ + parse_aclconfig (current_parsed_root->directory); } #ifdef CLIENT_SUPPORT diff -urN cvs-1.11.19.orig/src/mkmodules.c cvs-1.11.19/src/mkmodules.c --- cvs-1.11.19.orig/src/mkmodules.c 2005-01-31 23:14:17.000000000 +0100 +++ cvs-1.11.19/src/mkmodules.c 2005-03-14 19:55:09.000000000 +0100 @@ -316,6 +316,44 @@ NULL }; +/* cvsacl patch */ +static const char *const aclconfig_contents[] = { + "# Set `UseCVSACL' to yes to use CVSACL feature.\n", + "UseCVSACL=yes\n", + "\n", + "# Default CVSACL Permission to use.\n", + "#CVSACLDefaultPermissions=p\n", + "\n", + "# Default file location for CVS ACL file (access) is CVSROOT/access.\n", + "# If you want to use a different location, define it below.\n", + "#CVSACLFileLocation=/path/to/cvs/access\n", + "\n", + "# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).\n", + "UseSystemGroups=yes\n", + "\n", + "# Set `UseCVSGroups' to yes to use another group file.\n", + "#UseCVSGroups=yes\n", + "\n", + "# Default file location for CVS groups file is CVSROOT/group.\n", + "# If you want to use a different location, define it below.\n", + "#CVSGroupsFileLocation=/path/to/cvs/group\n", + "\n", + "# Set UseSeparateACLFileForEachDir to yes in order to use a\n", + "# separate 'access' file for each directory.\n", + "# This increased the performance if you have really big repository.\n", + "#UseSeparateACLFileForEachDir=no\n", + "\n", + "# If StopAtFirstPermissionDenied is set to yes\n", + "# operation will stop at first permission denied message.\n", + "# Default is no.\n", + "#StopAtFirstPermissionDenied=no\n", + "\n", + "# Set CVSServerRunAsUser to a system user, in order CVS server\n", + "# to run as.\n", + "#CVSServerRunAsUser=runascvsuser", + NULL +}; + static const struct admin_file filelist[] = { {CVSROOTADM_LOGINFO, "no logging of 'cvs commit' messages is done without a %s file", @@ -378,7 +416,12 @@ {CVSROOTADM_CONFIG, "a %s file configures various behaviors", config_contents}, - {NULL, NULL, NULL} + + /* cvsacl patch */ + {CVSROOTADM_ACLCONFIG, + "a %s file configures Access Control List behaviors", + aclconfig_contents}, + {NULL, NULL, NULL} }; /* Rebuild the checked out administrative files in directory DIR. */ @@ -962,6 +1005,26 @@ because xchmod() is too shy. */ chmod (info, 0666); } + + /* cvsacl patch */ + /* Make an empty 'CVSROOT/access' file */ + strcpy (info, adm); + strcat (info, "/"); + strcat (info, CVSROOTADM_ACCESS); + if (!isfile (info)) + { + FILE *fp; + + fp = open_file (info, "w"); + if (fputs ("# CVS ACL definitions file. DO NOT EDIT MANUALLY\n", + fp) < 0) + error (1, errno, "cannot write %s", info); + + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + + chmod (info, 0644); + } /* Make an empty val-tags file to prevent problems creating it later. */ strcpy (info, adm); diff -urN cvs-1.11.19.orig/src/parseinfo.c cvs-1.11.19/src/parseinfo.c --- cvs-1.11.19.orig/src/parseinfo.c 2005-01-31 23:14:54.000000000 +0100 +++ cvs-1.11.19/src/parseinfo.c 2005-03-14 19:55:09.000000000 +0100 @@ -453,3 +453,192 @@ free (line); return -1; } +/* cvsacl patch */ +int +parse_aclconfig (cvsroot) + char *cvsroot; +{ + char *infopath; + FILE *fp_info; + char *line = NULL; + size_t line_allocated = 0; + size_t len; + char *p; + /* FIXME-reentrancy: If we do a multi-threaded server, this would need + to go to the per-connection data structures. */ + static int parsed = 0; + + /* Authentication code and serve_root might both want to call us. + Let this happen smoothly. */ + if (parsed) + return 0; + parsed = 1; + + infopath = xmalloc (strlen (cvsroot) + + sizeof (CVSROOTADM_ACLCONFIG) + + sizeof (CVSROOTADM) + + 10); + if (infopath == NULL) + { + error (0, 0, "out of memory; cannot allocate infopath"); + goto error_return; + } + + strcpy (infopath, cvsroot); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM_ACLCONFIG); + + fp_info = CVS_FOPEN (infopath, "r"); + if (fp_info == NULL) + { + /* If no file, don't do anything special. */ + if (!existence_error (errno)) + { + /* Just a warning message; doesn't affect return + value, currently at least. */ + error (0, errno, "cannot open %s", infopath); + } + free (infopath); + return 0; + } + + while (getline (&line, &line_allocated, fp_info) >= 0) + { + /* Skip comments. */ + if (line[0] == '#') + continue; + + len = strlen (line) - 1; + if (line[len] == '\n') + line[len] = '\0'; + + /* Skip blank lines. */ + if (line[0] == '\0') + continue; + + /* The first '=' separates keyword from value. */ + p = strchr (line, '='); + if (p == NULL) + { + /* Probably should be printing line number. */ + error (0, 0, "syntax error in %s: line '%s' is missing '='", + infopath, line); + goto error_return; + } + + *p++ = '\0'; + + if (strcmp (line, "UseCVSACL") == 0) + { + if (strcmp (p, "no") == 0) + use_cvs_acl = 0; + else if (strcmp (p, "yes") == 0) + use_cvs_acl = 1; + else + { + error (0, 0, "unrecognized value '%s' for UseCVSACL", p); + goto error_return; + } + } + else if (strcmp (line, "UseSeparateACLFileForEachDir") == 0) + { + if (strcmp (p, "no") == 0) + use_separate_acl_file_for_each_dir = 0; + else if (strcmp (p, "yes") == 0) + use_separate_acl_file_for_each_dir = 1; + else + { + error (0, 0, "unrecognized value '%s' for UseSeparateACLFileForEachDir", p); + goto error_return; + } + } + else if (strcmp (line, "StopAtFirstPermissionDenied") == 0) + { + if (strcmp (p, "no") == 0) + stop_at_first_permission_denied = 0; + else if (strcmp (p, "yes") == 0) + stop_at_first_permission_denied = 1; + else + { + error (0, 0, "unrecognized value '%s' for StopAtFirstPermissionDenied", p); + goto error_return; + } + } + else if (strcmp (line, "CVSACLDefaultPermissions") == 0) + { + if (cvs_acl_default_permissions != NULL) + free (cvs_acl_default_permissions); + if (!given_perms_valid (p)) + error (1,0,"Invalid CVS ACL Default Permissions: '%s' in CVSROOT/aclconfig", p); + cvs_acl_default_permissions = xstrdup (p); + } + else if (strcmp (line, "UseCVSGroups") == 0) + { + if (strcmp (p, "no") == 0) + use_cvs_groups = 0; + else if (strcmp (p, "yes") == 0) + use_cvs_groups = 1; + else + { + error (0, 0, "unrecognized value '%s' for UseCVSGroups", p); + goto error_return; + } + } + else if (strcmp (line, "UseSystemGroups") == 0) + { + if (strcmp (p, "no") == 0) + use_system_groups = 0; + else if (strcmp (p, "yes") == 0) + use_system_groups = 1; + else + { + error (0, 0, "unrecognized value '%s' for UseSystemGroups", p); + goto error_return; + } + } + else if (strcmp (line, "CVSACLFileLocation") == 0) + { + if (cvs_acl_file_location != NULL) + free (cvs_acl_file_location); + cvs_acl_file_location = xstrdup (p); + } + else if (strcmp (line, "CVSGroupsFileLocation") == 0) + { + if (cvs_groups_file_location != NULL) + free (cvs_groups_file_location); + cvs_groups_file_location = xstrdup (p); + } + else if (strcmp (line, "CVSServerRunAsUser") == 0) + { + if (cvs_server_run_as != NULL) + free (cvs_server_run_as); + cvs_server_run_as = xstrdup (p); + } + + } + + if (ferror (fp_info)) + { + error (0, errno, "cannot read %s", infopath); + goto error_return; + } + if (fclose (fp_info) < 0) + { + error (0, errno, "cannot close %s", infopath); + goto error_return; + } + free (infopath); + if (line != NULL) + free (line); + return 0; + + error_return: + if (infopath != NULL) + free (infopath); + if (line != NULL) + free (line); + return -1; +} + diff -urN cvs-1.11.19.orig/src/patch.c cvs-1.11.19/src/patch.c --- cvs-1.11.19.orig/src/patch.c 2005-01-31 23:15:02.000000000 +0100 +++ cvs-1.11.19/src/patch.c 2005-03-14 19:55:09.000000000 +0100 @@ -503,6 +503,43 @@ goto out2; } +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (rev1) + { + if (!access_allowed (finfo->file, finfo->repository, rev1, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } + if (rev2) + { + if (!access_allowed (finfo->file, finfo->repository, rev2, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } + } +#endif + /* Create 3 empty files. I'm not really sure there is any advantage * to doing so now rather than just waiting until later. * diff -urN cvs-1.11.19.orig/src/remove.c cvs-1.11.19/src/remove.c --- cvs-1.11.19.orig/src/remove.c 2005-01-31 23:15:31.000000000 +0100 +++ cvs-1.11.19/src/remove.c 2005-03-14 19:55:09.000000000 +0100 @@ -255,6 +255,25 @@ { char *fname; +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 7, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + /* Re-register it with a negative version number. */ fname = xmalloc (strlen (vers->vn_user) + 5); (void) strcpy (fname, "-"); diff -urN cvs-1.11.19.orig/src/server.c cvs-1.11.19/src/server.c --- cvs-1.11.19.orig/src/server.c 2005-03-14 19:49:29.000000000 +0100 +++ cvs-1.11.19/src/server.c 2005-03-14 19:55:09.000000000 +0100 @@ -773,6 +773,10 @@ nothing. But for rsh, we need to do it now. */ parse_config (current_parsed_root->directory); + /* cvsacl patch */ + parse_aclconfig (current_parsed_root->directory); + + path = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + 2); @@ -3761,6 +3765,23 @@ do_cvs_command ("rlog", cvslog); } +/* cvsacl patch */ +static void +serve_acl (arg) + char *arg; +{ + do_cvs_command ("acl", cvsacl); +} + +/* cvsacl patch */ +static void +serve_racl (arg) + char *arg; +{ + cvs_cmd_name = "racl"; + do_cvs_command ("racl", cvsacl); +} + static void serve_add (arg) char *arg; @@ -4821,6 +4842,11 @@ REQ_LINE("diff", serve_diff, 0), REQ_LINE("log", serve_log, 0), REQ_LINE("rlog", serve_rlog, 0), + + /* cvsacl patch */ + REQ_LINE("acl", serve_acl, 0), + REQ_LINE("racl", serve_racl, 0), + REQ_LINE("add", serve_add, 0), REQ_LINE("remove", serve_remove, 0), REQ_LINE("update-patches", serve_ignore, 0), @@ -5294,6 +5320,10 @@ { struct passwd *pw; + /* cvsacl patch */ + if (use_cvs_acl && cvs_server_run_as) + username = cvs_server_run_as; + pw = getpwnam (username); if (pw == NULL) { @@ -5876,6 +5906,9 @@ in a new CVSROOT/config file to fix the broken one! */ parse_config (repository); + /* cvsacl patch */ + parse_aclconfig (repository); + /* We need the real cleartext before we hash it. */ descrambled_password = descramble (password); host_user = check_password (username, descrambled_password, repository); diff -urN cvs-1.11.19.orig/src/status.c cvs-1.11.19/src/status.c --- cvs-1.11.19.orig/src/status.c 2005-01-31 23:17:28.000000000 +0100 +++ cvs-1.11.19/src/status.c 2005-03-14 19:55:09.000000000 +0100 @@ -133,7 +133,27 @@ status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, 1, 0, &vers, 0); - sstat = "Classify Error"; + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + + sstat = "Classify Error"; switch (status) { case T_UNKNOWN: diff -urN cvs-1.11.19.orig/src/tag.c cvs-1.11.19/src/tag.c --- cvs-1.11.19.orig/src/tag.c 2005-01-31 23:17:45.000000000 +0100 +++ cvs-1.11.19/src/tag.c 2005-03-14 19:55:09.000000000 +0100 @@ -665,6 +665,25 @@ * correctly without breaking your link! */ +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, numtag, 4, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + if (delete_flag) return (rtag_delete (rcsfile)); @@ -885,6 +904,21 @@ if (nversion == NULL) goto free_vars_and_return; } + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 4, + NULL, NULL, 1)) + { + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + return (0); + } + } +#endif + if (delete_flag) { diff -urN cvs-1.11.19.orig/src/update.c cvs-1.11.19/src/update.c --- cvs-1.11.19.orig/src/update.c 2005-01-31 23:18:01.000000000 +0100 +++ cvs-1.11.19/src/update.c 2005-03-14 19:55:09.000000000 +0100 @@ -601,6 +601,26 @@ status = Classify_File (finfo, tag, date, options, force_tag_match, aflag, &vers, pipeout); + +/* cvsacl patch */ +#ifdef SERVER_SUPPORT + if (use_cvs_acl && server_active) + { + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5, + NULL, NULL, 1)) + { + if (stop_at_first_permission_denied) + error (1, 0, "permission denied for %s", + Short_Repository (finfo->repository)); + else + error (0, 0, "permission denied for %s/%s", + Short_Repository (finfo->repository), finfo->file); + + return (0); + } + } +#endif + /* Keep track of whether TAG is a branch tag. Note that if it is a branch tag in some files and a nonbranch tag in others, treat it as a nonbranch tag. It is possible that case diff -urN cvs-1.11.19.orig/src/version.c cvs-1.11.19/src/version.c --- cvs-1.11.19.orig/src/version.c 2005-01-31 23:18:34.000000000 +0100 +++ cvs-1.11.19/src/version.c 2005-03-14 19:55:09.000000000 +0100 @@ -30,7 +30,8 @@ #endif #endif - +/* cvsacl patch */ +char *aclpatch_string = "with CVSACL Patch 1.2.2 (cvsacl.sourceforge.net)\n"; static const char *const version_usage[] = { @@ -67,6 +68,8 @@ released. */ (void) fputs (PACKAGE_STRING, stdout); (void) fputs (config_string, stdout); + /* cvsacl patch */ + (void) fputs (aclpatch_string, stdout); #ifdef CLIENT_SUPPORT if (current_parsed_root && current_parsed_root->isremote)