Lines 1083-1088
Link Here
|
1083 |
check( $1, $2, $start, $remote_hostname, $remote_hostaddr ); |
1083 |
check( $1, $2, $start, $remote_hostname, $remote_hostaddr ); |
1084 |
} |
1084 |
} |
1085 |
|
1085 |
|
|
|
1086 |
elsif (/(LEARN) SPAMC\/(.*)/) { |
1087 |
learn( $1, $2, $start, $remote_hostname, $remote_hostaddr ); |
1088 |
} |
1089 |
|
1086 |
# Looks like a client is just seeing if we're alive. |
1090 |
# Looks like a client is just seeing if we're alive. |
1087 |
|
1091 |
|
1088 |
elsif (/PING SPAMC\/(.*)/) { |
1092 |
elsif (/PING SPAMC\/(.*)/) { |
Lines 1100-1105
Link Here
|
1100 |
return 1; |
1104 |
return 1; |
1101 |
} |
1105 |
} |
1102 |
|
1106 |
|
|
|
1107 |
sub handle_setuid_to_user { |
1108 |
|
1109 |
if ( $spamtest->{paranoid} ) { |
1110 |
logmsg("PARANOID: still running as root, closing connection."); |
1111 |
die; |
1112 |
} |
1113 |
logmsg( "Still running as root: user not specified with -u, " |
1114 |
. "not found, or set to root. Fall back to nobody." ); |
1115 |
my ( $uid, $gid ) = ( getpwnam('nobody') )[ 2, 3 ]; |
1116 |
$uid =~ /^(\d+)$/ and $uid = $1; # de-taint |
1117 |
$gid =~ /^(\d+)$/ and $gid = $1; # de-taint |
1118 |
|
1119 |
$) = "$gid $gid"; # eGID |
1120 |
$> = $uid; # eUID |
1121 |
if ( !defined($uid) || ( $> != $uid and $> != ( $uid - 2**32 ) ) ) { |
1122 |
logmsg("fatal: setuid to nobody failed"); |
1123 |
die; |
1124 |
} |
1125 |
|
1126 |
} |
1127 |
|
1103 |
sub check { |
1128 |
sub check { |
1104 |
my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_; |
1129 |
my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_; |
1105 |
local ($_); |
1130 |
local ($_); |
Lines 1124-1148
Link Here
|
1124 |
$expected_length = $hdrs->{expected_length}; |
1149 |
$expected_length = $hdrs->{expected_length}; |
1125 |
} |
1150 |
} |
1126 |
|
1151 |
|
1127 |
if ( $setuid_to_user && $> == 0 ) { |
1152 |
handle_setuid_to_user if ($setuid_to_user && $> == 0); |
1128 |
if ( $spamtest->{paranoid} ) { |
|
|
1129 |
logmsg("PARANOID: still running as root, closing connection."); |
1130 |
die; |
1131 |
} |
1132 |
logmsg( "Still running as root: user not specified with -u, " |
1133 |
. "not found, or set to root. Fall back to nobody." ); |
1134 |
my ( $uid, $gid ) = ( getpwnam('nobody') )[ 2, 3 ]; |
1135 |
$uid =~ /^(\d+)$/ and $uid = $1; # de-taint |
1136 |
$gid =~ /^(\d+)$/ and $gid = $1; # de-taint |
1137 |
|
1153 |
|
1138 |
$) = "$gid $gid"; # eGID |
|
|
1139 |
$> = $uid; # eUID |
1140 |
if ( !defined($uid) || ( $> != $uid and $> != ( $uid - 2**32 ) ) ) { |
1141 |
logmsg("fatal: setuid to nobody failed"); |
1142 |
die; |
1143 |
} |
1144 |
} |
1145 |
|
1146 |
if ( $opt{'sql-config'} && !defined($current_user) ) { |
1154 |
if ( $opt{'sql-config'} && !defined($current_user) ) { |
1147 |
unless ( handle_user_sql('nobody') ) { |
1155 |
unless ( handle_user_sql('nobody') ) { |
1148 |
service_unavailable_error("Error fetching user preferences via SQL"); |
1156 |
service_unavailable_error("Error fetching user preferences via SQL"); |
Lines 1318-1323
Link Here
|
1318 |
$mail->finish(); |
1326 |
$mail->finish(); |
1319 |
} |
1327 |
} |
1320 |
|
1328 |
|
|
|
1329 |
# XXX - yuck too much shared code with check |
1330 |
sub learn { |
1331 |
my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_; |
1332 |
local ($_); |
1333 |
my $expected_length; |
1334 |
my $learn_type; |
1335 |
my $forget = 0; |
1336 |
my $isspam = 1; |
1337 |
|
1338 |
my $hdrs = {}; |
1339 |
|
1340 |
# parse_headers returns !=0 on failure |
1341 |
return 1 |
1342 |
if parse_headers( |
1343 |
$hdrs, $client, |
1344 |
{ |
1345 |
'Content-length' => \&got_clen_header, |
1346 |
'User' => \&got_user_header, |
1347 |
'Learn-type' => \&got_learn_type_header, |
1348 |
} |
1349 |
); |
1350 |
|
1351 |
$expected_length = $hdrs->{expected_length}; |
1352 |
$learn_type = $hdrs->{learn_type}; |
1353 |
|
1354 |
&handle_setuid_to_user if ($setuid_to_user && $> == 0); |
1355 |
|
1356 |
if ( $opt{'sql-config'} && !defined($current_user) ) { |
1357 |
unless ( handle_user_sql('nobody') ) { |
1358 |
service_unavailable_error("Error fetching user preferences via SQL"); |
1359 |
return 1; |
1360 |
} |
1361 |
} |
1362 |
|
1363 |
if ( $opt{'ldap-config'} && !defined($current_user) ) { |
1364 |
handle_user_ldap('nobody'); |
1365 |
} |
1366 |
|
1367 |
my $resp = "EX_OK"; |
1368 |
|
1369 |
# Now read in message |
1370 |
my @msglines; |
1371 |
my $actual_length = 0; |
1372 |
while ( $_ = $client->getline() ) { |
1373 |
$actual_length += length($_); |
1374 |
push(@msglines, $_); |
1375 |
last if (defined $expected_length && $actual_length >= $expected_length); |
1376 |
} |
1377 |
|
1378 |
my $mail = $spamtest->parse(\@msglines); |
1379 |
# Free some mem. |
1380 |
undef @msglines; |
1381 |
|
1382 |
if ( $mail->get_header("X-Spam-Checker-Version") ) { |
1383 |
my $new_mail = $spamtest->parse($spamtest->remove_spamassassin_markup($mail), 1); |
1384 |
$mail->finish(); |
1385 |
$mail = $new_mail; |
1386 |
} |
1387 |
|
1388 |
# Extract the Message-Id(s) for logging purposes. |
1389 |
my $msgid = $mail->get_pristine_header("Message-Id"); |
1390 |
my $rmsgid = $mail->get_pristine_header("Resent-Message-Id"); |
1391 |
foreach my $id ((\$msgid, \$rmsgid)) { |
1392 |
if ( $$id ) { |
1393 |
while ( $$id =~ s/\([^\(\)]*\)// ) |
1394 |
{ } # remove comments and |
1395 |
$$id =~ s/^\s+|\s+$//g; # leading and trailing spaces |
1396 |
$$id =~ s/\s+/ /g; # collapse whitespaces |
1397 |
$$id =~ s/^.*?<(.*?)>.*$/$1/; # keep only the id itself |
1398 |
$$id =~ s/[^\x21-\x7e]/?/g; # replace all weird chars |
1399 |
$$id =~ s/[<>]/?/g; # plus all dangling angle brackets |
1400 |
$$id =~ s/^(.+)$/<$1>/; # re-bracket the id (if not empty) |
1401 |
} |
1402 |
} |
1403 |
|
1404 |
$msgid ||= "(unknown)"; |
1405 |
$current_user ||= "(unknown)"; |
1406 |
|
1407 |
my $learn_type_desc; |
1408 |
my $learn_type_desc_past; |
1409 |
|
1410 |
if ($learn_type == 0) { |
1411 |
$learn_type_desc = "learning spam"; |
1412 |
$learn_type_desc_past = "learned spam"; |
1413 |
$isspam = 1; |
1414 |
} |
1415 |
elsif ($learn_type == 1) { |
1416 |
$learn_type_desc = "learning ham"; |
1417 |
$learn_type_desc_past = "learned ham"; |
1418 |
$isspam = 0; |
1419 |
} |
1420 |
elsif ($learn_type == 2) { |
1421 |
$learn_type_desc = "forgetting"; |
1422 |
$learn_type_desc_past = "forgot"; |
1423 |
$forget = 1; |
1424 |
} |
1425 |
|
1426 |
logmsg( $learn_type_desc |
1427 |
. " message $msgid" |
1428 |
. ( $rmsgid ? " aka $rmsgid" : "" ) |
1429 |
. " for ${current_user}:$>" |
1430 |
. "." ); |
1431 |
|
1432 |
# Check length if we're supposed to. |
1433 |
if ( defined $expected_length && $actual_length != $expected_length ) { |
1434 |
protocol_error( |
1435 |
"(Content-Length mismatch: Expected $expected_length bytes, got $actual_length bytes)" |
1436 |
); |
1437 |
$mail->finish(); |
1438 |
return 1; |
1439 |
} |
1440 |
|
1441 |
my $status = $spamtest->learn( $mail, undef, $isspam, $forget ); |
1442 |
my $hdr; |
1443 |
|
1444 |
if ($status->did_learn()) { |
1445 |
$hdr .= "Learned: Yes"; |
1446 |
} |
1447 |
else { |
1448 |
$hdr .= "Learned: No"; |
1449 |
} |
1450 |
|
1451 |
print $client "SPAMD/1.1 $resphash{$resp} $resp\r\n", |
1452 |
$hdr . "\r\n\r\n"; |
1453 |
|
1454 |
my $scantime = sprintf( "%.1f", time - $start_time ); |
1455 |
|
1456 |
|
1457 |
logmsg( "$learn_type_desc_past message for $current_user:$> in" |
1458 |
. " $scantime seconds, $actual_length bytes." ); |
1459 |
$status->finish(); # added by jm to allow GC'ing |
1460 |
$mail->finish(); |
1461 |
} |
1462 |
|
1321 |
########################################################################### |
1463 |
########################################################################### |
1322 |
|
1464 |
|
1323 |
# generalised header parser. |
1465 |
# generalised header parser. |
Lines 1422-1427
Link Here
|
1422 |
return 0; |
1564 |
return 0; |
1423 |
} |
1565 |
} |
1424 |
|
1566 |
|
|
|
1567 |
sub got_learn_type_header { |
1568 |
my ( $hdrs, $header, $value ) = @_; |
1569 |
if ( $value !~ /^(\d*)$/ ) { |
1570 |
protocol_error("(Learn-type contains non-numeric bytes)"); |
1571 |
return 1; |
1572 |
} |
1573 |
my $type = $1; |
1574 |
if ($type != 0 && $type != 1 && $type != 2) { |
1575 |
protocol_error("(Learn-type contains invalid type)"); |
1576 |
return 1; |
1577 |
} |
1578 |
$hdrs->{learn_type} = $type; |
1579 |
return 0; |
1580 |
} |
1581 |
|
1425 |
sub protocol_error { |
1582 |
sub protocol_error { |
1426 |
my ($err) = @_; |
1583 |
my ($err) = @_; |
1427 |
my $resp = "EX_PROTOCOL"; |
1584 |
my $resp = "EX_PROTOCOL"; |