.\" -*- mode: troff; coding: utf-8 -*- .\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) .\" .\" 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 .. .\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. .ie n \{\ . ds C` "" . ds C' "" 'br\} .el\{\ . 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 2024-01-10 "perl v5.38.2" "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 SSL (see the \fISSL on an established connection\fR example in \fISYNOPSIS\fR) on an already established connection. You are able to combine \&\fIPOE::Filter::SSL\fR with other filters, for example have a HTTPS server together with \fIPOE::Filter::HTTPD\fR (see the \fIHTTPS-Server\fR example in \fISYNOPSIS\fR). .PP \&\fIPOE::Filter::SSL\fR is based on \fINet::SSLeay\fR, but got two XS 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 CRL 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 SSL or TLS protocol, for example with static AES encryption .RE .RE .RS 4 .RE .SH SYNOPSIS .IX Header "SYNOPSIS" By default \fIPOE::Filter::SSL\fR acts as a SSL server. To use it in client mode you just have to set the \fIclient\fR option of \fR\f(BInew()\fR\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 "SSL 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 IMAP server by username. It allows one the unencrypted transfer on port 143, with the option of SSL on the established connection (STARTTLS). On port 993 it allows one to do direct SSL. .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 HTTPS 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 .= "