1 diff -urN cvs-1.11.17.orig/INSTALL.cvsacl cvs-1.11.17/INSTALL.cvsacl
2 --- cvs-1.11.17.orig/INSTALL.cvsacl 1970-01-01 01:00:00.000000000 +0100
3 +++ cvs-1.11.17/INSTALL.cvsacl 2004-06-17 14:34:00.000000000 +0200
8 +- copy the file acl.c under src directory of CVS source distribution.
9 + "cp acl.c /path/to/cvs-1.11.17/src/"
10 +- copy the patch file cvsacl-patch-1.2.0 under CVS source distribution
12 + "cp cvsacl-patch-1.2.0 /path/to/cvs-1.11.17/"
13 +- cd to CVS source directory.
14 + "cd /path/to/cvs-1.11.17/"
16 + "patch -p0 < cvsacl-patch-1.2.0"
17 +- if you are initializing the repository after applying patch, related
18 + config files will be created with init command.
19 + "cvs -d /path/to/repository init"
20 +- if you already have a repository, you have to add the aclconfig file
21 + to your $CVSROOT/CVSROOT/. aclconfig.default is the default configuration
22 + file, you can rename it to aclconfig, and use it .
23 +- modify aclconfig file, if you need to change some options.
24 +- as the last step, you have to define yourself as acl administrator.
25 + "cvs -d /path/to/repository racl yourname:p -r ALL -d ALL"
26 + this command gives p (acl admin) rights to user (yourname),
27 + on all repository and tags/branches.
28 diff -urN cvs-1.11.17.orig/README.cvsacl cvs-1.11.17/README.cvsacl
29 --- cvs-1.11.17.orig/README.cvsacl 1970-01-01 01:00:00.000000000 +0100
30 +++ cvs-1.11.17/README.cvsacl 2004-06-17 14:33:55.000000000 +0200
33 +CVS Access Control List Extension Patch
35 +http://cvsacl.sourceforge.net/
36 +sbaris@users.sourceforge.net
38 +CVSACL is a patch for CVS. It adds two new subcommands
39 +(acl & racl) to cvs for access control list management. It
40 +provides advanced ACL definitions per modules, directories,
41 +and files on branch/tag for remote cvs repository connections.
42 +Execution of all CVS subcommands can be controlled with eight
43 +different permissions.
44 +ACL definitions works for only remote connections, local users
45 +can access and modify repository, if unix file system permissions
46 +allow. If you want all users to make remote connections to
47 +repository, and not allow local users to access repository, you
48 +have to set CVSServerRunAsUser keyword in aclconfig file
50 +Still local users can use acl and racl subcommands to set
51 +permissions on directories or files if they have acl admin rights (p)
52 +on related directories or files.
53 +So, in order to control all access to repository with this ACL
54 +extension, you should use CVSServerRunAsUser keyword and force all
55 +users to make remote connections. CVS repository administrator or
56 +project managers have to use acl and racl subcommands to manage
57 +permissions. But there is no gui client supporting these subcommands,
58 +so you have to use cvs client itself either locally or remotely.
66 + Command line character: n
67 + If a user given n permission, it is not allowed for any action on repository.
69 + Command line character: r
70 + r permission gives only read access on repository.
71 + With r permission you are allowed to run cvs subcommands: annotate,
72 + checkout, diff, export, log, rannotate, rdiff, rlog, status.
74 + Command line character: w
75 + w permission allows only cvs commit/checkin action.
76 + With w permission, you are not allowed to add/remove any file to/from
77 + repository, other permissions should be defines for that.
79 + Command line character: t
80 + t permission allows cvs tag and rtag subcommands to run, so you may
81 + control tagging and untagging operations. t permission includes r
82 + permission, since without reading you can not tag/untag a file.
83 + However t permission does not include write permission, you can not
84 + commit a file with only t permission.
86 + Command line character: c
87 + c permission allows cvs add and import subcommands to run. To add or
88 + import a file/directory to repository, you have to given a c
89 + permission. Again, c permission does not include write permission,
90 + thus you may only add or import files, but you can not modify any
91 + existing file. After issuing add subcommand, you have to commit the file
92 + to complete adding. This commit subcommand is allowed because you are
93 + adding file and not modifying existing one.
95 + Command line character: d
96 + d permission allows cvs remove command to run. To remove a file/directory
97 + from repository, d permission have to set. It does not include write
98 + permission, so you can not modify contents of an existing file on repository.
99 +- full access except admin rights
100 + Command line character: a
101 + a permission gives all access (above permissions) to repository, but it
102 + can not modify permissions. Only acl admins may modify the acl definitions.
104 + Command line character: p
105 + p permission means that user is an acl admin, so it is allowed to make anything on repository.
109 +The administrative file aclconfig contains miscellaneous settings which
110 +affect the behaviour of ACL extension. Currently defined keywords are:
113 +Use ACL definitions if set to yes. If you do not want to use ACLs for
114 +some repositories in a patched CVS server, set this keyword to no. The default is no.
116 +UseCVSACLDefaultPermissions=value
117 +Value can be any combination of valid permission types (w,r,t,c,d,t,a,p).
118 +if there is no defined ACL and default permission in access file, or no
119 +access file at all, this permissions are used. The default is p (admin rights),
120 +if aclconfig file is created with cvs init.
123 +CVS does not have a CVSROOT/passwd file. However it can be created manually
124 +(format should be same as /etc/group). If value set to yes, CVS checks for
125 +groups in file $CVSROOT/CVSROOT/group The default value is no.
127 +UseSystemGroups=value
128 +Group memberships for users are checked in file /etc/group, if value is set
129 +to yes. The default value is no.
131 +CVSACLFileLocation=value
132 +Originally access file is put under CVSROOT/CVSROOT, if you want a different
133 +location, set value to a valid path. The default value is $CVSROOT/CVSROOT/access.
135 +CVSGroupsFileLocation=value
136 +IF UseCVSGroups is set to yes, CVS looks for a group file under $CVSROOT/CVSROOT.
137 +To use a different location for group file set value to a valid path to group.
138 +The default value is $CVSROOT/CVSROOT/group.
140 +UseSeparateACLFileForEachDir=value
141 +If value is set to yes, a separate ACL file (access) is created for each
142 +directory in repository. If you have a really big repository
143 +(directories>10,000 and files>100,000), performance may drop due to a big
144 +acl file, access. Setting the value to yes, may increase performance. Normally,
145 +you will not need this. The default value is no.
147 +StopAtFirstPermissionDenied=value
148 +If StopAtFirstPermissionDenied is set to yes
149 +operation will stop at first permission denied message.
150 +e.g. when you send commit command for a directory, if you dont
151 +have write permission for just one file under the directory,
152 +by default you will have a warning and commit will continue
153 +on the other files. If you set this keyword to 'no', then
154 +commit operation will be stopped when inaccassable file found.
157 +CVSServerRunAsUser=value
158 +Set CVSServerRunAsUser keyword to a valid system user.
159 +When a user make a remote connection to CVS, after successfull authentication
160 +cvs process switch to run as that user, or defined system user in
161 +$CVSROOT/CVSROOT/passwd. So, you also have to set unix file permissions accordingly.
163 +Add a user and group such as both cvsadm.
164 +Set CVSServerRunAsUser keyword to cvsadm.
165 +Change unix file system permissions for your repository,
166 +make cvsadm user and group owner, and read,write,execute permissions and setgid.
167 +(chown cvsadm -R /path/to/your/repository)
168 +(chgrp cvsadm -R /path/to/your/repository)
169 +(chmod 2770 -R /path/to/your/repository)
170 +Add yourself to cvsadm group (since you are ACL administrator).
171 +Therefore, only users making remote connections will have access to repository
172 +if you give rights. Local users can not access to repository via a cvs client or directly.
175 +Command Line Usage Information
176 +acl command is used on checked out files or directories. racl command is
177 +used on repository without a working copy. Usage information can be obtained
178 +with standard cvs --help command.
179 +Output of cvs --help acl and cvs --help racl:
181 +Usage: cvs racl [user||group:permissions] [-Rl] [-r tag]
182 + -d [directory] -f [file]
183 + -R Process directories recursively.
184 + -r rev Existing revision/tag.
185 + -l List defined ACLs.
186 + -d dir Process on given directory.
187 + -f file Process on given file.
189 +Usage: cvs acl [user||group:permissions] [-Rl] [-r tag]
190 + -d [directory] -f [file]
191 + -R Process directories recursively.
192 + -r rev Existing revision/tag.
193 + -l List defined ACLs.
194 + -d dir Process on given directory.
195 + -f file Process on given file.
197 +You may directly set permissions for a user or group or add/remove
198 +permissions with + and - signs to/from existing permissions.
199 +If you do not give the branch/tag information, default value of HEAD
200 +(main branch) will be used. You have to give branch/tag name with -r option.
201 +You may type ALL for branch/tag field.
203 +While checking for permissions, it goes thorough the list below. So the highest
204 +significant permission is the first item in list.
206 +- permissions assigned to username for specific directory or file.
207 +- permissions assigned to group name for specific directory or file.
208 +- permissions as defaults for specific directory or file.
209 +- permissions assigned to parent folders (inherits from the first parent
210 + which permissions are assigned).
211 +- permissions as repository defaults.
212 +- permissions in aclconfig file.
242 +We have above directory structure for a cvs repository, and no defined permissions.
244 +Setting main default permissions:
246 +$ cvs -d /cvs/projectA racl cvsadmin:p -r ALL -d ALL
247 +$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL
248 +User cvsadmin will be an acl admin, and all other users will have only read
249 +rights on all branches/tags in projectA repository. This is the default acl
250 +definition and it overwrites default permissions in $CVSROOT/CVSROOT/aclconfig file.
252 +$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL
253 +$ cvs -d /cvs/projectA racl ALL:n -r ALL -d gui
254 +After executing these two commands, all users will have read access on all
255 +directories and files except gui directory. Everyone will be denied to access to gui
256 +directory becase no access, n, permissions is set.
258 +Setting permissions directly on a file or directory:
260 +$ cvs -d /cvs/projectA racl userX:wcd -d lib
261 +$ cvs -d /cvs/projectA racl group1:w -d lib
262 +First command will set write, create, and delete permissions for userX on directory
263 +lib with branch HEAD (since no branch/tag information given, branch defaults to HEAD).
264 +Second command will set only write permission for group1 on directory lib with branch HEAD.
265 +Members of group1 will have only commit rights on lib directory, branch HEAD, they can
266 +not add or remove any file, just modify existing files.
267 +If userX is also a member of group1, userX will have write, create, and delete permissions
268 +because it is specifically given these permissions.
270 +$ cvs -d /cvs/projectA racl userY:wcd -r develStream -d lib
271 +$ cvs -d /cvs/projectA racl userY:r -r integStream -d lib
272 +These commands will give wcd permissions to userY on lib directory with tag develstream,
273 +and r permissions on lib directory with tag integStream.
275 +$ cvs -d /cvs/projectA racl userZ:wcd -d src
276 +$ cvs -d /cvs/projectA racl userZ:r -f src/main.c
277 +First command will give wcd permissions to userZ on src directory, but only read
278 +permission on file main.c in src directory.
280 +Using + and - signs to set permissions on a file or directory:
282 +$ cvs -d /cvs/projectA racl userZ:+t -d src
283 +$ cvs -d /cvs/projectA racl userZ:-cd -d src
284 +$ cvs -d /cvs/projectA racl userZ:-wt -d src
285 +Before the first command, userZ has wcd permissions on src directory, after issuing
286 +command it will have wcdt permissions. Tag permission will be added. UserZ has wcdt
287 +permissions, and we execute the second command to remove create and delete permissions.
288 +So userZ has wt permissions. In the last command we also remove wt permissions, finally
289 +userZ has no defined permissions left, and it will use the default permissions if set.
291 +Listing permissions on a file or directory:
293 +$ cvs -d /cvs/projectA racl -l -d src
294 +$ cvs -d /cvs/projectA racl -l -f src
295 +$ cvs -d /cvs/projectA racl -l -f src/main.c
297 +First command will list the permissions for src directory.
299 +d src HEAD | userX:wcd group1:r | defaults:r
300 +userX and group1 has assigned permissions, all other users will have default
301 +permissions, which is only read.
303 +Second command will list the permissions for files in src directory.
305 +f src/main.c HEAD | userX:wcd group1:r | defaults:r
306 +f src/server.c HEAD | userX:wcd group1:r | defaults:r
307 +f src/client.c HEAD | userX:wcd group1:r | defaults:r
309 +Third command will list the permissions for main.c file in src directory.
311 +f src/main.c HEAD | userX:wcd group1:r | defaults:r
314 diff -urN cvs-1.11.17.orig/aclconfig.default cvs-1.11.17/aclconfig.default
315 --- cvs-1.11.17.orig/aclconfig.default 1970-01-01 01:00:00.000000000 +0100
316 +++ cvs-1.11.17/aclconfig.default 2004-06-17 14:33:44.000000000 +0200
318 +# Set `UseCVSACL' to yes to use CVS ACL feature.
321 +# Default CVS ACL Permission are to use.
322 +#CVSACLDefaultPermissions=a
324 +# Default file location for CVS ACL file (access) is CVSROOT/access.
325 +# If you want to use a different location, define it below.
326 +#CVSACLFileLocation=/path/to/cvs/access
328 +# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).
331 +# Set `UseCVSGroups' to yes to use another group file.
334 +# Default file location for CVS groups file is CVSROOT/group.
335 +# If you want to use a different location, define it below.
336 +#CVSGroupsFileLocation=/path/to/cvs/group
338 +# Set UseSeparateACLFileForEachDir to yes in order to use a
339 +# separate 'access' file for each directory.
340 +# This increased the performance if you have really big repository.
341 +#UseSeparateACLFileForEachDir=no
343 +# If StopAtFirstPermissionDenied is set to yes
344 +# operation will stop at first permission denied message.
346 +#StopAtFirstPermissionDenied=no
348 +# Set CVSServerRunAsUser to a system user, in order CVS server
350 +#CVSServerRunAsUser=runascvsuser
351 diff -urN cvs-1.11.17.orig/src/Makefile.am cvs-1.11.17/src/Makefile.am
352 --- cvs-1.11.17.orig/src/Makefile.am 2004-02-03 20:05:36.000000000 +0100
353 +++ cvs-1.11.17/src/Makefile.am 2004-06-23 21:02:34.000000000 +0200
362 diff -urN cvs-1.11.17.orig/src/Makefile.in cvs-1.11.17/src/Makefile.in
363 --- cvs-1.11.17.orig/src/Makefile.in 2004-06-09 16:46:19.000000000 +0200
364 +++ cvs-1.11.17/src/Makefile.in 2004-06-23 21:02:34.000000000 +0200
373 @@ -253,11 +254,11 @@
374 bin_PROGRAMS = cvs$(EXEEXT)
375 PROGRAMS = $(bin_PROGRAMS)
377 -am_cvs_OBJECTS = add.$(OBJEXT) admin.$(OBJEXT) annotate.$(OBJEXT) \
378 - buffer.$(OBJEXT) checkin.$(OBJEXT) checkout.$(OBJEXT) \
379 - classify.$(OBJEXT) client.$(OBJEXT) commit.$(OBJEXT) \
380 - create_adm.$(OBJEXT) cvsrc.$(OBJEXT) diff.$(OBJEXT) \
381 - edit.$(OBJEXT) entries.$(OBJEXT) error.$(OBJEXT) \
382 +am_cvs_OBJECTS = acl.$(OBJEXT) add.$(OBJEXT) admin.$(OBJEXT) \
383 + annotate.$(OBJEXT) buffer.$(OBJEXT) checkin.$(OBJEXT) \
384 + checkout.$(OBJEXT) classify.$(OBJEXT) client.$(OBJEXT) \
385 + commit.$(OBJEXT) create_adm.$(OBJEXT) cvsrc.$(OBJEXT) \
386 + diff.$(OBJEXT) edit.$(OBJEXT) entries.$(OBJEXT) error.$(OBJEXT) \
387 expand_path.$(OBJEXT) fileattr.$(OBJEXT) filesubr.$(OBJEXT) \
388 find_names.$(OBJEXT) hardlink.$(OBJEXT) hash.$(OBJEXT) \
389 history.$(OBJEXT) ignore.$(OBJEXT) import.$(OBJEXT) \
390 @@ -280,34 +281,34 @@
391 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
392 depcomp = $(SHELL) $(top_srcdir)/depcomp
393 am__depfiles_maybe = depfiles
394 -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/add.Po ./$(DEPDIR)/admin.Po \
395 -@AMDEP_TRUE@ ./$(DEPDIR)/annotate.Po ./$(DEPDIR)/buffer.Po \
396 -@AMDEP_TRUE@ ./$(DEPDIR)/checkin.Po ./$(DEPDIR)/checkout.Po \
397 -@AMDEP_TRUE@ ./$(DEPDIR)/classify.Po ./$(DEPDIR)/client.Po \
398 -@AMDEP_TRUE@ ./$(DEPDIR)/commit.Po ./$(DEPDIR)/create_adm.Po \
399 -@AMDEP_TRUE@ ./$(DEPDIR)/cvsrc.Po ./$(DEPDIR)/diff.Po \
400 -@AMDEP_TRUE@ ./$(DEPDIR)/edit.Po ./$(DEPDIR)/entries.Po \
401 -@AMDEP_TRUE@ ./$(DEPDIR)/error.Po ./$(DEPDIR)/expand_path.Po \
402 -@AMDEP_TRUE@ ./$(DEPDIR)/fileattr.Po ./$(DEPDIR)/filesubr.Po \
403 -@AMDEP_TRUE@ ./$(DEPDIR)/find_names.Po ./$(DEPDIR)/hardlink.Po \
404 -@AMDEP_TRUE@ ./$(DEPDIR)/hash.Po ./$(DEPDIR)/history.Po \
405 -@AMDEP_TRUE@ ./$(DEPDIR)/ignore.Po ./$(DEPDIR)/import.Po \
406 -@AMDEP_TRUE@ ./$(DEPDIR)/lock.Po ./$(DEPDIR)/log.Po \
407 -@AMDEP_TRUE@ ./$(DEPDIR)/login.Po ./$(DEPDIR)/logmsg.Po \
408 -@AMDEP_TRUE@ ./$(DEPDIR)/main.Po ./$(DEPDIR)/mkmodules.Po \
409 -@AMDEP_TRUE@ ./$(DEPDIR)/modules.Po ./$(DEPDIR)/myndbm.Po \
410 -@AMDEP_TRUE@ ./$(DEPDIR)/no_diff.Po ./$(DEPDIR)/parseinfo.Po \
411 -@AMDEP_TRUE@ ./$(DEPDIR)/patch.Po ./$(DEPDIR)/rcs.Po \
412 -@AMDEP_TRUE@ ./$(DEPDIR)/rcscmds.Po ./$(DEPDIR)/recurse.Po \
413 -@AMDEP_TRUE@ ./$(DEPDIR)/release.Po ./$(DEPDIR)/remove.Po \
414 -@AMDEP_TRUE@ ./$(DEPDIR)/repos.Po ./$(DEPDIR)/root.Po \
415 -@AMDEP_TRUE@ ./$(DEPDIR)/run.Po ./$(DEPDIR)/scramble.Po \
416 -@AMDEP_TRUE@ ./$(DEPDIR)/server.Po ./$(DEPDIR)/stack.Po \
417 -@AMDEP_TRUE@ ./$(DEPDIR)/status.Po ./$(DEPDIR)/subr.Po \
418 -@AMDEP_TRUE@ ./$(DEPDIR)/tag.Po ./$(DEPDIR)/update.Po \
419 -@AMDEP_TRUE@ ./$(DEPDIR)/vers_ts.Po ./$(DEPDIR)/version.Po \
420 -@AMDEP_TRUE@ ./$(DEPDIR)/watch.Po ./$(DEPDIR)/wrapper.Po \
421 -@AMDEP_TRUE@ ./$(DEPDIR)/zlib.Po
422 +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/acl.Po ./$(DEPDIR)/add.Po \
423 +@AMDEP_TRUE@ ./$(DEPDIR)/admin.Po ./$(DEPDIR)/annotate.Po \
424 +@AMDEP_TRUE@ ./$(DEPDIR)/buffer.Po ./$(DEPDIR)/checkin.Po \
425 +@AMDEP_TRUE@ ./$(DEPDIR)/checkout.Po ./$(DEPDIR)/classify.Po \
426 +@AMDEP_TRUE@ ./$(DEPDIR)/client.Po ./$(DEPDIR)/commit.Po \
427 +@AMDEP_TRUE@ ./$(DEPDIR)/create_adm.Po ./$(DEPDIR)/cvsrc.Po \
428 +@AMDEP_TRUE@ ./$(DEPDIR)/diff.Po ./$(DEPDIR)/edit.Po \
429 +@AMDEP_TRUE@ ./$(DEPDIR)/entries.Po ./$(DEPDIR)/error.Po \
430 +@AMDEP_TRUE@ ./$(DEPDIR)/expand_path.Po ./$(DEPDIR)/fileattr.Po \
431 +@AMDEP_TRUE@ ./$(DEPDIR)/filesubr.Po ./$(DEPDIR)/find_names.Po \
432 +@AMDEP_TRUE@ ./$(DEPDIR)/hardlink.Po ./$(DEPDIR)/hash.Po \
433 +@AMDEP_TRUE@ ./$(DEPDIR)/history.Po ./$(DEPDIR)/ignore.Po \
434 +@AMDEP_TRUE@ ./$(DEPDIR)/import.Po ./$(DEPDIR)/lock.Po \
435 +@AMDEP_TRUE@ ./$(DEPDIR)/log.Po ./$(DEPDIR)/login.Po \
436 +@AMDEP_TRUE@ ./$(DEPDIR)/logmsg.Po ./$(DEPDIR)/main.Po \
437 +@AMDEP_TRUE@ ./$(DEPDIR)/mkmodules.Po ./$(DEPDIR)/modules.Po \
438 +@AMDEP_TRUE@ ./$(DEPDIR)/myndbm.Po ./$(DEPDIR)/no_diff.Po \
439 +@AMDEP_TRUE@ ./$(DEPDIR)/parseinfo.Po ./$(DEPDIR)/patch.Po \
440 +@AMDEP_TRUE@ ./$(DEPDIR)/rcs.Po ./$(DEPDIR)/rcscmds.Po \
441 +@AMDEP_TRUE@ ./$(DEPDIR)/recurse.Po ./$(DEPDIR)/release.Po \
442 +@AMDEP_TRUE@ ./$(DEPDIR)/remove.Po ./$(DEPDIR)/repos.Po \
443 +@AMDEP_TRUE@ ./$(DEPDIR)/root.Po ./$(DEPDIR)/run.Po \
444 +@AMDEP_TRUE@ ./$(DEPDIR)/scramble.Po ./$(DEPDIR)/server.Po \
445 +@AMDEP_TRUE@ ./$(DEPDIR)/stack.Po ./$(DEPDIR)/status.Po \
446 +@AMDEP_TRUE@ ./$(DEPDIR)/subr.Po ./$(DEPDIR)/tag.Po \
447 +@AMDEP_TRUE@ ./$(DEPDIR)/update.Po ./$(DEPDIR)/vers_ts.Po \
448 +@AMDEP_TRUE@ ./$(DEPDIR)/version.Po ./$(DEPDIR)/watch.Po \
449 +@AMDEP_TRUE@ ./$(DEPDIR)/wrapper.Po ./$(DEPDIR)/zlib.Po
450 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
451 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
457 +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl.Po@am__quote@
458 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add.Po@am__quote@
459 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/admin.Po@am__quote@
460 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/annotate.Po@am__quote@
461 diff -urN cvs-1.11.17.orig/src/acl.c cvs-1.11.17/src/acl.c
462 --- cvs-1.11.17.orig/src/acl.c 1970-01-01 01:00:00.000000000 +0100
463 +++ cvs-1.11.17/src/acl.c 2004-06-17 14:55:00.000000000 +0200
467 + * CVS ACCESS CONTROL LIST EXTENSION
469 + * sbaris@users.sourceforge.net
471 + * http://cvsacl.sourceforge.net/
473 + * CVSACL is a patch for CVS versions
476 + * It adds two new subcommands (acl & racl) to cvs for access control
478 + * It provides advanced ACL definitions per modules, directories,
479 + * and files on branch/tag for remote cvs repository connections.
480 + * Execution of all CVS subcommands can be controlled with eight
481 + * different permissions.
482 + * ACL definitions works for only remote connections, local users can
483 + * access and modify repository, if unix file system permissions allow.
484 + * If you want all users to make remote connections to repository,
485 + * and not allow local users to access repository, you have to set
486 + * CVSServerRunAsUser keyword in aclconfig file (explained below).
487 + * Still local users can use acl and racl subcommands to set permissions
488 + * on directories or files if they have acl admin rights (p) on related
489 + * directories or files.
490 + * So, in order to control all access to repository with this ACL extension,
491 + * you should use CVSServerRunAsUser keyword and force all users to make
492 + * remote connections.
493 + * CVS repository administrator or project managers have to use acl and racl
494 + * subcommands to manage permissions. But there is no gui client supporting
495 + * these subcommands, so you have to use cvs client itself either
496 + * locally or remotely.
499 + * Permission Types:
500 + * - no permission (n) (1)
501 + * - all permissions (a) (2)
502 + * - write permission (w) (3)
503 + * - tag permission (t) (4)
504 + * - read permission (r) (5)
505 + * - add permission (c) (6)
506 + * - remove permission (d) (7)
507 + * - permission change (p) (8)
512 + * ********************************************************************
513 + * This program is free software; you can redistribute it and/or modify
514 + * it under the terms of the GNU General Public License as published by
515 + * the Free Software Foundation; either version 1, or (at your option)
516 + * any later version.
518 + * This program is distributed in the hope that it will be useful,
519 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
520 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
521 + * GNU General Public License for more details.
522 + * *******************************************************************
527 +#include "getline.h"
530 +static int acl_fileproc PROTO ((void *callerdat, struct file_info *finfo));
531 +static Dtype acl_dirproc PROTO ((void *callerdat, const char *dir, const char *repos,
532 + const char *update_dir, List *entries));
534 +static int acllist_fileproc PROTO ((void *callerdat, struct file_info *finfo));
535 +static Dtype acllist_dirproc PROTO ((void *callerdat, const char *dir, const char *repos,
536 + const char *update_dir, List *entries));
538 +static void acllist_print PROTO ((char *line, const char *obj));
540 +static int racl_proc PROTO((int argc, char **argv, char *xwhere,
541 + char *mwhere, char *mfile, int shorten,
542 + int local_specified, char *mname, char *msg));
544 +FILE *open_accessfile (char *xmode, char *repos, char **fname);
545 +FILE *open_groupfile (char *xmode);
547 +char *get_perms (char *xperms);
548 +char *make_perms (char *xperms, char *xfounduserpart, char **xerrmsg);
550 +static char *cache_repository;
551 +static int cache_retval;
552 +static int founddeniedfile;
553 +static int cache_perm;
557 +int use_cvs_acl = 0;
558 +char *cvs_acl_default_permissions;
559 +int use_cvs_groups = 0;
560 +int use_system_groups = 0;
561 +int use_separate_acl_file_for_each_dir = 0;
562 +char *cvs_acl_file_location = NULL;
563 +char *cvs_groups_file_location = NULL;
564 +char *cvs_server_run_as = NULL;
565 +int stop_at_first_permission_denied = 0;
571 +static int defaultperms;
573 +static char *default_perms_object;
574 +char *default_part_perms_accessfile;
585 +int aclconfig_default_used;
587 +static const char *const acl_usage[] =
589 + "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n",
590 + "\t-R\tProcess directories recursively.\n",
591 + "\t-r rev\tExisting revision/tag.\n",
592 + "\t-l\tList defined ACLs.\n",
593 + "\t-d dir\tProcess on given directory.\n",
594 + "\t-f file\tProcess on given file.\n",
595 + "(Specify the --help global option for a list of other help options)\n",
599 +static const char *const racl_usage[] =
601 + "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n",
602 + "\t-R\tProcess directories recursively.\n",
603 + "\t-r rev\tExisting revision/tag.\n",
604 + "\t-l\tList defined ACLs.\n",
605 + "\t-d dir\tProcess on given directory.\n",
606 + "\t-f file\tProcess on given file.\n",
607 + "(Specify the --help global option for a list of other help options)\n",
613 +access_allowed (file, repos, tag, perm, mline, mpos, usecache)
627 + size_t line_allocated = 0;
629 + char *part_type = NULL;
630 + char *part_object = NULL;
631 + char *part_tag = NULL;
632 + char *part_perms = NULL;
642 + int oneaccessfile = 0;
643 + int accessfilecount;
649 + const char *repository;
650 + char *filefullname = NULL;
659 + repository = xstrdup ("ALL");
662 + repository = Short_Repository (repos);
666 + && cache_repository != NULL
667 + && strcmp (cache_repository, repository) == 0
668 + && !founddeniedfile
669 + && perm == cache_perm)
670 + return (cache_retval);
673 + free(cache_repository);
674 + cache_repository = xstrdup(repository);
680 + filefullname = xmalloc (strlen (repository)
684 + strcpy (filefullname, repository);
685 + strcat (filefullname, "/");
686 + strcat (filefullname, file);
690 + iline = xstrdup(repository);
692 + tempv = strtok(iline, "/\t");
693 + tempc = xstrdup(tempv);
694 + tempsize = strlen(tempc);
698 + dirs[intcount] = xstrdup(tempc);
700 + while ((tempv = strtok(NULL, "/\t")) != NULL)
704 + xrealloc_and_strcat(&tempc, &tempsize, "/");
705 + xrealloc_and_strcat(&tempc, &tempsize, tempv);
707 + dirs[intcount] = xstrdup(tempc);
713 + dirs[intcount] = xstrdup(filefullname);
716 + for (accessfilecount = intcount; accessfilecount >= 0 && !oneaccessfile; accessfilecount--)
718 + if (!use_separate_acl_file_for_each_dir)
720 + else if (use_separate_acl_file_for_each_dir)
724 + accessfp = open_accessfile ("r", repos, NULL);
726 + accessfp = open_accessfile ("r", dirs[accessfilecount], NULL);
728 + if (accessfp != NULL)
730 + while (getline (&line, &line_allocated, accessfp) >= 0)
732 + if (line[0] == '#' || line[0] == '\0' || line[0] == '\n')
737 + xline = xstrdup (line);
738 + part_type = strtok (line, ":\t");
739 + part_object = strtok (NULL, ":\t");
740 + part_tag = strtok (NULL, ":\t");
741 + part_perms = strtok (NULL, ":\t");
743 + for (x = intcount; x >= signlevel; x--)
745 + if (strcmp (dirs[x], part_object) == 0)
747 + if (valid_tag (part_tag, tag))
751 + if (listacl || acldir || aclfile)
753 + *mline = xstrdup (xline);
754 + *mpos = ftell (accessfp);
757 + if (valid_perm (part_perms, perm))
759 + if (signlevel == x)
761 + if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used)
764 + else if (!aclconfig_default_used)
772 + if (signlevel == x)
774 + if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used)
777 + else if (!aclconfig_default_used)
782 + if (strncmp (part_type, "f", 1) == 0)
783 + founddeniedfile = 1;
790 + if (strncmp (xline, "d:ALL", 5) == 0 && (!groupfound && !userfound || listacl))
792 + /* a default found */
793 + if (valid_tag (part_tag, tag))
797 + default_part_perms_accessfile = xstrdup (part_perms);
799 + if (valid_perm (part_perms, perm))
812 + if (fclose (accessfp) == EOF)
813 + error (1, errno, "cannot close 'access' file");
821 + if (valid_perm (NULL, perm))
832 + cache_retval = retval;
834 + free (filefullname);
839 +/* Returns 1 if successful, 0 if not. */
841 +valid_tag (part_tag, tag)
846 + tag = xstrdup ("HEAD");
848 + if (strcmp (part_tag, "ALL") == 0)
851 + if (strcmp (tag, part_tag) == 0)
857 +/* Returns 1 if successful, 0 if not. */
859 +valid_perm (part_perms, perm)
866 + perms = get_perms (part_perms);
868 + /* Allow, if nothing found. */
869 + if (perms[0] == '\0')
873 + if (strstr (perms, "n"))
874 + retval = 0; /* no access allowed, exit */
875 + if (strstr (perms, "p"))
876 + retval = 1; /* admin rights */
877 + else if (strstr (perms, "a") && perm != 8)
878 + retval = 1; /* all access allowed, exit */
882 + case 3: /* write permission */
883 + if (strstr (perms, "w"))
886 + case 4: /* tag permission */
887 + if (strstr (perms, "t"))
890 + case 5: /* read permission */
891 + if (strstr (perms, "w") || strstr (perms, "t") || strstr (perms, "c") ||
892 + strstr (perms, "d") || strstr (perms, "r"))
895 + case 6: /* create permission */
896 + if (strstr (perms, "c"))
899 + case 7: /* delete permission */
900 + if (strstr (perms, "d"))
903 + case 8: /* permission change */
904 + if (strstr (perms, "p"))
908 + retval = 0; /* never reached */
916 +get_perms (part_perms)
922 + size_t xperms_len = 1;
928 + char *founduser = NULL;
929 + char *foundall = NULL;
930 + int default_checked = 0;
932 + aclconfig_default_used = 0;
934 + xperms = xmalloc (xperms_len);
937 + username = getcaller ();
939 + /* no defined acl, no default acl in access file,
940 + or no access file at all */
941 + if (part_perms == NULL)
942 + if (cvs_acl_default_permissions)
944 + aclconfig_default_used = 1;
945 + return (cvs_acl_default_permissions);
951 + founduser = strstr (part_perms, username);
952 + foundall = strstr (part_perms, "ALL!");
956 + usr = strtok (founduser, "!\t");
957 + per = strtok (NULL, ",\t");
959 + if (strcmp (usr, username) == 0)
961 + xperms = xstrdup (per);
962 + xperms_len = strlen (xperms);
969 + if (use_system_groups) {
970 + struct group *griter;
972 + while (griter = getgrent ()) {
973 + char **users=griter->gr_mem;
975 + char *userchk = users [index++];
976 + while(userchk != NULL) {
977 + if(strcmp (userchk, username) == 0)
979 + userchk = users[index++];
981 + if (userchk != NULL) {
983 + if ((grp = strstr (part_perms, griter->gr_name)) && grp[strlen (griter->gr_name)] == '!') {
984 + char *gperm = strtok (grp, "!\t");
985 + gperm = strtok (NULL, ",\t");
986 + xrealloc_and_strcat (&xperms, &xperms_len, gperm);
994 + else if (use_cvs_groups) {
995 + groupfp = open_groupfile ("r");
996 + if (groupfp != NULL)
999 + size_t line_allocated = 0;
1001 + while (getline (&line, &line_allocated, groupfp) >= 0)
1003 + if (strstr (line, username))
1006 + temp = strstr (line, username);
1008 + if (temp[strlen (username)] == ','
1009 + || temp[strlen (username)] == ' '
1010 + || temp[strlen (username)] == '\n')
1013 + tmp = strtok (line, ":\t");
1014 + if (strcmp (tmp, username) != 0)
1017 + if ((grp = strstr (part_perms, tmp)))
1018 + if (grp[strlen (tmp)] == '!')
1021 + gperm = strtok (grp, "!\t");
1022 + gperm = strtok (NULL, ",\t");
1024 + xrealloc_and_strcat (&xperms, &xperms_len, gperm);
1032 + if (fclose (groupfp) == EOF)
1033 + error (1, errno, "cannot close 'group' file");
1038 + if (foundall && (!groupfound && !userfound))
1040 + usr = strtok (strstr (part_perms, "ALL!"), "!\t");
1041 + per = strtok (NULL, ",\t");
1043 + if (!default_checked)
1044 + default_perms_object = xstrdup (per);
1046 + if (xperms[0] == '\0')
1048 + xperms = xstrdup (per);
1049 + xperms_len = strlen (xperms);
1052 + else if (xperms[0] == '\0' && !default_checked && default_part_perms_accessfile)
1054 + part_perms = xstrdup (default_part_perms_accessfile);
1055 + default_checked = 1;
1057 + goto check_default;
1060 + if (xperms[0] != '\0' && strcmp (xperms, "x") == 0)
1062 + if (default_perms_object)
1063 + xperms = xstrdup (default_perms_object);
1064 + else if (default_part_perms_accessfile)
1066 + part_perms = default_part_perms_accessfile;
1067 + default_checked = 1;
1068 + goto check_default;
1070 + else if (cvs_acl_default_permissions)
1072 + aclconfig_default_used = 1;
1073 + xperms = xstrdup (cvs_acl_default_permissions);
1077 + if (xperms[0] == '\0' && cvs_acl_default_permissions)
1079 + aclconfig_default_used = 1;
1080 + xperms = xstrdup (cvs_acl_default_permissions);
1088 +cvsacl (argc, argv)
1092 + char *chdirrepository;
1096 + int recursive = 0;
1103 + is_racl = (strcmp (cvs_cmd_name, "racl") == 0);
1106 + usage (is_racl ? racl_usage : acl_usage);
1108 + /* parse the args */
1111 + while ((c = getopt (argc, argv, "Rr:dfl")) != -1)
1119 + tag = xstrdup (optarg);
1132 + usage (is_racl ? racl_usage : acl_usage);
1140 + if (!acldir && !aclfile)
1141 + usage (is_racl ? racl_usage : acl_usage);
1142 + if (acldir && aclfile)
1143 + usage (is_racl ? racl_usage : acl_usage);
1146 + if (strstr (argv[0], ":"))
1147 + usage (is_racl ? racl_usage : acl_usage);
1149 + if (!strstr (argv[0], ":"))
1150 + usage (is_racl ? racl_usage : acl_usage);
1152 + if (argc < (is_racl ? 1 : 1))
1153 + usage (is_racl ? racl_usage : acl_usage);
1155 +#ifdef CLIENT_SUPPORT
1157 + if (current_parsed_root->isremote)
1184 + send_arg (argv[0]);
1193 + for (i = 0; i < argc; ++i)
1194 + send_arg (argv[i]);
1196 + send_to_server ("racl\012",0);
1200 + send_files (argc, argv, recursive, 0, SEND_NO_CONTENTS);
1201 + send_file_names (argc, argv, SEND_EXPAND_WILD);
1202 + send_to_server ("acl\012", 0);
1205 + return get_responses_and_close ();
1209 +#ifdef SERVER_SUPPORT
1213 + muser = strtok (argv[0], ":\t");
1214 + mperms = strtok (NULL, ":\t");
1216 + /* if set to 'default' */
1217 + if ((strlen (mperms) == 7) && (strncmp (mperms, "default", 7) == 0))
1218 + mperms = xstrdup ("x");
1220 + /* Check that the given permissions are valid. */
1221 + if (!given_perms_valid (mperms))
1222 + error (1,0,"Invalid permissions: '%s'", mperms);
1230 + tag = xstrdup ("HEAD");
1232 + if (!cvs_casecmp (argv[0], "ALL"))
1234 + argv[0] = xstrdup (".");
1236 + if (!use_separate_acl_file_for_each_dir)
1247 + db = open_module ();
1248 + for (i = 0; i < argc; i++)
1250 + err += do_module (db, argv[i], MISC, "ACL ing: ",
1251 + racl_proc, (char *) NULL, 0, !recursive, 0,
1254 + close_module (db);
1258 + err = racl_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, !recursive, NULL,
1262 + error (1, 0, "an error accured");
1270 +racl_proc (argc, argv, xwhere, mwhere, mfile, shorten, local, mname, msg)
1289 + repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
1290 + + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
1292 + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
1293 + where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
1295 + (void) strcpy (where, argv[0]);
1297 + /* if mfile isn't null, we need to set up to do only part of the module */
1298 + if (mfile != NULL)
1303 + /* if the portion of the module is a path, put the dir part on repos */
1304 + if ((cp = strrchr (mfile, '/')) != NULL)
1307 + (void) strcat (repository, "/");
1308 + (void) strcat (repository, mfile);
1309 + (void) strcat (where, "/");
1310 + (void) strcat (where, mfile);
1314 + /* take care of the rest */
1315 + path = xmalloc (strlen (repository) + strlen (mfile) + 5);
1316 + (void) sprintf (path, "%s/%s", repository, mfile);
1319 + /* directory means repository gets the dir tacked on */
1320 + (void) strcpy (repository, path);
1321 + (void) strcat (where, "/");
1322 + (void) strcat (where, mfile);
1326 + myargv[0] = argv[0];
1327 + myargv[1] = mfile;
1334 + /* cd to the starting repository */
1335 + if ( CVS_CHDIR (repository) < 0)
1337 + error (0, errno, "cannot chdir to %s", repository);
1338 + free (repository);
1342 + /* End section which is identical to patch_proc. */
1344 + which = W_REPOS | W_ATTIC;
1349 + which = W_LOCAL | W_REPOS | W_ATTIC;
1352 + err = start_recursion (acllist_fileproc, NULL, acllist_dirproc, NULL, NULL,
1353 + argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1,
1356 + err = start_recursion (acl_fileproc, NULL, acl_dirproc, NULL, NULL,
1357 + argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1,
1365 +acl_fileproc (callerdat, finfo)
1367 +struct file_info *finfo;
1373 + char *filefullname;
1375 + char *founduserpart = NULL;
1376 + char *newuserpart = NULL;
1377 + char *otheruserparts = NULL;
1378 + size_t otherslen = 0;
1380 + const char *frepository;
1381 + int foundline = 0;
1383 + char *line = NULL;
1384 + size_t line_allocated = 0;
1387 + char *part_type = NULL;
1388 + char *part_object = NULL;
1389 + char *part_tag = NULL;
1390 + char *part_perms = NULL;
1401 + frepository = Short_Repository (finfo->repository);
1403 + filefullname = xmalloc (strlen (frepository)
1404 + + strlen (finfo->file)
1406 + strcpy (filefullname, frepository);
1407 + strcat (filefullname, "/");
1408 + strcat (filefullname, finfo->file);
1410 + if (!access_allowed (finfo->file, finfo->repository, tag, 8, &line, &pos, 0))
1411 + error (1,0,"You do not have acl admin rights on '%s'", frepository);
1415 + part_type = strtok (line, ":\t");
1416 + part_object = strtok (NULL, ":\t");
1417 + part_tag = strtok (NULL, ":\t");
1418 + part_perms = strtok (NULL, ":\t");
1421 + userpart = strtok (part_perms, ",\t");
1423 + if (strstr (userpart, muser))
1424 + founduserpart = xstrdup (userpart);
1427 + otheruserparts = xstrdup (userpart);
1428 + otherslen = strlen (otheruserparts);
1431 + while ((userpart = strtok (NULL, ",\t")) != NULL)
1433 + if (strncmp (userpart, muser, strlen (muser)) == 0)
1434 + founduserpart = xstrdup (userpart);
1437 + if (otheruserparts != NULL)
1439 + xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
1440 + xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
1444 + otheruserparts = xstrdup (userpart);
1445 + otherslen = strlen (otheruserparts);
1451 + wperms = make_perms (mperms, founduserpart, &errmsg);
1452 + if (wperms == NULL)
1455 + error (0, 0, "%s %s", filefullname, errmsg);
1461 + cvs_output ("X ", 0);
1462 + cvs_output (filefullname, 0);
1463 + cvs_output ("\n", 0);
1465 + write_perms (muser, wperms, founduserpart, foundline,
1466 + otheruserparts, "f", filefullname, tag, pos, finfo->repository);
1473 +acl_dirproc (callerdat, dir, repos, update_dir, entries)
1477 +const char *update_dir;
1480 + const char *drepository;
1481 + char *founduserpart = NULL;
1482 + char *newuserpart = NULL;
1483 + char *otheruserparts = NULL;
1484 + size_t otherslen = 0;
1485 + int foundline = 0;
1487 + char *line = NULL;
1488 + size_t line_allocated = 0;
1492 + char *part_type = NULL;
1493 + char *part_object = NULL;
1494 + char *part_tag = NULL;
1495 + char *part_perms = NULL;
1503 + if (repos[0] == '\0')
1504 + repos = Name_Repository (dir, NULL);
1509 + if (!access_allowed (NULL, repos, tag, 8, &line, &pos, 0))
1510 + error (1,0,"You do not have admin rights on '%s'", Short_Repository (repos));
1512 + drepository = Short_Repository (repos);
1516 + part_type = strtok (line, ":\t");
1517 + part_object = strtok (NULL, ":\t");
1518 + part_tag = strtok (NULL, ":\t");
1519 + part_perms = strtok (NULL, ":\t");
1522 + userpart = strtok (part_perms, ",\t");
1524 + if (strstr (userpart, muser))
1525 + founduserpart = xstrdup (userpart);
1528 + otheruserparts = xstrdup (userpart);
1529 + otherslen = strlen (otheruserparts);
1532 + while ((userpart = strtok (NULL, ",\t")) != NULL)
1534 + if (strncmp (userpart, muser, strlen (muser)) == 0)
1535 + founduserpart = xstrdup (userpart);
1538 + if (otheruserparts != NULL)
1540 + xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
1541 + xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
1545 + otheruserparts = xstrdup (userpart);
1546 + otherslen = strlen (otheruserparts);
1552 + wperms = make_perms (mperms, founduserpart, &errmsg);
1553 + if (wperms == NULL)
1556 + error (0, 0, "%s %s", drepository, errmsg);
1564 + cvs_output ("X ", 0);
1565 + cvs_output ("ALL", 0);
1566 + cvs_output ("\n", 0);
1567 + write_perms (muser, wperms, founduserpart, foundline,
1568 + otheruserparts, "d", "ALL", tag, pos, repos);
1573 + cvs_output ("X ", 0);
1574 + cvs_output (drepository, 0);
1575 + cvs_output ("\n", 0);
1576 + write_perms (muser, wperms, founduserpart, foundline,
1577 + otheruserparts, "d", drepository, tag, pos, repos);
1584 +/* Open CVSROOT/access or defined CVSACLFileLocation file. */
1586 +open_accessfile (fmode, adir, fname)
1594 + if (!use_separate_acl_file_for_each_dir)
1596 + if (cvs_acl_file_location == NULL)
1598 + accessfile = xmalloc (strlen (current_parsed_root->directory)
1599 + + sizeof (CVSROOTADM)
1600 + + sizeof (CVSROOTADM_ACCESS)
1603 + strcpy (accessfile, current_parsed_root->directory);
1604 + strcat (accessfile, "/");
1605 + strcat (accessfile, CVSROOTADM);
1606 + strcat (accessfile, "/");
1607 + strcat (accessfile, CVSROOTADM_ACCESS);
1611 + accessfile = xmalloc (strlen (cvs_acl_file_location));
1612 + strcpy (accessfile, cvs_acl_file_location);
1617 + accessfile = xmalloc (strlen (current_parsed_root->directory)
1619 + + strlen ("access")
1622 + strcpy (accessfile, current_parsed_root->directory);
1623 + strcat (accessfile, "/");
1624 + strcat (accessfile, adir);
1625 + strcat (accessfile, "/");
1626 + strcat (accessfile, "access");
1629 + accessfp = CVS_FOPEN (accessfile, fmode);
1631 + if (accessfp == NULL)
1632 + error (0, 0, "cannot open file: %s", accessfile);
1634 + if (fname != NULL)
1635 + *fname = xstrdup (accessfile);
1637 + free (accessfile);
1639 + return (accessfp);
1642 +/* Open /etc/group file if UseSystemGroups=yes in config file. */
1643 +/* Open CVSROOT/group file if UseCVSGroups=yes in config file. */
1645 +open_groupfile (fmode)
1651 + if (use_cvs_groups)
1653 + if (cvs_groups_file_location != NULL)
1655 + groupfile = xmalloc (strlen (cvs_groups_file_location));
1656 + strcpy (groupfile, cvs_groups_file_location);
1660 + groupfile = xmalloc (strlen (current_parsed_root->directory)
1661 + + sizeof (CVSROOTADM)
1662 + + sizeof (CVSROOTADM_GROUP)
1665 + strcpy (groupfile, current_parsed_root->directory);
1666 + strcat (groupfile, "/");
1667 + strcat (groupfile, CVSROOTADM);
1668 + strcat (groupfile, "/");
1669 + strcat (groupfile, CVSROOTADM_GROUP);
1678 + groupfp = CVS_FOPEN (groupfile, "r");
1680 + if (groupfp == NULL)
1681 + error (0, 0, "cannot open file: %s", groupfile);
1689 +/* Check whether given permissions are valid or not. */
1690 +/* Returns 1 if permissions are valid. */
1691 +/* Returns 0 if permissions are NOT valid. */
1693 +given_perms_valid (cperms)
1700 + if (cperms[0] == '+' || cperms[0] == '-')
1705 + cperms_len = strlen (cperms);
1707 + switch (cperms[index])
1710 + if ((cperms_len - index) == 1 && cperms_len == 1)
1716 + if ((cperms_len - index) == 1 && cperms_len == 1)
1722 + if ((cperms_len - index) == 1)
1728 + if ((cperms_len - index) == 1)
1731 + for (i = index + 1; i < cperms_len; i++)
1732 + if (cperms[i] == 'p')
1738 + if ((cperms_len - index) == 1)
1741 + for (i = index + 1; i < cperms_len; i++)
1742 + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd')
1748 + if ((cperms_len - index) == 1)
1751 + for (i = index + 1; i < cperms_len; i++)
1752 + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd')
1758 + if ((cperms_len - index) == 1)
1761 + for (i = index + 1; i < cperms_len; i++)
1762 + if (cperms[i] == 'w' || cperms[i] == 'c' || cperms[i] == 'd')
1768 + if ((cperms_len - index) == 1)
1771 + for (i = index + 1; i < cperms_len; i++)
1772 + if (cperms[i] == 't' || cperms[i] == 'w' || cperms[i] == 'd')
1778 + if ((cperms_len - index) == 1)
1781 + for (i = index + 1; i < cperms_len; i++)
1782 + if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'w')
1795 +make_perms (perms, founduserpart, xerrmsg)
1797 +char *founduserpart;
1802 + size_t fperms_len;
1803 + char *retperms = NULL;
1807 + char *errmsg = NULL;
1808 + size_t retperms_len = 1;
1810 + retperms = xmalloc (retperms_len);
1811 + retperms[0] = '\0';
1813 + perms_len = strlen (perms);
1815 + if (perms[0] == '+' || perms[0] == '-')
1817 + if (founduserpart)
1820 + temp = strtok (founduserpart, "!\t");
1821 + fperms = strtok (NULL, "!\t");
1822 + fperms_len = strlen (fperms);
1824 + if (strncmp (fperms, "x", 1) == 0)
1827 + if (perms[0] == '+')
1828 + *xerrmsg = xstrdup ("cannot add default permission 'x'");
1830 + *xerrmsg = xstrdup ("cannot remove default permission 'x'");
1833 + for (i = 1; i < perms_len && !err; i++)
1841 + if (perms[0] == '+')
1842 + fperms = xstrdup ("p");
1843 + else if (perms[0] == '-')
1846 + fperms = xmalloc (fperms_len);
1851 + for (j = 0; j < fperms_len; j++)
1853 + if (fperms[j] == 'p')
1856 + *xerrmsg = xstrdup ("user has admin rights, cannot use +/- permissions");
1858 + else if (fperms[j] == 'a' && perms[0] == '+')
1861 + *xerrmsg = xstrdup ("user already has all ('a') permission");
1863 + else if (fperms[j] != 'a' && perms[0] == '-')
1866 + *xerrmsg = xstrdup ("user does not have all ('a') permission");
1869 + if (perms[0] == '+')
1871 + fperms = xstrdup ("a");
1872 + fperms_len = strlen (fperms);
1874 + else if (perms[0] == '-')
1877 + fperms = xmalloc (fperms_len);
1883 + for (i = 0; i < fperms_len; i++)
1885 + if (fperms[i] == 'n' && perms[0] == '+')
1887 + fperms = xstrdup ("r");
1888 + fperms_len = strlen (fperms);
1890 + else if (fperms[i] == 'r' && perms[0] == '-')
1893 + fperms = xmalloc (fperms_len);
1896 + else if (perms[0] == '-')
1899 + *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission");
1904 + *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission");
1911 + size_t tempfperms_len = 1;
1913 + tempfperms = xmalloc (tempfperms_len);
1914 + tempfperms[0] = '\0';
1916 + for (j = 0; j < fperms_len; j++)
1918 + if (fperms[j] == 't' || fperms[j] == 'c' || fperms[j] == 'd')
1921 + temp = xmalloc (2);
1922 + temp[0] = fperms[j];
1925 + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
1928 + else if (fperms[j] == 'a' || fperms[j] == 'p')
1931 + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- write permissions");
1933 + else if (fperms[j] == 'n' || fperms[j] == 'r')
1935 + if (perms[0] == '-')
1938 + *xerrmsg = xstrdup ("user does not have write ('w') permission");
1943 + fperms = xstrdup (tempfperms);
1944 + fperms_len = strlen (fperms);
1945 + free (tempfperms);
1947 + if (perms[0] == '+')
1949 + xrealloc_and_strcat (&fperms, &fperms_len, "w");
1956 + size_t tempfperms_len = 1;
1958 + tempfperms = xmalloc (tempfperms_len);
1959 + tempfperms[0] = '\0';
1961 + for (j = 0; j < fperms_len; j++)
1963 + if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 'd')
1966 + temp = xmalloc (2);
1967 + temp[0] = fperms[j];
1970 + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
1973 + else if (fperms[j] == 'a' || fperms[j] == 'p')
1976 + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- tag permissions");
1978 + else if (fperms[i] == 'n' || fperms[i] == 'r')
1980 + if (perms[0] == '-')
1981 + *xerrmsg = xstrdup ("user does not have tag ('t') permission");
1985 + fperms = xstrdup (tempfperms);
1986 + fperms_len = strlen (fperms);
1987 + free (tempfperms);
1989 + if (perms[0] == '+')
1991 + xrealloc_and_strcat (&fperms, &fperms_len, "t");
1998 + size_t tempfperms_len = 1;
2000 + tempfperms = xmalloc (tempfperms_len);
2001 + tempfperms[0] = '\0';
2003 + for (j = 0; j < fperms_len; j++)
2005 + if (fperms[j] == 'w' || fperms[j] == 't' || fperms[j] == 'd')
2008 + temp = xmalloc (2);
2009 + temp[0] = fperms[j];
2012 + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
2015 + else if (fperms[j] == 'a' || fperms[j] == 'p')
2018 + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- create permissions");
2020 + else if (fperms[i] == 'n' || fperms[i] == 'r')
2022 + if (perms[0] == '-')
2024 + *xerrmsg = xstrdup ("user does not have create ('c') permission");
2029 + fperms = xstrdup (tempfperms);
2030 + fperms_len = strlen (fperms);
2031 + free (tempfperms);
2033 + if (perms[0] == '+')
2035 + xrealloc_and_strcat (&fperms, &fperms_len, "c");
2042 + size_t tempfperms_len = 1;
2044 + tempfperms = xmalloc (tempfperms_len);
2045 + tempfperms[0] = '\0';
2047 + for (j = 0; j < fperms_len; j++)
2049 + if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 't')
2052 + temp = xmalloc (2);
2053 + temp[0] = fperms[j];
2056 + xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
2059 + else if (fperms[j] == 'a' || fperms[j] == 'p')
2062 + *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- delete permissions");
2064 + else if (fperms[i] == 'n' || fperms[i] == 'r')
2066 + if (perms[0] == '-')
2068 + *xerrmsg = xstrdup ("user does not have delete ('d') permission");
2072 + fperms = xstrdup (tempfperms);
2073 + fperms_len = strlen (fperms);
2074 + free (tempfperms);
2076 + if (perms[0] == '+')
2078 + xrealloc_and_strcat (&fperms, &fperms_len, "d");
2084 + *xerrmsg = xstrdup ("error in 'access' file format");
2087 + if (fperms[0] == '\0')
2088 + retperms = xstrdup ("none");
2090 + retperms = xstrdup (fperms);
2096 + *xerrmsg = xstrdup("user is not given any permissions to remove/add");
2102 + retperms = xstrdup (perms);
2108 + return (retperms);
2112 +write_perms (user, perms, founduserpart, foundline, otheruserparts,
2113 + part_type, part_object, part_tag, pos, arepos)
2116 +char *founduserpart;
2118 +char *otheruserparts;
2127 + char *tmpaccessout;
2129 + FILE *accessfpout;
2131 + char *newline = NULL;
2132 + size_t newlinelen = 1;
2134 + char *line = NULL;
2135 + size_t line_allocated = 0;
2137 + newline = xmalloc (newlinelen);
2138 + newline[0] = '\0';
2140 + if (!cvs_casecmp (part_tag, "ALL"))
2141 + part_tag = xstrdup ("ALL");
2143 + xrealloc_and_strcat (&newline, &newlinelen, part_type);
2144 + xrealloc_and_strcat (&newline, &newlinelen, ":");
2145 + xrealloc_and_strcat (&newline, &newlinelen, part_object);
2146 + xrealloc_and_strcat (&newline, &newlinelen, ":");
2147 + xrealloc_and_strcat (&newline, &newlinelen, part_tag);
2148 + xrealloc_and_strcat (&newline, &newlinelen, ":");
2150 + if (strncmp (perms, "none", 4) != 0)
2152 + xrealloc_and_strcat (&newline, &newlinelen, user);
2153 + xrealloc_and_strcat (&newline, &newlinelen, "!");
2154 + xrealloc_and_strcat (&newline, &newlinelen, perms);
2155 + if (otheruserparts != NULL)
2156 + xrealloc_and_strcat (&newline, &newlinelen, ",");
2159 + if (otheruserparts != NULL)
2161 + if (otheruserparts[strlen (otheruserparts) - 1] == '\n')
2162 + otheruserparts[strlen (otheruserparts) - 1] = '\0';
2164 + xrealloc_and_strcat (&newline, &newlinelen, otheruserparts);
2167 + xrealloc_and_strcat (&newline, &newlinelen, ":");
2171 + accessfpout = cvs_temp_file (&tmpaccessout);
2172 + accessfpin = open_accessfile ("r", arepos, &accessfile);
2174 + while (getline (&line, &line_allocated, accessfpin) >= 0)
2176 + if (pos != ftell (accessfpin))
2178 + if (fprintf (accessfpout, line) < 0)
2179 + error (1, errno, "writing temporary file: %s", tmpaccessout);
2183 + if (fprintf (accessfpout, "%s\n", newline) < 0)
2184 + error (1, errno, "writing temporary file: %s", tmpaccessout);
2188 + if (fclose (accessfpin) == EOF)
2189 + error (1, errno, "cannot close access file: %s", accessfile);
2191 + if (fclose (accessfpout) == EOF)
2192 + error (1, errno, "cannot close temporary file: %s", tmpaccessout);
2194 + if (CVS_UNLINK (accessfile) < 0)
2195 + error (0, errno, "cannot remove %s", accessfile);
2197 + copy_file (tmpaccessout, accessfile);
2199 + if (CVS_UNLINK (tmpaccessout) < 0)
2200 + error (0, errno, "cannot remove temporary file: %s", tmpaccessout);
2204 + accessfpout = open_accessfile ("r+", arepos, &accessfile);
2206 + if (accessfpout == NULL)
2208 + if (existence_error (errno))
2210 + accessfpout = open_accessfile ("w+", arepos, &accessfile);
2215 + if (fseek (accessfpout, 0, 2) != 0)
2216 + error (1, errno, "cannot fseek access file: %s", accessfile);
2218 + if (fprintf (accessfpout, "%s\n", newline) < 0)
2219 + error (1, errno, "writing access file: %s", accessfile);
2221 + if (fclose (accessfpout) == EOF)
2222 + error (1, errno, "cannot close access file: %s", accessfile);
2227 + chmod(accessfile, 0644);
2234 +acllist_fileproc (callerdat, finfo)
2236 +struct file_info *finfo;
2239 + char *filefullname;
2240 + const char *frepository;
2241 + char *line = NULL;
2247 + frepository = Short_Repository (finfo->repository);
2249 + filefullname = xmalloc (strlen (frepository)
2250 + + strlen (finfo->file)
2252 + strcpy (filefullname, frepository);
2253 + strcat (filefullname, "/");
2254 + strcat (filefullname, finfo->file);
2256 + if (!access_allowed (finfo->file, finfo->repository, tag, 5, &line, &pos, 0))
2257 + error (1,0,"You do not have admin rights on '%s'", frepository);
2259 + acllist_print (line, filefullname);
2265 +acllist_dirproc (callerdat, dir, repos, update_dir, entries)
2269 +const char *update_dir;
2273 + char *line = NULL;
2274 + const char *drepository;
2277 + if (repos[0] == '\0')
2278 + repos = Name_Repository (dir, NULL);
2283 + drepository = Short_Repository (repos);
2285 + if (!access_allowed (NULL, repos, tag, 5, &line, &pos, 0))
2286 + error (1, 0, "You do not have admin rights on '%s'", drepository);
2288 + acllist_print (line, drepository);
2295 +acllist_print (line, obj)
2304 + char *printedusers[255];
2305 + printedusers[0] = NULL;
2309 + temp = strtok (line, ":\t");
2312 + cvs_output ("d ", 0);
2314 + cvs_output ("f ", 0);
2316 + temp = strtok (NULL, ":\t");
2318 + cvs_output(obj, 0);
2319 + cvs_output (" | ", 0);
2321 + temp = strtok (NULL, ":\t");
2322 + cvs_output (temp, 0);
2323 + cvs_output (" | ", 0);
2325 + while ((temp = strtok (NULL, "!\t")) != NULL)
2327 + if (strncmp (temp, ":", 1) == 0)
2330 + if (strcmp (temp, "ALL") == 0)
2332 + temp = strtok (NULL, ",\t");
2336 + cvs_output (temp, 0);
2337 + cvs_output (":", 0);
2339 + while (printedusers[c] != NULL)
2342 + printedusers[c] = xstrdup (temp);
2344 + printedusers[c] = NULL;
2346 + temp = strtok (NULL, ",\t");
2348 + if (temp != NULL && temp[strlen (temp) - 2] == ':')
2349 + temp[strlen (temp) - 2] = '\0';
2351 + cvs_output (temp, 0);
2352 + cvs_output (" ", 0);
2355 + if (default_perms_object)
2357 + cvs_output ("| defaults ", 0);
2358 + cvs_output ("ALL:", 0);
2359 + cvs_output (default_perms_object, 0);
2361 + else if (default_part_perms_accessfile)
2364 + i = strlen (default_part_perms_accessfile);
2365 + xrealloc_and_strcat (&default_part_perms_accessfile, &i, ",");
2368 + line = xstrdup(default_part_perms_accessfile);
2370 + cvs_output ("| defaults ", 0);
2372 + temp = strtok (line, "!\t");
2373 + cvs_output (temp, 0);
2374 + cvs_output (":", 0);
2376 + temp = strtok (NULL, ",\t");
2378 + cvs_output (temp, 0);
2379 + cvs_output (" ", 0);
2381 + while ((temp = strtok (NULL, "!\t")) != NULL)
2385 + while (printedusers[c2] != NULL && printed == 0)
2387 + if (strcmp (printedusers[c2], temp) == 0)
2397 + cvs_output (temp, 0);
2398 + cvs_output (":", 0);
2401 + temp = strtok (NULL, ",\t");
2403 + if (temp[strlen (temp) - 2] == ':')
2404 + temp[strlen (temp) - 2] = '\0';
2408 + cvs_output (temp, 0);
2409 + cvs_output (" ", 0);
2413 + else if (cvs_acl_default_permissions)
2415 + cvs_output ("| defaults ", 0);
2416 + cvs_output ("ALL: ", 0);
2417 + cvs_output (cvs_acl_default_permissions, 0);
2420 + cvs_output ("\n", 0);
2426 + cvs_output ("d ", 0);
2428 + cvs_output ("f ", 0);
2429 + cvs_output (obj, 0);
2430 + cvs_output (" | ", 0);
2431 + cvs_output (tag, 0);
2432 + cvs_output (" | ", 0);
2434 + if (default_perms_object)
2436 + cvs_output ("| defaults ", 0);
2437 + cvs_output ("ALL:", 0);
2438 + cvs_output (default_perms_object, 0);
2439 + cvs_output ("\n", 0);
2441 + else if (default_part_perms_accessfile)
2444 + line = xstrdup(default_part_perms_accessfile);
2446 + cvs_output ("| defaults ", 0);
2448 + temp = strtok (line, "!\t");
2449 + cvs_output (temp, 0);
2450 + cvs_output (":", 0);
2452 + temp = strtok (NULL, ",\t");
2454 + if (temp[strlen (temp) - 2] == ':')
2455 + temp[strlen (temp) - 2] = '\0';
2457 + cvs_output (temp, 0);
2458 + cvs_output (" ", 0);
2460 + while ((temp = strtok (NULL, "!\t")) != NULL)
2462 + cvs_output (temp, 0);
2463 + cvs_output (":", 0);
2465 + temp = strtok (NULL, ",\t");
2467 + if (temp[strlen (temp) - 2] == ':')
2468 + temp[strlen (temp) - 2] = '\0';
2470 + cvs_output (temp, 0);
2471 + cvs_output (" ", 0);
2473 + cvs_output ("\n", 0);
2475 + else if (cvs_acl_default_permissions)
2477 + cvs_output ("| defaults ", 0);
2478 + cvs_output ("ALL: ", 0);
2479 + cvs_output (cvs_acl_default_permissions, 0);
2480 + cvs_output ("\n", 0);
2483 + cvs_output ("default:p (no perms)\n", 0);
2487 diff -urN cvs-1.11.17.orig/src/add.c cvs-1.11.17/src/add.c
2488 --- cvs-1.11.17.orig/src/add.c 2004-05-10 16:35:54.000000000 +0200
2489 +++ cvs-1.11.17/src/add.c 2004-06-23 21:02:34.000000000 +0200
2490 @@ -400,6 +400,24 @@
2495 +#ifdef SERVER_SUPPORT
2496 + if (use_cvs_acl && server_active)
2498 + if (!access_allowed (finfo.file, repository, vers->tag, 6,
2501 + if (stop_at_first_permission_denied)
2502 + error (1, 0, "permission denied for %s",
2503 + Short_Repository (finfo.repository));
2505 + error (0, 0, "permission denied for %s/%s",
2506 + Short_Repository (finfo.repository), finfo.file);
2512 /* There is a user file, so build the entry for it */
2513 if (build_entry (repository, finfo.file, vers->options,
2514 message, entries, vers->tag) != 0)
2515 @@ -656,6 +674,26 @@
2516 && isdir (finfo.file)
2517 && !wrap_name_has (finfo.file, WRAP_TOCVS))
2521 +#ifdef SERVER_SUPPORT
2522 + if (use_cvs_acl && server_active)
2524 + if (!access_allowed (NULL, repository, NULL, 6,
2527 + if (stop_at_first_permission_denied)
2528 + error (1, 0, "permission denied for %s",
2529 + Short_Repository (finfo.repository));
2531 + error (0, 0, "permission denied for %s/%s",
2532 + Short_Repository (finfo.repository), finfo.file);
2539 err += add_directory (&finfo);
2542 diff -urN cvs-1.11.17.orig/src/annotate.c cvs-1.11.17/src/annotate.c
2543 --- cvs-1.11.17.orig/src/annotate.c 2004-03-22 16:44:27.000000000 +0100
2544 +++ cvs-1.11.17/src/annotate.c 2004-06-23 21:02:34.000000000 +0200
2545 @@ -276,6 +276,25 @@
2546 if (version == NULL)
2550 +#ifdef SERVER_SUPPORT
2551 + if (use_cvs_acl && server_active)
2553 + if (!access_allowed (finfo->file, finfo->repository, version, 5,
2556 + if (stop_at_first_permission_denied)
2557 + error (1, 0, "permission denied for %s",
2558 + Short_Repository (finfo->repository));
2560 + error (0, 0, "permission denied for %s/%s",
2561 + Short_Repository (finfo->repository), finfo->file);
2568 /* Distinguish output for various files if we are processing
2570 cvs_outerr ("\nAnnotations for ", 0);
2571 diff -urN cvs-1.11.17.orig/src/commit.c cvs-1.11.17/src/commit.c
2572 --- cvs-1.11.17.orig/src/commit.c 2004-06-09 16:34:54.000000000 +0200
2573 +++ cvs-1.11.17/src/commit.c 2004-06-23 21:02:34.000000000 +0200
2574 @@ -1299,6 +1299,34 @@
2580 +#ifdef SERVER_SUPPORT
2581 + if (use_cvs_acl && server_active)
2584 + if (ci->status == T_MODIFIED)
2586 + else if (ci->status == T_ADDED)
2588 + else if (ci->status == T_REMOVED)
2591 + if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm,
2594 + if (stop_at_first_permission_denied)
2595 + error (1, 0, "permission denied for %s",
2596 + Short_Repository (finfo->repository));
2598 + error (0, 0, "permission denied for %s/%s",
2599 + Short_Repository (finfo->repository), finfo->file);
2606 if (ci->status == T_MODIFIED)
2608 if (finfo->rcs == NULL)
2609 diff -urN cvs-1.11.17.orig/src/cvs.h cvs-1.11.17/src/cvs.h
2610 --- cvs-1.11.17.orig/src/cvs.h 2004-06-09 16:34:54.000000000 +0200
2611 +++ cvs-1.11.17/src/cvs.h 2004-06-23 21:02:34.000000000 +0200
2612 @@ -195,6 +195,11 @@
2613 #define CVSROOTADM_PASSWD "passwd"
2614 #define CVSROOTADM_CONFIG "config"
2617 +#define CVSROOTADM_ACLCONFIG "aclconfig"
2618 +#define CVSROOTADM_ACCESS "access"
2619 +#define CVSROOTADM_GROUP "group"
2621 #define CVSNULLREPOS "Emptydir" /* an empty directory */
2623 /* Other CVS file names */
2624 @@ -571,6 +576,18 @@
2625 /* LockDir setting from CVSROOT/config. */
2626 extern char *lock_dir;
2629 +/* ACL Patch settings from CVSROOT/config */
2630 +extern int use_cvs_acl;
2631 +extern char *cvs_acl_default_permissions;
2632 +extern int use_cvs_groups;
2633 +extern int use_system_groups;
2634 +extern int use_separate_acl_file_for_each_dir;
2635 +extern char *cvs_acl_file_location;
2636 +extern char *cvs_groups_file_location;
2637 +extern char *cvs_server_run_as;
2638 +extern int stop_at_first_permission_denied;
2640 void Scratch_Entry PROTO((List * list, const char *fname));
2641 void ParseTag PROTO((char **tagp, char **datep, int *nonbranchp));
2642 void WriteTag PROTO ((const char *dir, const char *tag, const char *date,
2643 @@ -866,6 +883,10 @@
2644 int editors PROTO ((int argc, char **argv));
2645 int watchers PROTO ((int argc, char **argv));
2646 extern int annotate PROTO ((int argc, char **argv));
2649 +extern int cvsacl PROTO ((int argc, char **argv));
2651 extern int add PROTO ((int argc, char **argv));
2652 extern int admin PROTO ((int argc, char **argv));
2653 extern int checkout PROTO ((int argc, char **argv));
2654 diff -urN cvs-1.11.17.orig/src/diff.c cvs-1.11.17/src/diff.c
2655 --- cvs-1.11.17.orig/src/diff.c 2004-03-20 03:06:45.000000000 +0100
2656 +++ cvs-1.11.17/src/diff.c 2004-06-23 21:02:34.000000000 +0200
2657 @@ -474,6 +474,43 @@
2659 /* Skip all the following checks regarding the user file; we're
2663 +#ifdef SERVER_SUPPORT
2664 + if (use_cvs_acl && server_active)
2668 + if (!access_allowed (NULL, finfo->repository, diff_rev1, 5,
2671 + if (stop_at_first_permission_denied)
2672 + error (1, 0, "permission denied for %s",
2673 + Short_Repository (finfo->repository));
2675 + error (0, 0, "permission denied for %s/%s",
2676 + Short_Repository (finfo->repository), finfo->file);
2683 + if (!access_allowed (NULL, finfo->repository, diff_rev2, 5))
2685 + if (stop_at_first_permission_denied)
2686 + error (1, 0, "permission denied for %s",
2687 + Short_Repository (finfo->repository));
2689 + error (0, 0, "permission denied for %s/%s",
2690 + Short_Repository (finfo->repository), finfo->file);
2699 else if (vers->vn_user == NULL)
2701 @@ -827,6 +864,42 @@
2703 return (R_SKIP_ALL);
2706 +#ifdef SERVER_SUPPORT
2707 + if (use_cvs_acl && server_active)
2711 + if (!access_allowed (NULL, update_dir, diff_rev1, 5,
2714 + if (stop_at_first_permission_denied)
2715 + error (1, 0, "permission denied for %s",
2716 + Short_Repository (update_dir));
2718 + error (0, 0, "permission denied for %s/%s",
2719 + Short_Repository (update_dir), update_dir);
2726 + if (!access_allowed (NULL, update_dir, diff_rev2, 5))
2728 + if (stop_at_first_permission_denied)
2729 + error (1, 0, "permission denied for %s",
2730 + Short_Repository (update_dir));
2732 + error (0, 0, "permission denied for %s/%s",
2733 + Short_Repository (update_dir), update_dir);
2742 error (0, 0, "Diffing %s", update_dir);
2744 diff -urN cvs-1.11.17.orig/src/import.c cvs-1.11.17/src/import.c
2745 --- cvs-1.11.17.orig/src/import.c 2004-04-02 20:55:49.000000000 +0200
2746 +++ cvs-1.11.17/src/import.c 2004-06-23 21:02:34.000000000 +0200
2747 @@ -304,6 +304,20 @@
2748 error (1, 0, "attempt to import the repository");
2752 +#ifdef SERVER_SUPPORT
2753 + if (use_cvs_acl && server_active)
2755 + if (!access_allowed (NULL, repository, argv[1], 6, NULL, NULL, 1))
2757 + error (stop_at_first_permission_denied, 0, "permission denied for %s",
2758 + Short_Repository (repository));
2766 * Make all newly created directories writable. Should really use a more
2767 * sophisticated security mechanism here.
2768 diff -urN cvs-1.11.17.orig/src/log.c cvs-1.11.17/src/log.c
2769 --- cvs-1.11.17.orig/src/log.c 2004-05-10 15:37:16.000000000 +0200
2770 +++ cvs-1.11.17/src/log.c 2004-06-23 21:02:34.000000000 +0200
2771 @@ -837,6 +837,25 @@
2776 +#ifdef SERVER_SUPPORT
2777 + if (use_cvs_acl && server_active)
2779 + if (!access_allowed (finfo->file, finfo->repository, NULL, 5,
2782 + if (stop_at_first_permission_denied)
2783 + error (1, 0, "permission denied for %s",
2784 + Short_Repository (finfo->repository));
2786 + error (0, 0, "permission denied for %s/%s",
2787 + Short_Repository (finfo->repository), finfo->file);
2794 if (log_data->sup_header || !log_data->nameonly)
2797 diff -urN cvs-1.11.17.orig/src/main.c cvs-1.11.17/src/main.c
2798 --- cvs-1.11.17.orig/src/main.c 2004-03-22 16:44:27.000000000 +0100
2799 +++ cvs-1.11.17/src/main.c 2004-06-23 21:02:34.000000000 +0200
2800 @@ -104,6 +104,10 @@
2804 + /* cvsacl patch */
2805 + { "acl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
2806 + { "racl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
2808 { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
2809 { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
2810 { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR },
2812 if we didn't, then there would be no way to check in a new
2813 CVSROOT/config file to fix the broken one! */
2814 parse_config (current_parsed_root->directory);
2816 + /* cvsacl patch */
2817 + parse_aclconfig (current_parsed_root->directory);
2820 #ifdef CLIENT_SUPPORT
2821 diff -urN cvs-1.11.17.orig/src/mkmodules.c cvs-1.11.17/src/mkmodules.c
2822 --- cvs-1.11.17.orig/src/mkmodules.c 2004-02-03 15:39:40.000000000 +0100
2823 +++ cvs-1.11.17/src/mkmodules.c 2004-06-23 21:02:34.000000000 +0200
2824 @@ -311,6 +311,44 @@
2829 +static const char *const aclconfig_contents[] = {
2830 + "# Set `UseCVSACL' to yes to use CVSACL feature.\n",
2831 + "UseCVSACL=yes\n",
2833 + "# Default CVSACL Permission to use.\n",
2834 + "#CVSACLDefaultPermissions=p\n",
2836 + "# Default file location for CVS ACL file (access) is CVSROOT/access.\n",
2837 + "# If you want to use a different location, define it below.\n",
2838 + "#CVSACLFileLocation=/path/to/cvs/access\n",
2840 + "# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).\n",
2841 + "UseSystemGroups=yes\n",
2843 + "# Set `UseCVSGroups' to yes to use another group file.\n",
2844 + "#UseCVSGroups=yes\n",
2846 + "# Default file location for CVS groups file is CVSROOT/group.\n",
2847 + "# If you want to use a different location, define it below.\n",
2848 + "#CVSGroupsFileLocation=/path/to/cvs/group\n",
2850 + "# Set UseSeparateACLFileForEachDir to yes in order to use a\n",
2851 + "# separate 'access' file for each directory.\n",
2852 + "# This increased the performance if you have really big repository.\n",
2853 + "#UseSeparateACLFileForEachDir=no\n",
2855 + "# If StopAtFirstPermissionDenied is set to yes\n",
2856 + "# operation will stop at first permission denied message.\n",
2857 + "# Default is no.\n",
2858 + "#StopAtFirstPermissionDenied=no\n",
2860 + "# Set CVSServerRunAsUser to a system user, in order CVS server\n",
2862 + "#CVSServerRunAsUser=runascvsuser",
2866 static const struct admin_file filelist[] = {
2867 {CVSROOTADM_LOGINFO,
2868 "no logging of 'cvs commit' messages is done without a %s file",
2869 @@ -373,7 +411,12 @@
2871 "a %s file configures various behaviors",
2873 - {NULL, NULL, NULL}
2875 + /* cvsacl patch */
2876 + {CVSROOTADM_ACLCONFIG,
2877 + "a %s file configures Access Control List behaviors",
2878 + aclconfig_contents},
2879 + {NULL, NULL, NULL}
2882 /* Rebuild the checked out administrative files in directory DIR. */
2883 @@ -957,6 +1000,26 @@
2884 because xchmod() is too shy. */
2888 + /* cvsacl patch */
2889 + /* Make an empty 'CVSROOT/access' file */
2890 + strcpy (info, adm);
2891 + strcat (info, "/");
2892 + strcat (info, CVSROOTADM_ACCESS);
2893 + if (!isfile (info))
2897 + fp = open_file (info, "w");
2898 + if (fputs ("# CVS ACL definitions file. DO NOT EDIT MANUALLY\n",
2900 + error (1, errno, "cannot write %s", info);
2902 + if (fclose (fp) < 0)
2903 + error (1, errno, "cannot close %s", info);
2905 + chmod (info, 0644);
2908 /* Make an empty val-tags file to prevent problems creating it later. */
2910 diff -urN cvs-1.11.17.orig/src/parseinfo.c cvs-1.11.17/src/parseinfo.c
2911 --- cvs-1.11.17.orig/src/parseinfo.c 2004-03-19 21:36:39.000000000 +0100
2912 +++ cvs-1.11.17/src/parseinfo.c 2004-06-23 21:02:34.000000000 +0200
2913 @@ -448,3 +448,192 @@
2919 +parse_aclconfig (cvsroot)
2924 + char *line = NULL;
2925 + size_t line_allocated = 0;
2928 + /* FIXME-reentrancy: If we do a multi-threaded server, this would need
2929 + to go to the per-connection data structures. */
2930 + static int parsed = 0;
2932 + /* Authentication code and serve_root might both want to call us.
2933 + Let this happen smoothly. */
2938 + infopath = xmalloc (strlen (cvsroot)
2939 + + sizeof (CVSROOTADM_ACLCONFIG)
2940 + + sizeof (CVSROOTADM)
2942 + if (infopath == NULL)
2944 + error (0, 0, "out of memory; cannot allocate infopath");
2945 + goto error_return;
2948 + strcpy (infopath, cvsroot);
2949 + strcat (infopath, "/");
2950 + strcat (infopath, CVSROOTADM);
2951 + strcat (infopath, "/");
2952 + strcat (infopath, CVSROOTADM_ACLCONFIG);
2954 + fp_info = CVS_FOPEN (infopath, "r");
2955 + if (fp_info == NULL)
2957 + /* If no file, don't do anything special. */
2958 + if (!existence_error (errno))
2960 + /* Just a warning message; doesn't affect return
2961 + value, currently at least. */
2962 + error (0, errno, "cannot open %s", infopath);
2968 + while (getline (&line, &line_allocated, fp_info) >= 0)
2970 + /* Skip comments. */
2971 + if (line[0] == '#')
2974 + len = strlen (line) - 1;
2975 + if (line[len] == '\n')
2978 + /* Skip blank lines. */
2979 + if (line[0] == '\0')
2982 + /* The first '=' separates keyword from value. */
2983 + p = strchr (line, '=');
2986 + /* Probably should be printing line number. */
2987 + error (0, 0, "syntax error in %s: line '%s' is missing '='",
2989 + goto error_return;
2994 + if (strcmp (line, "UseCVSACL") == 0)
2996 + if (strcmp (p, "no") == 0)
2998 + else if (strcmp (p, "yes") == 0)
3002 + error (0, 0, "unrecognized value '%s' for UseCVSACL", p);
3003 + goto error_return;
3006 + else if (strcmp (line, "UseSeparateACLFileForEachDir") == 0)
3008 + if (strcmp (p, "no") == 0)
3009 + use_separate_acl_file_for_each_dir = 0;
3010 + else if (strcmp (p, "yes") == 0)
3011 + use_separate_acl_file_for_each_dir = 1;
3014 + error (0, 0, "unrecognized value '%s' for UseSeparateACLFileForEachDir", p);
3015 + goto error_return;
3018 + else if (strcmp (line, "StopAtFirstPermissionDenied") == 0)
3020 + if (strcmp (p, "no") == 0)
3021 + stop_at_first_permission_denied = 0;
3022 + else if (strcmp (p, "yes") == 0)
3023 + stop_at_first_permission_denied = 1;
3026 + error (0, 0, "unrecognized value '%s' for StopAtFirstPermissionDenied", p);
3027 + goto error_return;
3030 + else if (strcmp (line, "CVSACLDefaultPermissions") == 0)
3032 + if (cvs_acl_default_permissions != NULL)
3033 + free (cvs_acl_default_permissions);
3034 + if (!given_perms_valid (p))
3035 + error (1,0,"Invalid CVS ACL Default Permissions: '%s' in CVSROOT/aclconfig", p);
3036 + cvs_acl_default_permissions = xstrdup (p);
3038 + else if (strcmp (line, "UseCVSGroups") == 0)
3040 + if (strcmp (p, "no") == 0)
3041 + use_cvs_groups = 0;
3042 + else if (strcmp (p, "yes") == 0)
3043 + use_cvs_groups = 1;
3046 + error (0, 0, "unrecognized value '%s' for UseCVSGroups", p);
3047 + goto error_return;
3050 + else if (strcmp (line, "UseSystemGroups") == 0)
3052 + if (strcmp (p, "no") == 0)
3053 + use_system_groups = 0;
3054 + else if (strcmp (p, "yes") == 0)
3055 + use_system_groups = 1;
3058 + error (0, 0, "unrecognized value '%s' for UseSystemGroups", p);
3059 + goto error_return;
3062 + else if (strcmp (line, "CVSACLFileLocation") == 0)
3064 + if (cvs_acl_file_location != NULL)
3065 + free (cvs_acl_file_location);
3066 + cvs_acl_file_location = xstrdup (p);
3068 + else if (strcmp (line, "CVSGroupsFileLocation") == 0)
3070 + if (cvs_groups_file_location != NULL)
3071 + free (cvs_groups_file_location);
3072 + cvs_groups_file_location = xstrdup (p);
3074 + else if (strcmp (line, "CVSServerRunAsUser") == 0)
3076 + if (cvs_server_run_as != NULL)
3077 + free (cvs_server_run_as);
3078 + cvs_server_run_as = xstrdup (p);
3083 + if (ferror (fp_info))
3085 + error (0, errno, "cannot read %s", infopath);
3086 + goto error_return;
3088 + if (fclose (fp_info) < 0)
3090 + error (0, errno, "cannot close %s", infopath);
3091 + goto error_return;
3099 + if (infopath != NULL)
3106 diff -urN cvs-1.11.17.orig/src/patch.c cvs-1.11.17/src/patch.c
3107 --- cvs-1.11.17.orig/src/patch.c 2004-04-02 21:25:32.000000000 +0200
3108 +++ cvs-1.11.17/src/patch.c 2004-06-23 21:02:34.000000000 +0200
3109 @@ -498,6 +498,43 @@
3114 +#ifdef SERVER_SUPPORT
3115 + if (use_cvs_acl && server_active)
3119 + if (!access_allowed (finfo->file, finfo->repository, rev1, 5,
3122 + if (stop_at_first_permission_denied)
3123 + error (1, 0, "permission denied for %s",
3124 + Short_Repository (finfo->repository));
3126 + error (0, 0, "permission denied for %s/%s",
3127 + Short_Repository (finfo->repository), finfo->file);
3134 + if (!access_allowed (finfo->file, finfo->repository, rev2, 5,
3137 + if (stop_at_first_permission_denied)
3138 + error (1, 0, "permission denied for %s",
3139 + Short_Repository (finfo->repository));
3141 + error (0, 0, "permission denied for %s/%s",
3142 + Short_Repository (finfo->repository), finfo->file);
3150 /* Create 3 empty files. I'm not really sure there is any advantage
3151 * to doing so now rather than just waiting until later.
3153 diff -urN cvs-1.11.17.orig/src/remove.c cvs-1.11.17/src/remove.c
3154 --- cvs-1.11.17.orig/src/remove.c 2004-03-20 03:34:32.000000000 +0100
3155 +++ cvs-1.11.17/src/remove.c 2004-06-23 21:02:34.000000000 +0200
3156 @@ -250,6 +250,25 @@
3161 +#ifdef SERVER_SUPPORT
3162 + if (use_cvs_acl && server_active)
3164 + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 7,
3167 + if (stop_at_first_permission_denied)
3168 + error (1, 0, "permission denied for %s",
3169 + Short_Repository (finfo->repository));
3171 + error (0, 0, "permission denied for %s/%s",
3172 + Short_Repository (finfo->repository), finfo->file);
3179 /* Re-register it with a negative version number. */
3180 fname = xmalloc (strlen (vers->vn_user) + 5);
3181 (void) strcpy (fname, "-");
3182 diff -urN cvs-1.11.17.orig/src/server.c cvs-1.11.17/src/server.c
3183 --- cvs-1.11.17.orig/src/server.c 2004-06-09 16:34:55.000000000 +0200
3184 +++ cvs-1.11.17/src/server.c 2004-06-23 21:02:34.000000000 +0200
3185 @@ -773,6 +773,10 @@
3186 nothing. But for rsh, we need to do it now. */
3187 parse_config (current_parsed_root->directory);
3189 + /* cvsacl patch */
3190 + parse_aclconfig (current_parsed_root->directory);
3193 path = xmalloc (strlen (current_parsed_root->directory)
3194 + sizeof (CVSROOTADM)
3196 @@ -3761,6 +3765,23 @@
3197 do_cvs_command ("rlog", cvslog);
3205 + do_cvs_command ("acl", cvsacl);
3213 + cvs_cmd_name = "racl";
3214 + do_cvs_command ("racl", cvsacl);
3220 @@ -4820,6 +4841,11 @@
3221 REQ_LINE("diff", serve_diff, 0),
3222 REQ_LINE("log", serve_log, 0),
3223 REQ_LINE("rlog", serve_rlog, 0),
3225 + /* cvsacl patch */
3226 + REQ_LINE("acl", serve_acl, 0),
3227 + REQ_LINE("racl", serve_racl, 0),
3229 REQ_LINE("add", serve_add, 0),
3230 REQ_LINE("remove", serve_remove, 0),
3231 REQ_LINE("update-patches", serve_ignore, 0),
3232 @@ -5293,6 +5319,10 @@
3236 + /* cvsacl patch */
3237 + if (use_cvs_acl && cvs_server_run_as)
3238 + username = cvs_server_run_as;
3240 pw = getpwnam (username);
3243 @@ -5875,6 +5905,9 @@
3244 in a new CVSROOT/config file to fix the broken one! */
3245 parse_config (repository);
3247 + /* cvsacl patch */
3248 + parse_aclconfig (repository);
3250 /* We need the real cleartext before we hash it. */
3251 descrambled_password = descramble (password);
3252 host_user = check_password (username, descrambled_password, repository);
3253 diff -urN cvs-1.11.17.orig/src/status.c cvs-1.11.17/src/status.c
3254 --- cvs-1.11.17.orig/src/status.c 2004-03-20 02:40:12.000000000 +0100
3255 +++ cvs-1.11.17/src/status.c 2004-06-23 21:02:34.000000000 +0200
3256 @@ -128,7 +128,27 @@
3258 status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
3260 - sstat = "Classify Error";
3263 +#ifdef SERVER_SUPPORT
3264 + if (use_cvs_acl && server_active)
3266 + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5,
3269 + if (stop_at_first_permission_denied)
3270 + error (1, 0, "permission denied for %s",
3271 + Short_Repository (finfo->repository));
3273 + error (0, 0, "permission denied for %s/%s",
3274 + Short_Repository (finfo->repository), finfo->file);
3281 + sstat = "Classify Error";
3285 diff -urN cvs-1.11.17.orig/src/tag.c cvs-1.11.17/src/tag.c
3286 --- cvs-1.11.17.orig/src/tag.c 2004-04-06 20:37:10.000000000 +0200
3287 +++ cvs-1.11.17/src/tag.c 2004-06-23 21:02:34.000000000 +0200
3288 @@ -660,6 +660,25 @@
3289 * correctly without breaking your link!
3293 +#ifdef SERVER_SUPPORT
3294 + if (use_cvs_acl && server_active)
3296 + if (!access_allowed (finfo->file, finfo->repository, numtag, 4,
3299 + if (stop_at_first_permission_denied)
3300 + error (1, 0, "permission denied for %s",
3301 + Short_Repository (finfo->repository));
3303 + error (0, 0, "permission denied for %s/%s",
3304 + Short_Repository (finfo->repository), finfo->file);
3312 return (rtag_delete (rcsfile));
3314 @@ -880,6 +899,21 @@
3315 if (nversion == NULL)
3316 goto free_vars_and_return;
3320 +#ifdef SERVER_SUPPORT
3321 + if (use_cvs_acl && server_active)
3323 + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 4,
3326 + error (0, 0, "permission denied for %s/%s",
3327 + Short_Repository (finfo->repository), finfo->file);
3336 diff -urN cvs-1.11.17.orig/src/update.c cvs-1.11.17/src/update.c
3337 --- cvs-1.11.17.orig/src/update.c 2004-05-04 19:25:54.000000000 +0200
3338 +++ cvs-1.11.17/src/update.c 2004-06-23 21:02:34.000000000 +0200
3339 @@ -596,6 +596,26 @@
3340 status = Classify_File (finfo, tag, date, options, force_tag_match,
3341 aflag, &vers, pipeout);
3345 +#ifdef SERVER_SUPPORT
3346 + if (use_cvs_acl && server_active)
3348 + if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5,
3351 + if (stop_at_first_permission_denied)
3352 + error (1, 0, "permission denied for %s",
3353 + Short_Repository (finfo->repository));
3355 + error (0, 0, "permission denied for %s/%s",
3356 + Short_Repository (finfo->repository), finfo->file);
3363 /* Keep track of whether TAG is a branch tag.
3364 Note that if it is a branch tag in some files and a nonbranch tag
3365 in others, treat it as a nonbranch tag. It is possible that case
3366 diff -urN cvs-1.11.17.orig/src/version.c cvs-1.11.17/src/version.c
3367 --- cvs-1.11.17.orig/src/version.c 2004-02-03 15:37:56.000000000 +0100
3368 +++ cvs-1.11.17/src/version.c 2004-06-23 21:02:34.000000000 +0200
3375 +char *aclpatch_string = "with CVSACL Patch 1.2.0 (cvsacl.sourceforge.net)\n";
3377 static const char *const version_usage[] =
3381 (void) fputs (PACKAGE_STRING, stdout);
3382 (void) fputs (config_string, stdout);
3383 + /* cvsacl patch */
3384 + (void) fputs (aclpatch_string, stdout);
3386 #ifdef CLIENT_SUPPORT
3387 if (current_parsed_root && current_parsed_root->isremote)