develooper Front page | perl.cvs.qpsmtpd | Postings from January 2006

[svn:qpsmtpd] r605 - in trunk: . lib lib/Qpsmtpd

From:
jpeacock
Date:
January 26, 2006 13:31
Subject:
[svn:qpsmtpd] r605 - in trunk: . lib lib/Qpsmtpd
Message ID:
20060126213105.28008.qmail@x1.develooper.com
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



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About