1 |
# |
2 |
# @(#)$Id$ |
3 |
# |
4 |
# |
5 |
package OSSL; |
6 |
use strict; |
7 |
use POSIX; |
8 |
use File::Temp qw/ tempfile /; |
9 |
use IPC::Open3; |
10 |
use IO::Select; |
11 |
use Time::Local; |
12 |
use vars qw/ $log $cnf $opensslversion /; |
13 |
|
14 |
# Syntax: |
15 |
# OSSL->new( [path] ); |
16 |
# OSSL->setName( name); |
17 |
|
18 |
# |
19 |
sub new { |
20 |
my $obref = {}; bless $obref; |
21 |
my $self = shift; |
22 |
$self = $obref; |
23 |
my $openssl = shift; |
24 |
$self->{"openssl"} = "openssl"; |
25 |
$self->{"openssl"} = $::cnf->{_}->{"openssl"} if $::cnf->{_}->{"openssl"}; |
26 |
$self->setOpenSSL($openssl) if $openssl; |
27 |
$self->{"version"} = undef; |
28 |
return $self; |
29 |
} |
30 |
|
31 |
sub setOpenSSL($$) { |
32 |
my $self = shift or die "Invalid invocation of CRL::setOpenSSL\n"; |
33 |
my $openssl = shift; |
34 |
return 0 unless $openssl; |
35 |
|
36 |
$openssl =~ /\// and ! -x "$openssl" or |
37 |
$::log->err("OpenSSL binary $openssl is not executable or does not exist") |
38 |
and return 0; |
39 |
|
40 |
$::log->verb(4,"Using OpenSSL at $openssl"); |
41 |
$self->{"openssl"} = $openssl; |
42 |
$self->{"version"} = undef; |
43 |
|
44 |
return 1; |
45 |
} |
46 |
|
47 |
sub getVersion($) { |
48 |
my $self = shift or die "Invalid invocation of CRL::getVersion\n"; |
49 |
#$self->{"version"} and return $self->{"version"}; |
50 |
$opensslversion and return $opensslversion; |
51 |
|
52 |
my ($data,$errors) = $self->Exec3(undef,qw/version/); |
53 |
if ( defined $data ) { |
54 |
$data =~ /^OpenSSL\s+([\d\.]+\w)/ or |
55 |
$::log->err("Cannot get OpenSSL version from command: invalid format in $data".($errors?" ($errors)":"")) and |
56 |
return undef; |
57 |
|
58 |
$self->{"version"} = $1; |
59 |
$opensslversion = $self->{"version"}; |
60 |
return $1; |
61 |
} else { |
62 |
$::log->err("Cannot get OpenSSL version from command: $errors"); |
63 |
return undef; |
64 |
} |
65 |
} |
66 |
|
67 |
sub Exec3select($$@) { |
68 |
my $self = shift or die "Invalid invocation of CRL::OpenSSL\n"; |
69 |
my $datain = shift; |
70 |
my ($dataout, $dataerr) = ("",undef); |
71 |
my $rc = 0; |
72 |
local(*CMD_IN, *CMD_OUT, *CMD_ERR); |
73 |
|
74 |
$::log->verb(6,"Executing openssl",@_); |
75 |
my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $self->{"openssl"}, @_ ); |
76 |
|
77 |
$SIG{CHLD} = sub { |
78 |
$rc = $? >> 8 if waitpid($pid, 0) > 0 |
79 |
}; |
80 |
$datain and print CMD_IN $datain; |
81 |
close(CMD_IN); |
82 |
print STDERR "Printed " . length($datain). " bytes of data\n"; |
83 |
|
84 |
my $selector = IO::Select->new(); |
85 |
$selector->add(*CMD_ERR); |
86 |
$selector->add(*CMD_OUT); |
87 |
|
88 |
my ($char,$cnt); |
89 |
while ($selector->count) { |
90 |
my @ready = $selector->can_read(1); |
91 |
#my @ready = IO::Select->select($selector,undef,undef,1); |
92 |
foreach my $fh (@ready) { |
93 |
if (fileno($fh) == fileno(CMD_ERR)) { |
94 |
$cnt = sysread CMD_ERR, $char, 1; |
95 |
if ( $cnt ) { $dataerr .= $char; } |
96 |
else { $selector->remove($fh); $dataerr and print STDERR "$dataerr\n";} |
97 |
} else { |
98 |
$cnt = sysread CMD_OUT, $char, 1; |
99 |
if ( $cnt ) { $dataout .= $char; } |
100 |
else { $selector->remove($fh); $dataout and print STDERR "$dataout\n"; } |
101 |
} |
102 |
$selector->remove($fh) if eof($fh); |
103 |
} |
104 |
} |
105 |
close(CMD_OUT); |
106 |
close(CMD_ERR); |
107 |
|
108 |
if ( $rc >> 8 ) { |
109 |
$::log->warn("Execute openssl " . $ARGV[0] . " failed: $rc"); |
110 |
(my $errmsg = $dataerr) =~ s/\n.*//sgm; |
111 |
$::log->verb(6,"STDERR:",$errmsg); |
112 |
return undef unless wantarray; |
113 |
return (undef,$dataerr); |
114 |
} |
115 |
return $dataout unless wantarray; |
116 |
return ($dataout,$dataerr); |
117 |
} |
118 |
|
119 |
sub Exec3pipe($$@) { |
120 |
my $self = shift or die "Invalid invocation of CRL::OpenSSL\n"; |
121 |
my $datain = shift; |
122 |
my ($dataout, $dataerr) = ("",undef); |
123 |
my $rc = 0; |
124 |
local(*CMD_IN, *CMD_OUT, *CMD_ERR); |
125 |
|
126 |
$::log->verb(6,"Executing openssl",@_); |
127 |
|
128 |
my ($tmpfh,$tmpname); |
129 |
$datain and do { |
130 |
($tmpfh,$tmpname) = tempfile("fetchcrl3.XXXXXX", DIR=>'/tmp'); |
131 |
$|=1; |
132 |
print $tmpfh $datain; |
133 |
close $tmpfh; |
134 |
push @_, "-in", $tmpname; |
135 |
select undef,undef,undef,0.01; |
136 |
}; |
137 |
|
138 |
$|=1; |
139 |
|
140 |
my $pid = open3( *CMD_IN, *CMD_OUT, *CMD_ERR, $self->{"openssl"}, @_ ); |
141 |
|
142 |
# allow delay for child to startup - but will hang on many older platforms |
143 |
select undef,undef,undef,0.15; |
144 |
|
145 |
$SIG{CHLD} = sub { |
146 |
$rc = $? >> 8 if waitpid($pid, 0) > 0 |
147 |
}; |
148 |
|
149 |
#close(CMD_IN); |
150 |
CMD_OUT->autoflush; |
151 |
CMD_ERR->autoflush; |
152 |
|
153 |
my $selector = IO::Select->new(); |
154 |
$selector->add(*CMD_ERR, *CMD_OUT); |
155 |
|
156 |
while (my @ready = $selector->can_read(0.01)) { |
157 |
foreach my $fh (@ready) { |
158 |
if (fileno($fh) == fileno(CMD_ERR)) {$dataerr .= scalar <CMD_ERR>} |
159 |
else {$dataout .= scalar <CMD_OUT>} |
160 |
$selector->remove($fh) if eof($fh); |
161 |
} |
162 |
} |
163 |
close(CMD_OUT); |
164 |
close(CMD_ERR); |
165 |
$tmpname and unlink $tmpname; |
166 |
|
167 |
if ( $rc >> 8 ) { |
168 |
$::log->warn("Execute openssl " . $ARGV[0] . " failed: $rc"); |
169 |
(my $errmsg = $dataerr) =~ s/\n.*//sgm; |
170 |
$::log->verb(6,"STDERR:",$errmsg); |
171 |
return undef unless wantarray; |
172 |
return (undef,$dataerr); |
173 |
} |
174 |
return $dataout unless wantarray; |
175 |
return ($dataout,$dataerr); |
176 |
} |
177 |
|
178 |
|
179 |
sub Exec3file($$@) { |
180 |
my $self = shift or die "Invalid invocation of CRL::OpenSSL\n"; |
181 |
my $datain = shift; |
182 |
my ($dataout, $dataerr) = ("",undef); |
183 |
my $rc = 0; |
184 |
local(*CMD_IN, *CMD_OUT, *CMD_ERR); |
185 |
|
186 |
$::log->verb(6,"Executing openssl",@_); |
187 |
|
188 |
my ($tmpin,$tmpinname); |
189 |
my ($tmpout,$tmpoutname); |
190 |
my ($tmperr,$tmperrname); |
191 |
|
192 |
my $tmpdir = $::cnf->{_}->{exec3tmpdir} || $ENV{"TMPDIR"} || '/tmp'; |
193 |
|
194 |
$|=1; |
195 |
$datain and do { |
196 |
($tmpin,$tmpinname) = tempfile("fetchcrl3in.XXXXXX", |
197 |
DIR=>$tmpdir); |
198 |
print $tmpin $datain; |
199 |
close $tmpin; |
200 |
}; |
201 |
($tmpout,$tmpoutname) = tempfile("fetchcrl3out.XXXXXX", |
202 |
DIR=>$tmpdir); |
203 |
($tmperr,$tmperrname) = tempfile("fetchcrl3out.XXXXXX", |
204 |
DIR=>$tmpdir); |
205 |
|
206 |
my $pid = fork(); |
207 |
|
208 |
defined $pid or |
209 |
$::log->warn("Internal error, fork for openssl failed: $!") and |
210 |
return undef; |
211 |
|
212 |
if ( $pid == 0 ) { # I'm a kid |
213 |
close STDIN; |
214 |
if ( $tmpinname ) { |
215 |
open STDIN, "<", $tmpinname or |
216 |
die "Cannot open tempfile $tmpinname again $!\n"; |
217 |
} else { |
218 |
open STDIN, "<", "/dev/null" or |
219 |
die "Cannot open /dev/null ??? $!\n"; |
220 |
} |
221 |
close STDOUT; |
222 |
if ( $tmpoutname ) { |
223 |
open STDOUT, ">", $tmpoutname or |
224 |
die "Cannot open tempfile $tmpoutname again $!\n"; |
225 |
} else { |
226 |
open STDOUT, ">", "/dev/null" or |
227 |
die "Cannot open /dev/null ??? $!\n"; |
228 |
} |
229 |
close STDERR; |
230 |
if ( $tmpoutname ) { |
231 |
open STDERR, ">", $tmperrname or |
232 |
die "Cannot open tempfile $tmperrname again $!\n"; |
233 |
} else { |
234 |
open STDERR, ">", "/dev/null" or |
235 |
die "Cannot open /dev/null ??? $!\n"; |
236 |
} |
237 |
exec $self->{"openssl"}, @_; |
238 |
} |
239 |
$rc = $? >> 8 if waitpid($pid, 0) > 0; |
240 |
|
241 |
{ local $/; $dataout = <$tmpout>; }; |
242 |
{ local $/; $dataerr = <$tmperr>; }; |
243 |
|
244 |
$tmpinname and unlink $tmpinname; |
245 |
$tmpoutname and unlink $tmpoutname; |
246 |
$tmperrname and unlink $tmperrname; |
247 |
|
248 |
if ( $rc >> 8 ) { |
249 |
$::log->warn("Execute openssl " . $ARGV[0] . " failed: $rc"); |
250 |
(my $errmsg = $dataerr) =~ s/\n.*//sgm; |
251 |
$::log->verb(6,"STDERR:",$errmsg); |
252 |
return undef unless wantarray; |
253 |
return (undef,$dataerr); |
254 |
} |
255 |
return $dataout unless wantarray; |
256 |
return ($dataout,$dataerr); |
257 |
} |
258 |
|
259 |
sub Exec3($@) { |
260 |
my $self = shift; |
261 |
|
262 |
grep /^pipe$/, $::cnf->{_}->{exec3mode}||"" and return $self->Exec3pipe(@_); |
263 |
grep /^select$/, $::cnf->{_}->{exec3mode}||"" and return $self->Exec3select(@_); |
264 |
return $self->Exec3file(@_); # default |
265 |
} |
266 |
|
267 |
sub gms2t($$) { |
268 |
my $self = shift; |
269 |
my ( $month, $mday, $htm, $year, $tz ) = split(/\s+/,$_[0]); |
270 |
die "OSSL::gms2t: cannot hangle non GMT output from OpenSSL\n" |
271 |
unless $tz eq "GMT"; |
272 |
|
273 |
my %mon=("Jan"=>0,"Feb"=>1,"Mar"=>2,"Apr"=>3,"May"=>4,"Jun"=>5, |
274 |
"Jul"=>6,"Aug"=>7,"Sep"=>8,"Oct"=>9,"Nov"=>10,"Dec"=>11); |
275 |
|
276 |
my ( $hrs,$min,$sec ) = split(/:/,$htm); |
277 |
my $gmt = timegm($sec,$min,$hrs,$mday,$mon{$month},$year); |
278 |
|
279 |
#print STDERR ">>> converted $_[0] to $gmt\n"; |
280 |
return $gmt; |
281 |
} |
282 |
|
283 |
|
284 |
1; |