[svn:qpsmtpd] r605 - in trunk: . lib lib/Qpsmtpd
[email protected] 26 Jan 2006 21:31:05 -0000
| Newsgroups | perl.cvs.qpsmtpd |
|---|---|
| Message-ID | <[email protected]> |
Author: jpeacock
Date: Thu Jan 26 13:31:05 2006
New Revision: 605
Modified:
trunk/ (props changed)
trunk/lib/Qpsmtpd.pm
trunk/lib/Qpsmtpd/Auth.pm
trunk/lib/Qpsmtpd/Constants.pm
trunk/lib/Qpsmtpd/PollServer.pm
trunk/lib/Qpsmtpd/SMTP.pm
Log:
Working AUTH support in PollServer mode. All AUTH code moved to
SMTP.pm (the Auth.pm POD will get renamed to README.authentication).
Modified: trunk/lib/Qpsmtpd.pm
==============================================================================
--- trunk/lib/Qpsmtpd.pm (original)
+++ trunk/lib/Qpsmtpd.pm Thu Jan 26 13:31:05 2006
@@ -487,20 +487,30 @@ sub size_threshold {
return $Size_threshold;
}
+sub authenticated {
+ my ($self, $state) = @_;
+ $self->{_auth_state} = $state if $state;
+ return (defined $self->{_auth_state} ? $self->{_auth_state} : 0);
+}
+
sub auth_user {
my ($self, $user) = @_;
- $user =~ s/[\r\n].*//s;
- $self->{_auth_user} = $user if $user;
+ $self->{_auth_user} = $user if $user;
return (defined $self->{_auth_user} ? $self->{_auth_user} : "" );
}
+sub auth_ticket {
+ my ($self, $ticket) = @_;
+ $self->{_auth_ticket} = $ticket if $ticket;
+ return (defined $self->{_auth_ticket} ? $self->{_auth_ticket} : "" );
+}
+
sub auth_mechanism {
my ($self, $mechanism) = @_;
- $mechanism =~ s/[\r\n].*//s;
- $self->{_auth_mechanism} = $mechanism if $mechanism;
+ $self->{_auth_mechanism} = lc($mechanism) if $mechanism;
return (defined $self->{_auth_mechanism} ? $self->{_auth_mechanism} : "" );
}
-
+
sub fd {
return shift->{fd};
}
Modified: trunk/lib/Qpsmtpd/Auth.pm
==============================================================================
--- trunk/lib/Qpsmtpd/Auth.pm (original)
+++ trunk/lib/Qpsmtpd/Auth.pm Thu Jan 26 13:31:05 2006
@@ -214,119 +214,4 @@ Please see the LICENSE file included wit
=cut
-package Qpsmtpd::Auth;
-use Qpsmtpd::Constants;
-use MIME::Base64;
-
-sub e64
-{
- my ($arg) = @_;
- my $res = encode_base64($arg);
- chomp($res);
- return($res);
-}
-
-sub SASL {
-
- # $DB::single = 1;
- my ( $session, $mechanism, $prekey ) = @_;
- my ( $user, $passClear, $passHash, $ticket );
- $mechanism = lc($mechanism);
-
- if ( $mechanism eq "plain" ) {
- if (!$prekey) {
- $session->respond( 334, "Please continue" );
- $prekey= <>;
- }
- ( $passHash, $user, $passClear ) = split /\x0/,
- decode_base64($prekey);
-
- }
- elsif ($mechanism eq "login") {
-
- if ( $prekey ) {
- ($passHash, $user, $passClear) = split /\x0/, decode_base64($prekey);
- }
- else {
-
- $session->respond(334, e64("Username:"));
- $user = decode_base64(<>);
- #warn("Debug: User: '$user'");
- if ($user eq '*') {
- $session->respond(501, "Authentification canceled");
- return DECLINED;
- }
-
- $session->respond(334, e64("Password:"));
- $passClear = <>;
- $passClear = decode_base64($passClear);
- #warn("Debug: Pass: '$pass'");
- if ($passClear eq '*') {
- $session->respond(501, "Authentification canceled");
- return DECLINED;
- }
- }
- }
- elsif ( $mechanism eq "cram-md5" ) {
-
- # rand() is not cryptographic, but we only need to generate a globally
- # unique number. The rand() is there in case the user logs in more than
- # once in the same second, of if the clock is skewed.
- $ticket = sprintf( "<%x.%x\@" . $session->config("me") . ">",
- rand(1000000), time() );
-
- # We send the ticket encoded in Base64
- $session->respond( 334, encode_base64( $ticket, "" ) );
- my $line = <>;
- chop($line);
- chop($line);
-
- if ( $line eq '*' ) {
- $session->respond( 501, "Authentification canceled" );
- return DECLINED;
- }
-
- ( $user, $passHash ) = split( ' ', decode_base64($line) );
-
- }
- else {
- $session->respond( 500, "Unrecognized authentification mechanism" );
- return DECLINED;
- }
-
- # try running the specific hooks first
- my ( $rc, $msg ) =
- $session->run_hooks( "auth-$mechanism", $mechanism, $user, $passClear,
- $passHash, $ticket );
-
- # try running the polymorphous hooks next
- if ( !$rc || $rc == DECLINED ) {
- ( $rc, $msg ) =
- $session->run_hooks( "auth", $mechanism, $user, $passClear,
- $passHash, $ticket );
- }
-
- if ( $rc == OK ) {
- $msg = "Authentication successful for $user" .
- ( defined $msg ? " - " . $msg : "" );
- $session->respond( 235, $msg );
- $session->connection->relay_client(1);
- $session->log( LOGINFO, $msg );
-
- $session->auth_user($user);
- $session->auth_mechanism($mechanism);
-
- return OK;
- }
- else {
- $msg = "Authentication failed for $user" .
- ( defined $msg ? " - " . $msg : "" );
- $session->respond( 535, $msg );
- $session->log( LOGERROR, $msg );
- return DENY;
- }
-}
-
-# tag: qpsmtpd plugin that sets RELAYCLIENT when the user authentifies
-
1;
Modified: trunk/lib/Qpsmtpd/Constants.pm
==============================================================================
--- trunk/lib/Qpsmtpd/Constants.pm (original)
+++ trunk/lib/Qpsmtpd/Constants.pm Thu Jan 26 13:31:05 2006
@@ -26,6 +26,7 @@ my %return_codes = (
DECLINED => 909,
DONE => 910,
CONTINUATION => 911,
+ AUTH_PENDING => 912,
);
use vars qw(@ISA @EXPORT);
Modified: trunk/lib/Qpsmtpd/PollServer.pm
==============================================================================
--- trunk/lib/Qpsmtpd/PollServer.pm (original)
+++ trunk/lib/Qpsmtpd/PollServer.pm Thu Jan 26 13:31:05 2006
@@ -15,9 +15,10 @@ use fields qw(
hooks
start_time
cmd_timeout
- _auth
- _auth_user
_auth_mechanism
+ _auth_state
+ _auth_ticket
+ _auth_user
_commands
_config_cache
_connection
@@ -158,6 +159,9 @@ sub process_cmd {
}
return $resp;
}
+ elsif ( $self->authenticated == AUTH_PENDING ) {
+ return $self->auth_process($line);
+ }
else {
# No such method - i.e. unrecognized command
my ($rc, $msg) = $self->run_hooks("unrecognized_command", $meth, @params);
@@ -315,7 +319,7 @@ sub end_of_data {
}
# only true if client authenticated
- if ( defined $self->{_auth} and $self->{_auth} == OK ) {
+ if ( $self->authenticated == OK ) {
$header->add("X-Qpsmtpd-Auth","True");
}
Modified: trunk/lib/Qpsmtpd/SMTP.pm
==============================================================================
--- trunk/lib/Qpsmtpd/SMTP.pm (original)
+++ trunk/lib/Qpsmtpd/SMTP.pm Thu Jan 26 13:31:05 2006
@@ -12,6 +12,7 @@ use Qpsmtpd::Auth;
use Qpsmtpd::Address ();
use Mail::Header ();
+use MIME::Base64;
#use Data::Dumper;
use POSIX qw(strftime);
use Net::DNS;
@@ -48,6 +49,11 @@ sub dispatch {
$self->{_counter}++;
+ if ( $self->authenticated == AUTH_PENDING ) {
+ # must be in the middle of prompting for auth parameters
+ return $self->auth_process($cmd,@_);
+ }
+
if ($cmd !~ /^(\w{1,12})$/ or !exists $self->{_commands}->{$1}) {
my ($rc, $msg) = $self->run_hooks("unrecognized_command", $cmd, @_);
return $self->unrecognized_command_respond($rc, $msg, @_) unless $rc == CONTINUATION;
@@ -114,13 +120,13 @@ sub connect_respond {
elsif ($rc != DONE) {
my $greets = $self->config('smtpgreeting');
if ( $greets ) {
- $greets .= " ESMTP";
+ $greets .= " ESMTP";
}
else {
- $greets = $self->config('me')
- . " ESMTP qpsmtpd "
- . $self->version
- . " ready; send us your mail, but not your spam.";
+ $greets = $self->config('me')
+ . " ESMTP qpsmtpd "
+ . $self->version
+ . " ready; send us your mail, but not your spam.";
}
$self->respond(220, $greets);
@@ -197,8 +203,8 @@ sub ehlo_respond {
$self->transaction;
my @capabilities = $self->transaction->notes('capabilities')
- ? @{ $self->transaction->notes('capabilities') }
- : ();
+ ? @{ $self->transaction->notes('capabilities') }
+ : ();
# Check for possible AUTH mechanisms
my %auth_mechanisms;
@@ -229,17 +235,148 @@ HOOK: foreach my $hook ( keys %{$self->{
}
}
+sub e64
+{
+ my ($arg) = @_;
+ my $res = encode_base64($arg);
+ chomp($res);
+ return($res);
+}
+
sub auth {
- my ( $self, $arg, @stuff ) = @_;
+ my ( $self, $mechanism, $prekey ) = @_;
#they AUTH'd once already
return $self->respond( 503, "but you already said AUTH ..." )
- if ( defined $self->{_auth}
- and $self->{_auth} == OK );
+ if ( $self->authenticated == OK );
return $self->respond( 503, "AUTH not defined for HELO" )
if ( $self->connection->hello eq "helo" );
- return $self->{_auth} = Qpsmtpd::Auth::SASL( $self, $arg, @stuff );
+ # $DB::single = 1;
+
+ $self->auth_mechanism($mechanism);
+ $self->authenticated(AUTH_PENDING);
+ if ( $prekey ) { # easy single step
+ unless ( $mechanism =~ /^(plain|login)$/i ) {
+ # must be plain or login
+ $self->respond( 500, "Unrecognized authentification mechanism" );
+ return DECLINED;
+ }
+ my ($passHash, $user, $passClear) = split /\x0/,decode_base64($prekey);
+ # we have all of the elements ready to go now
+ if ( $mechanism =~ /login/i ) {
+ $self->auth_user($user);
+ return $self->auth_process(e64($passClear));
+ }
+ else {
+ return $self->auth_process($prekey);
+ }
+ }
+ else {
+ if ( $mechanism =~ /plain/i ) {
+ $self->respond( 334, "Please continue" );
+ }
+ elsif ( $mechanism =~ /login/i ) {
+ $self->respond( 334, e64("Username:") );
+ }
+ elsif ( $mechanism =~ /cram-md5/i ) {
+ # rand() is not cryptographic, but we only need to generate a globally
+ # unique number. The rand() is there in case the user logs in more than
+ # once in the same second, or if the clock is skewed.
+ my $ticket = sprintf( "<%x.%x\@" . $self->config("me") . ">",
+ rand(1000000), time() );
+
+ # Store this for later
+ $self->auth_ticket($ticket);
+ # We send the ticket encoded in Base64
+ $self->respond( 334, encode_base64( $ticket, "" ) );
+ }
+ }
+ return DECLINED;
+}
+
+sub auth_process {
+ my ($self, $line) = @_;
+ my ( $user, $passClear, $passHash, $ticket, $mechanism );
+
+ # do this once here
+ $mechanism = $self->auth_mechanism;
+ $user = $self->auth_user;
+ $ticket = $self->auth_ticket;
+
+ if ( $mechanism eq 'plain' ) {
+ ( $passHash, $user, $passClear ) = split /\x0/,
+ decode_base64($line);
+ }
+ elsif ( $mechanism eq 'login' ) {
+ if ( $user ) {
+ # must be getting the password now
+ $passClear = decode_base64($line);
+ }
+ else {
+ # must be getting the user now
+ $user = decode_base64($line);
+ $self->auth_user($user);
+ $self->respond(334, e64("Password:"));
+ }
+ }
+ elsif ( $mechanism eq "cram-md5" ) {
+ $line =~ tr/[\r\n]//d; # cannot simply chomp CRLF
+
+ ( $user, $passHash ) = split( ' ', decode_base64($line) );
+
+ }
+ else {
+ $self->respond( 500, "Unrecognized authentification mechanism" );
+ return DECLINED;
+ }
+ if ($user eq '*') {
+ $self->respond(501, "Authentification canceled");
+ return DECLINED;
+ }
+
+ # check to see if we can proceed with the hooks
+ if ( $user and ( $passClear or $passHash ) ) {
+ # try running the specific hooks first
+ my ( $rc, $msg ) =
+ $self->run_hooks( "auth-$mechanism",
+ $mechanism, $user, $passClear,
+ $passHash, $ticket );
+
+ # try running the polymorphous hooks next
+ if ( !$rc || $rc == DECLINED ) {
+ ( $rc, $msg ) =
+ $self->run_hooks( "auth", $mechanism, $user, $passClear,
+ $passHash, $ticket );
+ }
+ return $self->auth_respond($rc, $msg, $mechanism, $user)
+ unless $rc == CONTINUATION;
+ }
+ else {
+ return CONTINUATION;
+ }
+}
+
+
+sub auth_respond {
+ my ($self, $rc, $msg, $mechanism, $user) = @_;
+ if ( $rc == OK ) {
+ $msg = "Authentication successful for $user" .
+ ( defined $msg ? " - " . $msg : "" );
+ $self->respond( 235, $msg );
+ $self->connection->relay_client(1);
+ $self->log( LOGINFO, $msg );
+ $self->authenticated(OK);
+
+ return OK;
+ }
+ else {
+ $msg = "Authentication failed for $user" .
+ ( defined $msg ? " - " . $msg : "" );
+ $self->respond( 535, $msg );
+ $self->log( LOGERROR, $msg );
+ return DENY;
+ }
}
sub mail {
@@ -541,8 +678,8 @@ sub data_respond {
# FIXME - call plugins to work on just the header here; can
# save us buffering the mail content.
- # Save the start of just the body itself
- $self->transaction->set_body_start();
+ # Save the start of just the body itself
+ $self->transaction->set_body_start();
}
@@ -564,8 +701,9 @@ sub data_respond {
$self->transaction->header($header);
my $smtp = $self->connection->hello eq "ehlo" ? "ESMTP" : "SMTP";
- my $authheader = (defined $self->{_auth} and $self->{_auth} == OK) ?
- "(smtp-auth username $self->{_auth_user}, mechanism $self->{_auth_mechanism})\n" : "";
+ my $authheader = ($self->authenticated == OK)
+ ? "(smtp-auth username $self->auth_user, mechanism $self->auth_mechanism)\n"
+ : "";
$header->add("Received", "from ".$self->connection->remote_info
." (HELO ".$self->connection->hello_host . ") (".$self->connection->remote_ip