File Coverage

blib/lib/IO/Socket/SSL/Intercept.pm
Criterion Covered Total %
statement 15 96 15.6
branch 0 52 0.0
condition 0 11 0.0
subroutine 5 14 35.7
pod 4 6 66.6
total 24 179 13.4


line stmt bran cond sub pod time code
1              
2             package IO::Socket::SSL::Intercept;
3 1     1   624 use strict;
  1         4  
  1         35  
4 1     1   5 use warnings;
  1         2  
  1         44  
5 1     1   5 use Carp 'croak';
  1         2  
  1         58  
6 1     1   526 use IO::Socket::SSL::Utils;
  1         3  
  1         174  
7 1     1   8 use Net::SSLeay;
  1         2  
  1         1284  
8              
9             our $VERSION = '2.056';
10              
11              
12             sub new {
13 0     0 1   my ($class,%args) = @_;
14              
15 0           my $cacert = delete $args{proxy_cert};
16 0 0         if ( ! $cacert ) {
17 0 0         if ( my $f = delete $args{proxy_cert_file} ) {
18 0           $cacert = PEM_file2cert($f);
19             } else {
20 0           croak "no proxy_cert or proxy_cert_file given";
21             }
22             }
23              
24 0           my $cakey = delete $args{proxy_key};
25 0 0         if ( ! $cakey ) {
26 0 0         if ( my $f = delete $args{proxy_key_file} ) {
27 0           $cakey = PEM_file2key($f);
28             } else {
29 0           croak "no proxy_cert or proxy_cert_file given";
30             }
31             }
32              
33 0           my $certkey = delete $args{cert_key};
34 0 0         if ( ! $certkey ) {
35 0 0         if ( my $f = delete $args{cert_key_file} ) {
36 0           $certkey = PEM_file2key($f);
37             }
38             }
39              
40 0   0       my $cache = delete $args{cache} || {};
41 0 0         if (ref($cache) eq 'CODE') {
42             # check cache type
43 0           my $type = $cache->('type');
44 0 0         if (!$type) {
    0          
45             # old cache interface - change into new interface
46             # get: $cache->(fp)
47             # set: $cache->(fp,cert,key)
48 0           my $oc = $cache;
49             $cache = sub {
50 0     0     my ($fp,$create_cb) = @_;
51 0           my @ck = $oc->($fp);
52 0 0         $oc->($fp, @ck = &$create_cb) if !@ck;
53 0           return @ck;
54 0           };
55             } elsif ($type == 1) {
56             # current interface:
57             # get/set: $cache->(fp,cb_create)
58             } else {
59 0           die "invalid type of cache: $type";
60             }
61             }
62              
63             my $self = bless {
64             cacert => $cacert,
65             cakey => $cakey,
66             certkey => $certkey,
67             cache => $cache,
68             serial => delete $args{serial},
69 0           };
70 0           return $self;
71             }
72              
73             sub DESTROY {
74             # call various ssl _free routines
75 0 0   0     my $self = shift or return;
76 0 0         for ( \$self->{cacert},
77 0           map { \$_->{cert} } ref($self->{cache}) ne 'CODE' ? values %{$self->{cache}} :()) {
  0            
78 0 0         $$_ or next;
79 0           CERT_free($$_);
80 0           $$_ = undef;
81             }
82 0           for ( \$self->{cakey}, \$self->{pubkey} ) {
83 0 0         $$_ or next;
84 0           KEY_free($$_);
85 0           $$_ = undef;
86             }
87             }
88              
89             sub clone_cert {
90 0     0 1   my ($self,$old_cert,$clone_key) = @_;
91              
92 0           my $hash = CERT_asHash($old_cert);
93             my $create_cb = sub {
94             # if not in cache create new certificate based on original
95             # copy most but not all extensions
96 0 0   0     if (my $ext = $hash->{ext}) {
97             @$ext = grep {
98 0 0         defined($_->{sn}) && $_->{sn} !~m{^(?:
  0            
99             authorityInfoAccess |
100             subjectKeyIdentifier |
101             authorityKeyIdentifier |
102             certificatePolicies |
103             crlDistributionPoints
104             )$}x
105             } @$ext;
106             }
107             my ($clone,$key) = CERT_create(
108             %$hash,
109             issuer_cert => $self->{cacert},
110             issuer_key => $self->{cakey},
111             key => $self->{certkey},
112             serial =>
113             ! defined($self->{serial}) ? (unpack('L',$hash->{x509_digest_sha256}))[0] :
114             ref($self->{serial}) eq 'CODE' ? $self->{serial}($old_cert,$hash) :
115             ++$self->{serial},
116 0 0         );
    0          
117 0           return ($clone,$key);
118 0           };
119              
120 0   0       $clone_key ||= substr(unpack("H*", $hash->{x509_digest_sha256}),0,32);
121 0           my $c = $self->{cache};
122 0 0         return $c->($clone_key,$create_cb) if ref($c) eq 'CODE';
123              
124 0   0       my $e = $c->{$clone_key} ||= do {
125 0           my ($cert,$key) = &$create_cb;
126 0           { cert => $cert, key => $key };
127             };
128 0           $e->{atime} = time();
129 0           return ($e->{cert},$e->{key});
130             }
131              
132              
133 0     0 0   sub STORABLE_freeze { my $self = shift; $self->serialize() }
  0            
134 0     0 0   sub STORABLE_thaw { my ($class,undef,$data) = @_; $class->unserialize($data) }
  0            
135              
136             sub serialize {
137 0     0 1   my $self = shift;
138 0           my $data = pack("N",2); # version
139 0           $data .= pack("N/a", PEM_cert2string($self->{cacert}));
140 0           $data .= pack("N/a", PEM_key2string($self->{cakey}));
141 0 0         if ( $self->{certkey} ) {
142 0           $data .= pack("N/a", PEM_key2string($self->{certkey}));
143             } else {
144 0           $data .= pack("N/a", '');
145             }
146 0           $data .= pack("N",$self->{serial});
147 0 0         if ( ref($self->{cache}) eq 'HASH' ) {
148 0           while ( my($k,$v) = each %{ $self->{cache}} ) {
  0            
149             $data .= pack("N/aN/aN/aN", $k,
150             PEM_cert2string($k->{cert}),
151             $k->{key} ? PEM_key2string($k->{key}) : '',
152 0 0         $k->{atime});
153             }
154             }
155 0           return $data;
156             }
157              
158             sub unserialize {
159 0     0 1   my ($class,$data) = @_;
160 0 0         unpack("N",substr($data,0,4,'')) == 2 or
161             croak("serialized with wrong version");
162 0           ( my $cacert,my $cakey,my $certkey,my $serial,$data)
163             = unpack("N/aN/aN/aNa*",$data);
164 0 0 0       my $self = bless {
165             serial => $serial,
166             cacert => PEM_string2cert($cacert),
167             cakey => PEM_string2key($cakey),
168             $certkey ? ( certkey => PEM_string2key($certkey)):(),
169             }, ref($class)||$class;
170              
171 0 0         $self->{cache} = {} if $data ne '';
172 0           while ( $data ne '' ) {
173 0           (my $key,my $cert,my $certkey, my $atime,$data) = unpack("N/aN/aNa*",$data);
174 0 0         $self->{cache}{$key} = {
175             cert => PEM_string2cert($cert),
176             $key ? ( key => PEM_string2key($certkey)):(),
177             atime => $atime
178             };
179             }
180 0           return $self;
181             }
182              
183             1;
184              
185             __END__