~ubuntu-branches/ubuntu/intrepid/git-core/intrepid-security

« back to all changes in this revision

Viewing changes to contrib/hooks/update-paranoid

  • Committer: Package Import Robot
  • Author(s): Gerrit Pape
  • Date: 2007-10-04 08:27:01 UTC
  • mfrom: (1.1.23)
  • Revision ID: package-import@ubuntu.com-20071004082701-rsd058ontoqz4i30
Tags: 1:1.5.3.4-1
new upstream point release (closes: #445188).

Show diffs side-by-side

added added

removed removed

Lines of Context:
102
102
my $repository_name;
103
103
my %user_committer;
104
104
my @allow_rules;
 
105
my @path_rules;
 
106
my %diff_cache;
105
107
 
106
108
sub deny ($) {
107
109
        print STDERR "-Deny-    $_[0]\n" if $debug;
118
120
        print STDERR "-Info-    $_[0]\n" if $debug;
119
121
}
120
122
 
121
 
sub parse_config ($$) {
122
 
        my ($data, $fn) = @_;
123
 
        info "Loading $fn";
124
 
        open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn);
 
123
sub git_value (@) {
 
124
        open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;
 
125
}
 
126
 
 
127
sub match_string ($$) {
 
128
        my ($acl_n, $ref) = @_;
 
129
           ($acl_n eq $ref)
 
130
        || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
 
131
        || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:);
 
132
}
 
133
 
 
134
sub parse_config ($$$$) {
 
135
        my $data = shift;
 
136
        local $ENV{GIT_DIR} = shift;
 
137
        my $br = shift;
 
138
        my $fn = shift;
 
139
        info "Loading $br:$fn";
 
140
        open(I,'-|','git','cat-file','blob',"$br:$fn");
125
141
        my $section = '';
126
142
        while (<I>) {
127
143
                chomp;
128
144
                if (/^\s*$/ || /^\s*#/) {
129
145
                } elsif (/^\[([a-z]+)\]$/i) {
130
 
                        $section = $1;
 
146
                        $section = lc $1;
131
147
                } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) {
132
 
                        $section = "$1.$2";
 
148
                        $section = join('.',lc $1,$2);
133
149
                } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) {
134
 
                        push @{$data->{"$section.$1"}}, $2;
 
150
                        push @{$data->{join('.',$section,lc $1)}}, $2;
135
151
                } else {
136
 
                        deny "bad config file line $. in $fn";
 
152
                        deny "bad config file line $. in $br:$fn";
137
153
                }
138
154
        }
139
155
        close I;
202
218
        }
203
219
}
204
220
 
205
 
sub git_value (@) {
206
 
        open(T,'-|','git',@_); local $_ = <T>; chop; close T;
207
 
        $_;
 
221
sub load_diff ($) {
 
222
        my $base = shift;
 
223
        my $d = $diff_cache{$base};
 
224
        unless ($d) {
 
225
                local $/ = "\0";
 
226
                my %this_diff;
 
227
                if ($base =~ /^0{40}$/) {
 
228
                        open(T,'-|','git','ls-tree',
 
229
                                '-r','--name-only','-z',
 
230
                                $new) or return undef;
 
231
                        while (<T>) {
 
232
                                chop;
 
233
                                $this_diff{$_} = 'A';
 
234
                        }
 
235
                        close T or return undef;
 
236
                } else {
 
237
                        open(T,'-|','git','diff-tree',
 
238
                                '-r','--name-status','-z',
 
239
                                $base,$new) or return undef;
 
240
                        while (<T>) {
 
241
                                my $op = $_;
 
242
                                chop $op;
 
243
 
 
244
                                my $path = <T>;
 
245
                                chop $path;
 
246
 
 
247
                                $this_diff{$path} = $op;
 
248
                        }
 
249
                        close T or return undef;
 
250
                }
 
251
                $d = \%this_diff;
 
252
                $diff_cache{$base} = $d;
 
253
        }
 
254
        return $d;
208
255
}
209
256
 
210
257
deny "No GIT_DIR inherited from caller" unless $git_dir;
231
278
        && $ref =~ m,^heads/,
232
279
        && $old eq git_value('merge-base',$old,$new));
233
280
 
234
 
# Load the user's ACL file.
 
281
# Load the user's ACL file. Expand groups (user.memberof) one level.
235
282
{
236
283
        my %data = ('user.committer' => []);
237
 
        parse_config(\%data, "$acl_branch:users/$this_user.acl");
 
284
        parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl");
 
285
 
 
286
        %data = (
 
287
                'user.committer' => $data{'user.committer'},
 
288
                'user.memberof' => [],
 
289
        );
 
290
        parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl");
 
291
 
238
292
        %user_committer = map {$_ => $_} @{$data{'user.committer'}};
239
 
        my $rules = $data{"repository.$repository_name.allow"} || [];
 
293
        my $rule_key = "repository.$repository_name.allow";
 
294
        my $rules = $data{$rule_key} || [];
 
295
 
 
296
        foreach my $group (@{$data{'user.memberof'}}) {
 
297
                my %g;
 
298
                parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl");
 
299
                my $group_rules = $g{$rule_key};
 
300
                push @$rules, @$group_rules if $group_rules;
 
301
        }
 
302
 
 
303
RULE:
240
304
        foreach (@$rules) {
241
 
                if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
 
305
                while (/\${user\.([a-z][a-zA-Z0-9]+)}/) {
 
306
                        my $k = lc $1;
 
307
                        my $v = $data{"user.$k"};
 
308
                        next RULE unless defined $v;
 
309
                        next RULE if @$v != 1;
 
310
                        next RULE unless defined $v->[0];
 
311
                        s/\${user\.$k}/$v->[0]/g;
 
312
                }
 
313
 
 
314
                if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) {
 
315
                        my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);
 
316
                        $ops =~ s/ //g;
 
317
                        $pth =~ s/\\\\/\\/g;
 
318
                        $ref =~ s/\\\\/\\/g;
 
319
                        push @path_rules, [$ops, $pth, $ref, $bst];
 
320
                } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) {
 
321
                        my ($ops, $pth, $ref) = ($1, $2, $3);
 
322
                        $ops =~ s/ //g;
 
323
                        $pth =~ s/\\\\/\\/g;
 
324
                        $ref =~ s/\\\\/\\/g;
 
325
                        push @path_rules, [$ops, $pth, $ref, $old];
 
326
                } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
242
327
                        my $ops = $1;
243
328
                        my $ref = $2;
244
329
                        $ops =~ s/ //g;
272
357
        next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.
273
358
        next unless $acl_n;
274
359
        next unless $op =~ /^[$acl_ops]$/;
275
 
 
276
 
        grant "Allowed by: $acl_ops for $acl_n"
277
 
        if (
278
 
           ($acl_n eq $ref)
279
 
        || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
280
 
        || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:)
281
 
        );
 
360
        next unless match_string $acl_n, $ref;
 
361
 
 
362
        # Don't test path rules on branch deletes.
 
363
        #
 
364
        grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D';
 
365
 
 
366
        # Aggregate matching path rules; allow if there aren't
 
367
        # any matching this ref.
 
368
        #
 
369
        my %pr;
 
370
        foreach my $p_entry (@path_rules) {
 
371
                my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
 
372
                next unless $p_ref;
 
373
                push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;
 
374
        }
 
375
        grant "Allowed by: $acl_ops for $acl_n" unless %pr;
 
376
 
 
377
        # Allow only if all changes against a single base are
 
378
        # allowed by file path rules.
 
379
        #
 
380
        my @bad;
 
381
        foreach my $p_bst (keys %pr) {
 
382
                my $diff_ref = load_diff $p_bst;
 
383
                deny "Cannot difference trees." unless ref $diff_ref;
 
384
 
 
385
                my %fd = %$diff_ref;
 
386
                foreach my $p_entry (@{$pr{$p_bst}}) {
 
387
                        my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
 
388
                        next unless $p_ops =~ /^[AMD]+$/;
 
389
                        next unless $p_n;
 
390
 
 
391
                        foreach my $f_n (keys %fd) {
 
392
                                my $f_op = $fd{$f_n};
 
393
                                next unless $f_op;
 
394
                                next unless $f_op =~ /^[$p_ops]$/;
 
395
                                delete $fd{$f_n} if match_string $p_n, $f_n;
 
396
                        }
 
397
                        last unless %fd;
 
398
                }
 
399
 
 
400
                if (%fd) {
 
401
                        push @bad, [$p_bst, \%fd];
 
402
                } else {
 
403
                        # All changes relative to $p_bst were allowed.
 
404
                        #
 
405
                        grant "Allowed by: $acl_ops for $acl_n diff $p_bst";
 
406
                }
 
407
        }
 
408
 
 
409
        foreach my $bad_ref (@bad) {
 
410
                my ($p_bst, $fd) = @$bad_ref;
 
411
                print STDERR "\n";
 
412
                print STDERR "Not allowed to make the following changes:\n";
 
413
                print STDERR "(base: $p_bst)\n";
 
414
                foreach my $f_n (sort keys %$fd) {
 
415
                        print STDERR "  $fd->{$f_n} $f_n\n";
 
416
                }
 
417
        }
 
418
        deny "You are not permitted to $op $ref";
282
419
}
283
420
close A;
284
421
deny "You are not permitted to $op $ref";