Extend TLSProxy capabilities
[openssl.git] / util / TLSProxy / Proxy.pm
1 # Written by Matt Caswell for the OpenSSL project.
2 # ====================================================================
3 # Copyright (c) 1998-2015 The OpenSSL Project.  All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 #
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in
14 #    the documentation and/or other materials provided with the
15 #    distribution.
16 #
17 # 3. All advertising materials mentioning features or use of this
18 #    software must display the following acknowledgment:
19 #    "This product includes software developed by the OpenSSL Project
20 #    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
21 #
22 # 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23 #    endorse or promote products derived from this software without
24 #    prior written permission. For written permission, please contact
25 #    openssl-core@openssl.org.
26 #
27 # 5. Products derived from this software may not be called "OpenSSL"
28 #    nor may "OpenSSL" appear in their names without prior written
29 #    permission of the OpenSSL Project.
30 #
31 # 6. Redistributions of any form whatsoever must retain the following
32 #    acknowledgment:
33 #    "This product includes software developed by the OpenSSL Project
34 #    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
35 #
36 # THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37 # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
40 # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47 # OF THE POSSIBILITY OF SUCH DAMAGE.
48 # ====================================================================
49 #
50 # This product includes cryptographic software written by Eric Young
51 # (eay@cryptsoft.com).  This product includes software written by Tim
52 # Hudson (tjh@cryptsoft.com).
53
54 use strict;
55
56 package TLSProxy::Proxy;
57
58 use File::Spec;
59 use IO::Socket;
60 use IO::Select;
61 use TLSProxy::Record;
62 use TLSProxy::Message;
63 use TLSProxy::ClientHello;
64 use TLSProxy::ServerHello;
65 use TLSProxy::ServerKeyExchange;
66
67 sub new
68 {
69     my $class = shift;
70     my ($filter,
71         $execute,
72         $cert,
73         $debug) = @_;
74
75     my $self = {
76         #Public read/write
77         proxy_addr => "localhost",
78         proxy_port => 4453,
79         server_addr => "localhost",
80         server_port => 4443,
81         filter => $filter,
82
83         #Public read
84         execute => $execute,
85         cert => $cert,
86         debug => $debug,
87         cipherc => "AES128-SHA",
88         ciphers => "",
89         flight => 0,
90         record_list => [],
91         message_list => [],
92
93         #Private
94         message_rec_list => []
95     };
96
97     return bless $self, $class;
98 }
99
100 sub clear
101 {
102     my $self = shift;
103
104     $self->{cipherc} = "AES128-SHA";
105     $self->{ciphers} = "";
106     $self->{flight} = 0;
107     $self->{record_list} = [];
108     $self->{message_list} = [];
109     $self->{message_rec_list} = [];
110
111     TLSProxy::Message->clear();
112     TLSProxy::Record->clear();
113 }
114
115 sub restart
116 {
117     my $self = shift;
118
119     $self->clear;
120     $self->start;
121 }
122
123 sub start
124 {
125     my ($self) = shift;
126     my $pid;
127
128     $pid = fork();
129     if ($pid == 0) {
130         open(STDOUT, ">", File::Spec->devnull())
131             or die "Failed to redirect stdout";
132         open(STDERR, ">&STDOUT");
133         my $execcmd = $self->execute." s_server -testmode -accept "
134             .($self->server_port)
135             ." -cert ".$self->cert." -naccept 1";
136         if ($self->ciphers ne "") {
137             $execcmd .= " -cipher ".$self->ciphers;
138         }
139         exec($execcmd);
140     }
141
142     my $oldstdout;
143
144     if(!$self->debug) {
145         $oldstdout = select(File::Spec->devnull());
146     }
147
148     # Create the Proxy socket
149     my $proxy_sock = new IO::Socket::INET(
150         LocalHost   => $self->proxy_addr,
151         LocalPort   => $self->proxy_port,
152         Proto       => "tcp",
153         Listen      => SOMAXCONN,
154         Reuse       => 1
155     );
156
157     if ($proxy_sock) {
158         print "Proxy started on port ".$self->proxy_port."\n";
159     } else {
160         die "Failed creating proxy socket\n";
161     }
162
163     if ($self->execute) {
164         my $pid = fork();
165         if ($pid == 0) {
166             open(STDOUT, ">", File::Spec->devnull())
167                 or die "Failed to redirect stdout";
168             open(STDERR, ">&STDOUT");
169             my $execcmd = $self->execute
170                  ." s_client -testmode -connect "
171                  .($self->proxy_addr).":".($self->proxy_port);
172             if ($self->cipherc ne "") {
173                 $execcmd .= " -cipher ".$self->cipherc;
174             }
175             exec($execcmd);
176         }
177     }
178
179     # Wait for incoming connection from client
180     my $client_sock = $proxy_sock->accept() 
181         or die "Failed accepting incoming connection\n";
182
183     print "Connection opened\n";
184
185     # Now connect to the server
186     my $retry = 3;
187     my $server_sock;
188     #We loop over this a few times because sometimes s_server can take a while
189     #to start up
190     do {
191         $server_sock = new IO::Socket::INET(
192             PeerAddr => $self->server_addr,
193             PeerPort => $self->server_port,
194             Proto => 'tcp'
195         ); 
196
197         $retry--;
198         if (!$server_sock) {
199             if ($retry) {
200                 #Sleep for a short while
201                 select(undef, undef, undef, 0.1);
202             } else {
203                 die "Failed to start up server\n";
204             }
205         }
206     } while (!$server_sock);
207
208     my $sel = IO::Select->new($server_sock, $client_sock);
209     my $indata;
210     my @handles = ($server_sock, $client_sock);
211
212     #Wait for either the server socket or the client socket to become readable
213     my @ready;
214     while(!(TLSProxy::Message->end) && (@ready = $sel->can_read)) {
215         foreach my $hand (@ready) {
216             if ($hand == $server_sock) {
217                 $server_sock->sysread($indata, 16384) or goto END;
218                 $indata = $self->process_packet(1, $indata);
219                 $client_sock->syswrite($indata);
220             } elsif ($hand == $client_sock) {
221                 $client_sock->sysread($indata, 16384) or goto END;
222                 $indata = $self->process_packet(0, $indata);
223                 $server_sock->syswrite($indata);
224             } else {
225                 print "Err\n";
226                 goto END;
227             }
228         }
229     }
230
231     END:
232     print "Connection closed\n";
233     if($server_sock) {
234         $server_sock->close();
235     }
236     if($client_sock) {
237         #Closing this also kills the child process
238         $client_sock->close();
239     }
240     if($proxy_sock) {
241         $proxy_sock->close();
242     }
243     if(!$self->debug) {
244         select($oldstdout);
245     }
246 }
247
248
249 sub process_packet
250 {
251     my ($self, $server, $packet) = @_;
252     my $len_real;
253     my $decrypt_len;
254     my $data;
255     my $recnum;
256
257     if ($server) {
258         print "Received server packet\n";
259     } else {
260         print "Received client packet\n";
261     }
262
263     print "Packet length = ".length($packet)."\n";
264     print "Processing flight ".$self->flight."\n";
265
266     #Return contains the list of record found in the packet followed by the
267     #list of messages in those records
268     my @ret = TLSProxy::Record->get_records($server, $self->flight, $packet);
269     push @{$self->record_list}, @{$ret[0]};
270     $self->{message_rec_list} = $ret[0];
271     push @{$self->{message_list}}, @{$ret[1]};
272
273     print "\n";
274
275     #Finished parsing. Call user provided filter here
276     $self->filter->($self);
277
278     #Reconstruct the packet
279     $packet = "";
280     foreach my $record (@{$self->record_list}) {
281         #We only replay the records for the current flight
282         if ($record->flight != $self->flight) {
283             next;
284         }
285         $packet .= $record->reconstruct_record();
286     }
287
288     $self->{flight} = $self->{flight} + 1;
289
290     print "Forwarded packet length = ".length($packet)."\n\n";
291
292     return $packet;
293 }
294
295 #Read accessors
296 sub execute
297 {
298     my $self = shift;
299     return $self->{execute};
300 }
301 sub cert
302 {
303     my $self = shift;
304     return $self->{cert};
305 }
306 sub debug
307 {
308     my $self = shift;
309     return $self->{debug};
310 }
311 sub flight
312 {
313     my $self = shift;
314     return $self->{flight};
315 }
316 sub record_list
317 {
318     my $self = shift;
319     return $self->{record_list};
320 }
321 sub message_list
322 {
323     my $self = shift;
324     return $self->{message_list};
325 }
326 sub success
327 {
328     my $self = shift;
329     return $self->{success};
330 }
331 sub end
332 {
333     my $self = shift;
334     return $self->{end};
335 }
336
337 #Read/write accessors
338 sub proxy_addr
339 {
340     my $self = shift;
341     if (@_) {
342       $self->{proxy_addr} = shift;
343     }
344     return $self->{proxy_addr};
345 }
346 sub proxy_port
347 {
348     my $self = shift;
349     if (@_) {
350       $self->{proxy_port} = shift;
351     }
352     return $self->{proxy_port};
353 }
354 sub server_addr
355 {
356     my $self = shift;
357     if (@_) {
358       $self->{server_addr} = shift;
359     }
360     return $self->{server_addr};
361 }
362 sub server_port
363 {
364     my $self = shift;
365     if (@_) {
366       $self->{server_port} = shift;
367     }
368     return $self->{server_port};
369 }
370 sub filter
371 {
372     my $self = shift;
373     if (@_) {
374       $self->{filter} = shift;
375     }
376     return $self->{filter};
377 }
378 sub cipherc
379 {
380     my $self = shift;
381     if (@_) {
382       $self->{cipherc} = shift;
383     }
384     return $self->{cipherc};
385 }
386 sub ciphers
387 {
388     my $self = shift;
389     if (@_) {
390       $self->{ciphers} = shift;
391     }
392     return $self->{ciphers};
393 }
394 1;