1 |
# |
2 |
# @(#)$Id$ |
3 |
# |
4 |
# |
5 |
package CRL; |
6 |
use strict; |
7 |
require OSSL and import OSSL unless defined &OSSL::new; |
8 |
use vars qw/ $log $cnf /; |
9 |
|
10 |
# Syntax: |
11 |
# CRL->new( [name [,data]] ); |
12 |
# CRL->setName( name); |
13 |
# CRL->setData( datablob ); # load a CRL in PEM format or bails out |
14 |
# CRL->verify( cafilelist ); # returns path to CA or undef if verify failed |
15 |
# |
16 |
# |
17 |
sub new { |
18 |
my $obref = {}; bless $obref; |
19 |
my $self = shift; |
20 |
$self = $obref; |
21 |
my $name = shift; |
22 |
my $data = shift; |
23 |
|
24 |
$self->{"name"} = "unknown"; |
25 |
|
26 |
$self->setName($name) if $name; |
27 |
$self->setData($data) if $data; |
28 |
|
29 |
return $self; |
30 |
} |
31 |
|
32 |
sub setName($$) { |
33 |
my $self = shift or die "Invalid invocation of CRL::setName\n"; |
34 |
my $name = shift; |
35 |
return 0 unless $name; |
36 |
|
37 |
$self->{"name"} = $name; |
38 |
return 1; |
39 |
} |
40 |
|
41 |
sub setData($$) { |
42 |
my $self = shift or die "Invalid invocation of CRL::setData\n"; |
43 |
my $data = shift; |
44 |
my $pemdata = undef; |
45 |
my $errormsg; |
46 |
my $openssl = OSSL->new() or $::log->err("OpenSSL not found") and return 0; |
47 |
|
48 |
# try to recognise data type and normalise to PEM string |
49 |
# but extract only the first blob of PEM (so max one CRL per data object) |
50 |
# |
51 |
if ( $data =~ |
52 |
/(^-----BEGIN X509 CRL-----\n[^-]+\n-----END X509 CRL-----$)/sm ) { |
53 |
$pemdata = $1; |
54 |
} elsif ( substr($data,0,1) eq "0" ) { # looks a bit like an ASN.1 SEQ |
55 |
($pemdata,$errormsg) = |
56 |
$openssl->Exec3($data, qw/ crl -inform DER -outform PEM / ); |
57 |
$pemdata or |
58 |
$::log->warn("Apparent DER data for",$self->{"name"},"not recognised") |
59 |
and return 0; |
60 |
} else { |
61 |
$::log->warn("CRL data for",$self->{"name"},"not recognised"); |
62 |
return 0; |
63 |
} |
64 |
|
65 |
# extract other data from the pem blob with openssl |
66 |
(my $statusdata,$errormsg) = |
67 |
$openssl->Exec3($pemdata, qw/ crl |
68 |
-noout -issuer -sha1 -fingerprint -lastupdate -nextupdate/); |
69 |
defined $statusdata or do { |
70 |
( my $eline = $errormsg ) =~ s/\n.*//sgm; |
71 |
$::log->warn("Unable to extract CRL data for",$self->{"name"},$eline); |
72 |
return 0; |
73 |
}; |
74 |
$statusdata =~ /(?:^|\n)SHA1 Fingerprint=([^\n]+)\n/ and |
75 |
$self->{"sha1fp"} = $1; |
76 |
$statusdata =~ /(?:^|\n)issuer=([^\n]+)\n/ and |
77 |
$self->{"issuer"} = $1; |
78 |
$statusdata =~ /(?:^|\n)lastUpdate=([^\n]+)\n/ and |
79 |
$self->{"lastupdatestr"} = $1; |
80 |
$statusdata =~ /(?:^|\n)nextUpdate=([^\n]+)\n/ and |
81 |
$self->{"nextupdatestr"} = $1; |
82 |
|
83 |
$self->{"nextupdatestr"} and |
84 |
$self->{"nextupdate"} = $openssl->gms2t($self->{"nextupdatestr"}); |
85 |
$self->{"lastupdatestr"} and |
86 |
$self->{"lastupdate"} = $openssl->gms2t($self->{"lastupdatestr"}); |
87 |
|
88 |
#$self->{"nextupdate"} = time - 200; |
89 |
#$self->{"lastupdate"} = time + 200; |
90 |
|
91 |
$self->{"data"} = $data; |
92 |
$self->{"pemdata"} = $pemdata; |
93 |
|
94 |
return 1; |
95 |
} |
96 |
|
97 |
sub getLastUpdate($) { |
98 |
my $self = shift or die "Invalid invocation of CRL::getLastUpdate\n"; |
99 |
return $self->{"lastupdate"} || undef; |
100 |
} |
101 |
|
102 |
sub getNextUpdate($) { |
103 |
my $self = shift or die "Invalid invocation of CRL::getNextUpdate\n"; |
104 |
return $self->{"nextupdate"} || undef; |
105 |
} |
106 |
|
107 |
sub getAttribute($$) { |
108 |
my $self = shift or die "Invalid invocation of CRL::getNextUpdate\n"; |
109 |
my $key = shift; |
110 |
return $self->{$key} or undef; |
111 |
} |
112 |
|
113 |
sub getPEMdata($) { |
114 |
my $self = shift or die "Invalid invocation of CRL::getPEMdata\n"; |
115 |
$self->{"pemdata"} or |
116 |
$::log->err("Attempt to extract PEM data from bad CRL object", |
117 |
($self->{"name"}||"unknown")) and |
118 |
return undef; |
119 |
return $self->{"pemdata"}; |
120 |
} |
121 |
|
122 |
sub verify($@) { |
123 |
my $self = shift or die "Invalid invocation of CRL::verify\n"; |
124 |
my $openssl = OSSL->new() or $::log->err("OpenSSL not found") and return 0; |
125 |
$self->{"pemdata"} or |
126 |
$::log->err("verify called on empty data blob") and return 0; |
127 |
|
128 |
my @verifyStatus = (); |
129 |
# openssl crl verify works against a single CA and does not need a |
130 |
# full chain to be present. That suits us file (checked with OpenSSL |
131 |
# 0.9.5a and 1.0.0a) |
132 |
|
133 |
my $verifyOK; |
134 |
foreach my $cafile ( @_ ) { |
135 |
-e $cafile or |
136 |
$::log->err("CRL::verify called with nonexistent CA file $cafile") and |
137 |
next; |
138 |
|
139 |
my ($dataout,$dataerr) = |
140 |
$openssl->Exec3($self->{"pemdata"}, qw/crl -noout -CAfile/,$cafile); |
141 |
$dataerr and $dataout .= $dataerr; |
142 |
$dataout =~ /verify OK/ and $verifyOK = $cafile and last; |
143 |
} |
144 |
$verifyOK or push @verifyStatus, "CRL signature failed"; |
145 |
$verifyOK and |
146 |
$::log->verb(4,"Verified CRL",$self->{"name"},"against $verifyOK"); |
147 |
|
148 |
$self->{"nextupdate"} or |
149 |
push @verifyStatus, "CRL nextUpdate determination failed"; |
150 |
$self->{"lastupdate"} or |
151 |
push @verifyStatus, "CRL lastUpdate determination failed"; |
152 |
if ( $self->{"nextupdate"} and $self->{"nextupdate"} < time ) { |
153 |
push @verifyStatus, "CRL has nextUpdate time in the past"; |
154 |
} |
155 |
if ( $self->{"lastupdate"} and $self->{"lastupdate"} > time ) { |
156 |
push @verifyStatus, "CRL has lastUpdate time in the future"; |
157 |
} |
158 |
|
159 |
return @verifyStatus; |
160 |
} |
161 |
|
162 |
|
163 |
1; |