Hi,

setup summary:

spamc called from exim on a per-user-basis:

spamcheck:
  driver = pipe
  use_bsmtp = true
  batch_max = 1
  command = /usr/sbin/exim4 -oMr spam-scanned -bS
  transport_filter = /usr/bin/spamc -u ${local_part} -U /var/run/spamd
  user = Debian-exim
  group = Debian-exim
  ...

spamd:
  /usr/sbin/spamd --socketpath=/var/run/spamd ...

Of course, this is insecure, because any local user can call spamc on another local user's behalf.

To solve this, I've implemented

1.) SO_PEERCRED authentication for PF_UNIX sockets (including unsolved portability issue concerning "struct ucred" unpacking)
2.) a "trusted user" who may set any other user via the User: header if identified correctly

Thus, Debian-exim (trusted user) can use "-u $local_part" even with identification. Now the spamd command line becomes

/usr/sbin/spamd --socketpath=/var/run/spamd --auth-ident --trusted-user Debian-exim ...

I'll include the spamd diff just in case someone finds it useful. Of course, the --trusted-user option could be augmented to allow an array of trusted users.

Cheers,
Enrik
--- spamassassin-3.0.2/spamd/spamd.raw	2004-10-22 17:21:26.000000000 +0200
+++ spamassassin-3.0.2-patched/spamd/spamd.raw	2005-01-27 15:58:08.000000000 +0100
@@ -176,6 +176,7 @@
   'ssl'                      => \$opt{'ssl'},
   'syslog-socket=s'          => \$opt{'syslog-socket'},
   'syslog|s=s'               => \$opt{'syslog'},
+  'trusted-user=s'           => \$opt{'trusted-user'},
   'user-config'              => \$opt{'user-config'},
   'username|u=s'             => \$opt{'username'},
   'version|V'                => \$opt{'version'},
@@ -239,7 +240,6 @@
   defined $opt{'socketpath'}
   and ( ( @{ $opt{'allowed-ip'} } > 0 )
     or defined $opt{'ssl'}
-    or defined $opt{'auth-ident'}
     or defined $opt{'port'} )
   )
 {
@@ -270,13 +270,20 @@
 
 # ident-based spamc user authentication
 if ( $opt{'auth-ident'} ) {
-  eval { require Net::Ident };
-  die
+  if (defined $opt{'socketpath'}) {
+    eval { my $dummy = SO_PEERCRED() };
+    die
+"fatal: peercred-based authentication requested, but SO_PEERCRED is unavailble\n"
+      if ($@);
+  } else {
+    eval { require Net::Ident };
+    die
 "fatal: ident-based authentication requested, but Net::Ident is unavailable\n"
-    if ($@);
+      if ($@);
 
-  $opt{'ident-timeout'} = undef if $opt{'ident-timeout'} <= 0.0;
-  import Net::Ident qw(ident_lookup);
+    $opt{'ident-timeout'} = undef if $opt{'ident-timeout'} <= 0.0;
+    import Net::Ident qw(ident_lookup);
+  }
 }
 
 # Check for server certs
@@ -1278,7 +1285,7 @@
   }
 
   $current_user = $1;
-  if ($opt{'auth-ident'} && !auth_ident($current_user)) {
+  if ($opt{'auth-ident'} && !auth_ident($current_user, $opt{'trusted-user'})) {
     return 1;
   }
 
@@ -1347,15 +1354,40 @@
 
 sub auth_ident {
   my $username = shift;
-  my $ident_username = ident_lookup( $client, $opt{'ident-timeout'} );
+  my $trusted = shift;
+  my $ident_username;
+  if (defined $opt{'socketpath'}) {
+    $ident_username = peercred_lookup( $client );
+  } else {
+    $ident_username = ident_lookup( $client, $opt{'ident-timeout'} );
+  }
   my $dn = $ident_username || 'NONE';    # display name
-  warn "ident_username = $dn, spamc_username = $username\n" if $opt{'debug'};
-  if ( $username ne $ident_username ) {
-    logmsg( "fatal: ident username ($dn) does not match "
-        . "spamc username ($username)" );
-    return 0;
+  if ( defined $trusted ) {
+    warn "ident_username = $dn, spamc_username = $username, trusted_username = $trusted\n" if $opt{'debug'};
+  } else {
+    warn "ident_username = $dn, spamc_username = $username\n" if $opt{'debug'};
   }
-  return 1;
+  if ( defined $trusted && $trusted eq $ident_username ||
+       $username eq $ident_username ) {
+    return 1;
+  }
+  logmsg( "fatal: ident username ($dn) does not match "
+      . "spamc username ($username)" );
+  return 0;
+}
+
+sub peercred_lookup {
+  my $sock = shift;
+  my $username;
+
+  # PF_UNIX "ident"
+  my $ucred = $sock->sockopt(SO_PEERCRED);
+  if (defined $ucred) {
+    # XXX portability
+    my ($pid, $uid, $gid) = unpack("I3", $ucred);
+    $username = ( getpwuid $uid )[0];
+  }
+  return $username;
 }
 
 sub handle_user {
@@ -1935,8 +1967,11 @@
  -u username, --username=username   Run as username
  -v, --vpopmail                     Enable vpopmail config
  -x, --nouser-config                Disable user config files
- --auth-ident                       Use ident to authenticate spamc user
+ --auth-ident                       Use ident (tcp) or SO_PEERCRED (unix)
+                                    to authenticate spamc user
  --ident-timeout=timeout            Timeout for ident connections
+ --trusted-user=username            Trust this user if identified, i.e. may
+                                    set abritrary usernames via protocol
  -A host,..., --allowed-ips=..,..   Limit ip addresses which can connect
  -D, --debug                        Print debugging messages
  -L, --local                        Use local tests only (no DNS)

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature



Reply via email to