This file is indexed.

/usr/bin/s3acl is in libnet-amazon-s3-tools-perl 0.08-2.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
#!/usr/bin/env perl

# Copyright (C) 2007,2008 by Mark Atwood <mark@fallenpegasus.com>.
#
# This module is not an official Amazon product or service.  Information
# used to create this module was obtained only from publicly available
# information, mainly from the published Amazon documentation.
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 2.1 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
# and the GNU Lesser General Public License along with this program.
# If not, see <http://www.gnu.org/licenses/>.

use warnings;
use strict;
use Getopt::Long;
use Pod::Usage;
use Net::Amazon::S3;
use Net::Amazon::S3::Bucket;
use XML::Writer;
use XML::LibXML;
use Getopt::ArgvFile qw/argvFile/;
use File::HomeDir;

my $aws_access_key_id = $ENV{AWS_ACCESS_KEY_ID};
my $aws_secret_access_key = $ENV{AWS_ACCESS_KEY_SECRET};
my $opt_verbose =0;
my $opt_help =0;
my $opt_man =0;
my $opt_secure =0;

my $opt_xml =0;
my $opt_get =0;
my $opt_set =0;
my $opt_clear =0;
my $opt_add =0;
my $opt_del =0;
my $opt_canned =0;

# get the options from the users ~/.s3-tools file, if it exists
my $users_config = File::HomeDir->my_home() . '/.s3-tools';
if (-e $users_config) {
    unshift @ARGV, '@' . $users_config;
}
argvFile();

GetOptions('help|?' => \$opt_help, 'man' => \$opt_man,
	   'verbose+' => \$opt_verbose,
	   'access-key=s' => \$aws_access_key_id,
	   'secret-key=s' => \$aws_secret_access_key,
	   'secure' => \$opt_secure,
	   'xml' => \$opt_xml,
	   'get' => \$opt_get,
	   'set' => \$opt_set,
	   'clear' => \$opt_clear,
	   'add=s' => \$opt_add,
	   'del=s' => \$opt_del,
	   'acl-short=s' => \$opt_canned,
	   )
    or pod2usage(2);
pod2usage(1) if $opt_help;
pod2usage(-exitstatus => 0, -verbose => 2) if $opt_man;

use constant AMZN_GROUP_USERS =>
    'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
use constant AMZN_GROUP_WORLD =>
    'http://acs.amazonaws.com/groups/global/AllUsers';

my $s3p = { aws_access_key_id => $aws_access_key_id,
	    aws_secret_access_key => $aws_secret_access_key };
$s3p->{secure} = $opt_secure
    if ($opt_secure);
my $s3 = Net::Amazon::S3->new($s3p);
($s3) or die("$0: fail Net::Amazon::S3: $!, stopped");

my $bkts = make_bucketlist();

if ($opt_get) {
    foreach my $b (@{$bkts}) {
	my $x = get_xmlacl($b);
	unless ($x) {
	    printf STDERR "cant get acl of %s: %s", $b->{arg}, $b->{err};
	    next;
	}
	if ($opt_xml) {
	    print $x, "\n";
	} else {
	    my $r = parse_xmlacl($x,
				 $b->{bucketname},
				 $b->{itemkey});
	    print_acl($r);
	}
    }
} elsif ($opt_set) {
    if ($opt_canned) {

	foreach my $b (@{$bkts}) {
	    put_cannedacl($b, $opt_canned);
	}

    } elsif ($opt_xml) {
	my $x;
	# read the xml acl from stdin
	{
	    local $/;   # Set input to "slurp" mode.
	    $x = <STDIN>;
	}
	# blindly assume that is a correct XML ACL
	# apply it to each thing
	foreach my $b (@{$bkts}) {
	    put_xmlacl($b, $x);
	}
    } else {
	foreach my $b (@{$bkts}) {

	    # get the current acl
	    my $x = get_xmlacl($b);
	    unless ($x) {
		printf STDERR "fail %s: %s", $b->{arg}, $b->{err};
		next;
	    }

	    # parse it
	    my $r = parse_xmlacl($x,
				 $b->{bucketname},
				 $b->{itemkey});

	    # process opt_clear
	    if ($opt_clear) {
		$r->{Grant} = [];
	    }

	    # process opt_del
	    if ($opt_del) {
		foreach (split(',', $opt_del)) {
		    my($asu, $asp) = split(':', $_, 2);
		    next unless ($asu && $asp);
		    foreach my $gr (@{$r->{Grant}}) {
			if ((($asu eq "ANY")
			     || (($asu eq "OWNER")
				 && ($gr->{ID} eq $r->{Owner}->{ID}))
			     || (($asu eq "USERS")
				 && ($gr->{URI} eq AMZN_GROUP_USERS))
			     || (($asu eq "WORLD")
				 && ($gr->{URI} eq AMZN_GROUP_WORLD))
			     || (($asu =~ m/^\<(.*)\>$/)
				 && ($gr->{URI} eq $1))
			     || ($asu eq $gr->{ID}))
			    &&
			    (($asp eq "ANY")
			     || ($asp eq $gr->{Permission}))) {
			    # rather than mutate the @{$r->{Grant}} list,
			    #  put in a "deleted" marker, and then skip
			    #  this grant when coverting back to XML
			    $gr->{deleted} = 1;
			}
		    }
		}
	    }

	    # process opt_add
	    if ($opt_add) {
		foreach (split(',', $opt_add)) {
		    my($asu, $asp) = split(':', $_, 2);
		    next unless ($asu && $asp);
		    my $gr;  # the grant we will build and then add
		    if ($asu eq "OWNER") {
			$gr = { type => 'CanonicalUser', 
				ID => $r->{Owner}->{ID},
				Permission => $asp };
		    } elsif ($asu eq "USERS") {
			$gr = { type => 'Group',
				URI => AMZN_GROUP_USERS,
				Permission => $asp };
		    } elsif ($asu eq "WORLD") {
			$gr = { type => 'Group',
				URI => AMZN_GROUP_WORLD,
				Permission => $asp };
		    } elsif ($asu =~ m/^\<(.*)\>$/) {
			$gr = { type => 'Group',
				URI => $1,
				Permission => $asp };
		    } elsif ($asu =~ m/\@/) {
			$gr = { type => 'AmazonCustomerByEmail',
				EmailAddress => $asu,
				Permission => $asp };
		    } else {
			$gr = { type => 'CanonicalUser',
				ID => $asu,
				Permission => $asp };
		    }
		    push(@{$r->{Grant}}, $gr);

		}
	    }

	    # convert the acl back to xml
	    my $xn = gen_xmlacl($r);

	    # apply it to the thing
	    put_xmlacl($b, $xn);
	}
    }
}

sub make_bucketlist
{
    my @B;
    my ($bn, $ik, $b);
    foreach my $arg (@ARGV) {
	$b = undef;
	$b->{arg} = $arg;
	($bn, $ik) = split('/', $arg, 2);
	$b->{bucketname} = $bn;
	$b->{itemkey} = $ik
	    if ($ik);
	$b->{bucketobject} = $s3->bucket($bn);
	push @B, $b;
    }
    return \@B;
}

sub put_cannedacl
{
    my $b = shift;
    my $a = shift;
    my $rv;

    my $p = { acl_short => $a };
    if ($b->{itemkey}) { $p->{key} = $b->{itemkey}; }

    eval { $rv = $b->{bucketobject}->set_acl($p); };
    if ($@) {
	$b->{err} = $@;
	return undef;
    } elsif (!$rv && !defined($b->{err})) {
	$b->{err} = "fail set_acl";
    }
    return $rv;
}

sub put_xmlacl
{
    my $b = shift;
    my $x = shift;
    my $rv;
    my $p = { acl_xml => $x };
    if ($b->{itemkey}) { $p->{key} = $b->{itemkey}; }

    eval { $rv = $b->{bucketobject}->set_acl($p); };
    if ($@) {
	$b->{err} = $@;
	return undef;
    } elsif (!$rv && !defined($b->{err})) {
	$b->{err} = "fail set_acl";
    }
    return $rv;
}

sub get_xmlacl
{
    my $b = shift;
    my $x;

    eval { $x = $b->{bucketobject}->get_acl($b->{itemkey}); };
    if ($@) {
	$b->{err} = $@;
	return undef;
    } elsif (!$x && !defined($b->{err})) {
	$b->{err} = "fail get_acl";
    }
    return $x;
}

sub parse_xmlacl
{
    my $xml_acl_raw = shift;
    my $bucket_name = shift;
    my $item_name = shift;

    my $parser = XML::LibXML->new();
    my $doc = $parser->parse_string($xml_acl_raw);
    my $xpc = XML::LibXML::XPathContext->new($doc);
    $xpc->registerNs('s3', 'http://s3.amazonaws.com/doc/2006-03-01/');
    $xpc->registerNs('xsi', 'http://www.w3.org/2001/XMLSchema-instance');

    my $r;

    $r->{bucket} = $bucket_name;
    $r->{item} = $item_name if ($item_name); 

    $r->{Owner}->{ID} =
	$xpc->findvalue("/s3:AccessControlPolicy/s3:Owner/s3:ID");
    $r->{Owner}->{DisplayName} =
	$xpc->findvalue("/s3:AccessControlPolicy/s3:Owner/s3:DisplayName");

    $r->{Grant} = [];

    foreach my $xg ($xpc->findnodes("/s3:AccessControlPolicy/s3:AccessControlList/s3:Grant")) {
	my $g;

	$g->{type}= $xpc->find('./s3:Grantee/@xsi:type', $xg)->to_literal();

	if ($g->{type} eq 'CanonicalUser') {
	    $g->{ID} = $xpc->find('./s3:Grantee/s3:ID', $xg)->to_literal();
	    $g->{DisplayName} = $xpc->find('./s3:Grantee/s3:DisplayName', $xg)->to_literal();
	} elsif ($g->{type} eq 'Group') {
	    $g->{URI} = $xpc->find('./s3:Grantee/s3:URI', $xg)->to_literal();
	} elsif ($g->{type} eq 'AmazonCustomerByEmail') {
	    $g->{EmailAddress} = $xpc->find('./s3:Grantee/s3:EmailAddress', $xg)->to_literal();
	} else {
	    # unknown type
	}
	$g->{Permission} = $xpc->find("./s3:Permission", $xg)->to_literal();

	push(@{$r->{Grant}}, $g);
    }

    return $r;
}

sub parse_grant
{
    my $g = shift;
    my $r;

    $r->{'type'} = $g->{Grantee}->{'xsi:type'};
    foreach ("URI", "ID", "DisplayName", "EmailAddress") {
	if ($g->{Grantee}->{$_}) {
	    $r->{$_} = $g->{Grantee}->{$_};
	}
    }
    $r->{Permission} = $g->{Permission};

    return $r;
}

sub print_acl
{
    my $a = shift;

    print "# bucket: ", $a->{bucket}, "\n";
    print "# item: ", $a->{item}, "\n" if $a->{item};
    print "# owner: ", $a->{Owner}->{ID}, "\n";
    
    foreach my $g (@{$a->{Grant}}) {
	print str_grant($g, $a->{Owner}->{ID}), "\n";
    }
}

sub gen_xmlacl
{
    my $a = shift;
    my $x;

    my $w = XML::Writer->new(OUTPUT => \$x);
    $w->xmlDecl("UTF-8");
    $w->startTag('AccessControlPolicy',
		 'xmlns' => "http://s3.amazonaws.com/doc/2006-03-01/");
    $w->startTag('Owner');
    $w->dataElement('ID', $a->{Owner}->{ID});
    $w->dataElement('DisplayName', $a->{Owner}->{DisplayName});
    $w->endTag();  # Owner

    $w->startTag('AccessControlList');
    foreach my $g (@{$a->{Grant}}) {
	next if $g->{deleted};
	$w->startTag('Grant');
	$w->startTag('Grantee',
		     'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
		     'xsi:type' => $g->{type});
	foreach ("URI", "ID", "DisplayName", "EmailAddress") {
	    if ($g->{$_}) {
		$w->dataElement($_, $g->{$_});
	    }
	}
	$w->endTag();  # Grantee
	$w->dataElement('Permission', $g->{Permission});
	$w->endTag();  # Grant
    }
    $w->endTag();  # AccessControlList
    $w->endTag();  # AccessControlPolicy
    $w->end();

    return $x;
}

sub str_grant
{
    my $g = shift;
    my $owner = shift;
    my $s = "";

    if ($g->{type} eq "CanonicalUser") {
	if ($g->{ID} eq $owner) {
	    $s .= "OWNER";
	} else {
	    $s .= $g->{ID};
	}
    } elsif ($g->{type} eq "Group") {
	if ($g->{URI} eq AMZN_GROUP_WORLD) {
	    $s .= "WORLD";
	} elsif ($g->{URI} eq AMZN_GROUP_USERS) {
	    $s .= "USERS";
	} else {
	    $s .= '<' . $g->{URI} . '>';
	}
    } elsif ($g->{type} eq "AmazonCustomerByEmail") {
	$s .= $g->{EmailAddress};
    } else {
	$s .= '[UNKNOWN GRANTEE TYPE:' . $g->{type} . ']';
    }

    $s .= ':' . $g->{Permission};

    return $s;
}

__END__

=head1 NAME

s3acl - Display or manipulate the ACL of AWS S3 buckets and items

=head1 SYNOPSIS

s3acl [options] [[bucket|bucket/key] ...]

 Options:
   --access-key    AWS Access Key ID
   --secret-key    AWS Secret Access Key
   --get           Output the ACL to STDOUT
     --xml           in raw XML form instead of parsed form
   --set           Modify the ACL
     --clear                remove all grants from the ACL
     --add grant,grant,...  add grants to the ACL
     --del grant,grant,...  remove matching grants from the ACL
     --xml                  apply the XML ACL from STDIN to the item
     --acl-short cannedacl  apply the "canned" ACL to the item
 Environment:
   AWS_ACCESS_KEY_ID
   AWS_ACCESS_KEY_SECRET

=head1 OPTIONS

=over 8

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--verbose>

Output what is being done as it is done.

=item B<--access-key> and B<--secret-key>

Specify the "AWS Access Key Identifiers" for the AWS account.
B<--access-key> is the "Access Key ID", and B<--secret-key> is
the "Secret Access Key".  These are effectively the "username" and
"password" to the AWS account, and should be kept confidential.

The access keys MUST be specified, either via these command line
parameters, or via the B<AWS_ACCESS_KEY_ID> and
B<AWS_ACCESS_KEY_SECRET> environment variables.

Specifying them on the command line overrides the environment
variables.

=item B<--secure>

Uses SSL/TLS HTTPS to communicate with the AWS service, instead of
HTTP.

=item B<--get>

Retrieve and display the ACL for each specified bucket or item.

=item B<--xml>

When used with the B<--get> option, outputs to stdout the raw XML,
instead of parsed format.  This raw XML is documented in the "Amazon
S3 Developer Guide".

If more than one bucket or item is specified, the XML ACL for each
will be output, concatenated together.
This is probably not very useful.

The raw XML output can be used as input for the B<--set> option, like so:

  s3acl --get --xml bucketA | s3acl --set --xml bucketB bucketC

This does not work when specifying more than one bucket or item to the
B<--get> option, because the concatenation of multiple XML ACLs is not
a valid XML ACL.

=item B<--set>

Instead of displaying the ACL, modify it.  An ACL can be modified by
using B<--clear>, B<--add>, and B<--del>, or by using B<--xml>, or by
using B<--acl-short>.

=item B<--clear>

Removes all of the grants from the ACL.  This includes access by the
owner of the bucket or item.  This is done before the B<--add> or
B<--del> options are applied, no matter what order options are specified
on the command line.

It is usually accompanied by the B<--add> option to add some grants
back to the now empty ACL.

=item B<--del>

Remove matching grants from the ACL.  This is done before the B<--add>
option is applied, no matter what order options are specified on the
command line.

Grants are specified in parsed form, and then joined together by
commas with no whitespace.

There is an extension to the parsed grant format. If the grantee is
specified as "ANY", then it matches any and all grantees in the ACL.
If the permission is specified as "ANY", then it matches any
permission.  Thus

  s3acl --set --del ANY:READ mybucket

removes all grants that give READ permission, and

  s3acl --set --del someuserid:ANY mybucket

removes all grants to the user someuserid 

  s3acl --set --del ANY:ANY mybucket

does the same thing as 

  s3acl --set --clear mybucket

Due to a limitation in the semantics of the S3 API, it is not possible
to delete a grantee by email address, only by canonical ID.

=item B<--add>

Add the specified grants to the ACL.  Grants are specified in parsed
form, and then joined together by commas with no whitespace.

It is possible to add the same grant to a bucket or item more than
once.  This is a surprising behavior of the S3 service.

=item B<--xml>

When used with B<--set>, and instead of using <--clear>, <--del>, and
B<--add>, read a raw XML ACL from STDIN, and then apply it to each
given bucket or item.  This will completely overwrite the existing ACL
for each given bucket or item.

=item B<--acl-short>

Instead of using <--clear>, <--del>, and B<--add>, or using <--xml>,
apply a "canned ACL" to each given bucket or item.  This will
completely overwrite the existing ACL for each given bucket or item.

The following canned ACLs are currently defined by S3:

=over 8

=item B<private>

Owner gets C<FULL_CONTROL>.
No one else has any access rights.
This is the default for newly created buckets and items.

=item B<public-read>

Owner gets C<FULL_CONTROL>.
The anonymous principal is granted C<READ> access.

=item B<public-read-write>

Owner gets C<FULL_CONTROL>.

The anonymous principal is granted C<READ> and C<WRITE> access.
This is a useful policy to apply to a bucket, if you intend for any
anonymous user to PUT objects into the bucket.

=item B<authenticated-read>

Owner gets C<FULL_CONTROL> .
Any principal authenticated as a registered Amazon S3 user is granted
C<READ> access.

=back

=item B<bucket> or B<bucket/key>

One or more bucket names or bucket and key names, specifies an item.
As many as possible will be be processed.

If just a bucket name is given, the ACL for that bucket is retrieved
or modified.

If a bucket name and a key, separated by a slash, is given,
the ACL for that key in that bucket is retrieved or modified.

If a bucket name begins with one or more dashes, it might be mistaken
for a command line option.  If this is the case, separate the command
line options from the bucket or bucket/key names with two dashes, like
so:

  s3acl --get -- --bucketname/keyname

=back

=head1 ENVIRONMENT VARIABLES

=over 8

=item B<AWS_ACCESS_KEY_ID> and B<AWS_ACCESS_KEY_SECRET>

Specify the "AWS Access Key Identifiers" for the AWS account.
B<AWS_ACCESS_KEY_ID> contains the "Access Key ID", and
B<AWS_ACCESS_KEY_SECRET> contains the "Secret Access Key".  These are
effectively the "username" and "password" to the AWS service, and
should be kept confidential.

The access keys MUST be specified, either via these environment
variables, or via the B<--access-key> and B<--secret-key> command line
parameters.

If the command line parameters are set, they override these
environment variables.

=back

=head1 CONFIGURATION FILE

The configuration options will be read from the file C<~/.s3-tools> if it
exists.  The format is the same as the command line options with one option
per line.  For example, the file could contain:

    --access-key <AWS access key>
    --secret-key <AWS secret key>
    --secure

This example configuration file would specify the AWS access keys and that a
secure connection using HTTPS should be used for all communications.

=head1 DESCRIPTION

Retrieves and outputs the Access Control List (ACL) of buckets
and of items in buckets in the Amazon Simple Storage Service (S3).

=head2 Principals

(Much of the following text is taken from the "Amazon S3 Developer
Guide (API Version 2006-03-01)".)

Every bucket and item has an Access Control List (ACL). When a request
is made, S3 determines the principal making the request, and then
checks the access control list to see if that principal is authorized
to make the request. If the ACL contains an entry authorizing that
principal to make this request, the request is allowed to proceed,
otherwise an error is returned.

A principal may be someone with an AWS S3 account who has logged in,
or "authenticated".  The principal might be the creator and owner of
the bucket or item.  Or the principal might be some anonymous web
browser out on the internet.

=head2 ACL is sequence of grants. A grant is 1 grantee and 1 permission.

An access control list is a sequence of grants. It may contain up to
100 grants. A grant is composed of one grantee, which is a description
of the principal who will be allowed access, and one permission, which
is a description of what that principal is allowed to do with that
bucket or item.

=head2 User Grantee

A user grantee must be registered as an Amazon.com customer, but does
not have to be registered as an AWS customer.

When an ACL is read, the user grantee will be displayed in a canonical
format, which consists of 64 hex characters.  The exception is if the
grantee is the owner.  Amazon still stores and returns the grantee in
canonical form, but this tool displays it as "OWNER".

=head2 Group Grantee

The only groups available are those pre-defined by S3.
In the current release of S3, you cannot create your own group.
There are currently two pre-defined groups.

The first is represented by the string "WORLD" by this tool. All
principals, whether they are anonymous or authenticated, are
considered part of this group.

The second is represented by the string "USERS" by this tool.  Every
non-anonymous principal is considered part of the group. Note that
permission granted by virtue of this grant does not trump other access
control considerations. For example, if a user is registered with AWS,
they may be part of this group, but if they have not subscribed to S3,
they will still not be granted access.

There is also a special pseudo group with the string "ANY".  It is
used by the B<--del> option to match against a ACL item to select
for deletion.

If Amazon updates S3 to define any additional groups before this tool
is updated, they will be represented as a URI surrounded by angle
brackets.

=head2 Owner

Every bucket and item in S3 has an owner attribute associated with
it. The owner the user that created the bucket or item. The only way
to change the owner of a bucket is to delete the bucket and create it
again under a different user identity. The only way to change the
owner of an item is to overwrite the item using a different identity.

The owner of a bucket or item is subject to the access control policy
of that bucket or item just like everybody else, with two notable
exceptions: The owner of a resource always has the ability to read and
write the ACL of that resource, no matter what the associated ACL
says. For example, as the owner of an item, you could remove yourself
from the associated access control list, and find that you can no
longer read the item's data and metadata. However, by virtue of being
owner, you always have the right to re-grant yourself permissions to
it. This policy prevents the situation where an item becomes
"stranded," with nobody able to ever modify or even delete it.

=head2 Permissions

The permission in a grant describes the type of access to be granted
to the respective grantee. The following permissions are supported:

=over 8

=item READ

When applied to a bucket, this grants permission to list the
bucket. When applied to an item, this grants permission to read the
item data and/or metadata.

=item WRITE

When applied to a bucket, this grants permission to create, overwrite,
and delete any item in the bucket. This permission is not supported
for items (it is reserved for future use).

=item READ_ACP

Grants permission to read the access control policy (ACL and owner)
for the applicable bucket or item. The owner of a bucket or item
always has this permission implicitly.

=item WRITE_ACP

Grants permission to overwrite the ACP for the applicable bucket or
item. The owner of a bucket or item always has this permission
implicitly. Note that granting this permission is equivalent to
granting FULL_CONTROL, because the grant recipient can now make any
whatever changes to the ACP he or she likes!

=item FULL_CONTROL

This permission is short-hand for the union of READ, WRITE, READ_ACP,
and WRITE_ACP permissions. It does not convey additional rights, and
is provided only for convenience.  It is probably unwise to give this
permission to WORLD.

=item ANY

This is not really a permission, but is used by the B<--del> option.
It matches any permission to select a grant for deletion.

=back

=head2 Default ACL

If no ACL is provided at the time a bucket is created or an item
written then a default ACL is created. The default ACL for new
resources consists of a single grant that gives the owner of the
resource (i.e. the principal making the request to create the bucket
or to write the item) FULL_CONTROL permission. Note that if you
overwrite an existing item, the ACL for the existing item is
always overwritten as well, and defaulted back to OWNER:FULL_CONTROL
if no explicit ACL is provided.

=head2 Raw XML ACL Format

The XML ACL format is documented in the "Amazon S3 Developer Guide".

=head2 Parsed ACL Format

This tool parses the raw XML ACL format into a more readable form.

A parsed ACL consists of several lines.  Comments are lines that begin
with a hash character.  Lines that are not comments are grants.

For buckets, the comments give the bucket name, and the Amazon
canonical user string for the owner.

For items, the comments give the bucket name, the item key, and the
Amazon canonical user string for the owner.

A grant is grantee string and a permission string, separated with a
colon character.  A grantee can be one of the strings "OWNER",
"WORLD", or "USERS", or a URI wrapped in angle brackets, or the email
address of an Amazon user, or a Amazon canonical user string, which is
64 hex characters.  A permission is one of the strings "READ",
"WRITE", "READ_ACP", "WRITE_ACP", or "FULL_CONTROL".

  $ ./s3getacl example
  # bucket: example
  # owner: 5a1568e09392dad4b4ceb54f29f0a64d651a531350d6f720fbd2367eed995f08
  OWNER:FULL_CONTROL
  a00490decea9d0ad76e5ef8b450b3efa63861adccfb9197cfb42f837eb222df2:WRITE
  USERS:READ
  WORLD:READ
  $ ./s3getacl example/thingee
  # bucket: example
  # item: thingee
  # owner: 5a1568e09392dad4b4ceb54f29f0a64d651a531350d6f720fbd2367eed995f08
  OWNER:FULL_CONTROL
  $ _

=head1 BUGS

Report bugs to Mark Atwood L<mark@fallenpegasus.com>.

Occasionally the S3 service will randomly fail for no externally
apparent reason.  When that happens, this tool should retry, with a
delay and a backoff.

Access to the S3 service can be authenticated with a X.509
certificate, instead of via the "AWS Access Key Identifiers".  This
tool should support that.

It might be useful to be able to specify the "AWS Access Key
Identifiers" in the user's C<~/.netrc> file.  This tool should support
that.

Errors and warnings are very "Perl-ish", and can be confusing.

Trying to access a bucket or item that does not exist or is not
accessible by the user generates less than helpful error messages.

L<Net::Amazon::S3> already uses L<XML::LibXML> and
L<XML::LibXML::XPathContext>, so this tool should use those instead of
using L<XML::Writer>, to have fewer module
dependences.

It is possible to add the same grant to a bucket or item more than
once.  This is a surprising behavior of the S3 service.  Both
identical grants will be removed by using the B<--del> option.

Due to a limitation in the semantics of the S3 API, it is not possible
to delete a grantee by email address, only by canonical ID.

=head1 AUTHOR

Written by Mark Atwood L<mark@fallenpegasus.com>.

Many thanks to Wotan LLC L<http://wotanllc.com>, for supporting the
development of these S3 tools.

Many thanks to the Amazon AWS engineers for developing S3.

=head1 SEE ALSO

These tools use the L<Net::Amazon:S3> Perl module.

The Amazon Simple Storage Service (S3) is documented at
L<http://aws.amazon.com/s3>.

=cut