Bug 1028 - Spamc/spamd + ssl
Summary: Spamc/spamd + ssl
Status: RESOLVED FIXED
Alias: None
Product: Spamassassin
Classification: Unclassified
Component: spamc/spamd (show other bugs)
Version: 2.41
Hardware: All All
: P2 enhancement
Target Milestone: 3.0.0
Assignee: Malte S. Stretz
URL:
Whiteboard:
Keywords: spamd
Depends on:
Blocks:
 
Reported: 2002-09-25 13:09 UTC by Nate
Modified: 2002-12-18 06:47 UTC (History)
1 user (show)



Attachment Type Modified Status Actions Submitter/CLA Status
Patch to INSTALL patch None Nate [NoCLA]
Patch to libspamc.c patch None Nate [NoCLA]
Patch to spamc.c patch None Nate [NoCLA]
Patch to spamc.pod patch None Nate [NoCLA]
Patch to spamd patch None Nate [NoCLA]

Note You need to log in before you can comment on or make changes to this bug.
Description Nate 2002-09-25 13:09:36 UTC
I wrote an ssl patch to spamc/spamd.  It makes a couple of changes in order to
do that:

	Switch from Socket.pm to IO::Socket or IO::Socket::SSL
	Move spamc -s to spamc -m and use -s for ssl

To build with SSL support set CFLAGS to "-DSPAMC_SSL -lssl -lcrypto".  I'm sure
there's a better way to do this, I'm not sure how.  

I apologize in advance if this patch file is all messed up.

Index: spamassassin-2.41/INSTALL
diff -c spamassassin-2.41/INSTALL:1.1.1.1 spamassassin-2.41/INSTALL:1.2
*** spamassassin-2.41/INSTALL:1.1.1.1	Mon Sep 23 15:27:43 2002
--- spamassassin-2.41/INSTALL	Wed Sep 25 14:25:43 2002
***************
*** 39,44 ****
--- 39,45 ----
  To install as non-root, do something like this:
  
  	[unzip/untar the archive]
+ 	[add -DSPAMC_SSL to $CFLAGS to build an SSL enabled spamc]
  	cd Mail-SpamAssassin-*
  	perl Makefile.PL PREFIX=~/sausr SYSCONFDIR=~/saetc
  	make
Index: spamassassin-2.41/spamd/libspamc.c
diff -c spamassassin-2.41/spamd/libspamc.c:1.1.1.1
spamassassin-2.41/spamd/libspamc.c:1.3
*** spamassassin-2.41/spamd/libspamc.c:1.1.1.1	Mon Sep 23 15:27:44 2002
--- spamassassin-2.41/spamd/libspamc.c	Tue Sep 24 15:51:32 2002
***************
*** 19,24 ****
--- 19,31 ----
  #include <netdb.h>
  #include <arpa/inet.h>
  
+ #ifdef SPAMC_SSL
+ #include <openssl/crypto.h>
+ #include <openssl/pem.h>
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+ #endif
+ 
  #ifdef HAVE_SYSEXITS_H
  #include <sysexits.h>
  #endif
***************
*** 346,351 ****
--- 353,370 ----
      int sock;
      float version;
      int response;
+ #ifdef SPAMC_SSL
+     SSL_CTX* ctx;
+     SSL* ssl;
+     SSL_METHOD *meth;
+ 
+     if(flags&SPAMC_USE_SSL){	
+       SSLeay_add_ssl_algorithms();
+       meth = SSLv2_client_method();
+       SSL_load_error_strings();
+       ctx = SSL_CTX_new(meth);
+     }    
+ #endif
  
      m->is_spam=EX_TOOBIG;
      if((buf=malloc(8192))==NULL) return EX_OSERR;
***************
*** 372,386 ****
          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
          return i;
      }
  
      /* Send to spamd */
!     full_write(sock, buf, len);
!     full_write(sock, m->msg, m->msg_len);
!     shutdown(sock, SHUT_WR);
  
      /* Now, read from spamd */
      for(len=0; len<8192; len++){
!         i=read(sock, buf+len, 1);
          if(i<0){
              free(buf);
              free(m->out); m->out=m->msg; m->out_len=m->msg_len;
--- 391,429 ----
          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
          return i;
      }
+     
+ #ifdef SPAMC_SSL
+     if(flags&SPAMC_USE_SSL){
+       ssl = SSL_new(ctx);
+       SSL_set_fd(ssl, sock);
+       SSL_connect(ssl);
+     }
+ #endif    
  
      /* Send to spamd */
! #ifdef SPAMC_SSL
!     if(flags&SPAMC_USE_SSL){	
!       SSL_write(ssl, buf, len);
!       SSL_write(ssl, m->msg, m->msg_len);
!     } else{
! #endif
!       full_write(sock, buf, len);
!       full_write(sock, m->msg, m->msg_len);
! #ifdef SPAMC_SSL
!     }
! #endif
  
      /* Now, read from spamd */
      for(len=0; len<8192; len++){
! #ifdef SPAMC_SSL
!       if(flags&SPAMC_USE_SSL){	  
!         i=SSL_read(ssl, buf+len, 1);
!       } else{
! #endif
! 	i=read(sock, buf+len, 1);
! #ifdef SPAMC_SSL
!       }
! #endif
          if(i<0){
              free(buf);
              free(m->out); m->out=m->msg; m->out_len=m->msg_len;
***************
*** 416,422 ****
          /* Handle different versioned headers */
          if(version-1.0>0.01){
              for(len=0; len<8192; len++){
!                 i=read(sock, buf+len, 1);
                  if(i<=0){
                      free(buf);
                      free(m->out); m->out=m->msg; m->out_len=m->msg_len;
--- 459,473 ----
          /* Handle different versioned headers */
          if(version-1.0>0.01){
              for(len=0; len<8192; len++){
! #ifdef SPAMC_SSL
! 	      if(flags&SPAMC_USE_SSL){
! 		i=SSL_read(ssl, buf+len, 1);
! 	      } else{
! #endif
! 		i=read(sock, buf+len, 1);
! #ifdef SPAMC_SSL
! 	      }
! #endif
                  if(i<=0){
                      free(buf);
                      free(m->out); m->out=m->msg; m->out_len=m->msg_len;
***************
*** 445,451 ****
                      }
  
                      /* Should be end of headers now */
!                     if(full_read(sock, buf, 2, 2)!=2 || buf[0]!='\r' ||
buf[1]!='\n'){
                          /* Nope, bail. */
                          free(buf);
                          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
--- 496,511 ----
                      }
  
                      /* Should be end of headers now */
! #ifdef SPAMC_SSL
! 		    if(flags&SPAMC_USE_SSL){
! 		      i=SSL_read(ssl, buf, 2);
! 		    } else{
! #endif
! 		      i=read(sock, buf, 2);
! #ifdef SPAMC_SSL
! 		    }
! #endif		    
!                     if(i!=2 || buf[0]!='\r' || buf[1]!='\n'){
                          /* Nope, bail. */
                          free(buf);
                          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
***************
*** 465,478 ****
          return EX_PROTOCOL;
      }
  
!     len=full_read(sock, m->out+m->out_len,
m->max_len+EXPANSION_ALLOWANCE+1-m->out_len,
m->max_len+EXPANSION_ALLOWANCE+1-m->out_len);
      if(len+m->out_len>m->max_len+EXPANSION_ALLOWANCE){
          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
          return EX_TOOBIG;
      }
      m->out_len+=len;
  
!     shutdown(sock, SHUT_RD);
  
      if(m->out_len!=expected_len){
          syslog(LOG_ERR, "failed sanity check, %d bytes claimed, %d bytes
seen", expected_len, m->out_len);
--- 525,552 ----
          return EX_PROTOCOL;
      }
  
! #ifdef SPAMC_SSL
!     if(flags&SPAMC_USE_SSL){
!       len=SSL_read(ssl, m->out+m->out_len,
m->max_len+EXPANSION_ALLOWANCE+1-m->out_len);
!     } else{
! #endif
!       len=read(sock, m->out+m->out_len,
m->max_len+EXPANSION_ALLOWANCE+1-m->out_len);
! #ifdef SPAMC_SSL
!     }
! #endif    
      if(len+m->out_len>m->max_len+EXPANSION_ALLOWANCE){
          free(m->out); m->out=m->msg; m->out_len=m->msg_len;
          return EX_TOOBIG;
      }
      m->out_len+=len;
  
!     close(sock);
! #ifdef SPAMC_SSL
!     if(flags&SPAMC_USE_SSL){
!       SSL_free(ssl);
!       SSL_CTX_free(ctx);
!     }
! #endif
  
      if(m->out_len!=expected_len){
          syslog(LOG_ERR, "failed sanity check, %d bytes claimed, %d bytes
seen", expected_len, m->out_len);
Index: spamassassin-2.41/spamd/libspamc.h
diff -c spamassassin-2.41/spamd/libspamc.h:1.1.1.1
spamassassin-2.41/spamd/libspamc.h:1.2
*** spamassassin-2.41/spamd/libspamc.h:1.1.1.1	Mon Sep 23 15:27:44 2002
--- spamassassin-2.41/spamd/libspamc.h	Tue Sep 24 15:51:32 2002
***************
*** 26,31 ****
--- 26,32 ----
  #define SPAMC_RAW_MODE       0
  #define SPAMC_BSMTP_MODE     1
  
+ #define SPAMC_USE_SSL        1<<29
  #define SPAMC_SAFE_FALLBACK  1<<30
  #define SPAMC_CHECK_ONLY     1<<31
  
Index: spamassassin-2.41/spamd/spamc.c
diff -c spamassassin-2.41/spamd/spamc.c:1.1.1.1
spamassassin-2.41/spamd/spamc.c:1.3
*** spamassassin-2.41/spamd/spamc.c:1.1.1.1	Mon Sep 23 15:27:44 2002
--- spamassassin-2.41/spamd/spamc.c	Wed Sep 25 14:44:05 2002
***************
*** 65,72 ****
    printf("-e command [args]: Command to output to instead of stdout. MUST BE
THE LAST OPTION.\n");
    printf("-f: fallback safely - in case of comms error, dump original message
unchanges instead of setting exitcode\n");
    printf("-h: print this help message\n");
    printf("-p port: specify port for connection [default: 783]\n");
!   printf("-s size: specify max message size, any bigger and it will be
returned w/out processing [default: 250k]\n");
    printf("-u username: specify the username for spamd to process this message
under\n");
  }
  
--- 65,73 ----
    printf("-e command [args]: Command to output to instead of stdout. MUST BE
THE LAST OPTION.\n");
    printf("-f: fallback safely - in case of comms error, dump original message
unchanges instead of setting exitcode\n");
    printf("-h: print this help message\n");
+   printf("-m size: specify max message size, any bigger and it will be
returned w/out processing [default: 250k]\n");
    printf("-p port: specify port for connection [default: 783]\n");
!   printf("-s: use SSL to talk to spamd\n");
    printf("-u username: specify the username for spamd to process this message
under\n");
  }
  
***************
*** 75,81 ****
  {
    int opt, i, j;
  
!   while(-1 != (opt = getopt(argc,argv,"-Bcd:e:fhp:t:s:u:")))
    {
      switch(opt)
      {
--- 76,82 ----
  {
    int opt, i, j;
  
!   while(-1 != (opt = getopt(argc,argv,"-Bcd:e:fhsp:t:u:m:")))
    {
      switch(opt)
      {
***************
*** 119,127 ****
  	*username = optarg;
  	break;
        }
!     case 's':
        {
  	*max_size = atoi(optarg);
  	break;
        }
      case '?': {
--- 120,133 ----
  	*username = optarg;
  	break;
        }
!     case 'm':
        {
  	*max_size = atoi(optarg);
+ 	break;
+       }
+     case 's':
+       {
+ 	flags |= SPAMC_USE_SSL;
  	break;
        }
      case '?': {
Index: spamassassin-2.41/spamd/spamc.pod
diff -c spamassassin-2.41/spamd/spamc.pod:1.1.1.1
spamassassin-2.41/spamd/spamc.pod:1.2
*** spamassassin-2.41/spamd/spamc.pod:1.1.1.1	Mon Sep 23 15:27:44 2002
--- spamassassin-2.41/spamd/spamc.pod	Wed Sep 25 14:23:02 2002
***************
*** 7,13 ****
  
  =over
  
! =item spamc [-c] [-d host] [-f] [-h] [-p port] [-s max_size] [-u username] [-e
command [args]]
  
  =back
  
--- 7,13 ----
  
  =over
  
! =item spamc [-c] [-d host] [-f] [-h] [-s] [-p port] [-m max_size] [-u
username] [-e command [args]]
  
  =back
  
***************
*** 60,70 ****
  
  Print this help message and terminate without action
  
  =item B<-p> I<port>
  
  Connect to spamd server listening on given port
  
! =item B<-s> I<max_size>
  
  Set the maximum message size which will be sent to spamd -- any bigger than
  this threshold and the message will be returned unprocessed.  Note that the
--- 60,74 ----
  
  Print this help message and terminate without action
  
+ =item B<-s>
+ 
+ Encrypt data to and from the spamd server with SSL
+ 
  =item B<-p> I<port>
  
  Connect to spamd server listening on given port
  
! =item B<-m> I<max_size>
  
  Set the maximum message size which will be sent to spamd -- any bigger than
  this threshold and the message will be returned unprocessed.  Note that the
Index: spamassassin-2.41/spamd/spamd.raw
diff -c spamassassin-2.41/spamd/spamd.raw:1.1.1.1
spamassassin-2.41/spamd/spamd.raw:1.8
*** spamassassin-2.41/spamd/spamd.raw:1.1.1.1	Mon Sep 23 15:27:44 2002
--- spamassassin-2.41/spamd/spamd.raw	Wed Sep 25 14:43:48 2002
***************
*** 12,18 ****
  
  use lib '../lib';	# added by jm for use inside the distro
  use strict;
! use Socket;
  use Carp;
  use Config;
  use Mail::SpamAssassin;
--- 12,20 ----
  
  use lib '../lib';	# added by jm for use inside the distro
  use strict;
! use IO::Socket;
! use IO::Handle;
  use Carp;
  use Config;
  use Mail::SpamAssassin;
***************
*** 48,53 ****
--- 50,58 ----
  my @OLD_ARGV = @ARGV;    # Getopt::Long tends to clear @ARGV
  Getopt::Long::Configure ("bundling");
  GetOptions(
+ 	'server-key=s' => \$opt{'server-key'},
+ 	'server-cert=s' => \$opt{'server-cert'},
+ 	'ssl' => \$opt{'ssl'},
  	'auto-whitelist|whitelist|a' => \$opt{'auto-whitelist'},
  	'create-prefs!', => \$opt{'create-prefs'}, 'c' => \$opt{'create-prefs'},
  	'daemonize!' => \$opt{'daemonize'}, 'd' => \$opt{'daemonize'},
***************
*** 79,84 ****
--- 84,107 ----
  
  $opt{'help'} and pod2usage(-exitval => $resphash{'EX_USAGE'}, -verbose => 0,
-message => 'For more details, use "man spamd"');
  
+ # Check for server certs
+ $opt{'server-key'} ||= "$LOCAL_RULES_DIR/certs/server-key.pem";
+ $opt{'server-cert'} ||= "$LOCAL_RULES_DIR/certs/server-cert.pem";
+ if ($opt{'ssl'})
+ {
+     require IO::Socket::SSL;
+ 
+     if (!-e $opt{'server-key'})
+     {
+ 	die "The server key file $opt{'server-key'} does not exist";
+     }
+     if (!-e $opt{'server-cert'})
+     {
+ 	die "The server certificate file $opt{'server-cert'} does not exist";
+     }
+ }
+ 
  # These can be changed on command line with -A flag
  if(@{$opt{'allowed-ip'}})
  {
***************
*** 138,153 ****
  setlogsock($socktype) unless $log_facility eq 'stderr';
  
  my $port = $opt{'port'} || 783;
! my $addr = gethostbyname($opt{'listen-ip'} || '127.0.0.1');
  my $proto = getprotobyname('tcp');
  
  ($port) = $port =~ /^(\d+)$/ or die "invalid port";
  
  # Be a well-behaved daemon
! socket(Server, PF_INET, SOCK_STREAM, $proto)            || die "socket: $!";
! setsockopt(Server,SOL_SOCKET,SO_REUSEADDR,pack("l",1))  || die "setsockopt:
$!";
! bind(Server, sockaddr_in($port, $addr))                 || die "bind: $!";
! listen(Server,SOMAXCONN)                                || die "listen: $!";
  
  $opt{'daemonize'} and daemonize();
  
--- 161,196 ----
  setlogsock($socktype) unless $log_facility eq 'stderr';
  
  my $port = $opt{'port'} || 783;
! my $addr = (gethostbyname($opt{'listen-ip'} || '127.0.0.1'))[0];
  my $proto = getprotobyname('tcp');
  
  ($port) = $port =~ /^(\d+)$/ or die "invalid port";
  
  # Be a well-behaved daemon
! my $server;
! if ($opt{'ssl'})
! {
!     $server = new IO::Socket::SSL(LocalAddr => $addr,
! 				  LocalPort => $port,
! 				  Proto => $proto,
! 				  Type => SOCK_STREAM,
! 				  ReuseAddr => 1,
! 				  Listen => 5,
! 				  SSL_verify_mode => 0x00,
! 				  SSL_key_file => $opt{'server-key'},
! 				  SSL_cert_file => $opt{'server-cert'})
! 	|| die "new IO::Socket::SSL: $! $@";
! }
! else
! {
!     $server = new IO::Socket::INET(LocalAddr => $addr,
! 				   LocalPort => $port,
! 				   Proto => $proto,
! 				   Type => SOCK_STREAM,
! 				   ReuseAddr => 1,
! 				   Listen => 10)
! 	|| die "new IO::Socket::INET: $! $@";
! }
  
  $opt{'daemonize'} and daemonize();
  
***************
*** 205,210 ****
--- 248,254 ----
  
  my $current_user;
  my $paddr;
+ my $client;
  
  sub REAPER { # Adapted from the perlipc manpage
  	cleanupchildren;
***************
*** 227,238 ****
  }
  logmsg "server started on port $port (running version
".Mail::SpamAssassin::Version().")";
  
! for ( ; ($paddr = accept(Client,Server)) ; close Client)
  {
! 
      my $start = time;
  
!     my($port,$iaddr) = sockaddr_in($paddr);
      my $name = gethostbyaddr($iaddr,AF_INET);
  
      if (ip_is_allowed(inet_ntoa($iaddr))) {
--- 271,283 ----
  }
  logmsg "server started on port $port (running version
".Mail::SpamAssassin::Version().")";
  
! while (1)
  {
!     $client = $server->accept
! 	|| next;
      my $start = time;
  
!     my($port,$iaddr) = sockaddr_in($client->peername);
      my $name = gethostbyaddr($iaddr,AF_INET);
  
      if (ip_is_allowed(inet_ntoa($iaddr))) {
***************
*** 241,257 ****
      } else {
  	logmsg "unauthorized connection from $name [",
  	inet_ntoa($iaddr),"] at port $port";
  	next;
      }
  
      spawn sub {
  	$|=1; # always immediately flush output
  
  	# First request line off stream
!         local $_ = <STDIN>;
  
  	if (!defined $_) {
! 	    protocol_error ("(closed before headers)");
  	    return 1;
  	}
  
--- 286,305 ----
      } else {
  	logmsg "unauthorized connection from $name [",
  	inet_ntoa($iaddr),"] at port $port";
+ 	$client->close;
  	next;
      }
  
      spawn sub {
  	$|=1; # always immediately flush output
  
+ 	my ($client) = @_;
+ 
  	# First request line off stream
!         local $_ = <$client>;
  
  	if (!defined $_) {
! 	    protocol_error ("(closed before headers)", $client);
  	    return 1;
  	}
  
***************
*** 285,294 ****
  	    {
  		while(1)
                  {
!                     $_ = <STDIN>;
                      if(!defined $_)
                      {
!                         protocol_error ("(EOF during headers)");
                          return 1;
                      }
  
--- 333,342 ----
  	    {
  		while(1)
                  {
!                     $_ = <$client>;
                      if(!defined $_)
                      {
!                         protocol_error ("(EOF during headers)", $client);
                          return 1;
                      }
  
***************
*** 348,354 ****
              my $actual_length;
              my $in_header = 1;
              my $msgid;
!             for (<STDIN>) {
                  if ($in_header) {
                      if (/^$/) {
                          $in_header = 0;
--- 396,402 ----
              my $actual_length;
              my $in_header = 1;
              my $msgid;
!             while (<$client>) {
                  if ($in_header) {
                      if (/^$/) {
                          $in_header = 0;
***************
*** 363,368 ****
--- 411,417 ----
                  }
                  push(@msglines, $_);
                  $actual_length += length;
+ 		last if $actual_length == $expected_length;
              }
  
              logmsg "checking message $msgid for $current_user:$>" .
***************
*** 403,411 ****
              {
                  $response_header .= "Spam: False ; $msg_score /
$msg_threshold";
  	    }
!             print $response_header, "\r\n\r\n";
! 	    print $status->get_names_of_tests_hit,"\r\n" if ($method eq "SYMBOLS");
! 	    print $status->get_report,"\r\n" if ($method eq "REPORT" or $method eq
"REPORT_IFSPAM" and $status->is_spam);
  	    $current_user ||= '(unknown)';
  	    logmsg "$was_it_spam ($msg_score/$msg_threshold) for $current_user:$> in
".
  		sprintf("%3d", time - $start) ." seconds, $actual_length bytes.\n";
--- 452,460 ----
              {
                  $response_header .= "Spam: False ; $msg_score /
$msg_threshold";
  	    }
!             print $client $response_header, "\r\n\r\n";
! 	    print $client $status->get_names_of_tests_hit,"\r\n" if ($method eq
"SYMBOLS");
! 	    print $client $status->get_report,"\r\n" if ($method eq "REPORT" or
$method eq "REPORT_IFSPAM" and $status->is_spam);
  	    $current_user ||= '(unknown)';
  	    logmsg "$was_it_spam ($msg_score/$msg_threshold) for $current_user:$> in
".
  		sprintf("%3d", time - $start) ." seconds, $actual_length bytes.\n";
***************
*** 429,438 ****
  	    {
  		while(1)
                  {
!                     $_ = <STDIN>;
                      if(!defined $_)
                      {
!                         protocol_error ("(EOF during headers)");
                          return 1;
                      }
  
--- 478,487 ----
  	    {
  		while(1)
                  {
!                     $_ = <$client>;
                      if(!defined $_)
                      {
!                         protocol_error ("(EOF during headers)", $client);
                          return 1;
                      }
  
***************
*** 492,498 ****
              my $actual_length;
              my $in_header = 1;
              my $msgid;
!             for (<STDIN>) {
                  if ($in_header) {
                      if (/^$/) {
                          $in_header = 0;
--- 541,547 ----
              my $actual_length;
              my $in_header = 1;
              my $msgid;
!             while (<$client>) {
                  if ($in_header) {
                      if (/^$/) {
                          $in_header = 0;
***************
*** 507,512 ****
--- 556,562 ----
                  }
                  push(@msglines, $_);
                  $actual_length += length;
+ 		last if $actual_length == $expected_length;
              }
  
              logmsg "processing message $msgid for $current_user:$>" .
***************
*** 519,525 ****
  	    # Check length if we're supposed to
  	    if($expected_length)
  	    {
! 		if($actual_length != $expected_length) { protocol_error ("(Content-length
mismatch: $expected_length vs. $actual_length)"); return 1; }
  	    }
  
  	    # Now use copy-on-writed (hopefully) SA object
--- 569,576 ----
  	    # Check length if we're supposed to
  	    if($expected_length)
  	    {
! 		if($actual_length != $expected_length) {
! 	    protocol_error ("(Content-length mismatch: $expected_length vs.
$actual_length)", $client); return 1; }
  	    }
  
  	    # Now use copy-on-writed (hopefully) SA object
***************
*** 531,543 ****
  	    my $msg_resp_length = length($msg_resp);
  	    if($version >= 1.2) # Spamc protocol 1.2 means it accepts content-length
  	    {
! 		print "SPAMD/1.1 $resphash{$resp} $resp\r\n",
  		"Content-length: $msg_resp_length\r\n\r\n",
  		$msg_resp;
  	    }
  	    else # Earlier than 1.2 didn't accept content-length
  	    {
! 		print "SPAMD/1.0 $resphash{$resp} $resp\r\n",
  		$msg_resp;
  	    }
  	    my $was_it_spam;
--- 582,594 ----
  	    my $msg_resp_length = length($msg_resp);
  	    if($version >= 1.2) # Spamc protocol 1.2 means it accepts content-length
  	    {
! 		print $client "SPAMD/1.1 $resphash{$resp} $resp\r\n",
  		"Content-length: $msg_resp_length\r\n\r\n",
  		$msg_resp;
  	    }
  	    else # Earlier than 1.2 didn't accept content-length
  	    {
! 		print $client "SPAMD/1.0 $resphash{$resp} $resp\r\n",
  		$msg_resp;
  	    }
  	    my $was_it_spam;
***************
*** 555,561 ****
  
  	else
  	{
! 	    protocol_error ($_);
  	}
      };
  
--- 606,612 ----
  
  	else
  	{
! 	    protocol_error ($_, $client);
  	}
      };
  
***************
*** 566,578 ****
      # essentially does this!
  
      cleanupchildren;
  }
  
  sub protocol_error {
      local $_ = shift;
  
      my $resp = "EX_PROTOCOL";
!     print "SPAMD/1.0 $resphash{$resp} Bad header line: $_\r\n";
      logmsg "bad protocol: header error: $_";
  }
  
--- 617,631 ----
      # essentially does this!
  
      cleanupchildren;
+     $client->close;
  }
  
  sub protocol_error {
      local $_ = shift;
+     my $client = shift;
  
      my $resp = "EX_PROTOCOL";
!     print $client "SPAMD/1.0 $resphash{$resp} Bad header line: $_\r\n";
      logmsg "bad protocol: header error: $_";
  }
  
***************
*** 605,614 ****
      }
      # else I'm the child -- go spawn
  
!     close Server;
!     open(STDIN,  "<&Client")   || die "can't dup client to stdin";
!     open(STDOUT, ">&Client")   || die "can't dup client to stdout";
!     exit &$coderef();
  }
  
  sub handle_user
--- 658,667 ----
      }
      # else I'm the child -- go spawn
  
!     $server->close;
!     exit &$coderef($client);
  }
  
  sub handle_user
***************
*** 773,779 ****
  {
      my ($sig) = @_;
      logmsg "server killed by SIG$sig, shutting down";
!     close Server;
      defined($opt{'pidfile'}) and unlink($opt{'pidfile'});
      exit 0;
  }
--- 826,832 ----
  {
      my ($sig) = @_;
      logmsg "server killed by SIG$sig, shutting down";
!     $server->close;
      defined($opt{'pidfile'}) and unlink($opt{'pidfile'});
      exit 0;
  }
***************
*** 855,860 ****
--- 908,916 ----
   -V, --virtual-config=dir           Enable Virtual configs (needs -x)
   -r pidfile, --pidfile              Write the process id to pidfile
   -s facility, --syslog=facility     Specify the syslog facility (default:
mail)
+  --server-key keyfile               Specify an SSL keyfile
+  --server-cert certfile             Specify an SSL certificate
+  --ssl                              Run an SSL server
   -u username, --username=username   Run as username
   -v, --vpopmail                     Enable vpopmail config
   -x, --nouser-config                Disable user config files
Comment 1 Malte S. Stretz 2002-09-25 13:31:44 UTC
Sounds interesting. Could you please re-generate the diff in unified format   
(with -u) and attach it to this bug via "Create a new attachment". Please  
split the patch in three files: One for libspamc, one for spamd part and one  
containing the changes for INSTALL et al.  
   
Two things disturb me on the first glance:   
* -s shouldn't change it's meaning. Use -S for SSL.   
* I don't know if IO::Socket is shipped with Perl. If not, it would be nice if 
SA fell back to Socket.pm if IO::Socket is not available. 
Comment 2 Nate 2002-09-25 14:15:38 UTC
Created attachment 357 [details]
Patch to INSTALL
Comment 3 Nate 2002-09-25 14:16:30 UTC
Created attachment 358 [details]
Patch to libspamc.c
Comment 4 Nate 2002-09-25 14:16:49 UTC
Created attachment 359 [details]
Patch to spamc.c
Comment 5 Nate 2002-09-25 14:17:05 UTC
Created attachment 360 [details]
Patch to spamc.pod
Comment 6 Nate 2002-09-25 14:17:24 UTC
Created attachment 361 [details]
Patch to spamd
Comment 7 Nate 2002-09-25 14:21:16 UTC
> Sounds interesting. Could you please re-generate the diff in unified format   
> (with -u) and attach it to this bug via "Create a new attachment". Please  
> split the patch in three files: One for libspamc, one for spamd part and one  
> containing the changes for INSTALL et al.  
  
Done.
 
> Two things disturb me on the first glance:   
> * -s shouldn't change it's meaning. Use -S for SSL.   

Good call, I made the switch

> * I don't know if IO::Socket is shipped with Perl. If not, it would be nice if 
> SA fell back to Socket.pm if IO::Socket is not available. 

IO::Socket's been part of the 5.? standard package as long as I can see.  Anyone
with an install newer then 1997 should have it for sure.
Comment 8 Malte S. Stretz 2002-09-25 14:39:23 UTC
Seems like IO::Socket was shipped with 5.6.1. It wasn't shipped with 5.005 but   
that doesn't matter as it didn't ship Socket.pm, too ;-) I'll have a deeper  
look into your patch(es) tomorrow.  
Comment 9 Daniel Quinlan 2002-09-25 16:25:04 UTC
Okay, first the patch...

- Please attach patches as attachments, not cut-and-paste.  (I think Malte
  covered this.)

- Since changes are all related, one patch would be preferred over the
  split-up version.  It's easier to download and if someone (Malte)
  wants a split-up version, he can just run diffsplit.

  Also, if you end-up with several versions of the patch, I don't really want
  there to be 5 * (number of versions) attachments.

  Okay, it's perhaps Malte's call since he took the bug, but I think you're
  much less likely to get review from other developers by splitting up the
  patch into a zillion attachments.

  Malte, perhaps you'd be interested in my diffsplit tool:

  http://www.pathname.com/~quinlan/software/diffsplit/diffsplit

  "cvsdiff" is a related useful tool for generating correct CVS diffs:

  http://www.pathname.com/~quinlan/software/cvsdiff/cvsdiff

  :-)

Now for the general comments:

How much load does SSL add to the spamd server?  It seems like it would be
less expensive and more economical to run spamd inside your firewall.  Or
perhaps I should ask why it's better/economical to run spamd outside your
firewall?
Comment 10 Nate 2002-09-25 17:08:07 UTC
> Now for the general comments:

> How much load does SSL add to the spamd server?  It seems like it would be
> less expensive and more economical to run spamd inside your firewall.  Or
> perhaps I should ask why it's better/economical to run spamd outside your
> firewall?

Not a lot.  I haven't done any real tests, but it takes about the same amout of
time to talk to an ssl server as it does to talk to a regular one.  Other
daemons I've ssl-atized haven't seen big performance hits.  

As for the firewall, unless you're running a "deny all" type setup you're still
running the risk of intruders.  Even the best firewall doesn't protect you from
people on the inside.  At a site like ours (8500 users, 1500+ computers) the
internal threat is often the bigger one.  I wrote the path because we don't send
anything in the clear over the network if we can help it.  Most people probably
aren't so picky, but they don't have to use SSL if they don't want to.
Comment 11 Malte S. Stretz 2002-09-26 01:56:43 UTC
Dan: Thanks for the links, very helpful ;-) I hoped to get three patches, one   
for spamc, one for spamd and one for the rest but maybe I wasn't very clear in   
this point.  
  
Nate is probably right: It's a "use it or leave it" option for the paranoid. 
I'll backport the patch to my local 2.42-cvs installation and test it 
thoroughly before I commit it to HEAD. 
Comment 12 Daniel Quinlan 2002-09-26 02:42:09 UTC
Subject: Re: [SAdev]  Spamc/spamd + ssl

spamassassin-contrib@msquadrat.de wrote:

> Dan: Thanks for the links, very helpful ;-) I hoped to get three
> patches, one for spamc, one for spamd and one for the rest but maybe
> I wasn't very clear in this point.

Well, they are helpful.  :-)

I should probably post my "cvslog" script.  It's very nice too.
   
> Nate is probably right: It's a "use it or leave it" option for the
> paranoid.  I'll backport the patch to my local 2.42-cvs installation
> and test it thoroughly before I commit it to HEAD.

I have no objections to the patch, paranoia is a good thing.

I'm still a bit foggy on why people want to run spamd/spamc from one
machine to another, though.  :-)

Dan

Comment 13 Malte S. Stretz 2002-09-26 03:28:09 UTC
> I'm still a bit foggy on why people want to run spamd/spamc from one 
> machine to another, though.  :-) 
 
Some kind of load balancing? Just a guess :-] 
Comment 14 Craig Hughes 2002-09-26 07:36:46 UTC
Yeah, load balancing is important.  I know of one site using spamassassin to filter mail on 
an SMTP gateway for 350,000 mailboxes spread across about 50 companies.  There's 
just no way you can process that volume of traffic on a single box.
Comment 15 Nate 2002-09-26 11:02:06 UTC
> I'm still a bit foggy on why people want to run spamd/spamc from one
> machine to another, though.  :-)

We have people receiving mail on over 100 different computers.  It's a lot 
easier for us to maintain one spamd then keep it running all over the 
department.

Comment 16 nix 2002-09-28 06:29:58 UTC
The mailserver/firewall here has 8Mb of RAM in total; spamassassin / spamd's
working set is over twice that size, so it couldn't run SA at all without spamd.

With spamd, everything is just peachy; spamc has a tiny memory requirement, and
spamd can run on a box with more memory (about a hundred times as much) behind
the firewall.
Comment 17 Duncan Findlay 2002-12-13 20:32:51 UTC
Malte, what's the status on this?
Comment 18 Justin Mason 2002-12-18 15:47:20 UTC
ok, now in (sorry Malte ;)