• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

lib/POE/Filter/H30-Mar-2016-1,518656

t/H07-Jul-2017-4227

ChangesH A D15-Feb-20184.1 KiB161118

MANIFESTH A D09-Feb-2018243 1413

META.ymlH A D15-Feb-2018755 2928

Makefile.PLH A D31-Oct-2017878 2522

READMEH A D15-Feb-201823.7 KiB675543

SSL.xsH A D09-Feb-20182.7 KiB118101

ppport.hH A D07-Jul-2017159.3 KiB6,4382,655

typemapH A D17-Jul-2017167 109

README

1NAME
2    POE::Filter::SSL - The easiest and flexiblest way to SSL in POE!
3
4VERSION
5    Version 0.41
6
7DESCRIPTION
8    This module allows one to secure connections of *POE::Wheel::ReadWrite*
9    with OpenSSL by a *POE::Filter* object, and behaves (beside of SSLing)
10    as *POE::Filter::Stream*.
11
12    *POE::Filter::SSL* can be added, switched and removed during runtime,
13    for example if you want to initiate SSL (see the *SSL on an established
14    connection* example in *SYNOPSIS*) on an already established connection.
15    You are able to combine *POE::Filter::SSL* with other filters, for
16    example have a HTTPS server together with *POE::Filter::HTTPD* (see the
17    *HTTPS-Server* example in *SYNOPSIS*).
18
19    *POE::Filter::SSL* is based on *Net::SSLeay*, but got two XS functions
20    which *Net::SSLeay* is missing.
21
22    Features
23
24          Full non-blocking processing
25
26          No use of sockets at all
27
28          Server and client mode
29
30          Optional client certificate verification
31
32          Allows one to accept connections with invalid or missing client
33          certificate and return custom error data
34
35          CRL check of client certificates
36
37          Retrieve client certificate details (subject name, issuer name,
38          certificate serial)
39
40    Upcoming Features
41
42          Direct cipher encryption without SSL or TLS protocol, for example
43          with static AES encryption
44
45SYNOPSIS
46    By default *POE::Filter::SSL* acts as a SSL server. To use it in client
47    mode you just have to set the *client* option of *new()*.
48
49    TCP-Client
50        #!perl
51
52        use warnings;
53        use strict;
54
55        use POE qw(Component::Client::TCP Filter::SSL);
56
57        POE::Component::Client::TCP->new(
58          RemoteAddress => "yahoo.com",
59          RemotePort    => 443,
60          Filter        => [ "POE::Filter::SSL", client => 1 ],
61          Connected     => sub {
62            $_[HEAP]{server}->put("HEAD /\r\n\r\n");
63          },
64          ServerInput   => sub {
65            print "from server: ".$_[ARG0]."\n";
66          },
67        );
68
69        POE::Kernel->run();
70        exit;
71
72    TCP-Server
73        #!perl
74
75        use warnings;
76        use strict;
77
78        use POE qw(Component::Server::TCP);
79
80        POE::Component::Server::TCP->new(
81          Port => 443,
82          ClientFilter => [ "POE::Filter::SSL", crt => 'server.crt', key => 'server.key' ],
83          ClientConnected => sub {
84            print "got a connection from $_[HEAP]{remote_ip}\n";
85            $_[HEAP]{client}->put("Smile from the server!\r\n");
86          },
87          Alias => "tcp",
88          ClientInput => sub {
89            my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
90            $_[HEAP]{client}->put("You sent:\r\n".$_[ARG0]);
91            $_[KERNEL]->yield("shutdown");
92          },
93        );
94
95        POE::Kernel->run;
96        exit;
97
98    HTTPS-Server
99        use POE::Filter::SSL::PreFilter
100        use POE::Filter::SSL;
101        use POE::Component::Server::HTTP;
102        use HTTP::Status;
103        my $aliases = POE::Component::Server::HTTP->new(
104          Port => 443,
105          ContentHandler => {
106            '/' => \&handler,
107            '/dir/' => sub { return; },
108            '/file' => sub { return; }
109          },
110          Headers => { Server => 'My Server' },
111          PreFilter => POE::Filter::SSL->new(
112            crt    => 'server.crt',
113            key    => 'server.key',
114            cacrt  => 'ca.crt'
115          )
116        );
117
118        sub handler {
119          my ($request, $response) = @_;
120          $response->code(RC_OK);
121          $response->content("Hi, you fetched ". $request->uri);
122          return RC_OK;
123        }
124
125        POE::Kernel->run();
126        POE::Kernel->call($aliases->{httpd}, "shutdown");
127        # next line isn't really needed
128        POE::Kernel->call($aliases->{tcp}, "shutdown");
129
130  SSL on an established connection
131    Advanced Example
132      This example is an IMAP-Relay which forwards the connections to a IMAP
133      server by username. It allows one the unencrypted transfer on port
134      143, with the option of SSL on the established connection (STARTTLS).
135      On port 993 it allows one to do direct SSL.
136
137      Tested with Thunderbird version 3.0.5.
138
139        #!perl
140
141        use warnings;
142        use strict;
143
144        use POE qw(Component::Server::TCP Component::Client::TCP Filter::SSL Filter::Stream);
145
146        my $defaultImapServer = "not.existing.de";
147        my $usernameToImapServer = {
148          user1 => 'mailserver1.domain.de',
149          user2 => 'mailserver2.domain.de',
150          # ...
151        };
152
153        POE::Component::Server::TCP->new(
154          Port => 143,
155          ClientFilter => "POE::Filter::Stream",
156          ClientDisconnected => \&disconnect,
157          ClientConnected => \&connected,
158          ClientInput => \&handleInput,
159          InlineStates => {
160            send_stuff => \&send_stuff,
161            _child => \&child
162          }
163        );
164
165        POE::Component::Server::TCP->new(
166          Port => 993,
167          ClientFilter => [ "POE::Filter::SSL", crt => 'server.crt', key => 'server.key' ],
168          ClientConnected => \&connected,
169          ClientDisconnected => \&disconnect,
170          ClientInput => \&handleInput,
171          InlineStates => {
172            send_stuff => \&send_stuff,
173            _child => \&child
174          }
175        );
176
177        sub disconnect {
178          my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
179          logevent('server got disconnect', $session);
180          $kernel->post($heap->{client_id} => "shutdown");
181        }
182
183        sub connected {
184          my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
185          logevent("got a connection from ".$heap->{remote_ip}, $session);
186          $heap->{client}->put("* OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS] IMAP Relay v0.1 ready.\r\n");
187        }
188
189        sub send_stuff {
190          my ($heap, $stuff, $session) = @_[HEAP, ARG0, SESSION];
191          logevent("-> ".length($stuff)." Bytes", $session);
192          (defined($heap->{client})) && (ref($heap->{client}) eq "POE::Wheel::ReadWrite") &&
193          $heap->{client}->put($stuff);
194        }
195
196        sub child {
197          my ($heap, $child_op, $child) = @_[HEAP, ARG0, ARG1];
198          if ($child_op eq "create") {
199            $heap->{client_id} = $child->ID;
200          }
201        }
202
203        sub handleInput {
204          my ($kernel, $session, $heap, $input) = @_[KERNEL, SESSION, HEAP, ARG0];
205          if($heap->{forwarding}) {
206            return $kernel->yield("shutdown") unless (defined($heap->{client_id}));
207            $kernel->post($heap->{client_id} => send_stuff => $input);
208          } elsif ($input =~ /^(\d+)\s+STARTTLS[\r\n]+/i) {
209            $_[HEAP]{client}->put($1." OK Begin SSL/TLS negotiation now.\r\n");
210            logevent("SSLing now...", $session);
211            $_[HEAP]{client}->set_filter(POE::Filter::SSL->new(crt => 'server.crt', key => 'server.key'));
212          } elsif ($input =~ /^(\d+)\s+CAPABILITY[\r\n]+/i) {
213            $_[HEAP]{client}->put("* CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS\r\n");
214            $_[HEAP]{client}->put($1." OK CAPABILITY completed\r\n");
215          } elsif ($input =~ /^(\d+)\s+login\s+\"(\S+)\"\s+\"(\S+)\"[\r\n]+/i) {
216            my $username = $2;
217            my $pass = $3;
218            logevent("login of user ".$username, $session);
219            spawn_client_side($username, $input);
220            $heap->{forwarding}++;
221          } else {
222            logevent("unknown command before login, disconnecting.", $session);
223            return $kernel->yield("shutdown");
224          }
225        }
226
227        sub spawn_client_side {
228          my $username = shift;
229          POE::Component::Client::TCP->new(
230            RemoteAddress => $usernameToImapServer->{$username} || $defaultImapServer,
231            RemotePort    => 143,
232            Filter => "POE::Filter::Stream",
233            Started       => sub {
234              $_[HEAP]->{server_id} = $_[SENDER]->ID;
235              $_[HEAP]->{buf} = $_[ARG0];
236              $_[HEAP]->{skip} = 0;
237            },
238            Connected => sub {
239              my ($heap, $session) = @_[HEAP, SESSION];
240              logevent('client connected', $session);
241              $heap->{server}->put($heap->{buf});
242              delete $heap->{buf};
243            },
244            ServerInput => sub {
245              my ($kernel, $heap, $session, $input) = @_[KERNEL, HEAP, SESSION, ARG0];
246              #logevent('client got input', $session, $input);
247              $kernel->post($heap->{server_id} => send_stuff => $input) if ($heap->{skip}++);
248            },
249            Disconnected => sub {
250              my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];
251              logevent('client disconnected', $session);
252              $kernel->post($heap->{server_id} => 'shutdown');
253            },
254            InlineStates => {
255              send_stuff => sub {
256                my ($heap, $stuff, $session) = @_[HEAP, ARG0, SESSION];
257                logevent("<- ".length($stuff)." Bytes", $session);
258                (defined($heap->{server})) && (ref($heap->{server}) eq "POE::Wheel::ReadWrite") &&
259                $heap->{server}->put($stuff);
260              },
261            },
262            Args => [ shift ]
263          );
264        }
265
266        sub logevent {
267          my ($state, $session, $arg) = @_;
268          my $id = $session->ID();
269          print "session $id $state ";
270          print ": $arg" if (defined $arg);
271          print "\n";
272        }
273
274        POE::Kernel->run;
275
276  Client certificate verification
277    Advanced Example
278      The following example implements a HTTPS server with client
279      certificate verification, which shows details about the verified
280      client certificate.
281
282        #!perl
283
284        use strict;
285        use warnings;
286        use Socket;
287        use POE qw(
288          Wheel::SocketFactory
289          Wheel::ReadWrite
290          Driver::SysRW
291          Filter::SSL
292          Filter::Stackable
293          Filter::HTTPD
294        );
295
296        POE::Session->create(
297          inline_states => {
298            _start       => sub {
299              my $heap = $_[HEAP];
300              $heap->{listener} = POE::Wheel::SocketFactory->new(
301                BindAddress  => '0.0.0.0',
302                BindPort     => 443,
303                Reuse        => 'yes',
304                SuccessEvent => 'socket_birth',
305                FailureEvent => '_stop',
306              );
307            },
308            _stop => sub {
309              delete $_[HEAP]->{listener};
310            },
311            socket_birth => sub {
312              my ($socket) = $_[ARG0];
313              POE::Session->create(
314                inline_states => {
315                  _start       => sub {
316                    my ($heap, $kernel, $connected_socket, $address, $port) = @_[HEAP, KERNEL, ARG0, ARG1, ARG2];
317                    $heap->{sslfilter} = POE::Filter::SSL->new(
318                       crt    => 'server.crt',
319                       key    => 'server.key',
320                       cacrt  => 'ca.crt',
321                       cipher => 'DHE-RSA-AES256-GCM-SHA384:AES256-SHA',
322                       #cacrl  => 'ca.crl', # Uncomment this, if you have a CRL file.
323                       debug  => 1,
324                       clientcert => 1
325                    );
326                    $heap->{socket_wheel} = POE::Wheel::ReadWrite->new(
327                      Handle     => $connected_socket,
328                      Driver     => POE::Driver::SysRW->new(),
329                      Filter     => POE::Filter::Stackable->new(Filters => [
330                        $heap->{sslfilter},
331                        POE::Filter::HTTPD->new()
332                      ]),
333                      InputEvent => 'socket_input',
334                      ErrorEvent => '_stop',
335                    );
336                  },
337                  socket_input => sub {
338                    my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0];
339                    my (@certid) = ($heap->{sslfilter}->clientCertIds());
340                    my $content = '';
341                    if ($heap->{sslfilter}->clientCertValid()) {
342                      $content .= "Hello <font color=green>valid</font> client Certifcate:";
343                    } else {
344                      $content .= "None or <font color=red>invalid</font> client certificate:";
345                    }
346                    $content .= "<hr>";
347                    foreach my $certid (@certid) {
348                      $certid = $certid ? $certid->[0]."<br>".$certid->[1]."<br>SERIAL=".$heap->{sslfilter}->hexdump($certid->[2]) : 'No client certificate';
349                      $content .= $certid."<hr>";
350                    }
351                    $content .= "Your URL was: ".$buf->uri."<hr>"
352                      if (ref($buf) eq "HTTP::Request");
353                    $content .= localtime(time());
354                    my $response = HTTP::Response->new(200);
355                    $response->push_header('Content-type', 'text/html');
356                    $response->content($content);
357                    $heap->{socket_wheel}->put($response);
358                    $kernel->delay(_stop => 1);
359                  },
360                  _stop => sub {
361                    delete $_[HEAP]->{socket_wheel};
362                  }
363                },
364                args => [$socket],
365              );
366            }
367          }
368        );
369
370        $poe_kernel->run();
371
372FUNCTIONS
373    new(option = value, option => value, option...)>
374        Returns a new *POE::Filter::SSL* object. It accepts the following
375        options:
376
377        client
378          By default *POE::Filter::SSL* acts as a SSL server. To use it in
379          client mode, you have to set this option.
380
381        crt{mem}
382          The certificate file (.crt) for the server, a client certificate
383          in client mode.
384
385          You are able to pass the already inmemory crt file as scalar via
386          *crtmem*.
387
388        key{mem}
389          The key file (.key) of the certificate (see *crt* above).
390
391          You are able to pass the already inmemory key file as scalar via
392          *keymem*.
393
394        cacrt{mem}
395          The ca certificate file (ca.crt), which is used to verificate the
396          client certificates against a CA. You can store multiple ca in one
397          file, all of them gets imported.
398
399          You are able to pass the already inmemory cacrt file as scalar via
400          *cacrtmem* or as an array ref of scalars, if you have multiple ca.
401
402        caverifydepth
403          By default the ca verify depth is 5, you can override this via
404          this option.
405
406        chain
407          Chain certificate, you need it for example for startssl.org which
408          needs a intermedia certificates. Here you can configure it. You
409          can generate this the following way:
410
411          cat client.crt intermediate.crt ca.crt > chain.pem
412
413          In this case, you normalyly have no *key* and *crt* option.
414          Currently it is not possible to pass this inmemory, only by file.
415
416        cacrl
417          Configures a CRL (ca.crl) against the client certificate is
418          verified by *clientCertValid()*.
419
420        dhcert{mem}
421          If you want to enable perfect forward secrecy, here you can enable
422          Diffie-Hellman. You just have to create a dhparam file and there
423          here the path to the path/to/FILENAME.pem where your
424          Diffie-Hellman (pem format) stays.
425
426          openssl dhparam -check -text -5 2048 -out path/to/FILENAME.pem
427
428          You are able to pass the already inmemory dhparam file as
429          scalar(string) via *dhcertmem*.
430
431        clientcert
432          Only in server mode: Request during ssl handshake from the client
433          a client certificat.
434
435          WARNING: If the client provides an untrusted or no client
436          certificate, the connection is not failing. You have to ask
437          *clientCertValid()* if the certificate is valid!
438
439        sni
440          Allows one to set the SNI hostname indication in first packet of
441          handshake. See
442          https://de.wikipedia.org/wiki/Server_Name_Indication
443
444        tls
445          Force in the handshake the use of tls, disables support for the
446          obsolete SSL handshake.
447
448        tls1_2
449          Force in the handshake the use of tls in version 1.2, disables
450          support for the obsolete SSL handshake.
451
452        nohonor
453          By default, as server, *POE::Filter:SSL* sets the option
454          *SSL_OP_CIPHER_SERVER_PREFERENCE*. For more information you may
455          google the pendant of apache *SSLHonorCipherOrder*.
456
457          To flip back to the old behaviour, not setting this option, you
458          can set nohonor.
459
460        cipher
461          Specify which ciphers are allowed for the synchronous encrypted
462          transfer of the data over the ssl connection.
463
464          Example:
465
466            cipher => 'DHE-RSA-AES256-GCM-SHA384:AES256-SHA'
467
468        blockbadclientcert
469          Let OpenSSL deny the connection if there is no client certificate.
470
471          WARNING: If the client is listed in the CRL file or an invalid
472          client certifiate has been sent, the connection will be
473          established! You have to ask *clientCertValid()* if you have the
474          *crl* option set on *new()*, otherwise to ask
475          *clientCertNotOnCRL()* if the certificate is listed on your CRL
476          file!
477
478        ignoreVerifyErrors
479          WARNING: Before using this option, you should be realy sure that
480          you know what you are doing!
481
482          Specify to ignore specific errors on verifying the certificate
483          chain: This is for example useful to be able to fetch the time
484          from via secure and trusted TLS connection. In this case, your
485          time is wrong, so must ignore time errors, which are 9:
486          X509_V_ERR_CERT_NOT_YET_VALID (certificate is not yet valid) and
487          10: X509_V_ERR_CERT_HAS_EXPIRED (certificate has expired).
488
489          The list of errors you can ignore can be found on the
490          documentation:
491
492          <https://wiki.openssl.org/index.php/Manual:Verify(1)>
493
494          Example:
495
496            ignoreVerifyErrors => [ 9, 10, ]
497
498    handshakeDone(options)
499        Returns *true* if the handshake is done and all data for handshake
500        has been written out. It accepts the following options:
501
502        ignorebuf
503          Returns *true* if OpenSSL has established the connection,
504          regardless if all data has been written out. This is needed if you
505          want to exchange the Filter of *POE::Wheel::ReadWrite* before the
506          first data comes in. This option have been only used by
507          *doHandshake()* to be able to add new filters before first
508          cleartext data to be processed gets in.
509
510    clientCertNotOnCRL($file)
511        Verifies if the serial of the client certificate is not contained in
512        the CRL $file. No file caching is done, each call opens the file
513        again.
514
515        WARNING: If your CRL file is missing, can not be opened is empty or
516        has no blocked certificate at all in it, then every call will get
517        blocked!
518
519    clientCertIds()
520        Returns an array of every certificate found by OpenSSL. Each element
521        is again a array. The first element is the value of
522        *X509_get_subject_name*, second is the value of
523        *X509_get_issuer_name* and third element is the serial of the
524        certificate in binary form. You have to use *split()* and *ord()*,
525        or the *hexdump()* function, to convert it to a readable form.
526
527        Example:
528
529          my ($certid) = ($heap->{sslfilter}->clientCertIds());
530          $certid = $certid ? $certid->[0]."<br>".$certid->[1]."<br>SERIAL=".$heap->{sslfilter}->hexdump($certid->[2]) : 'No client certificate';
531
532    getCipher()
533        Returns the used cryptographic algorithm and length.
534
535        Example:
536
537          $sslfilter->getCipher()
538
539    clientCertValid()
540        Returns *true* if there is a client certificate that is valid. It
541        also tests against the CRL, if you have the *cacrl* option set on
542        *new()*.
543
544    doHandshake($readWrite, $filter, $filter, ...) !!!REMOVED!!!
545        WARNING: POE::Filter:SSL now is able to do the ssh handshake now
546        without any helpers. Because of this, this function has been
547        removed!
548
549        Allows one to add filters after the ssl handshake. It has to be
550        called in the input handler, and needs the passing of the
551        *POE::Wheel::ReadWhile* object. If it returns false, you have to
552        return from the input handler.
553
554        See the *HTTPS-Server*, *SSL on an established connection* and
555        *Client certificate verification* examples in *SYNOPSIS*
556
557    clientCertExists()
558        Returns *true* if there is a client certificate, that might be
559        untrusted.
560
561        WARNING: If the client provides an untrusted client certificate a
562        client certificate that is listed in CRL, this function returns
563        *true*. You have to ask *clientCertValid()* if the certificate is
564        valid!
565
566    errorhandler
567        By default, every ssl error is escalated via carp. You may change
568        this behaviour via this option to:
569
570        "ignore"
571          Do not report any error.
572
573        *CODE*
574          Setting errorhandler to a reference of a function allows one to be
575          called it callback function with the following options:
576
577          ARG1: POE:SSL::Filter instance
578
579          ARG2: Ref on a Hash with the following keys:
580
581            ret        The return code of Net::SSLeay::connect (client) or Net::SSLeay::accept (server)
582            ssl        The SSL context (SSL_CTX)
583            msg        The error message as text, as normally reported via carp
584            get_error  The error code of get_error the ssl context
585            error      The error code of get_error without context
586
587        "carp" (or undef)
588          Do Carp/carp on error.
589
590        "confess"
591          Do Carp/confess (stacktrace) on error.
592
593        "carponetime"
594          Report carp for one occurrence only one time - over all!
595
596    debug
597        Shows debug messages of *clientCertNotOnCRL()*.
598
599    hexdump($string)
600        Returns string data in hex format.
601
602        Example:
603
604          perl -e 'use POE::Filter::SSL; print POE::Filter::SSL->hexdump("test")."\n";'
605          74:65:73:74
606
607  Internal functions and POE::Filter handler
608    VERIFY()
609    POE_FILTER_X509_get_serialNumber()
610    POE_FILTER_SSL_CTX_set_tmp_dh()
611    POE_FILTER_SSL_CTX_set_tmp_rsa()
612    POE_FILTER_SSL_set_tmp_dh()
613    clone()
614    doSSL()
615    get()
616    get_one()
617    get_one_start()
618    get_pending()
619    writeToSSLBIO()
620    writeToSSL()
621    put()
622    verify_serial_against_crl_file()
623    DOSENDBACK()
624    checkForDoSendback()
625    CTX_add_client_CA()
626    PEMdataToEVP_PKEY
627    PEMdataToX509
628    dataToBio
629
630AUTHOR
631    Markus Schraeder, "<privi at cpan.org>"
632
633BUGS
634    Please report any bugs or feature requests to "bug-poe-filter-ssl at
635    rt.cpan.org", or through the web interface at
636    <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Filter-SSL>. I will
637    be notified, and then you'll automatically be notified of progress on
638    your bug as I make changes.
639
640SUPPORT
641    You can find documentation for this module with the perldoc command.
642
643        perldoc POE::Filter::SSL
644
645    You can also look for information at:
646
647    *   RT: CPAN's request tracker
648
649        <http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Filter-SSL>
650
651    *   AnnoCPAN: Annotated CPAN documentation
652
653        <http://annocpan.org/dist/POE-Filter-SSL>
654
655    *   CPAN Ratings
656
657        <http://cpanratings.perl.org/d/POE-Filter-SSL>
658
659    *   Search CPAN
660
661        <http://search.cpan.org/dist/POE-Filter-SSL>
662
663Commercial support
664    Commercial support can be gained at <sslsupport at cryptomagic.eu>.
665
666    Used in our products, you can find on <https://www.cryptomagic.eu/>
667
668COPYRIGHT & LICENSE
669    Copyright 2010-2017 Markus Schraeder, CryptoMagic GmbH, all rights
670    reserved.
671
672    This program is free software; you can redistribute it and/or modify it
673    under the same terms as Perl itself.
674
675