1 # <@LICENSE> 2 # Copyright 2004 Apache Software Foundation 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 # 16 17 =head1 NAME 18 19 Mail::SpamAssassin::Conf::LDAP - load SpamAssassin scores from LDAP database 20 21 =head1 SYNOPSIS 22 23 (see Mail::SpamAssassin) 24 25 26 =head1 DESCRIPTION 27 28 Mail::SpamAssassin is a module to identify spam using text analysis and 29 several internet-based realtime blacklists. 30 31 This class is used internally by SpamAssassin to load scores from an LDAP 32 database. Please refer to the C documentation for public 33 interfaces. 34 35 =head1 METHODS 36 37 =over 4 38 39 =cut 40 41 package Mail::SpamAssassin::Conf::LDAP; 42 43 use Mail::SpamAssassin::Logger; 44 45 use strict; 46 use warnings; 47 use bytes; 48 use Carp; 49 50 use vars qw{ 51 @ISA 52 }; 53 54 @ISA = qw(); 55 56 ########################################################################### 57 58 sub new { 59 my $class = shift; 60 $class = ref($class) || $class; 61 my ($main) = @_; 62 63 my $self = { 64 'main' => $main 65 }; 66 67 bless ($self, $class); 68 $self; 69 } 70 71 ########################################################################### 72 73 sub load_modules { # static 74 dbg("ldap: loading Net::LDAP and URI"); 75 eval { 76 require Net::LDAP; # actual server connection 77 require URI; # parse server connection dsn 78 }; 79 80 # do any other preloading that will speed up operation 81 } 82 83 ########################################################################### 84 85 =item $f->load ($username) 86 87 Read configuration paramaters from LDAP server and parse scores from it. 88 89 =cut 90 91 sub load { 92 my ($self, $username) = @_; 93 94 my $url = $self->{main}->{conf}->{user_scores_dsn}; # an ldap URI 95 dbg("ldap: URL is $url"); 96 if(!defined($url) || $url eq '') { 97 dbg("ldap: No URL defined; skipping LDAP"); 98 return; 99 } 100 101 eval { 102 # make sure we can see croak messages from DBI 103 local $SIG{'__DIE__'} = sub { warn "$_[0]"; }; 104 require Net::LDAP; 105 require URI; 106 load_with_ldap($self, $username, $url); 107 }; 108 109 if ($@) { 110 warn "ldap: failed to load user scores from LDAP server, ignored\n"; 111 } 112 } 113 114 sub load_with_ldap { 115 my ($self, $username, $url) = @_; 116 117 # ldapurl = scheme "://" [hostport] ["/" 118 # [dn ["?" [attributes] ["?" [scope] 119 # ["?" [filter] ["?" extensions]]]]]] 120 121 my $uri = URI->new("$url"); 122 123 my $host = $uri->host; 124 if (!defined($host) || $host eq '') { 125 dbg("ldap: No server specified, assuming localhost"); 126 $host = "localhost"; 127 } 128 my $port = $uri->port; 129 my $base = $uri->dn; 130 my @attr = $uri->attributes; 131 my $scope = $uri->scope; 132 my $filter = $uri->filter; 133 my %extn = $uri->extensions; # unused 134 135 $filter =~ s/__USERNAME__/$username/g; 136 dbg("ldap: host=$host, port=$port, base='$base', attr=${attr[0]}, scope=$scope, filter='$filter'"); 137 138 my $main = $self->{main}; 139 my $ldapuser = $main->{conf}->{user_scores_ldap_username}; 140 my $ldappass = $main->{conf}->{user_scores_ldap_password}; 141 142 if(!$ldapuser) { 143 undef($ldapuser); 144 } else { 145 dbg("ldap: user='$ldapuser'"); 146 } 147 148 if(!$ldappass) { 149 undef($ldappass); 150 } else { 151 # don't log this to avoid leaking sensitive info 152 # dbg("ldap: pass='$ldappass'"); 153 } 154 155 my $f_attribute = $attr[0]; 156 157 my $ldap = Net::LDAP->new ("$host:$port", onerror => "warn"); 158 if (!defined($ldapuser) && !defined($ldappass)) { 159 $ldap->bind; 160 } else { 161 $ldap->bind($ldapuser, password => $ldappass); 162 } 163 164 my $result = $ldap->search( base => $base, 165 filter => $filter, 166 scope => $scope, 167 attrs => \@attr 168 ); 169 170 my $conf = ''; 171 foreach my $entry ($result->all_entries) { 172 my @v = $entry->get_value($f_attribute); 173 foreach my $v (@v) { 174 dbg("ldap: retrieving prefs for $username: $v"); 175 $conf .= $v."\n"; 176 } 177 } 178 $main->{conf}->{main} = $main; 179 $main->{conf}->parse_scores_only($conf); 180 delete $main->{conf}->{main}; 181 return; 182 } 183 184 ########################################################################### 185 186 sub sa_die { Mail::SpamAssassin::sa_die(@_); } 187 188 ########################################################################### 189 190 1;