File Coverage

blib/lib/VOMS/Lite/AC.pm
Criterion Covered Total %
statement 143 417 34.2
branch 47 220 21.3
condition 3 63 4.7
subroutine 11 12 91.6
pod 0 2 0.0
total 204 714 28.5


line stmt bran cond sub pod time code
1             package VOMS::Lite::AC;
2              
3 1     1   18 use 5.004;
  1         4  
  1         42  
4 1     1   6 use strict;
  1         2  
  1         247  
5 1     1   7 use Time::Local;
  1         2  
  1         66  
6 1     1   5 use VOMS::Lite::ASN1Helper qw(ASN1Unwrap ASN1OIDtoOID Hex DecToHex ASN1BitStr ASN1Wrap ASN1Index);
  1         1  
  1         80  
7 1     1   5 use VOMS::Lite::CertKeyHelper qw(digestSign buildchain);
  1         2  
  1         47  
8 1     1   4 use VOMS::Lite::PEMHelper qw(readCert);
  1         2  
  1         40  
9 1     1   5 use VOMS::Lite::X509;
  1         2  
  1         54  
10 1     1   6 use VOMS::Lite::KEY;
  1         2  
  1         44  
11 1     1   7 use Sys::Hostname;
  1         2  
  1         60  
12             #use Regexp::Common qw (URI);
13              
14             require Exporter;
15 1     1   14 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         6248  
16             @ISA = qw(Exporter);
17              
18             $VERSION = '0.20';
19              
20             #############################################
21             sub Examine {
22 0     0 0 0 my ($decoded,$dataref)=@_;
23 0 0       0 $dataref={Start=>"",End=>"",FQANs=>"",IssuerDN=>"",HolderIssuerDN=>"",VOMSDIR=>"/etc/grid-security/vomsdir"} if( ! defined $dataref );
24 0         0 my %Values=%$dataref;
25 0         0 my @ASN1Index=ASN1Index($decoded);
26 0         0 my @Values;
27 0 0       0 return ( {Errors=>"Unable to parse attribute certificate"} ) if (@ASN1Index==0);
28              
29 0         0 my ($index,$ignoreuntil)=(0,0);
30              
31             # Drill down into the certificate
32 0         0 shift @ASN1Index; # skip the wrapping of the attribute certificate sequence
33 0         0 shift @ASN1Index; # skip the wrapping of the bundle of atribute certificate sequence
34              
35             # Get each AC
36 0         0 my @ACs;
37 0         0 foreach (@ASN1Index) {
38 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @$_;
39 0 0       0 if ( $HEADSTART < $ignoreuntil ) { next; }
  0         0  
40             else {
41 0         0 push @ACs,[$CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN];
42 0         0 $ignoreuntil=$HEADSTART+$HEADLEN+$CHUNKLEN;
43             }
44             }
45              
46 0         0 foreach (@ACs) {
47 0         0 my %LocalValues;
48 0         0 my ($ACversion,$ACholder,$ACissuer,$ACalgorithmId,$ACSerial,$ACvalidity,$ACattribute,$ACUniqueId,$ACExtensions,$ACSignatureType,$ACSignature);
49 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN)=@$_;
50 0         0 my ($TBSAC,$SIGType,$SIG);
51 0         0 $ignoreuntil=$HEADSTART+$HEADLEN;
52 0         0 my $ignoreafter=$HEADSTART+$HEADLEN+$CHUNKLEN;
53 0         0 my $index=0;
54 0         0 my $SIGSTART;
55 0         0 foreach (@ASN1Index) {
56 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @$_;
57 0 0       0 if ( $HEADSTART < $ignoreuntil ) { next; }
  0 0       0  
58 0         0 elsif ( $HEADSTART >= $ignoreafter ) { last; }
59             else {
60 0 0 0     0 if ($index==0) { $TBSAC = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN)); $index++; $SIGSTART=$HEADSTART+$HEADLEN+$CHUNKLEN; next;} #this is a container
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
    0          
    0          
    0          
    0          
    0          
    0          
    0          
61 0         0 elsif ($index==1) { $ACversion = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
62 0         0 elsif ($index==2) { $ACholder = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
63 0         0 elsif ($index==3) { $ACissuer = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
64 0         0 elsif ($index==4) { $ACalgorithmId = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
65 0         0 elsif ($index==5) { $ACSerial = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
66 0         0 elsif ($index==6) { $ACvalidity = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));;}
67 0         0 elsif ($index==7) { $ACattribute = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
68 0         0 elsif ($index==8 && $HEADSTART < $SIGSTART) { $ACExtensions = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
  0         0  
69 0         0 elsif ($index==8) {$index++; next;}
70 0         0 elsif ($index==9) { $ACSignatureType = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN));}
71 0         0 elsif ($index==10){ $ACSignature = substr($decoded,$HEADSTART,($HEADLEN+$CHUNKLEN)); last;}
  0         0  
72 0         0 $index++;
73 0         0 $ignoreuntil=$HEADSTART+$HEADLEN+$CHUNKLEN;
74             }
75             }
76             # Extract the main components of the Attribute Certificate
77              
78             #Standard
79 0 0       0 if (defined $Values{TBSAC}) {$LocalValues{TBSAC}=$TBSAC;}
  0         0  
80 0 0       0 if (defined $Values{ACversion}) {$LocalValues{ACversion}=$ACversion;}
  0         0  
81 0 0       0 if (defined $Values{ACholder}) {$LocalValues{ACholder}=$ACholder;}
  0         0  
82 0 0       0 if (defined $Values{ACissuer}) {$LocalValues{ACissuer}=$ACissuer;}
  0         0  
83 0 0       0 if (defined $Values{ACalgorithmId}) {$LocalValues{ACalgorithmId}=$ACalgorithmId;}
  0         0  
84 0 0       0 if (defined $Values{ACSerial}) {$LocalValues{ACSerial}=$ACSerial;}
  0         0  
85 0 0       0 if (defined $Values{ACvalidity}) {$LocalValues{ACvalidity}=$ACvalidity;}
  0         0  
86 0 0       0 if (defined $Values{ACattribute}) {$LocalValues{ACattribute}=$ACattribute;}
  0         0  
87 0 0       0 if (defined $Values{ACExtensions}) {$LocalValues{ACExtensions}=$ACExtensions;}
  0         0  
88 0 0       0 if (defined $Values{ACSignatureType}) {$LocalValues{ACSignatureType}=$ACSignatureType;}
  0         0  
89 0 0       0 if (defined $Values{ACSignature}) {$LocalValues{ACSignature}=$ACSignature;}
  0         0  
90              
91 0 0       0 if ($ACExtensions ne "" ) {
92 0         0 my @ACExtensionIndex=ASN1Index($ACExtensions);
93 0         0 shift @ACExtensionIndex; #Unwrap extensions;
94 0         0 while (@ACExtensionIndex) {
95 0         0 my $Seqref=shift(@ACExtensionIndex);
96 0         0 my $OIDref=shift(@ACExtensionIndex);
97 0         0 my $OSref=shift(@ACExtensionIndex);
98 0 0       0 my $Critical=( $OSref =~ /\x01\x01[^\0]/ )?1:0;
99 0 0       0 $OSref=shift(@ACExtensionIndex) if ($Critical);
100 0         0 my $OID=substr($ACExtensions,(${ $OIDref }[3]+${ $OIDref }[4]),${ $OIDref }[5]);
  0         0  
  0         0  
  0         0  
101 0         0 my $OIDstr=ASN1OIDtoOID($OID);
102 0 0 0     0 if($OIDstr eq "2.5.29.56" && defined $Values{'noRevAvail'}) {
103 0         0 $LocalValues{noRevAvail} = "\x01";
104             }
105 0 0 0     0 if($OIDstr eq "2.5.29.35" && defined $Values{'authorityKeyIdentifier'}) {
106 0         0 my $AKI=substr($ACExtensions,(${ $OSref }[3]+${ $OSref }[4]),${ $OSref }[5]);
  0         0  
  0         0  
  0         0  
107 0         0 $AKI=ASN1Unwrap($AKI);
108 0         0 $LocalValues{authorityKeyIdentifier}=$AKI;
109 0         0 $LocalValues{authorityKeyIdentifierSkid} = undef; #explicitly undefine these incase they were set in the call!
110 0         0 $LocalValues{authorityKeyIdentifierIssuer} = undef; #
111 0         0 $LocalValues{authorityKeyIdentifierSerial} = undef; #
112 0         0 until (length($AKI) == 0) {
113 0         0 my ($headlen,$reallen,$Class,$Constructed,$Tag,$str)=ASN1Unwrap($AKI);
114 0         0 $AKI=substr($AKI,($headlen+$reallen));
115 0 0       0 if ($Tag==0) {$LocalValues{authorityKeyIdentifierSkid}=$str;}
  0 0       0  
  0 0       0  
116 0         0 elsif ($Tag==1) {$LocalValues{authorityKeyIdentifierIssuer}=$str;}
117             elsif ($Tag==2) {$LocalValues{authorityKeyIdentifierSerial}=Hex($str);}
118             }
119             }
120 0 0 0     0 if($OIDstr eq "1.3.6.1.4.1.8005.100.100.11" && defined $Values{'vOMSTags'} ) {
121 0         0 $LocalValues{vOMSTags}=substr($ACExtensions,(${ $OSref }[3]+${ $OSref }[4]),${ $OSref }[5]);
  0         0  
  0         0  
  0         0  
122             }
123 0 0 0     0 if($OIDstr eq "1.3.6.1.4.1.8005.100.100.10" && ( defined $Values{'vOMSACCertList'} || defined $Values{Verify} ) ) {
      0        
124 0         0 my $SEQ=ASN1Unwrap(substr($ACExtensions,(${ $OSref }[3]+${ $OSref }[4]),${ $OSref }[5]));
  0         0  
  0         0  
  0         0  
125 0         0 my @DERs;
126 0         0 until (length($SEQ) == 0) {
127 0         0 my ($headlen,$reallen,$Class,$Constructed,$Tag,$cert)=ASN1Unwrap($SEQ);
128 0         0 $SEQ=substr($SEQ,($headlen+$reallen));
129 0         0 push @DERs,$cert;
130             }
131 0         0 $LocalValues{vOMSACCertList} = \@DERs;
132             }
133             }
134             }
135              
136             #Deep
137 0 0       0 if (defined $Values{Version} ) {
138 0         0 $LocalValues{Version} = ASN1Unwrap($ACversion);
139             }
140              
141             #To whom does the AC belong
142 0 0 0     0 if ( defined $Values{HolderIssuerDN} || defined $Values{HolderSerial} ) {
143 0         0 $Values{HolderSerial}=""; $Values{HolderIssuerDN}=""; # one doesn't make sense without the other makesure both are set
  0         0  
144 0         0 my $a0=ASN1Unwrap($ACholder);
145 0         0 my $SEQ=ASN1Unwrap($a0);
146 0         0 my ($headlen,$reallen,$Class,$Constructed,$Tag,$name)=ASN1Unwrap($SEQ);
147 0         0 my $int=ASN1Unwrap(substr($SEQ,($headlen+$reallen)));
148 0         0 $SEQ=ASN1Unwrap($name);
149 0         0 my @rdns=ASN1Index($SEQ);
150 0         0 while (@rdns) {
151 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN)=(0,0,0,0,0);
152 0         0 until ($TAG == 6 ) { ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @rdns}; }
  0         0  
  0         0  
153 0         0 my $OID=substr($SEQ,($HEADSTART+$HEADLEN),$CHUNKLEN);
154 0         0 ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @rdns};
  0         0  
155 0         0 my $Value=substr($SEQ,($HEADSTART+$HEADLEN),$CHUNKLEN);
156 0         0 $LocalValues{HolderIssuerDN}.="/".VOMS::Lite::CertKeyHelper::OIDtoDNattrib(ASN1OIDtoOID($OID))."=$Value";
157             }
158 0         0 $LocalValues{HolderSerial} = "0x".Hex($int);
159             }
160              
161             # Who was the Issuer
162 0 0 0     0 if ( defined $Values{IssuerDN} || defined $Values{Verify}) {
163 0         0 my $SEQ=ASN1Unwrap($ACissuer);
164 0         0 my $name=ASN1Unwrap($SEQ);
165 0         0 $SEQ=ASN1Unwrap($name);
166 0         0 my @rdns=ASN1Index($SEQ);
167 0         0 while (@rdns) {
168 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN)=(0,0,0,0,0);
169 0         0 until ($TAG == 6 ) { ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @rdns}; }
  0         0  
  0         0  
170 0         0 my $OID=substr($SEQ,($HEADSTART+$HEADLEN),$CHUNKLEN);
171 0         0 ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @rdns};
  0         0  
172 0         0 my $Value=substr($SEQ,($HEADSTART+$HEADLEN),$CHUNKLEN);
173 0         0 $LocalValues{IssuerDN}.="/".VOMS::Lite::CertKeyHelper::OIDtoDNattrib(ASN1OIDtoOID($OID))."=$Value";
174             }
175             }
176              
177             # What was/were the Attribute(s)
178 0 0 0     0 if ( defined $Values{PA} || defined $Values{FQANs} ) {
179 0         0 my @AttrIndex=ASN1Index($ACattribute);
180 0         0 my @Attrs;
181             my $PA;
182 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN)=(0,0,-1,0,0);
183 0   0     0 until ($CLASS==2 && $TAG == 6 ) { ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @AttrIndex}; }
  0         0  
  0         0  
184 0         0 $PA=substr($ACattribute,($HEADSTART+$HEADLEN),$CHUNKLEN);
185 0         0 while (@AttrIndex) {
186 0         0 my ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN)=(0,0,-1,0,0);
187 0   0     0 until ($CLASS==0 && $TAG == 4 ) { ($CLASS,$CONSTRUCTED,$TAG,$HEADSTART,$HEADLEN,$CHUNKLEN) = @{shift @AttrIndex}; }
  0         0  
  0         0  
188 0         0 my $Value=substr($ACattribute,($HEADSTART+$HEADLEN),$CHUNKLEN);
189 0         0 push @Attrs,$Value;
190             }
191 0         0 $LocalValues{PA}=$PA;
192 0         0 $LocalValues{FQANs}=\@Attrs;
193             }
194              
195             # Values of Start and End Time Seconds since Epoch
196 0 0 0     0 if (defined $Values{Start} || defined $Values{End}) {
197 0         0 my @validity=ASN1Unwrap($ACvalidity);
198 0         0 my @st=ASN1Unwrap($validity[5]);
199 0         0 my @et=ASN1Unwrap(substr($validity[5],$st[0]+$st[1]));
200 0 0 0     0 if ( $st[4] eq "23" && $st[5]=~ /^(..)(..)(..)(..)(..)(..)Z$/ ) { $LocalValues{Start} = timegm($6,$5,$4,$3,($2-1),$1); }
  0 0 0     0  
201 0         0 elsif ( $st[4] eq "24" && $st[5]=~ /^(....)(..)(..)(..)(..)(..)Z$/ ) { $LocalValues{Start} = timegm($6,$5,$4,$3,($2-1),$1); }
202 0 0 0     0 if ( $et[4] eq "23" && $et[5]=~ /^(..)(..)(..)(..)(..)(..)Z$/ ) { $LocalValues{End} = timegm($6,$5,$4,$3,($2-1),$1); }
  0 0 0     0  
203 0         0 elsif ( $et[4] eq "24" && $et[5]=~ /^(....)(..)(..)(..)(..)(..)Z$/ ) { $LocalValues{End} = timegm($6,$5,$4,$3,($2-1),$1); }
204             }
205              
206             # Signature Value
207 0 0 0     0 if (defined $Values{SignatureValue} || defined $Values{SignatureType} || defined $Values{Verify}) {
      0        
208 0         0 $LocalValues{'EncSignatureValue'}=Hex(substr(ASN1Unwrap($ACSignature),1));
209 0         0 my $HexACSignature=Hex($ACSignatureType);
210 0 0       0 if ( $HexACSignature eq "300d06092a864886f70d0101040500" ) { $LocalValues{'SignatureType'}="md5WithRSA"; }
  0 0       0  
    0          
    0          
211 0         0 elsif ( $HexACSignature eq "300d06092a864886f70d0101050500" ) { $LocalValues{'SignatureType'}="sha1WithRSA"; }
212 0         0 elsif ( $HexACSignature eq "300d06092a864886f70d0101030500" ) { $LocalValues{'SignatureType'}="md4WithRSA"; }
213 0         0 elsif ( $HexACSignature eq "300d06092a864886f70d0101020500" ) { $LocalValues{'SignatureType'}="md2WithRSA"; }
214 0         0 else { $LocalValues{'SignatureType'}="unrecognised"; }
215             }
216              
217             # Verify it
218 0 0       0 if (defined $Values{Verify}) {
219 0         0 my @ACIssuers;
220 0 0       0 if ( ! defined $Values{'VOMSDIR'} ) { $Values{'VOMSDIR'}="/etc/grid-security/vomsdir"; }
  0         0  
221 0 0       0 if ( -d $Values{'VOMSDIR'} ) {
222 0         0 opendir(my $dh, $Values{'VOMSDIR'});
223 0 0       0 @ACIssuers = grep { /^[^.]/ && -f "$Values{VOMSDIR}/$_" } readdir($dh);
  0         0  
224 0         0 closedir $dh;
225             }
226 0         0 $LocalValues{Verify}=0;
227              
228 0         0 for (my $II=-1;$II<@ACIssuers;$II++) {
229 0         0 $Values{'IssuerDN'}=""; # set Issuer DN to be exported
230 0         0 $Values{'InternalVOMSCert'}=""; # set indicator of VOMS cert attached
231 0         0 my @decodedCERTS;
232 0 0       0 if ( $II == -1 ) { # 1st time round try attached certs. Assuming the certlist is a chain not a list of possible issuers
233 0 0       0 next if ( ! defined $LocalValues{vOMSACCertList} );
234 0         0 @decodedCERTS=@{ $LocalValues{vOMSACCertList} };
  0         0  
235 0         0 $LocalValues{'InternalVOMSCert'} = "Attached";
236             }
237             else {
238 0         0 @decodedCERTS=readCert($Values{'VOMSDIR'}."/$ACIssuers[$II]");
239 0         0 $LocalValues{'InternalVOMSCert'} = "Local";
240             }
241              
242 0         0 my %Chain = %{ buildchain( { trustedCAdirs => ["/etc/grid-security/certificates"], ####Can this be an option?
  0         0  
243             suppliedcerts => \@decodedCERTS,
244             trustedCAs => [] } ) };
245              
246 0 0       0 next if ( @{ shift @{ $Chain{Errors} } } );
  0         0  
  0         0  
247 0 0       0 next if (! ${ $Chain{TrustedCA} }[-1] );
  0         0  
248 0 0       0 next if ( $Chain{'EndEntityDN'} ne $LocalValues{'IssuerDN'} );
249 0         0 my $X509REF=VOMS::Lite::X509::Examine($Chain{EndEntityCert},{Keymodulus=>"",KeypublicExponent=>""});
250 0 0       0 if (VOMS::Lite::CertKeyHelper::verifySignature(
  0         0  
251             $LocalValues{'SignatureType'},
252             $LocalValues{'EncSignatureValue'},
253             $TBSAC,
254 0         0 Hex(${ $X509REF }{'KeypublicExponent'}),
255             Hex(${ $X509REF }{'Keymodulus'}))) {
256 0         0 $LocalValues{'Verify'} = 1 ; last;
  0         0  
257             }
258             }
259             }
260              
261 0         0 push @Values,{};
262 0         0 foreach (keys %Values) { ${ $Values[-1] }{$_}=$LocalValues{$_}; }
  0         0  
  0         0  
263             }
264              
265 0         0 return @Values;
266             }
267              
268              
269             ###############################
270              
271             sub Create {
272 1     1 0 4 my $inputref = shift;
273 1         2 my %context = %{$inputref};
  1         7  
274 1         4 my @error=();
275 1         2 my @warning=();
276 1         2 my $AC;
277              
278             # Check for values which need to be defined
279 1 50       5 if ( ! defined $context{'Cert'} ) { push @error, "VOMS::Lite::AC: Holder certificate not supplied"; }
  0         0  
280 1 50       5 if ( ! defined $context{'VOMSCert'} ) { push @error, "VOMS::Lite::AC: VOMS certificate not supplied"; }
  0         0  
281 1 50       5 if ( ! defined $context{'VOMSKey'} ) { push @error, "VOMS::Lite::AC: VOMS key not supplied"; }
  0         0  
282 1 50       4 if ( ! defined $context{'Lifetime'} ) { push @error, "VOMS::Lite::AC: VOMS AC Lifetime not supplied"; }
  0         0  
283 1 50       4 if ( ! defined $context{'Server'} ) { push @error, "VOMS::Lite::AC: VOMS Server FQDN not supplied"; }
  0         0  
284 1 50       5 if ( ! defined $context{'Port'} ) { push @error, "VOMS::Lite::AC: VOMS Server Port not supplied"; }
  0         0  
285 1 50       5 if ( ! defined $context{'Serial'} ) { push @error, "VOMS::Lite::AC: VOMS AC Serial not supplied"; }
  0         0  
286 1 50       5 if ( ! defined $context{'Code'} ) { push @warning, "VOMS::Lite::AC: Code not supplied, using Port Value"; }
  0         0  
287 1 50       3 if ( ! defined $context{'Attribs'} ) { push @error, "VOMS::Lite::AC: VOMS Attributes not supplied"; }
  0         0  
288              
289             # Bail if there isn't enough information
290 1 50       4 if ( @error > 0 ) { return { Errors => \@error} ; }
  0         0  
291              
292             # Load input data into local variables
293 1 50       17 my $CertInfoRef = (($context{'Cert'} =~ /^(\060.*)$/s) ? VOMS::Lite::X509::Examine($&, {X509issuer=>"", X509serial=>"", X509subject=>""}) : undef);
294 1 50       60 my $VCertInfoRef = (($context{'VOMSCert'} =~ /^(\060.+)$/s) ? VOMS::Lite::X509::Examine($&, {X509issuer=>"", subjectKeyIdentifier=>"", X509subject=>""}) : undef);
295 1 50       25 my $VKeyInfoRef = (($context{'VOMSKey'} =~ /^(\060.+)$/s) ? VOMS::Lite::KEY::Examine($&, {Keymodulus=>"", KeyprivateExponent=>""}) : undef);
296 1 50       5 my %CERTINFO; if ( defined $CertInfoRef ) { %CERTINFO=%$CertInfoRef; } else { push @error, "VOMS::Lite::AC: Unable to parse holder certificate."; }
  1         5  
  1         8  
  0         0  
297 1 50       2 my %VCERTINFO; if ( defined $VCertInfoRef ) { %VCERTINFO=%$VCertInfoRef; } else { push @error, "VOMS::Lite::AC: Unable to parse VOMS certificate."; }
  1         4  
  1         7  
  0         0  
298 1 50       2 my %VKEYINFO; if ( defined $VKeyInfoRef ) { %VKEYINFO=%$VKeyInfoRef; } else { push @error, "VOMS::Lite::AC: Unable to parse VOMS key."; }
  1         4  
  1         5  
  0         0  
299 1 50       5 if ( @error > 0 ) { return { Errors => \@error} ; }
  0         0  
300              
301 1 50       11 my $Lifetime = (($context{'Lifetime'} =~ /^([0-9]+)$/) ? $& : undef);
302 1 50       7 my $Server = (($context{'Server'} =~ /^([a-z0-9_.-]+)$/) ? $& : undef);
303 1 50 33     13 my $Port = (($context{'Port'} =~ /^([0-9]{1,5})$/ && $context{'Port'} < 65536) ? $& : undef);
304 1 50       37 my $Serial = (($context{'Serial'} =~ /^([0-9a-f]+)$/) ? $& : undef);
305 1 50       7 my $Code = (($context{'Code'} =~ /^([0-9]+)$/) ? $& : undef);
306 1         3 my $AttribRef = $context{'Attribs'};
307 1         2 my $Broken = $context{'Broken'};
308              
309             # Get the attributes from the supplied reference
310 1         3 my @Attribs=();
311 1         4 foreach ( @$AttribRef ) {
312 1         2 my ($cap,$rl);
313 1 50       7 if ( /(\/Capability=[\w.-]+)$/ ) { $cap = $1; }
  1         5  
314 1 50       70 if ( /(\/Role=[\w.-]+)$cap$/ ) { $rl = $1; }
  1         3  
315 1 50       52 if ( /^((?:\/[\w.-]+)+$rl$cap)$/ ) { push @Attribs,$&; }
  1         5  
316             }
317              
318             # Get any targets from the supplied reference
319 1         3 my @Targets=();
320 1 50 33     6 if (defined $context{'Targets'} && $context{'Targets'} =~ /^ARRAY/ ) {
321 0         0 foreach ( @{ $context{'Targets'} } ) {
  0         0  
322 0 0       0 if (/^([a-zA-Z0-9()'*~!._;\/?:\@&=+\$,#-]|%[a-fA-F0-9]{2})+$/) { push @Targets, $1; }
  0         0  
323             # if (/^($RE{URI})$/) { push @Targets, $1;} --- Regexp: a sledge hammer -- we shouldn't be so prescriptive
324 0         0 else { push @error, "VOMS::Lite::AC: At least 1 target was an invalid URI (see eg RFC2396)";}
325             }
326             }
327              
328             # Check for errors in local variables
329 1 50       4 if ( ! defined $Lifetime ) { push @error, "VOMS::Lite::AC: Invalid Lifetime"; }
  0         0  
330 1 50       3 if ( ! defined $Server ) { push @error, "VOMS::Lite::AC: Invalid Server"; }
  0         0  
331 1 50       4 if ( ! defined $Port ) { push @error, "VOMS::Lite::AC: Invalid Port"; }
  0         0  
332 1 50       5 if ( ! defined $Serial ) { push @error, "VOMS::Lite::AC: Invalid Serial Number"; }
  0         0  
333 1 50       5 if ( ! defined $Code ) { $Code = $Port; }
  0         0  
334 1 50       4 if ( ! defined $CERTINFO{X509issuer} ) { push @error, "VOMS::Lite::AC: Unable to get holder certificate's issuer"; }
  0         0  
335 1 50       4 if ( ! defined $CERTINFO{X509serial} ) { push @error, "VOMS::Lite::AC: Unable to get holder certificate's serial"; }
  0         0  
336 1 50       4 if ( ! defined $CERTINFO{X509subject} ) { push @error, "VOMS::Lite::AC: Unable to get holder certificate's subject"; }
  0         0  
337 1 50       3 if ( ! defined $VCERTINFO{X509issuer} ) { push @error, "VOMS::Lite::AC: Unable to get VOMS certificate's issuer"; }
  0         0  
338 1 50       3 if ( ! defined $VCERTINFO{subjectKeyIdentifier} ) { push @error, "VOMS::Lite::AC: Unable to get VOMS certificate's Subject Key Identifier"; }
  0         0  
339 1 50       4 if ( ! defined $VCERTINFO{X509subject} ) { push @error, "VOMS::Lite::AC: Unable to get VOMS certificate's subject"; }
  0         0  
340 1 50       3 if ( ! defined $VKEYINFO{Keymodulus} ) { push @error, "VOMS::Lite::AC: Unable to get VOMS key's Modulus"; }
  0         0  
341 1 50       5 if ( ! defined $VKEYINFO{KeyprivateExponent} ) { push @error, "VOMS::Lite::AC: Unable to get VOMS key's Exponent"; }
  0         0  
342 1 50       5 if ( $#Attribs < 0 ) { push @error, "VOMS::Lite::AC: No Attributes supplied"; }
  0         0  
343              
344             # Bail if any required variable failed to load
345 1 50       4 if ( @error > 0 ) { return { Targets => \@Targets, Attribs => \@Attribs, Warnings => \@warning, Errors => \@error }; }
  0         0  
346              
347             # Pad serial number
348 1         9 $Serial =~ s/^.(..)*$/0$&/;
349              
350             # The Identity of this VOMS from first part of first Attribute
351 1 50       6 my $Group=(($Attribs[0] =~ /^\/?([^\/]+)/) ? $1 : undef);
352 1 50       4 if ( ! defined $Group ) { push @error, "VOMS::Lite::AC: VOMS Group not defined"; }
  0         0  
353 1 50       4 if ( @error > 0 ) { return { Targets => \@Targets, Attribs => \@Attribs, Warnings => \@warning, Errors => \@error }; }
  0         0  
354 1         4 my $VOMSURI=$Group."://".$Server.":".$Port;
355              
356             # Get times Now and Now + N hours
357 1         3 my $NOW=time();
358 1         7 my @NOW=gmtime($NOW);
359 1         5 my @FUT=gmtime($NOW+$Lifetime);
360 1         9 my $NotBeforeDate = sprintf("%04i%02i%02i%02i%02i%02iZ",($NOW[5]+1900),($NOW[4]+1),$NOW[3],$NOW[2],$NOW[1],$NOW[0]);
361 1         6 my $NotAfterDate = sprintf("%04i%02i%02i%02i%02i%02iZ",($FUT[5]+1900),($FUT[4]+1),$FUT[3],$FUT[2],$FUT[1],$FUT[0]);
362              
363             ###########################################################
364             # OK Let's create a VOMS Attribute Certificate! This consists of:
365             # AttCertVersion Holder AttCertIssuer AlgorithmIdentifier CertificateSerialNumber
366             # AttCertValidityPeriod AttributeSequence UniqueIdentifier Extensions
367              
368             # Version (=2 (i.e. 01))
369 1         2 my $AttCertVersion="020101";
370              
371             # Holder of Attribute. This this is a sequence containing the holder certificate's issuer DN and serial.
372 1 50 33     12 my $HolderIssuer = Hex( ( defined $Broken && $Broken ) ? $CERTINFO{X509subject}:$CERTINFO{X509issuer} );
373 1         5 my $HolderSerial = Hex( $CERTINFO{X509serial} );
374 1         6 my $HolderInfo = ASN1Wrap( "30",ASN1Wrap( "a4",$HolderIssuer ) ).$HolderSerial;
375 1         13 my $Holder = ASN1Wrap( "30",ASN1Wrap( "a0",$HolderInfo ) );
376              
377             # Issuer of Attribute Certificate
378 1         4 my $AttCertIssuerInfo = Hex($VCERTINFO{X509subject});
379 1         5 my $AttCertIssuer = ASN1Wrap("a0",ASN1Wrap("30",ASN1Wrap("a4",$AttCertIssuerInfo)));
380              
381             # Signing Algorythm used in this Attribute Certificate
382 1         3 my $AlgorithmIdentifier = "300d06092a864886f70d0101040500";
383              
384             # Serial Number
385 1         6 my $SN = $Serial.DecToHex($Code);
386 1 50       5 if ( length($SN) > 80 ) {
387 0         0 push @warning, "AC: The size of the serial number is too large, using truncated version.";
388 0         0 $SN = substr($SN,-40);
389             }
390 1         5 my $CertificateSerialNumber = ASN1Wrap("02",$SN);
391              
392             # Attribute Certificate validity period
393 1         5 my $AttCertValidityPeriod = ASN1Wrap("30",ASN1Wrap("18",Hex($NotBeforeDate)).ASN1Wrap("18",Hex($NotAfterDate)));
394              
395             # Attributes from Attrib array supplied and VOMS URI (from group, server and port)
396 1         3 my $VOMSOIDChunck = "060a2b06010401be45646404"; # OID, encoded-length=10, 1.3.6.1.4.1.8005.100.100.4
397 1         6 my $VOMSURIChunck = ASN1Wrap("a0",ASN1Wrap("86",Hex("$VOMSURI")));
398 1         4 my $VOMSTripleChunck = "";
399 1         2 my $VT="";
400 1         3 foreach (@Attribs) { $VT .= ASN1Wrap("04",Hex($_)); } # Concatination of wrapped Attributes
  1         10  
401 1         4 $VOMSTripleChunck = ASN1Wrap("30",$VT);
402 1         6 my $VOMSAttribChunck = ASN1Wrap("31",ASN1Wrap("30",$VOMSURIChunck.$VOMSTripleChunck));
403 1         6 my $AttributeSequence = ASN1Wrap("30",ASN1Wrap("30",$VOMSOIDChunck.$VOMSAttribChunck));
404              
405             #Unique Identifier
406 1         3 my $UniqueIdentifier=""; # Optional and we do not specify it here
407              
408             #Extensions
409             #Targets
410 1         3 my $ACTargets="";
411 1         1 my $targetInformation="";
412 1         4 foreach my $uniformResourceIdentifier (@Targets) {
413 0         0 $ACTargets.=ASN1Wrap("30",ASN1Wrap("a0",ASN1Wrap("a0",ASN1Wrap("86",$uniformResourceIdentifier))));
414             }
415 1 50       5 if ($ACTargets ne "") { $targetInformation=ASN1Wrap("30","0603551d37". # OID 2.5.29.55
  0         0  
416             "0101ff". # Critical
417             ASN1Wrap("04",ASN1Wrap("30",$ACTargets)));}
418             #Issuer Certs
419             # my $IssuerCerts="";
420 1         5 my $IssuerCerts=ASN1Wrap("30","060a2b06010401be4564640a".ASN1Wrap("04",ASN1Wrap("30",ASN1Wrap("30",Hex($context{'VOMSCert'})))));
421             #NoRevocation
422 1         5 my $NoRevAvail = "30090603551d3804020500"; # OID 2.5.29.56 + contents=Null
423             #Issuer Unique ID
424 1         7 my $IssuerUniqueID=ASN1Wrap("30","0603551d23".ASN1Wrap("04",ASN1Wrap("30",ASN1Wrap("80",Hex($VCERTINFO{subjectKeyIdentifier})))));
425             #Tags
426 1         4 my $Tag="";
427              
428 1         8 my $Extensions=ASN1Wrap("30",$targetInformation.$IssuerCerts.$NoRevAvail.$IssuerUniqueID.$Tag);
429              
430             # Concatinate and wrap into a ToBeSignedAttributeCertificate
431 1         16 my $UnsignedAC = ASN1Wrap("30",$AttCertVersion.
432             $Holder.
433             $AttCertIssuer.
434             $AlgorithmIdentifier.
435             $CertificateSerialNumber.
436             $AttCertValidityPeriod.
437             $AttributeSequence.
438             $UniqueIdentifier.
439             $Extensions);
440              
441             ###########################################################
442             # Make MD5 Checksum
443 1         3 my $BinaryUnsignedAC = $UnsignedAC;
444 1         6 $BinaryUnsignedAC =~ s/(..)/pack('C',hex($&))/ge;
  977         2210  
445              
446             # Make MD5 signature and rsa sign it
447 1         5 my $RSAsignedDigest = digestSign("md5WithRSA",$BinaryUnsignedAC,Hex($VKEYINFO{KeyprivateExponent}),Hex($VKEYINFO{Keymodulus}));
448              
449 1         9 my $ACSignature = ASN1Wrap("03",ASN1BitStr($RSAsignedDigest)); #(Always n*8 bits for MDnRSA and SHA1RSA)
450              
451             # Wrap it all up
452             # $AC=ASN1Wrap("30",ASN1Wrap("30",ASN1Wrap("30",$UnsignedAC.$AlgorithmIdentifier.$ACSignature)));
453 1         9 $AC=ASN1Wrap("30",$UnsignedAC.$AlgorithmIdentifier.$ACSignature);
454 1         10 $AC=~s/(..)/pack('C',hex($&))/ge;
  1063         2127  
455              
456 1         52 return { AC => $AC, Targets => \@Targets, Attribs => \@Attribs, Warnings => \@warning };
457             }
458              
459             1;
460             __END__