Codebase list libnet-sslglue-perl / b1cb840
Imported Upstream version 1.052 Dominic Hargreaves 10 years ago
15 changed file(s) with 984 addition(s) and 118 deletion(s). Raw diff Collapse all Expand all
0 1.052 2014/01/16
1 - FTPS: reuse same SSL session for control and data channnel to work
2 with default configuration of proftpd.
3
4 1.051 2014/01/10
5 - fixes to Net::FTP SSL support
6 - examples/ftps-tests.pl has lots of tests for FTP against live server
7
8 1.05 2014/01/09
9 - added support for SSL+IPv6 in Net::FTP
10 - new package Net::SSLGlue::Socket for a socket which combines plain,ssl,ipv6
11 - fixed some tests - some checks for bad certificates do not work anymore
12 because these certs were fixed
13
014 1.04 2013/08/01
115 replace Net::Cmd::getline via Net::SSLGlue::POP3 because it assumed, that it
216 just needs to wait for read events on the sockets - which is not the case for
0 Changes
1 COPYRIGHT
2 examples/ftps-direct.pl
3 examples/ftps-starttls.pl
4 examples/ftps-tests.pl
5 examples/lwp.pl
6 examples/lwp_post.pl
7 examples/send-ssl-mail.pl
8 examples/send-starttls-mail.pl
09 lib/Net/SSLGlue.pm
10 lib/Net/SSLGlue/FTP.pm
111 lib/Net/SSLGlue/LDAP.pm
212 lib/Net/SSLGlue/LWP.pm
13 lib/Net/SSLGlue/POP3.pm
314 lib/Net/SSLGlue/SMTP.pm
4 lib/Net/SSLGlue/POP3.pm
15 lib/Net/SSLGlue/Socket.pm
516 Makefile.PL
617 MANIFEST This list of files
18 README
719 t/01_load.t
820 t/external/02_smtp.t
921 t/external/03_lwp.t
1022 t/external/04_pop3.t
11 TODO
12 COPYRIGHT
13 examples/lwp.pl
14 examples/lwp_post.pl
15 examples/send-ssl-mail.pl
16 examples/send-starttls-mail.pl
17 Changes
18 README
23 t/external/05_ftp.t
1924 META.yml Module meta-data (added by MakeMaker)
00 --- #YAML:1.0
11 name: Net-SSLGlue
2 version: 1.04
2 version: 1.052
33 abstract: ~
44 author: []
55 license: unknown
1010 ExtUtils::MakeMaker: 0
1111 requires:
1212 IO::Socket::SSL: 1.19
13 resources:
14 repository: https://github.com/noxxi/p5-net-sslglue
1315 no_index:
1416 directory:
1517 - t
00 use ExtUtils::MakeMaker;
11 require 5.008;
22 my $xt = prompt( "Should I do external tests?\n".
3 "These tests will fail if there is no internet connection or if a firewall\n".
4 "blocks some traffic.\n".
5 "[y/N]", 'n' );
3 "These tests will fail if there is no internet connection or if a firewall\n".
4 "blocks some traffic.\n".
5 "[y/N]", 'n' );
66 WriteMakefile(
7 NAME => 'Net::SSLGlue',
8 VERSION_FROM => 'lib/Net/SSLGlue.pm',
9 PREREQ_PM => {
10 'IO::Socket::SSL' => 1.19,
7 NAME => 'Net::SSLGlue',
8 VERSION_FROM => 'lib/Net/SSLGlue.pm',
9 PREREQ_PM => {
10 'IO::Socket::SSL' => 1.19,
11 },
12 $xt =~m{^y}i ? ( test => { TESTS => 't/*.t t/external/*.t' }):(),
13 META_MERGE => {
14 resources => {
15 repository => 'https://github.com/noxxi/p5-net-sslglue',
1116 },
12 $xt =~m{^y}i ? ( test => { TESTS => 't/*.t t/external/*.t' }):(),
17 },
1318 );
+0
-1
TODO less more
0 ldap tests
0 use strict;
1 use warnings;
2 use Net::SSLGlue::FTP;
3
4 my $ftp = Net::FTP->new( 'ftp.example.com',
5 SSL => 1,
6 SSL_ca_path => '/etc/ssl/certs',
7 Passive => 1,
8 Debug => 1,
9 );
10 $ftp->login('foo','bar');
11 print $ftp->ls;
0 use strict;
1 use warnings;
2 use Net::SSLGlue::FTP;
3
4 my $ftp = Net::FTP->new( 'ftp.example.com',
5 Passive => 1,
6 Debug => 1,
7 );
8 $ftp->starttls( SSL_ca_path => '/etc/ssl/certs' )
9 or die "tls upgrade failed";
10 $ftp->login('foo','bar');
11 print $ftp->ls;
12
13 # change protection to clear
14 $ftp->prot('C');
15 $ftp->ls;
16
17 # stop TLS on control channel
18 $ftp->stoptls;
19 $ftp->ls;
20
0 #!/usr/bin/perl
1 use strict;
2 use warnings;
3
4 # This runs lots of tests with SSL against a test server
5 # - plain
6 # - with SSL upgrade and plain data connections
7 # - with SSL upgrade and SSL data connections
8 # - with SSL upgrade and downgrade after auth
9 # - with direct SSL connection
10
11 # setup stuff here
12 # you need a server where you can write and read and create directories
13 # SSL support is optional, but preferred
14 # IPv6 support should be possible
15
16 my $testhost = '127.0.0.1'; # where your test server is, IPv6 should be ok
17 my $plain_port = 2021; # port where server listens for plain ftp
18 my $user = 'foo'; # login as user
19 my $pass = 'bar'; # with pass
20 my $can_auth = 1; # does server support AUTH TLS
21 my $ssl_port = 2090; # does server support direct SSL
22 my %sslargs = (
23 # should be enabled if you want to verify certificates
24 SSL_verify_mode => 1,
25 # for CAs known to the system this might be maybe ommitted
26 # otherwise set this or SSL_ca_path
27 SSL_ca_file => 'ca.pem',
28 # if the certificate has a different name then $testhost set it here
29 SSL_verifycn_name => 'server.local',
30 );
31
32
33 use Net::SSLGlue::FTP;
34 use IO::Socket::SSL;
35 use Carp 'croak';
36
37 my @test = (
38 # basic FTP server stuff
39 { Passive => 0 },
40 { Passive => 1 },
41 $can_auth ? (
42 # SSL upgrade with data connections unprotected
43 { Passive => 0, _starttls => 1, _prot => 'C' },
44 { Passive => 1, _starttls => 1, _prot => 'C' },
45 # SSL upgrade with data connections protected
46 { Passive => 0, _starttls => 1 },
47 { Passive => 1, _starttls => 1 },
48 # SSL upgrade with SSL downgrade after auth
49 { Passive => 0, _starttls => 1, _stoptls => 1 },
50 { Passive => 1, _starttls => 1, _stoptls => 1 },
51 ):(),
52 # direct SSL on separate port
53 $ssl_port ? (
54 { Passive => 0, SSL => 1, Port => $ssl_port },
55 { Passive => 1, SSL => 1, Port => $ssl_port },
56 ):(),
57 );
58
59 my $testbase = sprintf("test-%04x%04x-",rand(2**16),rand(2**16));
60 for( my $i=0;$i<@test;$i++ ) {
61
62 my %conf = %{$test[$i]};
63 my $starttls = delete $conf{_starttls};
64 my $stoptls = delete $conf{_stoptls};
65 my $prot = delete $conf{_prot};
66 my $dir = "$testbase$i";
67
68 print STDERR "------------ $dir\n";
69 my $ftp = Net::FTP->new( $testhost,
70 Port => $plain_port,
71 Debug => 1,
72 %sslargs,
73 %conf,
74 ) or die "ftp connect failed";
75
76 my $ftperr = sub {
77 my $msg = shift;
78 croak "$msg failed (@_): ".$ftp->message;
79 };
80
81 # upgrade to SSL
82 $ftp->starttls or $ftperr->('auth tls', $SSL_ERROR)
83 if $starttls;
84
85 # login
86 $ftp->login($user,$pass) or $ftperr->('login');
87
88 # downgrade from SSL
89 $ftp->stoptls or $ftperr->('ccc') if $stoptls;
90
91 # change protection level
92 $ftp->prot($prot) or $ftperr->("PROT $prot")
93 if $prot;
94
95 # create directory for test and change into it
96 $ftp->mkdir($dir) or $ftperr->('mkd');
97 $ftp->cwd($dir) or $ftperr->('cwd');
98
99 # check that dir is empty
100 my @files = $ftp->ls;
101 $ftp->ok or $ftperr->('nlst');
102 @files and die "directory should be empty";
103
104 # create a file in dir
105 $ftp->put( _s2f( my $foo = 'foo' ,'<' ), 'foo.txt' )
106 or $ftperr->('stor');
107 # append some bytes to it
108 $ftp->append( _s2f('bar'),'foo.txt' ) or $ftperr->('appe');
109 # check that it is there
110 @files = $ftp->ls;
111 "@files" eq 'foo.txt' or die "wrong ls: @files";
112
113 # retrieve file and verify content
114 $ftp->get( 'foo.txt', _s2f( $foo = '','>' ));
115 $foo eq 'foobar' or die "wrong data: 'foobar' != '$foo'";
116
117 $ftp->quit;
118 }
119
120 sub _s2f {
121 open( my $fh,$_[1] || '<',\$_[0] );
122 return $fh
123 }
0
1 package Net::SSLGlue::FTP;
2
3 use strict;
4 use warnings;
5 use Carp 'croak';
6 use IO::Socket::SSL '$SSL_ERROR';
7 use Net::SSLGlue::Socket;
8 use Socket 'AF_INET';
9
10 our $VERSION = 1.001;
11
12 BEGIN {
13 for my $class (qw(Net::FTP Net::FTP::dataconn)) {
14 eval "require $class" or die "failed to load $class";
15 no strict 'refs';
16 my $fixed;
17 for( @{ "${class}::ISA" } ) {
18 $_ eq 'IO::Socket::INET' or next;
19 $_ = 'Net::SSLGlue::Socket';
20 $fixed = 1;
21 last;
22 }
23 die "cannot replace IO::Socket::INET with Net::SSLGlue::Socket in ${class}::ISA"
24 if ! $fixed;
25 }
26 $Net::FTP::VERSION eq '2.77'
27 or warn "Not tested with Net::FTP version $Net::FTP::VERSION";
28 }
29
30 # redefine Net::FTP::new so that it understands SSL => 1 and connects directly
31 # with SSL to the server
32 {
33 no warnings 'redefine';
34 my $onew = Net::FTP->can('new');
35 *Net::FTP::new = sub {
36 my $class = shift;
37 my %args = @_%2 ? ( Host => shift(), @_ ): @_;
38 my %sslargs = map { $_ => delete $args{$_} }
39 grep { m{^SSL_} } keys %args;
40
41 my $self;
42 if ( $args{SSL} ) {
43 # go immediatly to SSL
44 # Net::FTP::new gives only specific args to socket class
45 $args{Port} ||= 990;
46 local %Net::SSLGlue::Socket::ARGS = ( SSL => 1, %sslargs );
47 $self = $onew->($class,%args) or return;
48 ${*$self}{net_ftp_tlstype} = 'P';
49 } else {
50 $self = $onew->($class,%args) or return;
51 }
52 ${*$self}{net_ftp_tlsargs} = \%sslargs;
53 return $self;
54 };
55 }
56
57 # add starttls method to upgrade connection to SSL: AUTH TLS
58 sub Net::FTP::starttls {
59 my $self = shift;
60 $self->is_ssl and croak("called starttls within SSL session");
61 $self->_AUTH('TLS') == Net::FTP::CMD_OK or return;
62
63 my $host = $self->host;
64 # for name verification strip port from domain:port, ipv4:port, [ipv6]:port
65 $host =~s{(?<!:):\d+$}{};
66
67 my %args = (
68 SSL_verify_mode => 1,
69 SSL_verifycn_scheme => 'ftp',
70 SSL_verifycn_name => $host,
71 # reuse SSL session of control connection in data connections
72 SSL_session_cache => Net::SSLGlue::FTP::SingleSessionCache->new,
73 %{ ${*$self}{net_ftp_tlsargs}},
74 @_
75 );
76
77 $self->start_SSL(%args) or return;
78 ${*$self}{net_ftp_tlsargs} = \%args;
79 $self->prot('P');
80 return 1;
81 }
82
83 # add prot method to set protection level (PROT C|P)
84 sub Net::FTP::prot {
85 my ($self,$type) = @_;
86 $type eq 'C' or $type eq 'P' or croak("type must by C or P");
87 $self->_PBSZ(0) or return;
88 $self->_PROT($type) or return;
89 ${*$self}{net_ftp_tlstype} = $type;
90 return 1;
91 }
92
93 # add stoptls method to downgrade connection from SSL: CCC
94 sub Net::FTP::stoptls {
95 my $self = shift;
96 $self->is_ssl or croak("called stoptls outside SSL session");
97 $self->_CCC() or return;
98 $self->stop_SSL();
99 return 1;
100 }
101
102 # add EPSV for new style passive mode (incl. IPv6)
103 sub Net::FTP::epsv {
104 my $self = shift;
105 @_ and croak 'usage: $ftp->epsv()';
106 delete ${*$self}{net_ftp_intern_port};
107
108 $self->_EPSV && $self->message =~ m{\(([\x33-\x7e])\1\1(\d+)\1\)}
109 ? ${*$self}{'net_ftp_pasv'} = [ $self->peerhost, $2 ]
110 : undef;
111 }
112
113 # redefine PASV so that it uses EPSV on IPv6
114 # also net_ftp_pasv contains now the parsed [ip,port]
115 {
116 no warnings 'redefine';
117 *Net::FTP::pasv = sub {
118 my $self = shift;
119 @_ and croak 'usage: $ftp->port()';
120 return $self->epsv if $self->sockdomain != AF_INET;
121 delete ${*$self}{net_ftp_intern_port};
122
123 if ( $self->_PASV &&
124 $self->message =~ m{(\d+,\d+,\d+,\d+),(\d+),(\d+)} ) {
125 my $port = 256 * $2 + $3;
126 ( my $ip = $1 ) =~s{,}{.}g;
127 return ${*$self}{'net_ftp_pasv'} = [ $ip,$port ];
128 }
129 return;
130 };
131 }
132
133 # add EPRT for new style passive mode (incl. IPv6)
134 sub Net::FTP::eprt {
135 @_ == 1 || @_ == 2 or croak 'usage: $self->eprt([PORT])';
136 return _eprt('EPRT',@_);
137 }
138
139 # redefine PORT to use EPRT for IPv6
140 {
141 no warnings 'redefine';
142 *Net::FTP::port = sub {
143 @_ == 1 || @_ == 2 or croak 'usage: $self->port([PORT])';
144 return _eprt('PORT',@_);
145 };
146 }
147
148 sub _eprt {
149 my ($cmd,$self,$port) = @_;
150 delete ${*$self}{net_ftp_intern_port};
151 unless ($port) {
152 my $listen = ${*$self}{net_ftp_listen} ||= Net::SSLGlue::Socket->new(
153 Listen => 1,
154 Timeout => $self->timeout,
155 LocalAddr => $self->sockhost,
156 );
157 ${*$self}{net_ftp_intern_port} = 1;
158 my $fam = ($listen->sockdomain == AF_INET) ? 1:2;
159 if ( $cmd eq 'EPRT' || $fam == 2 ) {
160 $port = "|$fam|".$listen->sockhost."|".$listen->sockport."|";
161 $cmd = 'EPRT';
162 } else {
163 my $p = $listen->sockport;
164 $port = join(',',split(m{\.},$listen->sockhost),$p >> 8,$p & 0xff);
165 }
166 }
167 my $ok = $cmd eq 'EPRT' ? $self->_EPRT($port) : $self->_PORT($port);
168 ${*$self}{net_ftp_port} = $port if $ok;
169 return $ok;
170 }
171
172
173
174 for my $cmd (qw(PBSZ PROT CCC EPRT EPSV)) {
175 no strict 'refs';
176 *{"Net::FTP::_$cmd"} = sub {
177 shift->command("$cmd @_")->response() == Net::FTP::CMD_OK
178 }
179 }
180
181 # redefine _dataconn to
182 # - support IPv6
183 # - upgrade data connection to SSL if PROT P
184 {
185
186 no warnings 'redefine';
187 *Net::FTP::_dataconn = sub {
188 my $self = shift;
189 my $pkg = "Net::FTP::" . $self->type;
190 eval "require $pkg";
191 $pkg =~ s/ /_/g;
192 delete ${*$self}{net_ftp_dataconn};
193
194 my $conn;
195 if ( my $pasv = ${*$self}{net_ftp_pasv} ) {
196 $conn = $pkg->new(
197 PeerAddr => $pasv->[0],
198 PeerPort => $pasv->[1],
199 LocalAddr => ${*$self}{net_ftp_localaddr},
200 ) or return;
201 } elsif (my $listen = delete ${*$self}{net_ftp_listen}) {
202 $conn = $listen->accept($pkg) or return;
203 close($listen);
204 }
205
206 if (( ${*$self}{net_ftp_tlstype} || '') eq 'P'
207 && ! $conn->start_SSL( $self->is_ssl
208 ? ( SSL_reuse_ctx => $self )
209 : ( %{${*$self}{net_ftp_tlsargs}} )
210 ) ) {
211 croak("failed to ssl upgrade dataconn: $SSL_ERROR");
212 return;
213 }
214
215 $conn->timeout($self->timeout);
216 ${*$self}{net_ftp_dataconn} = $conn;
217 ${*$conn} = "";
218 ${*$conn}{net_ftp_cmd} = $self;
219 ${*$conn}{net_ftp_blksize} = ${*$self}{net_ftp_blksize};
220 return $conn;
221 };
222 }
223
224 {
225 # Session Cache with single entry
226 # used to make sure that we reuse same session for control channel and data
227 package Net::SSLGlue::FTP::SingleSessionCache;
228 sub new { my $x; return bless \$x,shift }
229 sub add_session {
230 my ($self,$key,$session) = @_;
231 Net::SSLeay::SESSION_free($$self) if $$self;
232 $$self = $session;
233 }
234 sub get_session {
235 my $self = shift;
236 return $$self
237 }
238 sub DESTROY {
239 my $self = shift;
240 Net::SSLeay::SESSION_free($$self) if $$self;
241 }
242 }
243
244 1;
245
246 =head1 NAME
247
248 Net::SSLGlue::FTP - extend Net::FTP for FTPS (SSL) and IPv6
249
250 =head1 SYNOPSIS
251
252 use Net::SSLGlue::FTP;
253 # SSL right from start
254 my $ftps = Net::FTP->new( $host,
255 SSL => 1,
256 SSL_ca_path => ...
257 );
258
259 # SSL through upgrade of plain connection
260 my $ftp = Net::FTP->new( $host );
261 $ftp->starttls( SSL_ca_path => ... );
262
263 # change protection mode to unencrypted|encrypted
264 $ftp->prot('C'); # clear
265 $ftp->prot('P'); # protected
266
267 =head1 DESCRIPTION
268
269 L<Net::SSLGlue::FTP> extends L<Net::FTP> so one can either start directly with
270 SSL or switch later to SSL using starttls method (AUTH TLS command).
271 If IO::Socket::IP or IO::Socket::INET6 are installed it will also transparently
272 use IPv6.
273
274 By default it will take care to verify the certificate according to the rules
275 for FTP implemented in L<IO::Socket::SSL>.
276
277 =head1 METHODS
278
279 =over 4
280
281 =item new
282
283 The method C<new> of L<Net::FTP> is now able to start directly with SSL when
284 the argument C<<SSL => 1>> is given. One can give the usual C<SSL_*> parameter
285 of L<IO::Socket::SSL> to C<Net::FTP::new>.
286
287 =item starttls
288
289 If the connection is not yet SSLified it will issue the "AUTH TLS" command and
290 change the object, so that SSL will now be used. The usual C<SSL_*> parameter of
291 L<IO::Socket::SSL> will be given.
292
293 =item peer_certificate ...
294
295 Once the SSL connection is established you can use this method to get
296 information about the certificate. See the L<IO::Socket::SSL> documentation.
297
298 =back
299
300 All of these methods can take the C<SSL_*> parameter from L<IO::Socket::SSL> to
301 change the behavior of the SSL connection. The following parameters are
302 especially useful:
303
304 =over 4
305
306 =item SSL_ca_path, SSL_ca_file
307
308 Specifies the path or a file where the CAs used for checking the certificates
309 are located. This is typically L</etc/ssl/certs> on UNIX systems.
310
311 =item SSL_verify_mode
312
313 If set to 0, verification of the certificate will be disabled. By default
314 it is set to 1 which means that the peer certificate is checked.
315
316 =item SSL_verifycn_name
317
318 Usually the name given as the hostname in the constructor is used to verify the
319 identity of the certificate. If you want to check the certificate against
320 another name you can specify it with this parameter.
321
322 =back
323
324 =head1 SEE ALSO
325
326 IO::Socket::SSL, Net::FTP, Net::SSLGlue::Socket
327
328 =head1 COPYRIGHT
329
330 This module is copyright (c) 2013, Steffen Ullrich.
331 All Rights Reserved.
332 This module is free software. It may be used, redistributed and/or modified
333 under the same terms as Perl itself.
0
1 package Net::SSLGlue::Socket;
2 our $VERSION = 1.001;
3
4 use strict;
5 use warnings;
6 use Carp 'croak';
7 use Symbol 'gensym';
8 use IO::Socket::SSL;
9 my $IPCLASS;
10 BEGIN {
11 for(qw(IO::Socket::IP IO::Socket::INET6 IO::Socket::INET)) {
12 $IPCLASS = $_,last if eval "require $_";
13 }
14 }
15
16 # this can be overwritten (with local) to get arguments passed around
17 # to strict calls of the socket class new
18 our %ARGS;
19
20 sub new {
21 my $class = shift;
22 my %args = @_>1 ? @_ : ( PeerAddr => shift() );
23 %args = ( %args, %ARGS );
24
25 my %sslargs;
26 for(keys %args) {
27 $sslargs{$_} = delete $args{$_} if m{^SSL_};
28 }
29
30 my $ssl = delete $args{SSL};
31 my $sock = $ssl
32 ? IO::Socket::SSL->new(%args,%sslargs)
33 : $IPCLASS->new(%args)
34 or return;
35
36 my $self = gensym();
37 *$self = *$sock; # clone handle
38 bless $self,$class;
39 ${*$self}{sock} = $sock;
40 ${*$self}{ssl} = $ssl;
41 ${*$self}{sslargs} = \%sslargs;
42
43 return $self;
44 }
45
46 for my $sub (qw(
47 fileno sysread syswrite close connect fcntl
48 read write readline print printf getc say eof getline getlines
49 blocking autoflush timeout
50 sockhost sockport peerhost peerport sockdomain
51 truncate stat setbuf setvbuf fdopen ungetc send recv
52 )) {
53 no strict 'refs';
54 *$sub = sub {
55 my $self = shift;
56 my $sock = ${*$self}{sock} or return;
57 my $sock_sub = $sock->can($sub) or croak("$sock does not support $sub");
58 unshift @_,$sock;
59 # warn "*** $sub called";
60 goto &$sock_sub;
61 };
62 }
63
64 sub accept {
65 my ($self,$class) = @_;
66 my $sock = ${*$self}{sock} or return;
67 my $conn = $sock->accept();
68
69 return bless $conn,$class
70 if $class && ! $class->isa('Net::SSLGlue::Socket');
71
72 $class ||= ref($self);
73 my $wrap = gensym;
74 *$wrap = *$conn; # clone original handle
75 bless $wrap, $class;
76 ${*$wrap}{sock} = $conn;
77 ${*$wrap}{ssl} = ${*$self}{ssl};
78 ${*$wrap}{sslargs} = ${*$self}{sslargs};
79 return $wrap;
80 };
81
82 sub start_SSL {
83 my $self = shift;
84 croak("start_SSL called on SSL socket") if ${*$self}{ssl};
85 IO::Socket::SSL->start_SSL(${*$self}{sock},%{${*$self}{sslargs}},@_)
86 or return;
87 ${*$self}{ssl} = 1;
88 return $self;
89 }
90
91 sub stop_SSL {
92 my $self = shift;
93 croak("stop_SSL called on plain socket") if ! ${*$self}{ssl};
94 ${*$self}{sock}->stop_SSL(@_) or return;
95 ${*$self}{ssl} = 0;
96 return $self;
97 }
98
99 sub can_read {
100 my ($self,$timeout) = @_;
101 return 1 if ${*$self}{ssl} && ${*$self}{sock}->pending;
102 vec( my $vec,fileno(${*$self}{sock}),1) = 1;
103 return select($vec,undef,undef,$timeout);
104 }
105
106 sub peer_certificate {
107 my $self = shift;
108 return ${*$self}{ssl} && ${*$self}{sock}->peer_certificate(@_);
109 }
110
111 sub is_ssl {
112 my $self = shift;
113 return ${*$self}{ssl} && ${*$self}{sock};
114 }
115
116 1;
117
118 =head1 NAME
119
120 Net::SSLGlue::Socket - socket which can be either SSL or plain IP (IPv4/IPv6)
121
122 =head1 SYNOPSIS
123
124 use Net::SSLGlue::Socket;
125 # SSL right from start
126 my $ssl = Net::SSLGlue::Socket->new(
127 PeerHost => ..., # IPv4|IPv6 address
128 PeerPort => ...,
129 SSL => 1,
130 SSL_ca_path => ...
131 );
132
133 # SSL through upgrade of plain connection
134 my $plain = Net::SSLGlue::Socket->new(...);
135 $plain->start_SSL( SSL_ca_path => ... );
136 ...
137 $plain->stop_SSL
138
139
140 =head1 DESCRIPTION
141
142 L<Net::SSLGlue::Socket> implements a socket which can be either plain or SSL.
143 If IO::Socket::IP or IO::Socket::INET6 are installed it will also transparently
144 handle IPv6 connections.
145
146 A socket can be either start directly with SSL or it can be start plain and
147 later be upgraded to SSL (because of a STARTTLS commando or similar) and also
148 downgraded again.
149
150 It is possible but not recommended to use the socket in non-blocking
151 mode, because in this case special care must be taken with SSL (see
152 documentation of L<IO::Socket::SSL>).
153
154 Additionally to the usual socket methods the following methods are defined or
155 extended:
156
157 =head1 METHODS
158
159 =over 4
160
161 =item new
162
163 The method C<new> of L<Net::SSLGlue::Socket> can have the argument SSL. If this
164 is true the SSL upgrade will be done immediatly. If not set any SSL_* args will
165 still be saved and used at a later start_SSL call.
166
167 =item start_SSL
168
169 This will upgrade the plain socket to SSL. See L<IO::Socket::SSL> for
170 arguments to C<start_SSL>. Any SSL_* arguments given to new will be applied
171 here too.
172
173 =item stop_SSL
174
175 This will downgrade the socket from SSL to plain.
176
177 =item peer_certificate ...
178
179 Once the SSL connection is established you can use this method to get
180 information about the certificate. See the L<IO::Socket::SSL> documentation.
181
182 =item can_read(timeout)
183
184 This will check for available data. For a plain socket this will only use
185 C<select> to check the socket, but for SSL it will check if there are any
186 pending data before trying a select.
187 Because SSL needs to read the whole frame before decryption can be done, a
188 successful return of can_read is no guarantee that data can be read
189 immediatly, only that new data are either available or in the process of
190 arriving.
191
192 =back
193
194 =head1 SEE ALSO
195
196 IO::Socket::SSL
197
198 =head1 COPYRIGHT
199
200 This module is copyright (c) 2013, Steffen Ullrich.
201 All Rights Reserved.
202 This module is free software. It may be used, redistributed and/or modified
203 under the same terms as Perl itself.
00 package Net::SSLGlue;
1 our $VERSION = '1.04';
1 our $VERSION = '1.052';
22
33 =head1 NAME
44
2020
2121 =item Net::POP3 - add SSL from beginning or using STLS
2222
23 =item Net::FTP - add SSL and IPv6 support to Net::FTP
24
2325 =item Net::LDAP - add proper certificate checking
2426
2527 =item LWP - add proper certificate checking
2628
2729 =back
30
31 There is also a Net::SSLGlue::Socket package which combines ssl and non-ssl
32 and ipv6 capabilities to make it easier to enhance modules based on
33 IO::Socket::INET.
2834
2935 =head1 COPYRIGHT
3036
3737 exit
3838 };
3939
40 # ssl to the wrong host
41 # the certificate mail.gmx.de returns is for mail.gmx.net
42 diag( "connect ssl to mail.gmx.de:465" );
43 IO::Socket::SSL->new(
44 PeerAddr => 'mail.gmx.de:465',
45 SSL_ca_path => $capath,
46 SSL_verify_mode => 1,
47 SSL_verifycn_scheme => 'smtp'
48 ) and do {
49 print "1..0 # mail.gmx.de:465 reachable with SSL\n";
50 exit
51 };
5240
53 print "1..6\n";
41 print "1..3\n";
5442
5543 # first direct SSL
5644 my $smtp = Net::SMTP->new( 'mail.gmx.net',
6654 # check that we can talk on connection
6755 print $smtp->quit ? "ok\n": "not ok # quit failed\n";
6856
69 # against wrong host should fail
70 $smtp = Net::SMTP->new( 'mail.gmx.de' ); # should succeed
71 $ok = $smtp->starttls( SSL_ca_path => $capath );
72 print $ok ? "not ok # smtp starttls mail.gmx.de did not fail\n": "ok\n";
73
74 # but not if we specify the right SSL_verifycn_name
75 $smtp = Net::SMTP->new( 'mail.gmx.de' ); # should succeed
76 $ok = $smtp->starttls( SSL_ca_path => $capath, SSL_verifycn_name => 'mail.gmx.net' );
77 print $ok ? "ok\n" : "not ok # smtp starttls mail.gmx.de/net\n";
78
79 # or disable verification
80 $smtp = Net::SMTP->new( 'mail.gmx.de' ); # should succeed
81 $ok = $smtp->starttls( SSL_verify_mode => 0 );
82 print $ok ? "ok\n" : "not ok # smtp starttls mail.gmx.de\n";
8357
8458 sub diag {
8559 #print STDERR "@_\n"
22 use warnings;
33
44 BEGIN {
5 eval "use LWP";
6 if ( $@ ) {
7 print "1..0 # no LWP\n";
8 exit
9 }
5 eval "use LWP";
6 if ( $@ ) {
7 print "1..0 # no LWP\n";
8 exit
9 }
1010 }
1111
1212 use Net::SSLGlue::LWP;
13 use IO::Socket::SSL;
1314 use LWP::Simple;
15
16 my $goodhost = 'google.de';
17 # this does not work any longer - will be skipped in test
18 my $badhost = 'www.fedora.org';
1419
1520 my $capath = '/etc/ssl/certs/'; # unix?
1621 -d $capath or do {
17 print "1..0 # cannot find system CA-path\n";
18 exit
22 print "1..0 # cannot find system CA-path\n";
23 exit
1924 };
20 Net::SSLGlue::LWP->import( SSL_ca_path => $capath );
25 Net::SSLGlue::LWP->import(
26 SSL_ca_path => $capath,
27 # LWP might define SSL_ca_file - remove it to avoid conflict
28 SSL_ca_file => undef
29 );
2130
2231 #
2332 # first check everything directly with IO::Socket::SSL
2433 #
2534
26 # signin.ebay.de has a certificate, which is for signin.ebay.com
27 # but where signin.ebay.de is a subjectAltName
28 IO::Socket::SSL->new(
29 PeerAddr => 'signin.ebay.de:443',
35 diag("connecting to $goodhost:443 with IO::Socket::INET");
36 my $sock = IO::Socket::INET->new(
37 PeerAddr => "$goodhost:443",
38 Timeout => 10
39 ) or do {
40 print "1..0 # connect $goodhost failed: $!\n";
41 exit
42 };
43 diag("ssl upgrade $goodhost");
44 IO::Socket::SSL->start_SSL( $sock,
45 SSL_ca_path => $capath,
46 SSL_verifycn_name => "$goodhost",
47 SSL_verify_mode => 1,
48 SSL_verifycn_scheme => 'http',
49 ) or do {
50 print "1..0 # ssl upgrade $goodhost failed: $SSL_ERROR\n";
51 exit
52 };
53
54 diag("connecting to $badhost:443 with IO::Socket::INET");
55 if ( $sock = IO::Socket::INET->new(
56 PeerAddr => "$badhost:443",
57 Timeout => 10,
58 )) {
59 diag("upgrading to https - should fail because of bad certificate");
60 if ( IO::Socket::SSL->start_SSL( $sock,
3061 SSL_ca_path => $capath,
3162 SSL_verify_mode => 1,
32 SSL_verifycn_scheme => 'http'
33 ) or do {
34 print "1..0 # ssl connect signin.ebay.de failed\n";
35 exit
36 };
37
38 # www.fedora.org has a certificate which has nothing in common
39 # with the hostname
40 my $sock = IO::Socket::INET->new( 'www.fedora.org:443' ) or do {
41 print "1..0 # connect to www.fedora.org failed\n";
42 exit
43 };
44 IO::Socket::SSL->start_SSL( $sock,
45 SSL_ca_path => $capath,
46 SSL_verify_mode => 1,
47 SSL_verifycn_scheme => 'http'
48 ) and do {
49 print "1..0 # certificate for www.fedora.org unexpectly correct\n";
50 exit
51 };
63 )) {
64 diag("certificate for $badhost unexpectly correct");
65 $badhost = undef;
66 };
67 } else {
68 diag("connect to $badhost failed: $!");
69 $badhost = undef;
70 }
5271
5372 #
5473 # and than check, that LWP uses the same checks
5574 #
5675
57 print "1..3\n";
76 print "1..".( $badhost ? 3:1 )."\n";
5877
59 # signin.ebay.de -> should succeed
60 my $content = get( 'https://signin.ebay.de' );
61 print $content ? "ok\n": "not ok # lwp connect signin.ebay.de: $@\n";
78 # $goodhost -> should succeed
79 diag("connecting to $goodhost:443 with LWP");
80 my $content = get( "https://$goodhost" );
81 print $content ? "ok\n": "not ok # lwp connect $goodhost: $@\n";
6282
63 # www.fedora.org -> should fail
64 $content = get( 'https://www.fedora.org' );
65 print $content ? "not ok # lwp ssl connect www.fedora.org should fail\n": "ok\n";
83 if ( $badhost ) {
84 # $badhost -> should fail
85 diag("connecting to $badhost:443 with LWP");
86 $content = get( 'https://$badhost' );
87 print $content ? "not ok # lwp ssl connect $badhost should fail\n": "ok\n";
6688
67 # www.fedora.org -> should succeed if verify mode is 0
68 {
89 # $badhost -> should succeed if verify mode is 0
90 {
6991 local %Net::SSLGlue::LWP::SSLopts = %Net::SSLGlue::LWP::SSLopts;
7092 $Net::SSLGlue::LWP::SSLopts{SSL_verify_mode} = 0;
71 $content = get( 'https://www.fedora.org' );
72 print $content ? "ok\n": "not ok # lwp ssl www.fedora.org w/o ssl verify\n";
93 $content = get( 'https://$badhost' );
94 print $content ? "ok\n": "not ok # lwp ssl $badhost w/o ssl verify\n";
95 }
7396 }
7497
98 sub diag { print "# @_\n" }
3737 exit
3838 };
3939
40 # ssl to the wrong host
41 # the certificate pop.gmx.de returns is for pop.gmx.net
42 diag( "connect ssl to pop.gmx.de:995" );
43 IO::Socket::SSL->new(
44 PeerAddr => 'pop.gmx.de:995',
45 SSL_ca_path => $capath,
46 SSL_verify_mode => 1,
47 SSL_verifycn_scheme => 'smtp'
48 ) and do {
49 print "1..0 # pop.gmx.de:995 reachable with SSL\n";
50 exit
51 };
5240
53 print "1..6\n";
41 print "1..3\n";
5442
5543 # first direct SSL
5644 my $smtp = Net::POP3->new( 'pop.gmx.net',
6654 # check that we can talk on connection
6755 print $smtp->quit ? "ok\n": "not ok # quit failed\n";
6856
69 # against wrong host should fail
70 $smtp = Net::POP3->new( 'pop.gmx.de' ); # should succeed
71 $ok = $smtp->starttls( SSL_ca_path => $capath );
72 print $ok ? "not ok # smtp starttls pop.gmx.de did not fail\n": "ok\n";
73
74 # but not if we specify the right SSL_verifycn_name
75 $smtp = Net::POP3->new( 'pop.gmx.de' ); # should succeed
76 $ok = $smtp->starttls( SSL_ca_path => $capath, SSL_verifycn_name => 'pop.gmx.net' );
77 print $ok ? "ok\n" : "not ok # smtp starttls pop.gmx.de/net\n";
78
79 # or disable verification
80 $smtp = Net::POP3->new( 'pop.gmx.de' ); # should succeed
81 $ok = $smtp->starttls( SSL_verify_mode => 0 );
82 print $ok ? "ok\n" : "not ok # smtp starttls pop.gmx.de\n";
83
8457 sub diag {
8558 #print STDERR "@_\n"
8659 }
0
1 use strict;
2 use warnings;
3 use Test::More;
4
5 my $server = 'ftp.rebex.net';
6 my $debug = 0;
7
8 BEGIN {
9 eval "use Net::FTP";
10 if ( $@ ) {
11 print "1..0 # no Net::FTP\n";
12 exit
13 }
14 }
15
16 use Net::SSLGlue::FTP;
17 use IO::Socket::SSL;
18 use File::Temp;
19
20 # first try to connect w/o ftp
21 # plain
22 diag( "connect inet to $server:21" );
23 IO::Socket::INET->new( "$server:21" ) or do {
24 plan skip_all => "$server:21 not reachable";
25 };
26
27 # ssl to the right host
28 diag( "connect inet to $server:990" );
29 my $sock = IO::Socket::INET->new( "$server:990") or do {
30 plan skip_all => "$server:999 not reachable";
31 };
32
33 # now we need CAs
34 my $cafh = File::Temp->new( UNLINK => 0, SUFFIX => '.crt' );
35 my %sslargs = ( SSL_ca_file => $cafh->filename );
36 print $cafh <DATA>;
37 close($cafh);
38
39 diag( "upgrade to ssl $server:990" );
40 IO::Socket::SSL->start_SSL($sock,
41 %sslargs,
42 SSL_verify_mode => 1,
43 SSL_verifycn_name => $server,
44 SSL_verifycn_scheme => 'ftp'
45 ) or do {
46 plan skip_all => "$server:999 not upgradable to SSL: $SSL_ERROR";
47 };
48
49 plan tests => 9;
50
51 # first direct SSL
52 diag( "connect ftp over ssl to $server" );
53 my $ftp = Net::FTP->new($server,
54 SSL => 1,
55 %sslargs,
56 Debug => $debug,
57 Passive => 1,
58 );
59 ok($ftp,"ftp ssl connect $server");
60 $ftp->login("anonymous",'net-sslglue-ftp@test.perl')
61 or die "login to $server failed";
62 diag("logged in");
63 # check that we can talk on connection
64 ok(~~$ftp->ls,"directory listing protected");
65 $ftp->prot('C');
66 ok(~~$ftp->ls,"directory listing clear");
67
68 # then TLS upgrade inside plain connection
69 $ftp = Net::FTP->new($server, Passive => 1, Debug => $debug);
70 ok($ftp,"ftp plain connect $server");
71 my $ok = $ftp->starttls(%sslargs);
72 ok($ok,"ssl upgrade");
73 $ftp->login("anonymous",'net-sslglue-ftp@test.perl')
74 or die "login to $server failed";
75 diag("logged in");
76 # check that we can talk on connection
77 ok(~~$ftp->ls,"directory listing protected");
78 $ftp->prot('C');
79 ok(~~$ftp->ls,"directory listing clear");
80 $ok = $ftp->stoptls;
81 ok($ok,"ssl downgrade");
82 ok(~~$ftp->ls,"directory listing after downgrade");
83
84
85 __DATA__
86 # Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 1 Primary Intermediate Server CA
87 # Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
88 -----BEGIN CERTIFICATE-----
89 MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
90 MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
91 Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
92 dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
93 jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
94 IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
95 YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
96 IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
97 gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
98 pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
99 kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
100 ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
101 xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
102 AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
103 VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
104 F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
105 L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
106 YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
107 dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
108 c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
109 BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
110 BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
111 LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
112 tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
113 xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
114 xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
115 t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
116 RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
117 YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
118 WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
119 SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
120 wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
121 p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
122 0q6Dp6jOW6c=
123 -----END CERTIFICATE-----
124 # Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
125 # Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
126 -----BEGIN CERTIFICATE-----
127 MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
128 MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
129 Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
130 dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
131 MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
132 U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
133 cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
134 A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
135 pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
136 OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
137 Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
138 Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
139 HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
140 Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
141 +2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
142 Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
143 Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
144 26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
145 AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
146 VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
147 F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
148 ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
149 ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
150 aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
151 YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
152 c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
153 aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
154 d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
155 CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
156 dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
157 wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
158 Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
159 0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
160 pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
161 CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
162 P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
163 1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
164 KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
165 JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
166 8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
167 fyWl8kgAwKQB2j8=
168 -----END CERTIFICATE-----