compile: (large changes) parse delegated fragments if any

[Note: this is a fairly involved commit, compared to most of the others.
    See doc/5-delegation.mkd for a user-level feature description.]

    parse delegated config fragments (found as conf/fragments/*.conf).  Any
    repos being referenced within a fragment config *must* belong to the
    "@group" with the same name as the fragment.

    That is, a fragment called conf/fragments/abc.conf can only refer to repos
    that are members of the "@abc" repo group.  It cannot specify access
    control for any other repos.  If it does, those settings are ignored, and
    a warning message is produced.

    since the delegated config must have the flexibility of (re-)defining
    group names for internal convenience, and since all such definitions go
    into the same "groups" hash, it is quite easy for conf/fragments/abc.conf
    to write in its own (re-)definition of "@abc"!  That would be a neat
    little security hole :)

    The way to close it is to consider only members of the "@abc" groupset
    defined in the main ("master") config file for this purpose.
This commit is contained in:
Sitaram Chamarty 2009-10-04 09:56:40 +05:30
parent fa5567f22c
commit 616d8a5f7d

View file

@ -121,7 +121,14 @@ sub expand_list
sub parse_conf_file
{
my ($conffile) = @_;
my ($conffile, $fragment) = @_;
# the second arg, $fragment, is passed in as "master" when parsing the
# main config, and the fragment name when parsing a fragment. In the
# latter case, the parser uses that information to ignore (and warn about)
# any repos in the fragment that are not members of the "repo group" of
# the same name.
my %ignored = ();
my $conf_fh = wrap_open( "<", $conffile );
# the syntax is fairly simple, so we parse it inline
@ -142,7 +149,9 @@ sub parse_conf_file
# user or repo groups
if (/^(@\S+) = (.*)/)
{
do { $groups{$1}{$_} = 1 } for ( expand_list( split(' ', $2) ) );
# store the members of each group as hash key. Keep track of when
# the group was *first* created by using $fragment as the *value*
do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) );
# again, we take the more "relaxed" pattern
die "$ATTN bad group $1\n" unless $1 =~ $REPONAME_PATT;
}
@ -174,6 +183,22 @@ sub parse_conf_file
# ok, we can finally populate the %repos hash
for my $repo (@repos) # each repo in the current stanza
{
# if we're processing a delegated config file (not the master
# config), and if that fragment name is not the same as the
# current repo
if ($fragment ne 'master' and $fragment ne $repo)
{
# then the fragment must be a group name and the repo
# being processed must be a member of that "@group".
# Also, the value of the hash for that combination must be
# "master", signifying a group created in the master
# config file and not in one of the delegates
unless ( ($groups{"\@$fragment"}{$repo} || '') eq 'master')
{
$ignored{$fragment}{$repo} = 1;
next;
}
}
for my $user (@users)
{
$user_list{$user}++; # only to catch lint, see later
@ -195,9 +220,25 @@ sub parse_conf_file
die "$ATTN can't make head or tail of '$_'\n";
}
}
for my $ig (sort keys %ignored)
{
warn "\n\t\t***** WARNING *****\n" .
"\t$ig.conf attempting to set access for " .
join (", ", sort keys %{ $ignored{$ig} }) . "\n";
}
}
parse_conf_file($GL_CONF);
# parse the main config file
parse_conf_file($GL_CONF, 'master');
# parse any delegated fragments
wrap_chdir($GL_ADMINDIR);
for my $fragment_file (glob("conf/fragments/*.conf"))
{
my $fragment = $fragment_file;
$fragment =~ s/^conf\/fragments\/(.*).conf$/$1/;
parse_conf_file($fragment_file, $fragment);
}
my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED );
print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]);