.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "POE::Filter::SSL 3pm" .TH POE::Filter::SSL 3pm "2020-11-09" "perl v5.32.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" POE::Filter::SSL \- The easiest and flexiblest way to SSL in POE! .SH "VERSION" .IX Header "VERSION" Version 0.41 .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module allows one to secure connections of \fIPOE::Wheel::ReadWrite\fR with OpenSSL by a \&\fIPOE::Filter\fR object, and behaves (beside of SSLing) as \fIPOE::Filter::Stream\fR. .PP \&\fIPOE::Filter::SSL\fR can be added, switched and removed during runtime, for example if you want to initiate \s-1SSL\s0 (see the \fI\s-1SSL\s0 on an established connection\fR example in \fI\s-1SYNOPSIS\s0\fR) on an already established connection. You are able to combine \&\fIPOE::Filter::SSL\fR with other filters, for example have a \s-1HTTPS\s0 server together with \fIPOE::Filter::HTTPD\fR (see the \fIHTTPS-Server\fR example in \fI\s-1SYNOPSIS\s0\fR). .PP \&\fIPOE::Filter::SSL\fR is based on \fINet::SSLeay\fR, but got two \s-1XS\s0 functions which \fINet::SSLeay\fR is missing. .IP "\fBFeatures\fR" 4 .IX Item "Features" .RS 4 .RS 2 Full non-blocking processing .Sp No use of sockets at all .Sp Server and client mode .Sp Optional client certificate verification .Sp Allows one to accept connections with invalid or missing client certificate and return custom error data .Sp \&\s-1CRL\s0 check of client certificates .Sp Retrieve client certificate details (subject name, issuer name, certificate serial) .RE .RE .RS 4 .RE .IP "\fBUpcoming Features\fR" 4 .IX Item "Upcoming Features" .RS 4 .RS 2 Direct cipher encryption without \s-1SSL\s0 or \s-1TLS\s0 protocol, for example with static \s-1AES\s0 encryption .RE .RE .RS 4 .RE .SH "SYNOPSIS" .IX Header "SYNOPSIS" By default \fIPOE::Filter::SSL\fR acts as a \s-1SSL\s0 server. To use it in client mode you just have to set the \fIclient\fR option of \fI\f(BInew()\fI\fR. .IP "TCP-Client" 2 .IX Item "TCP-Client" .Vb 1 \& #!perl \& \& use warnings; \& use strict; \& \& use POE qw(Component::Client::TCP Filter::SSL); \& \& POE::Component::Client::TCP\->new( \& RemoteAddress => "yahoo.com", \& RemotePort => 443, \& Filter => [ "POE::Filter::SSL", client => 1 ], \& Connected => sub { \& $_[HEAP]{server}\->put("HEAD /\er\en\er\en"); \& }, \& ServerInput => sub { \& print "from server: ".$_[ARG0]."\en"; \& }, \& ); \& \& POE::Kernel\->run(); \& exit; .Ve .IP "TCP-Server" 2 .IX Item "TCP-Server" .Vb 1 \& #!perl \& \& use warnings; \& use strict; \& \& use POE qw(Component::Server::TCP); \& \& POE::Component::Server::TCP\->new( \& Port => 443, \& ClientFilter => [ "POE::Filter::SSL", crt => \*(Aqserver.crt\*(Aq, key => \*(Aqserver.key\*(Aq ], \& ClientConnected => sub { \& print "got a connection from $_[HEAP]{remote_ip}\en"; \& $_[HEAP]{client}\->put("Smile from the server!\er\en"); \& }, \& Alias => "tcp", \& ClientInput => sub { \& my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP]; \& $_[HEAP]{client}\->put("You sent:\er\en".$_[ARG0]); \& $_[KERNEL]\->yield("shutdown"); \& }, \& ); \& \& POE::Kernel\->run; \& exit; .Ve .IP "HTTPS-Server" 2 .IX Item "HTTPS-Server" .Vb 10 \& use POE::Filter::SSL::PreFilter \& use POE::Filter::SSL; \& use POE::Component::Server::HTTP; \& use HTTP::Status; \& my $aliases = POE::Component::Server::HTTP\->new( \& Port => 443, \& ContentHandler => { \& \*(Aq/\*(Aq => \e&handler, \& \*(Aq/dir/\*(Aq => sub { return; }, \& \*(Aq/file\*(Aq => sub { return; } \& }, \& Headers => { Server => \*(AqMy Server\*(Aq }, \& PreFilter => POE::Filter::SSL\->new( \& crt => \*(Aqserver.crt\*(Aq, \& key => \*(Aqserver.key\*(Aq, \& cacrt => \*(Aqca.crt\*(Aq \& ) \& ); \& \& sub handler { \& my ($request, $response) = @_; \& $response\->code(RC_OK); \& $response\->content("Hi, you fetched ". $request\->uri); \& return RC_OK; \& } \& \& POE::Kernel\->run(); \& POE::Kernel\->call($aliases\->{httpd}, "shutdown"); \& # next line isn\*(Aqt really needed \& POE::Kernel\->call($aliases\->{tcp}, "shutdown"); .Ve .SS "\s-1SSL\s0 on an established connection" .IX Subsection "SSL on an established connection" .IP "Advanced Example" 2 .IX Item "Advanced Example" This example is an IMAP-Relay which forwards the connections to a \s-1IMAP\s0 server by username. It allows one the unencrypted transfer on port 143, with the option of \s-1SSL\s0 on the established connection (\s-1STARTTLS\s0). On port 993 it allows one to do direct \s-1SSL.\s0 .Sp Tested with Thunderbird version 3.0.5. .Sp .Vb 1 \& #!perl \& \& use warnings; \& use strict; \& \& use POE qw(Component::Server::TCP Component::Client::TCP Filter::SSL Filter::Stream); \& \& my $defaultImapServer = "not.existing.de"; \& my $usernameToImapServer = { \& user1 => \*(Aqmailserver1.domain.de\*(Aq, \& user2 => \*(Aqmailserver2.domain.de\*(Aq, \& # ... \& }; \& \& POE::Component::Server::TCP\->new( \& Port => 143, \& ClientFilter => "POE::Filter::Stream", \& ClientDisconnected => \e&disconnect, \& ClientConnected => \e&connected, \& ClientInput => \e&handleInput, \& InlineStates => { \& send_stuff => \e&send_stuff, \& _child => \e&child \& } \& ); \& \& POE::Component::Server::TCP\->new( \& Port => 993, \& ClientFilter => [ "POE::Filter::SSL", crt => \*(Aqserver.crt\*(Aq, key => \*(Aqserver.key\*(Aq ], \& ClientConnected => \e&connected, \& ClientDisconnected => \e&disconnect, \& ClientInput => \e&handleInput, \& InlineStates => { \& send_stuff => \e&send_stuff, \& _child => \e&child \& } \& ); \& \& sub disconnect { \& my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP]; \& logevent(\*(Aqserver got disconnect\*(Aq, $session); \& $kernel\->post($heap\->{client_id} => "shutdown"); \& } \& \& sub connected { \& my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP]; \& logevent("got a connection from ".$heap\->{remote_ip}, $session); \& $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.\er\en"); \& } \& \& sub send_stuff { \& my ($heap, $stuff, $session) = @_[HEAP, ARG0, SESSION]; \& logevent("\-> ".length($stuff)." Bytes", $session); \& (defined($heap\->{client})) && (ref($heap\->{client}) eq "POE::Wheel::ReadWrite") && \& $heap\->{client}\->put($stuff); \& } \& \& sub child { \& my ($heap, $child_op, $child) = @_[HEAP, ARG0, ARG1]; \& if ($child_op eq "create") { \& $heap\->{client_id} = $child\->ID; \& } \& } \& \& sub handleInput { \& my ($kernel, $session, $heap, $input) = @_[KERNEL, SESSION, HEAP, ARG0]; \& if($heap\->{forwarding}) { \& return $kernel\->yield("shutdown") unless (defined($heap\->{client_id})); \& $kernel\->post($heap\->{client_id} => send_stuff => $input); \& } elsif ($input =~ /^(\ed+)\es+STARTTLS[\er\en]+/i) { \& $_[HEAP]{client}\->put($1." OK Begin SSL/TLS negotiation now.\er\en"); \& logevent("SSLing now...", $session); \& $_[HEAP]{client}\->set_filter(POE::Filter::SSL\->new(crt => \*(Aqserver.crt\*(Aq, key => \*(Aqserver.key\*(Aq)); \& } elsif ($input =~ /^(\ed+)\es+CAPABILITY[\er\en]+/i) { \& $_[HEAP]{client}\->put("* CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS\er\en"); \& $_[HEAP]{client}\->put($1." OK CAPABILITY completed\er\en"); \& } elsif ($input =~ /^(\ed+)\es+login\es+\e"(\eS+)\e"\es+\e"(\eS+)\e"[\er\en]+/i) { \& my $username = $2; \& my $pass = $3; \& logevent("login of user ".$username, $session); \& spawn_client_side($username, $input); \& $heap\->{forwarding}++; \& } else { \& logevent("unknown command before login, disconnecting.", $session); \& return $kernel\->yield("shutdown"); \& } \& } \& \& sub spawn_client_side { \& my $username = shift; \& POE::Component::Client::TCP\->new( \& RemoteAddress => $usernameToImapServer\->{$username} || $defaultImapServer, \& RemotePort => 143, \& Filter => "POE::Filter::Stream", \& Started => sub { \& $_[HEAP]\->{server_id} = $_[SENDER]\->ID; \& $_[HEAP]\->{buf} = $_[ARG0]; \& $_[HEAP]\->{skip} = 0; \& }, \& Connected => sub { \& my ($heap, $session) = @_[HEAP, SESSION]; \& logevent(\*(Aqclient connected\*(Aq, $session); \& $heap\->{server}\->put($heap\->{buf}); \& delete $heap\->{buf}; \& }, \& ServerInput => sub { \& my ($kernel, $heap, $session, $input) = @_[KERNEL, HEAP, SESSION, ARG0]; \& #logevent(\*(Aqclient got input\*(Aq, $session, $input); \& $kernel\->post($heap\->{server_id} => send_stuff => $input) if ($heap\->{skip}++); \& }, \& Disconnected => sub { \& my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION]; \& logevent(\*(Aqclient disconnected\*(Aq, $session); \& $kernel\->post($heap\->{server_id} => \*(Aqshutdown\*(Aq); \& }, \& InlineStates => { \& send_stuff => sub { \& my ($heap, $stuff, $session) = @_[HEAP, ARG0, SESSION]; \& logevent("<\- ".length($stuff)." Bytes", $session); \& (defined($heap\->{server})) && (ref($heap\->{server}) eq "POE::Wheel::ReadWrite") && \& $heap\->{server}\->put($stuff); \& }, \& }, \& Args => [ shift ] \& ); \& } \& \& sub logevent { \& my ($state, $session, $arg) = @_; \& my $id = $session\->ID(); \& print "session $id $state "; \& print ": $arg" if (defined $arg); \& print "\en"; \& } \& \& POE::Kernel\->run; .Ve .SS "Client certificate verification" .IX Subsection "Client certificate verification" .IP "Advanced Example" 2 .IX Item "Advanced Example" The following example implements a \s-1HTTPS\s0 server with client certificate verification, which shows details about the verified client certificate. .Sp .Vb 1 \& #!perl \& \& use strict; \& use warnings; \& use Socket; \& use POE qw( \& Wheel::SocketFactory \& Wheel::ReadWrite \& Driver::SysRW \& Filter::SSL \& Filter::Stackable \& Filter::HTTPD \& ); \& \& POE::Session\->create( \& inline_states => { \& _start => sub { \& my $heap = $_[HEAP]; \& $heap\->{listener} = POE::Wheel::SocketFactory\->new( \& BindAddress => \*(Aq0.0.0.0\*(Aq, \& BindPort => 443, \& Reuse => \*(Aqyes\*(Aq, \& SuccessEvent => \*(Aqsocket_birth\*(Aq, \& FailureEvent => \*(Aq_stop\*(Aq, \& ); \& }, \& _stop => sub { \& delete $_[HEAP]\->{listener}; \& }, \& socket_birth => sub { \& my ($socket) = $_[ARG0]; \& POE::Session\->create( \& inline_states => { \& _start => sub { \& my ($heap, $kernel, $connected_socket, $address, $port) = @_[HEAP, KERNEL, ARG0, ARG1, ARG2]; \& $heap\->{sslfilter} = POE::Filter::SSL\->new( \& crt => \*(Aqserver.crt\*(Aq, \& key => \*(Aqserver.key\*(Aq, \& cacrt => \*(Aqca.crt\*(Aq, \& cipher => \*(AqDHE\-RSA\-AES256\-GCM\-SHA384:AES256\-SHA\*(Aq, \& #cacrl => \*(Aqca.crl\*(Aq, # Uncomment this, if you have a CRL file. \& debug => 1, \& clientcert => 1 \& ); \& $heap\->{socket_wheel} = POE::Wheel::ReadWrite\->new( \& Handle => $connected_socket, \& Driver => POE::Driver::SysRW\->new(), \& Filter => POE::Filter::Stackable\->new(Filters => [ \& $heap\->{sslfilter}, \& POE::Filter::HTTPD\->new() \& ]), \& InputEvent => \*(Aqsocket_input\*(Aq, \& ErrorEvent => \*(Aq_stop\*(Aq, \& ); \& }, \& socket_input => sub { \& my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0]; \& my (@certid) = ($heap\->{sslfilter}\->clientCertIds()); \& my $content = \*(Aq\*(Aq; \& if ($heap\->{sslfilter}\->clientCertValid()) { \& $content .= "Hello valid client Certifcate:"; \& } else { \& $content .= "None or invalid client certificate:"; \& } \& $content .= "