diff options
Diffstat (limited to 'contrib/cvs/contrib/cvs_acls.in')
-rwxr-xr-x | contrib/cvs/contrib/cvs_acls.in | 963 |
1 files changed, 0 insertions, 963 deletions
diff --git a/contrib/cvs/contrib/cvs_acls.in b/contrib/cvs/contrib/cvs_acls.in deleted file mode 100755 index 8ee8bb3..0000000 --- a/contrib/cvs/contrib/cvs_acls.in +++ /dev/null @@ -1,963 +0,0 @@ -#! @PERL@ -T -# -*-Perl-*- - -# Copyright (C) 1994-2005 The Free Software Foundation, Inc. - -# 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 2, 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. - -############################################################################### -############################################################################### -############################################################################### -# -# THIS SCRIPT IS PROBABLY BROKEN. REMOVING THE -T SWITCH ON THE #! LINE ABOVE -# WOULD FIX IT, BUT THIS IS INSECURE. WE RECOMMEND FIXING THE ERRORS WHICH THE -# -T SWITCH WILL CAUSE PERL TO REPORT BEFORE RUNNING THIS SCRIPT FROM A CVS -# SERVER TRIGGER. PLEASE SEND PATCHES CONTAINING THE CHANGES YOU FIND -# NECESSARY TO RUN THIS SCRIPT WITH THE TAINT-CHECKING ENABLED BACK TO THE -# <@PACKAGE_BUGREPORT@> MAILING LIST. -# -# For more on general Perl security and taint-checking, please try running the -# `perldoc perlsec' command. -# -############################################################################### -############################################################################### -############################################################################### - -=head1 Name - -cvs_acls - Access Control List for CVS - -=head1 Synopsis - -In 'commitinfo': - - repository/path/to/restrict $CVSROOT/CVSROOT/cvs_acls [-d][-u $USER][-f <logfile>] - -where: - - -d turns on debug information - -u passes the client-side userId to the cvs_acls script - -f specifies an alternate filename for the restrict_log file - -In 'cvsacl': - - {allow.*,deny.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]] - -where: - - allow|deny - allow: commits are allowed; deny: prohibited - user - userId to be allowed or restricted - repos - file or directory to be allowed or restricted - branch - branch to be allowed or restricted - -See below for examples. - -=head1 Licensing - -cvs_acls - provides access control list functionality for CVS - -Copyright (c) 2004 by Peter Connolly <peter.connolly@cnet.com> -All rights reserved. - -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 2 of the License, 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. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -=head1 Description - -This script--cvs_acls--is invoked once for each directory within a -"cvs commit". The set of files being committed for that directory as -well as the directory itself, are passed to this script. This script -checks its 'cvsacl' file to see if any of the files being committed -are on the 'cvsacl' file's restricted list. If any of the files are -restricted, then the cvs_acls script passes back an exit code of 1 -which disallows the commits for that directory. - -Messages are returned to the committer indicating the file(s) that -he/she are not allowed to committ. Additionally, a site-specific -set of messages (e.g., contact information) can be included in these -messages. - -When a commit is prohibited, log messages are written to a restrict_log -file in $CVSROOT/CVSROOT. This default file can be redirected to -another destination. - -The script is triggered from the 'commitinfo' file in $CVSROOT/CVSROOT/. - -=head1 Enhancements - -This section lists the bug fixes and enhancements added to cvs_acls -that make up the current cvs_acls. - -=head2 Fixed Bugs - -This version attempts to get rid the following bugs from the -original version of cvs_acls: - -=over 2 - -=item * -Multiple entries on an 'cvsacl' line will be matched individually, -instead of requiring that all commit files *exactly* match all -'cvsacl' entries. Commiting a file not in the 'cvsacl' list would -allow *all* files (including a restricted file) to be committed. - -[IMO, this basically made the original script unuseable for our -situation since any arbitrary combination of committed files could -avoid matching the 'cvsacl's entries.] - -=item * -Handle specific filename restrictions. cvs_acls didn't restrict -individual files specified in 'cvsacl'. - -=item * -Correctly handle multiple, specific filename restrictions - -=item * -Prohibit mix of dirs and files on a single 'cvsacl' line -[To simplify the logic and because this would be normal usage.] - -=item * -Correctly handle a mixture of branch restrictions within one work -directory - -=item * -$CVSROOT existence is checked too late - -=item * -Correctly handle the CVSROOT=:local:/... option (useful for -interactive testing) - -=item * -Replacing shoddy "$universal_off" logic -(Thanks to Karl-Konig Konigsson for pointing this out.) - -=back - -=head2 Enhancements - -=over 2 - -=item * -Checks modules in the 'cvsacl' file for valid files and directories - -=item * -Accurately report restricted entries and their matching patterns - -=item * -Simplified and commented overly complex PERL REGEXPs for readability -and maintainability - -=item * -Skip the rest of processing if a mismatch on portion of the 'cvsacl' line - -=item * -Get rid of opaque "karma" messages in favor of user-friendly messages -that describe which user, file(s) and branch(es) were disallowed. - -=item * -Add optional 'restrict_msg' file for additional, site-specific -restriction messages. - -=item * -Take a "-u" parameter for $USER from commit_prep so that the script -can do restrictions based on the client-side userId rather than the -server-side userId (usually 'cvs'). - -(See discussion below on "Admin Setup" for more on this point.) - -=item * -Added a lot more debug trace - -=item * -Tested these restrictions with concurrent use of pserver and SSH -access to model our transition from pserver to ext access. - -=item * -Added logging of restricted commit attempts. -Restricted commits can be sent to a default file: -$CVSROOT/CVSROOT/restrictlog or to one passed to the script -via the -f command parameter. - -=back - -=head2 ToDoS - -=over 2 - -=item * -Need to deal with pserver/SSH transition with conflicting umasks? - -=item * -Use a CPAN module to handle command parameters. - -=item * -Use a CPAN module to clone data structures. - -=back - -=head1 Version Information - -This is not offered as a fix to the original 'cvs_acls' script since it -differs substantially in goals and methods from the original and there -are probably a significant number of people out there that still require -the original version's functionality. - -The 'cvsacl' file flags of 'allow' and 'deny' were intentionally -changed to 'allow' and 'deny' because there are enough differences -between the original script's behavior and this one's that we wanted to -make sure that users will rethink their 'cvsacl' file formats before -plugging in this newer script. - -Please note that there has been very limited cross-platform testing of -this script!!! (We did not have the time or resources to do exhaustive -cross-platform testing.) - -It was developed and tested under Red Hat Linux 9.0 using PERL 5.8.0. -Additionally, it was built and tested under Red Hat Linux 7.3 using -PERL 5.6.1. - -$Id: cvs_acls.in,v 1.4.4.6 2005/09/01 13:44:49 dprice Exp $ - -This version is based on the 1.11.13 version of cvs_acls -peter.connolly@cnet.com (Peter Connolly) - - Access control lists for CVS. dgg@ksr.com (David G. Grubbs) - Branch specific controls added by voisine@bytemobile.com (Aaron Voisine) - -=head1 Installation - -To use this program, do the following four things: - -0. Install PERL, version 5.6.1 or 5.8.0. - -1. Admin Setup: - - There are two choices here. - - a) The first option is to use the $ENV{"USER"}, server-side userId - (from the third column of your pserver 'passwd' file) as the basis for - your restrictions. In this case, you will (at a minimum) want to set - up a new "cvsadmin" userId and group on the pserver machine. - CVS administrators will then set up their 'passwd' file entries to - run either as "cvs" (for regular users) or as "cvsadmin" (for power - users). Correspondingly, your 'cvsacl' file will only list 'cvs' - and 'cvsadmin' as the userIds in the second column. - - Commentary: A potential weakness of this is that the xinetd - cvspserver process will need to run as 'root' in order to switch - between the 'cvs' and the 'cvsadmin' userIds. Some sysadmins don't - like situations like this and may want to chroot the process. - Talk to them about this point... - - b) The second option is to use the client-side userId as the basis for - your restrictions. In this case, all the xinetd cvspserver processes - can run as userId 'cvs' and no 'root' userId is required. If you have - a 'passwd' file that lists 'cvs' as the effective run-time userId for - all your users, then no changes to this file are needed. Your 'cvsacl' - file will use the individual, client-side userIds in its 2nd column. - - As long as the userIds in pserver's 'passwd' file match those userIds - that your Linux server know about, this approach is ideal if you are - planning to move from pserver to SSH access at some later point in time. - Just by switching the CVSROOT var from CVSROOT=:pserver:<userId>... to - CVSROOT=:ext:<userId>..., users can switch over to SSH access without - any other administrative changes. When all users have switched over to - SSH, the inherently insecure xinetd cvspserver process can be disabled. - [http://ximbiot.com/cvs/manual/cvs-1.11.17/cvs_2.html#SEC32] - - :TODO: The only potential glitch with the SSH approach is the possibility - that each user can have differing umasks that might interfere with one - another, especially during a transition from pserver to SSH. As noted - in the ToDo section, this needs a good strategy and set of tests for that - yet... - -2. Put two lines, as the *only* non-comment lines, in your commitinfo file: - - ALL $CVSROOT/CVSROOT/commit_prep - ALL $CVSROOT/CVSROOT/cvs_acls [-d][-u $USER ][-f <logfilename>] - - where "-d" turns on debug trace - "-u $USER" passes the client-side userId to cvs_acls - "-f <logfilename"> overrides the default filename used to log - restricted commit attempts. - - (These are handled in the processArgs() subroutine.) - -If you are using client-side userIds to restrict access to your -repository, make sure that they are in this order since the commit_prep -script is required in order to pass the $USER parameter. - -A final note about the repository matching pattern. The example above -uses "ALL" but note that this means that the cvs_acls script will run -for each and every commit in your repository. Obviously, in a large -repository this adds up to a lot of overhead that may not be necesary. -A better strategy is to use a repository pattern that is more specific -to the areas that you wish to secure. - -3. Install this file as $CVSROOT/CVSROOT/cvs_acls and make it executable. - -4. Create a file named CVSROOT/cvsacl and optionally add it to - CVSROOT/checkoutlist and check it in. See the CVS manual's - administrative files section about checkoutlist. Typically: - - $ cvs checkout CVSROOT - $ cd CVSROOT - [ create the cvsacl file, include 'commitinfo' line ] - [ add cvsacl to checkoutlist ] - $ cvs add cvsacl - $ cvs commit -m 'Added cvsacl for use with cvs_acls.' cvsacl checkoutlist - -Note: The format of the 'cvsacl' file is described in detail immediately -below but here is an important set up point: - - Make sure to include a line like the following: - - deny||CVSROOT/commitinfo CVSROOT/cvsacl - allow|cvsadmin|CVSROOT/commitinfo CVSROOT/cvsacl - - that restricts access to commitinfo and cvsacl since this would be one of - the easiest "end runs" around this ACL approach. ('commitinfo' has the - line that executes the cvs_acls script and, of course, all the - restrictions are in 'cvsacl'.) - -5. (Optional) Create a 'restrict_msg' file in the $CVSROOT/CVSROOT directory. - Whenever there is a restricted file or dir message, cvs_acls will look - for this file and, if it exists, print its contents as part of the - commit-denial message. This gives you a chance to print any site-specific - information (e.g., who to call, what procedures to look up,...) whenever - a commit is denied. - -=head1 Format of the cvsacl file - -The 'cvsacl' file determines whether you may commit files. It contains lines -read from top to bottom, keeping track of whether a given user, repository -and branch combination is "allowed" or "denied." The script will assume -"allowed" on all repository paths until 'allow' and 'deny' rules change -that default. - -The normal pattern is to specify an 'deny' rule to turn off -access to ALL users, then follow it with a matching 'allow' rule that will -turn on access for a select set of users. In the case of multiple rules for -the same user, repository and branch, the last one takes precedence. - -Blank lines and lines with only comments are ignored. Any other lines not -beginning with "allow" or "deny" are logged to the restrict_log file. - -Lines beginning with "allow" or "deny" are assumed to be '|'-separated -triples: (All spaces and tabs are ignored in a line.) - - {allow.*,deny.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]] - - 1. String starting with "allow" or "deny". - 2. Optional, comma-separated list of usernames. - 3. Optional, comma-separated list of repository pathnames. - These are pathnames relative to $CVSROOT. They can be directories or - filenames. A directory name allows or restricts access to all files and - directories below it. One line can have either directories or filenames - but not both. - 4. Optional, comma-separated list of branch tags. - If not specified, all branches are assumed. Use HEAD to reference the - main branch. - -Example: (Note: No in-line comments.) - - # ----- Make whole repository unavailable. - deny - - # ----- Except for user "dgg". - allow|dgg - - # ----- Except when "fred" or "john" commit to the - # module whose repository is "bin/ls" - allow|fred, john|bin/ls - - # ----- Except when "ed" commits to the "stable" - # branch of the "bin/ls" repository - allow|ed|/bin/ls|stable - -=head1 Program Logic - -CVS passes to @ARGV an absolute directory pathname (the repository -appended to your $CVSROOT variable), followed by a list of filenames -within that directory that are to be committed. - -The script walks through the 'cvsacl' file looking for matches on -the username, repository and branch. - -A username match is simply the user's name appearing in the second -column of the cvsacl line in a space-or-comma separate list. If -blank, then any user will match. - -A repository match: - -=over 2 - -=item * -Each entry in the modules section of the current 'cvsacl' line is -examined to see if it is a dir or a file. The line must have -either files or dirs, but not both. (To simplify the logic.) - -=item * -If neither, then assume the 'cvsacl' file was set up in error and -skip that 'allow' line. - -=item * -If a dir, then each dir pattern is matched separately against the -beginning of each of the committed files in @ARGV. - -=item * -If a file, then each file pattern is matched exactly against each -of the files to be committed in @ARGV. - -=item * -Repository and branch must BOTH match together. This is to cover -the use case where a user has multiple branches checked out in -a single work directory. Commit files can be from different -branches. - -A branch match is either: - -=over 4 - -=item * -When no branches are listed in the fourth column. ("Match any.") - -=item * -All elements from the fourth column are matched against each of -the tag names for $ARGV[1..$#ARGV] found in the %branches file. - -=back - -=item * -'allow' match remove that match from the tally map. - -=item * -Restricted ('deny') matches are saved in the %repository_matches -table. - -=item * -If there is a match on user, repository and branch: - - If repository, branch and user match - if 'deny' - add %repository_matches entries to %restricted_entries - else if 'allow' - remove %repository_matches entries from %restricted_entries - -=item * -At the end of all the 'cvsacl' line checks, check to see if there -are any entries in the %restricted_entries. If so, then deny the -commit. - -=back - -=head2 Pseudocode - - read CVS/Entries file and create branch{file}->{branch} hash table - + for each 'allow' and 'deny' line in the 'cvsacl' file: - | user match? - | - Yes: set $user_match = 1; - | repository and branch match? - | - Yes: add to %repository_matches; - | did user, repository match? - | - Yes: if 'deny' then - | add %repository_matches -> %restricted_entries - | if 'allow' then - | remove %repository_matches <- %restricted_entries - + end for loop - any saved restrictions? - no: exit, - set exit code allowing commits and exit - yes: report restrictions, - set exit code prohibiting commits and exit - -=head2 Sanity Check - - 1) file allow trumps a dir deny - deny||java/lib - allow||java/lib/README - 2) dir allow can undo a file deny - deny||java/lib/README - allow||java/lib - 3) file deny trumps a dir allow - allow||java/lib - deny||java/lib/README - 4) dir deny trumps a file allow - allow||java/lib/README - deny||java/lib - ... so last match always takes precedence - -=cut - -$debug = 0; # Set to 1 for debug messages - -%repository_matches = (); # hash of match file and pattern from 'cvsacl' - # repository_matches --> [branch, matching-pattern] - # (Used during module/branch matching loop) - -%restricted_entries = (); # hash table of restricted commit files (from @ARGV) - # restricted_entries --> branch - # (If user/module/branch all match on an 'deny' - # line, then entries added to this map.) - -%branch; # hash table of key: commit file; value: branch - # Built from ".../CVS/Entries" file of directory - # currently being examined - -# ---------------------------------------------------------------- get CVSROOT -$cvsroot = $ENV{'CVSROOT'}; -die "Must set CVSROOT\n" if !$cvsroot; -if ($cvsroot =~ /:([\/\w]*)$/) { # Filter ":pserver:", ":local:"-type prefixes - $cvsroot = $1; -} - -# ------------------------------------------------------------- set file paths -$entries = "CVS/Entries"; # client-side file??? -$cvsaclfile = $cvsroot . "/CVSROOT/cvsacl"; -$restrictfile = $cvsroot . "/CVSROOT/restrict_msg"; -$restrictlog = $cvsroot . "/CVSROOT/restrict_log"; - -# --------------------------------------------------------------- process args -$user_name = processArgs(\@ARGV); - -print("$$ \@ARGV after processArgs is: @ARGV.\n") if $debug; -print("$$ ========== Begin $PROGRAM_NAME for \"$ARGV[0]\" repository. ========== \n") if $debug; - -# --------------------------------------------------------------- filter @ARGV -eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';" - while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV)); -exit 255 if $die; # process any variable=value switches - -print("$$ \@ARGV after shift processing contains:",join("\, ",@ARGV),".\n") if $debug; - -# ---------------------------------------------------------------- get cvsroot -($repository = shift) =~ s:^$cvsroot/::; -grep($_ = $repository . '/' . $_, @ARGV); - -print("$$ \$cvsroot is: $cvsroot.\n") if $debug; -print "$$ Repos: $repository\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug; - -$exit_val = 0; # presume good exit value for commit - -# ---------------------------------------------------------------------------- -# ---------------------------------- create hash table $branch{file -> branch} -# ---------------------------------------------------------------------------- - -# Here's a typical Entries file: -# -# /checkoutlist/1.4/Wed Feb 4 23:51:23 2004// -# /cvsacl/1.3/Tue Feb 24 23:05:43 2004// -# ... -# /verifymsg/1.1/Fri Mar 16 19:56:24 2001// -# D/backup//// -# D/temp//// - -open(ENTRIES, $entries) || die("Cannot open $entries.\n"); -print("$$ File / Branch\n") if $debug; -my $i = 0; -while(<ENTRIES>) { - chop; - next if /^\s*$/; # Skip blank lines - $i = $i + 1; - if (m| - / # 1st slash - ([\w.-]*) # file name -> $1 - / # 2nd slash - .* # revision number - / # 3rd slash - .* # date and time - / # 4th slash - .* # keyword - / # 5th slash - T? # 'T' constant - (\w*) # branch -> #2 - |x) { - $branch{$repository . '/' . $1} = ($2) ? $2 : "HEAD"; - print "$$ CVS Entry $i: $1/$2\n" if $debug; - } -} -close(ENTRIES); - -# ---------------------------------------------------------------------------- -# ------------------------------------- evaluate each active line from 'cvsacl' -# ---------------------------------------------------------------------------- -open (CVSACL, $cvsaclfile) || exit(0); # It is ok for cvsacl file not to exist -while (<CVSACL>) { - chop; - next if /^\s*\#/; # skip comments - next if /^\s*$/; # skip blank lines - # --------------------------------------------- parse current 'cvsacl' line - print("$$ ==========\n$$ Processing \'cvsacl\' line: $_.\n") if $debug; - ($cvsacl_flag, $cvsacl_userIds, $cvsacl_modules, $cvsacl_branches) = split(/[\s,]*\|[\s,]*/, $_); - - # ------------------------------ Validate 'allow' or 'deny' line prefix - if ($cvsacl_flag !~ /^allow/ && $cvsacl_flag !~ /^deny/) { - print ("Bad cvsacl line: $_\n") if $debug; - $log_text = sprintf "Bad cvsacl line: %s", $_; - write_restrictlog_record($log_text); - next; - } - - # -------------------------------------------------- init loop match flags - $user_match = 0; - %repository_matches = (); - - # ------------------------------------------------------------------------ - # ---------------------------------------------------------- user matching - # ------------------------------------------------------------------------ - # $user_name considered "in user list" if actually in list or is NULL - $user_match = (!$cvsacl_userIds || grep ($_ eq $user_name, split(/[\s,]+/,$cvsacl_userIds))); - print "$$ \$user_name: $user_name \$user_match match flag is: $user_match.\n" if $debug; - if (!$user_match) { - next; # no match, skip to next 'cvsacl' line - } - - # ------------------------------------------------------------------------ - # ---------------------------------------------------- repository matching - # ------------------------------------------------------------------------ - if (!$cvsacl_modules) { # blank module list = all modules - if (!$cvsacl_branches) { # blank branch list = all branches - print("$$ Adding all modules to \%repository_matches; null " . - "\$cvsacl_modules and \$cvsacl_branches.\n") if $debug; - for $commit_object (@ARGV) { - $repository_matches{$commit_object} = [$branch{$commit_object}, $cvsacl_modules]; - print("$$ \$repository_matches{$commit_object} = " . - "[$branch{$commit_object}, $cvsacl_modules].\n") if $debug; - } - } - else { # need to check for repository match - @branch_list = split (/[\s,]+/,$cvsacl_branches); - print("$$ Branches from \'cvsacl\' record: ", join(", ",@branch_list),".\n") if $debug; - for $commit_object (@ARGV) { - if (grep($branch{$commit_object}, @branch_list)) { - $repository_matches{$commit_object} = [$branch{$commit_object}, $cvsacl_modules]; - print("$$ \$repository_matches{$commit_object} = " . - "[$branch{$commit_object}, $cvsacl_modules].\n") if $debug; - } - } - } - } - else { - # ----------------------------------- check every argument combination - # parse 'cvsacl' modules to array - my @module_list = split(/[\s,]+/,$cvsacl_modules); - # ------------- Check all modules in list for either file or directory - my $fileType = ""; - if (($fileType = checkFileness(@module_list)) eq "") { - next; # skip bad file types - } - # ---------- Check each combination of 'cvsacl' modules vs. @ARGV files - print("$$ Checking matches for \@module_list: ", join("\, ",@module_list), ".\n") if $debug; - # loop thru all command-line commit objects - for $commit_object (@ARGV) { - # loop thru all modules on 'cvsacl' line - for $cvsacl_module (@module_list) { - print("$$ Is \'cvsacl\': $cvsacl_modules pattern in: \@ARGV " . - "\$commit_object: $commit_object?\n") if $debug; - # Do match of beginning of $commit_object - checkModuleMatch($fileType, $commit_object, $cvsacl_module); - } # end for commit objects - } # end for cvsacl modules - } # end if - - print("$$ Matches for: \%repository_matches: ", join("\, ", (keys %repository_matches)), ".\n") if $debug; - - # ------------------------------------------------------------------------ - # ----------------------------------------------------- setting exit value - # ------------------------------------------------------------------------ - if ($user_match && %repository_matches) { - print("$$ An \"$cvsacl_flag\" match on User(s): $cvsacl_userIds; Module(s):" . - " $cvsacl_modules; Branch(es): $cvsacl_branches.\n") if $debug; - if ($cvsacl_flag eq "deny") { - # Add all matches to the hash of restricted modules - foreach $commitFile (keys %repository_matches) { - print("$$ Adding \%repository_matches entry: $commitFile.\n") if $debug; - $restricted_entries{$commitFile} = $repository_matches{$commitFile}[0]; - } - } - else { - # Remove all matches from the restricted modules hash - foreach $commitFile (keys %repository_matches) { - print("$$ Removing \%repository_matches entry: $commitFile.\n") if $debug; - delete $restricted_entries{$commitFile}; - } - } - } - print "$$ ==== End of processing for \'cvsacl\' line: $_.\n" if $debug; -} -close(CVSACL); - -# ---------------------------------------------------------------------------- -# --------------------------------------- determine final 'commit' disposition -# ---------------------------------------------------------------------------- -if (%restricted_entries) { # any restricted entries? - $exit_val = 1; # don't commit - print("**** Access denied: Insufficient authority for user: '$user_name\' " . - "to commit to \'$repository\'.\n**** Contact CVS Administrators if " . - "you require update access to these directories or files.\n"); - print("**** file(s)/dir(s) restricted were:\n\t", join("\n\t",keys %restricted_entries), "\n"); - printOptionalRestrictionMessage(); - write_restrictlog(); -} -elsif (!$exit_val && $debug) { - print "**** Access allowed: Sufficient authority for commit.\n"; -} - -print "$$ ==== \$exit_val = $exit_val\n" if $debug; -exit($exit_val); - -# ---------------------------------------------------------------------------- -# -------------------------------------------------------------- end of "main" -# ---------------------------------------------------------------------------- - - -# ---------------------------------------------------------------------------- -# -------------------------------------------------------- process script args -# ---------------------------------------------------------------------------- -sub processArgs { - -# This subroutine is passed a reference to @ARGV. - -# If @ARGV contains a "-u" entry, use that as the effective userId. In this -# case, the userId is the client-side userId that has been passed to this -# script by the commit_prep script. (This is why the commit_prep script must -# be placed *before* the cvs_acls script in the commitinfo admin file.) - -# Otherwise, pull the userId from the server-side environment. - - my $userId = ""; - my ($argv) = shift; # pick up ref to @ARGV - my @argvClone = (); # immutable copy for foreach loop - for ($i=0; $i<(scalar @{$argv}); $i++) { - $argvClone[$i]=$argv->[$i]; - } - - print("$$ \@_ to processArgs is: @_.\n") if $debug; - - # Parse command line arguments (file list is seen as one arg) - foreach $arg (@argvClone) { - print("$$ \$arg for processArgs loop is: $arg.\n") if $debug; - # Set $debug flag? - if ($arg eq '-d') { - shift @ARGV; - $debug = 1; - print("$$ \$debug flag set on.\n") if $debug; - print STDERR "Debug turned on...\n"; - } - # Passing in a client-side userId? - elsif ($arg eq '-u') { - shift @ARGV; - $userId = shift @ARGV; - print("$$ client-side \$userId set to: $userId.\n") if $debug; - } - # An override for the default restrictlog file? - elsif ($arg eq '-f') { - shift @ARGV; - $restrictlog = shift @ARGV; - } - else { - next; - } - } - - # No client-side userId passed? then get from server env - if (!$userId) { - $userId = $ENV{"USER"} if !($userId = $ENV{"LOGNAME"}); - print("$$ server-side \$userId set to: $userId.\n") if $debug; - } - - print("$$ processArgs returning \$userId: $userId.\n") if $debug; - return $userId; - -} - - -# ---------------------------------------------------------------------------- -# --------------------- Check all modules in list for either file or directory -# ---------------------------------------------------------------------------- -sub checkFileness { - -# Module patterns on the 'cvsacl' record can be files or directories. -# If it's a directory, we pattern-match the directory name from 'cvsacl' -# against the left side of the committed filename to see if the file is in -# that hierarchy. By contrast, files use an explicit match. If the entries -# are neither files nor directories, then the cvsacl file has been set up -# incorrectly; we return a "" and the caller skips that line as invalid. -# -# This function determines whether the entries on the 'cvsacl' record are all -# directories or all files; it cannot be a mixture. This restriction put in -# to simplify the logic (without taking away much functionality). - - my @module_list = @_; - print("$$ Checking \"fileness\" or \"dir-ness\" for \@module_list entries.\n") if $debug; - print("$$ Entries are: ", join("\, ",@module_list), ".\n") if $debug; - my $filetype = ""; - for $cvsacl_module (@module_list) { - my $reposDirName = $cvsroot . '/' . $cvsacl_module; - my $reposFileName = $reposDirName . "\,v"; - print("$$ In checkFileness: \$reposDirName: $reposDirName; \$reposFileName: $reposFileName.\n") if $debug; - if (((-d $reposDirName) && ($filetype eq "file")) || ((-f $reposFileName) && ($filetype eq "dir"))) { - print("Can\'t mix files and directories on single \'cvsacl\' file record; skipping entry.\n"); - print(" Please contact a CVS administrator.\n"); - $filetype = ""; - last; - } - elsif (-d $reposDirName) { - $filetype = "dir"; - print("$$ $reposDirName is a directory.\n") if $debug; - } - elsif (-f $reposFileName) { - $filetype = "file"; - print("$$ $reposFileName is a regular file.\n") if $debug; - } - else { - print("***** Item to commit was neither a regular file nor a directory.\n"); - print("***** Current \'cvsacl\' line ignored.\n"); - print("***** Possible problem with \'cvsacl\' admin file. Please contact a CVS administrator.\n"); - $filetype = ""; - $text = sprintf("Module entry on cvsacl line: %s is not a valid file or directory.\n", $cvsacl_module); - write_restrictlog_record($text); - last; - } # end if - } # end for - - print("$$ checkFileness will return \$filetype: $filetype.\n") if $debug; - return $filetype; -} - - -# ---------------------------------------------------------------------------- -# ----------------------------------------------------- check for module match -# ---------------------------------------------------------------------------- -sub checkModuleMatch { - -# This subroutine checks for a match between the directory or file pattern -# specified in the 'cvsacl' file (i.e., $cvsacl_modules) versus the commit file -# objects passed into the script via @ARGV (i.e., $commit_object). - -# The directory pattern only has to match the beginning portion of the commit -# file's name for a match since all files under that directory are considered -# a match. File patterns must exactly match. - -# Since (theoretically, if not normally in practice) a working directory can -# contain a mixture of files from different branches, this routine checks to -# see if there is also a match on branch before considering the file -# comparison a match. - - my $match_flag = ""; - - print("$$ \@_ in checkModuleMatch is: @_.\n") if $debug; - my ($type,$commit_object,$cvsacl_module) = @_; - - if ($type eq "file") { # Do exact file match of $commit_object - if ($commit_object eq $cvsacl_module) { - $match_flag = "file"; - } # Do dir match at beginning of $commit_object - } - elsif ($commit_object =~ /^$cvsacl_module\//) { - $match_flag = "dir"; - } - - if ($match_flag) { - print("$$ \$repository: $repository matches \$commit_object: $commit_object.\n") if $debug; - if (!$cvsacl_branches) { # empty branch pattern matches all - print("$$ blank \'cvsacl\' branch matches all commit files.\n") if $debug; - $repository_matches{$commit_object} = [$branch{$commit_object}, $cvsacl_module]; - print("$$ \$repository_matches{$commit_object} = [$branch{$commit_object}, $cvsacl_module].\n") if $debug; - } - else { # otherwise check branch hash table - @branch_list = split (/[\s,]+/,$cvsacl_branches); - print("$$ Branches from \'cvsacl\' record: ", join(", ",@branch_list),".\n") if $debug; - if (grep(/$branch{$commit_object}/, @branch_list)) { - $repository_matches{$commit_object} = [$branch{$commit_object}, $cvsacl_module]; - print("$$ \$repository_matches{$commit_object} = [$branch{$commit_object}, " . - "$cvsacl_module].\n") if $debug; - } - } - } - -} - -# ---------------------------------------------------------------------------- -# ------------------------------------------------------- check for file match -# ---------------------------------------------------------------------------- -sub printOptionalRestrictionMessage { - -# This subroutine optionally prints site-specific file restriction information -# whenever a restriction condition is met. If the file 'restrict_msg' does -# not exist, the routine immediately exits. If there is a 'restrict_msg' file -# then all the contents are printed at the end of the standard restriction -# message. - -# As seen from examining the definition of $restrictfile, the default filename -# is: $CVSROOT/CVSROOT/restrict_msg. - - open (RESTRICT, $restrictfile) || return; # It is ok for cvsacl file not to exist - while (<RESTRICT>) { - chop; - # print out each line - print("**** $_\n"); - } - -} - -# ---------------------------------------------------------------------------- -# ---------------------------------------------------------- write log message -# ---------------------------------------------------------------------------- -sub write_restrictlog { - -# This subroutine iterates through the list of restricted entries and logs -# each one to the error logfile. - - # write each line in @text out separately - foreach $commitfile (keys %restricted_entries) { - $log_text = sprintf "Commit attempt by: %s for: %s on branch: %s", - $user_name, $commitfile, $branch{$commitfile}; - write_restrictlog_record($log_text); - } - -} - -# ---------------------------------------------------------------------------- -# ---------------------------------------------------------- write log message -# ---------------------------------------------------------------------------- -sub write_restrictlog_record { - -# This subroutine receives a scalar string and writes it out to the -# $restrictlog file as a separate line. Each line is prepended with the date -# and time in the format: "2004/01/30 12:00:00 ". - - $text = shift; - - # return quietly if there is a problem opening the log file. - open(FILE, ">>$restrictlog") || return; - - (@time) = localtime(); - - # write each line in @text out separately - $log_record = sprintf "%04d/%02d/%02d %02d:%02d:%02d %s.\n", - $time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0], $text; - print FILE $log_record; - print("$$ restrict_log record being written: $log_record to $restrictlog.\n") if $debug; - - close(FILE); -} |