From 6212af81288f31a7b0c8bdea087615b41b37a503 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Mon, 16 Oct 2023 14:02:02 +0900 Subject: [PATCH] Add support for encode()/decode() methods in Net::Cmd Some mechanisms in Authen::SASL offer additional security layers and encode()/decode() methods for them. The I/O routines in Net::Cmd now use it if they are available. Currently only Net::SMTP defines protocol-specific methods. In addition to that, EHLO is now issued just after a successful authentication with an additional security layer as described in Sec. 4 in RFC 4954. --- lib/Net/Cmd.pm | 19 +++++++++++++++- lib/Net/SMTP.pm | 58 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/lib/Net/Cmd.pm b/lib/Net/Cmd.pm index ef1896b..41f5cf0 100644 --- a/lib/Net/Cmd.pm +++ b/lib/Net/Cmd.pm @@ -188,9 +188,24 @@ sub set_status { 1; } +# The default encode/decode methods +sub encode { + my ($cmd, $text, $len) = @_; + + $text; +} + + +sub decode { + my ($cmd, $text, $len) = @_; + + $text; +} + + sub _syswrite_with_timeout { my $cmd = shift; - my $line = shift; + my $line = $cmd->encode($_[0], length($_[0])); my $len = length($line); my $offset = 0; @@ -352,6 +367,8 @@ sub getline { substr($buf, 0, 0) = $partial; ## prepend from last sysread + $buf = $cmd->decode($buf, length($buf)); ## decode it + my @buf = split(/\015?\012/, $buf, -1); ## break into lines $partial = pop @buf; diff --git a/lib/Net/SMTP.pm b/lib/Net/SMTP.pm index 3b82a60..f37222e 100644 --- a/lib/Net/SMTP.pm +++ b/lib/Net/SMTP.pm @@ -122,6 +122,8 @@ sub new { return; } } + # {Hello} will be reused when a security layer is negotiated after the EHLO + ${*$obj}{'net_smtp_hello'} = $arg{Hello} || ""; $obj; } @@ -158,6 +160,34 @@ sub etrn { } +# Overload encode method when Authen::SASL is available +sub encode { + my ($self, $text, $len) = @_; + my $sasl = ${*$self}{'net_smtp_sasl'}; + + if ($sasl and $sasl->can('encode')) { + my $ret = $sasl->encode($text, $len); + + # Add the leading 4 octects for the length (Sec 3.7, RFC 4422) + pack('N', length($ret)) . $ret; + } else { + $self->SUPER::encode($text, $len); + } +} + + +# Overload decode method when Authen::SASL is available +sub decode { + my ($self, $text, $len) = @_; + my $sasl = ${*$self}{'net_smtp_sasl'}; + + # Remove the leading 4 octects for the length (Sec 3.7, RFC 4422) + return ($sasl and $sasl->can('decode')) + ? $sasl->decode(substr($text, 4), unpack('N', substr($text, 0, 4))) + : $self->SUPER::decode($text, $len); +} + + sub auth { my ($self, $username, $password) = @_; @@ -216,13 +246,6 @@ sub auth { # todo that we would really need to change the ISA hierarchy # so we don't inherit from IO::Socket, but instead hold it in an attribute - # DIGEST-MD5 can support integrity and/or confidentiality protection - # over the socket traffic (auth-int and auth-conf) which we do not - # support here for now. To disable them, set maxssf=minssf=0. - - $client->property('maxssf' => 0, 'minssf' => 0) - if ($client->mechanism eq 'DIGEST-MD5'); - my @cmd = ("AUTH", $client->mechanism); my $code; @@ -241,6 +264,27 @@ sub auth { $self->debug_print(1, "(decoded) " . $str . "\n") if $self->debug; } + # Some mechanisms in Authen::SASL offer additional security layers + # for integrity and/or confidentiality and define encode() and + # decode() methods. To support them, store the Authen::SASL + # object in {net_smtp_sasl}. + # + ${*$self}{'net_smtp_sasl'} = $sasl->{conn}; + + $self->debug_print(0, "sasl->conn is installed\n"); + + # When an additional security layer is negotiated, the client SHOULD send + # an EHLO again (Sec. 4, RFC 4954) + # + if ($sasl->{conn}->can('encode') or $sasl->{conn}->can('decode')) { + unless ($self->hello(${*$self}{'net_smtp_hello'})) { + my $err = ref($self) . ": " . $self->code . " " . $self->message; + $self->close(); + $@ = $err; + return; + } + } + $code == CMD_OK; }