1 |
davidg |
1758 |
# |
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; |