View | Details | Raw Unified | Return to bug 1201
Collapse All | Expand All

(-)spamassassin-trunk/spamc/libspamc.h (-3 / +6 lines)
Lines 97-102 Link Here
97
/* log to stderr */
97
/* log to stderr */
98
#define SPAMC_LOG_TO_STDERR  (1<<22)
98
#define SPAMC_LOG_TO_STDERR  (1<<22)
99
99
100
/* Nov 24, 2004 NP: added learning support */
101
#define SPAMC_LEARN  (1<<21)
102
100
/* Aug 14, 2002 bj: A struct for storing a message-in-progress */
103
/* Aug 14, 2002 bj: A struct for storing a message-in-progress */
101
typedef enum
104
typedef enum
102
{
105
{
Lines 204-210 Link Here
204
 * failover, more than one host is defined, but if there is only one there,
207
 * failover, more than one host is defined, but if there is only one there,
205
 * no failover is done.
208
 * no failover is done.
206
 */
209
 */
207
int message_filter(struct transport *tp, const char *username,
210
int message_filter(struct transport *tp, const char *username, int learntype,
208
		   int flags, struct message *m);
211
		   int flags, struct message *m);
209
212
210
/* Dump the message. If there is any data in the message (typically, m->type
213
/* Dump the message. If there is any data in the message (typically, m->type
Lines 216-222 Link Here
216
/* Do a message_read->message_filter->message_write sequence, handling errors
219
/* Do a message_read->message_filter->message_write sequence, handling errors
217
 * appropriately with dump_message or appropriate CHECK_ONLY output. Returns
220
 * appropriately with dump_message or appropriate CHECK_ONLY output. Returns
218
 * EX_OK or EX_ISSPAM/EX_NOTSPAM on success, some error EX on error. */
221
 * EX_OK or EX_ISSPAM/EX_NOTSPAM on success, some error EX on error. */
219
int message_process(struct transport *trans, char *username, int max_size,
222
int message_process(struct transport *trans, char *username, int learntype, int max_size,
220
		    int in_fd, int out_fd, const int flags);
223
		    int in_fd, int out_fd, const int flags);
221
224
222
/* Cleanup the resources we allocated for storing the message. Call after
225
/* Cleanup the resources we allocated for storing the message. Call after
Lines 224-230 Link Here
224
void message_cleanup(struct message *m);
227
void message_cleanup(struct message *m);
225
228
226
/* Aug 14, 2002 bj: This is now legacy, don't use it. */
229
/* Aug 14, 2002 bj: This is now legacy, don't use it. */
227
int process_message(struct transport *tp, char *username,
230
int process_message(struct transport *tp, char *username, int learntype,
228
		    int max_size, int in_fd, int out_fd,
231
		    int max_size, int in_fd, int out_fd,
229
		    const int check_only, const int safe_fallback);
232
		    const int check_only, const int safe_fallback);
230
233
(-)spamassassin-trunk/spamc/spamc.c (-8 / +16 lines)
Lines 140-146 Link Here
140
        "                      [default: 250k]\n");
140
        "                      [default: 250k]\n");
141
    usg("  -u username         User for spamd to process this message under.\n"
141
    usg("  -u username         User for spamd to process this message under.\n"
142
        "                      [default: current user]\n");
142
        "                      [default: current user]\n");
143
    
143
    usg("  -L learntype        Message gets learned as spam (0),\n"
144
		"                      or learned as ham (1), or forgotten (2)\n");
144
    usg("  -B                  Assume input is a single BSMTP-formatted\n"
145
    usg("  -B                  Assume input is a single BSMTP-formatted\n"
145
        "                      message.\n");
146
        "                      message.\n");
146
    
147
    
Lines 173-185 Link Here
173
 */
174
 */
174
int
175
int
175
read_args(int argc, char **argv,
176
read_args(int argc, char **argv,
176
          int *max_size, char **username,
177
          int *max_size, char **username, int *learntype,
177
          struct transport *ptrn)
178
          struct transport *ptrn)
178
{
179
{
179
#ifndef _WIN32
180
#ifndef _WIN32
180
    const char *opts = "-BcrRd:e:fyp:t:s:u:xSHU:ElhV";
181
    const char *opts = "-BcrRd:e:fyp:t:s:u:L:xSHU:ElhV";
181
#else
182
#else
182
    const char *opts = "-BcrRd:fyp:t:s:u:xSHElhV";
183
    const char *opts = "-BcrRd:fyp:t:s:u:L:xSHElhV";
183
#endif
184
#endif
184
    int opt;
185
    int opt;
185
    int ret = EX_OK;
186
    int ret = EX_OK;
Lines 280-285 Link Here
280
                *username = optarg;
281
                *username = optarg;
281
                break;
282
                break;
282
            }
283
            }
284
            case 'L':
285
			{
286
				flags |= SPAMC_LEARN;
287
				*learntype = atoi(optarg);
288
				break;
289
			}
283
#ifndef _WIN32
290
#ifndef _WIN32
284
            case 'U':
291
            case 'U':
285
            {
292
            {
Lines 298-304 Link Here
298
                flags |= SPAMC_SYMBOLS;
305
                flags |= SPAMC_SYMBOLS;
299
                break;
306
                break;
300
            }
307
            }
301
            
302
            case '?':
308
            case '?':
303
            case ':':
309
            case ':':
304
            {
310
            {
Lines 460-465 Link Here
460
    int out_fd = -1;
466
    int out_fd = -1;
461
    int result;
467
    int result;
462
    int ret;
468
    int ret;
469
	int learntype = 0;
463
470
464
    transport_init(&trans);
471
    transport_init(&trans);
465
472
Lines 476-487 Link Here
476
   /* Now parse the command line arguments. First, set the defaults. */
483
   /* Now parse the command line arguments. First, set the defaults. */
477
   max_size = 250 * 1024;
484
   max_size = 250 * 1024;
478
   username = NULL;
485
   username = NULL;
479
   if ((ret = read_args(argc, argv, &max_size, &username, &trans)) != EX_OK) {
486
487
   if ((ret = read_args(argc, argv, &max_size, &username, &learntype, &trans)) != EX_OK) {
480
       if (ret == EX_TEMPFAIL )
488
       if (ret == EX_TEMPFAIL )
481
           ret = EX_OK;
489
           ret = EX_OK;
482
       goto finish;
490
       goto finish;
483
   }
491
   }
484
   
492
 
485
   ret = get_current_user(&username);
493
   ret = get_current_user(&username);
486
   if (ret != EX_OK)
494
   if (ret != EX_OK)
487
       goto finish;
495
       goto finish;
Lines 518-524 Link Here
518
	
526
	
519
	if (ret == EX_OK) {
527
	if (ret == EX_OK) {
520
	    
528
	    
521
	    ret = message_filter(&trans, username, flags, &m);
529
	    ret = message_filter(&trans, username, learntype, flags, &m);
522
	    free(username); username = NULL;
530
	    free(username); username = NULL;
523
	    
531
	    
524
	    if (ret == EX_OK) {
532
	    if (ret == EX_OK) {
(-)spamassassin-trunk/spamc/libspamc.c (-9 / +34 lines)
Lines 805-811 Link Here
805
    return EX_PROTOCOL;
805
    return EX_PROTOCOL;
806
}
806
}
807
807
808
int message_filter(struct transport *tp, const char *username,
808
int message_filter(struct transport *tp, const char *username, int learntype,
809
		   int flags, struct message *m)
809
		   int flags, struct message *m)
810
{
810
{
811
    char buf[8192];
811
    char buf[8192];
Lines 814-819 Link Here
814
    int sock = -1;
814
    int sock = -1;
815
    int rc;
815
    int rc;
816
    char versbuf[20];
816
    char versbuf[20];
817
	char strlearntype[1];
817
    float version;
818
    float version;
818
    int response;
819
    int response;
819
    int failureval;
820
    int failureval;
Lines 843-851 Link Here
843
    }
844
    }
844
    m->out_len = 0;
845
    m->out_len = 0;
845
846
847
    /* Build spamd protocol header */
846
848
847
    /* Build spamd protocol header */
849
	if (flags & SPAMC_CHECK_ONLY)
848
    if (flags & SPAMC_CHECK_ONLY)
849
	strcpy(buf, "CHECK ");
850
	strcpy(buf, "CHECK ");
850
    else if (flags & SPAMC_REPORT_IFSPAM)
851
    else if (flags & SPAMC_REPORT_IFSPAM)
851
	strcpy(buf, "REPORT_IFSPAM ");
852
	strcpy(buf, "REPORT_IFSPAM ");
Lines 853-859 Link Here
853
	strcpy(buf, "REPORT ");
854
	strcpy(buf, "REPORT ");
854
    else if (flags & SPAMC_SYMBOLS)
855
    else if (flags & SPAMC_SYMBOLS)
855
	strcpy(buf, "SYMBOLS ");
856
	strcpy(buf, "SYMBOLS ");
856
    else
857
    else if (flags & SPAMC_LEARN )
858
	{
859
		strcpy(buf, "LEARN ");
860
		len = strlen(buf);
861
	}
862
	else
857
	strcpy(buf, "PROCESS ");
863
	strcpy(buf, "PROCESS ");
858
864
859
    len = strlen(buf);
865
    len = strlen(buf);
Lines 868-873 Link Here
868
    strcat(buf, "\r\n");
874
    strcat(buf, "\r\n");
869
    len = strlen(buf);
875
    len = strlen(buf);
870
876
877
878
	if (flags & SPAMC_LEARN)
879
	{
880
		if ((learntype > 2) | (learntype < 0 ))
881
		{
882
		    free(m->out);
883
		    m->out = m->msg;
884
			m->out_len = m->msg_len;
885
			return EX_OSERR;
886
		}
887
		sprintf(strlearntype,"%d",learntype);
888
		strcpy(buf + len, "Learn-type: ");
889
		strcat(buf + len, strlearntype);
890
		strcat(buf + len, "\r\n");
891
		len += strlen(buf + len);
892
	}
893
871
    if (username != NULL) {
894
    if (username != NULL) {
872
	if (strlen(username) + 8 >= (bufsiz - len)) {
895
	if (strlen(username) + 8 >= (bufsiz - len)) {
873
	    free(m->out);
896
	    free(m->out);
Lines 880-886 Link Here
880
	strcat(buf + len, "\r\n");
903
	strcat(buf + len, "\r\n");
881
	len += strlen(buf + len);
904
	len += strlen(buf + len);
882
    }
905
    }
883
    if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) {
906
907
	if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) {
884
	free(m->out);
908
	free(m->out);
885
	m->out = m->msg;
909
	m->out = m->msg;
886
	m->out_len = m->msg_len;
910
	m->out_len = m->msg_len;
Lines 1048-1054 Link Here
1048
}
1072
}
1049
1073
1050
1074
1051
int message_process(struct transport *trans, char *username, int max_size,
1075
int message_process(struct transport *trans, char *username, int learntype, int max_size,
1052
		    int in_fd, int out_fd, const int flags)
1076
		    int in_fd, int out_fd, const int flags)
1053
{
1077
{
1054
    int ret;
1078
    int ret;
Lines 1060-1066 Link Here
1060
    ret = message_read(in_fd, flags, &m);
1084
    ret = message_read(in_fd, flags, &m);
1061
    if (ret != EX_OK)
1085
    if (ret != EX_OK)
1062
	goto FAIL;
1086
	goto FAIL;
1063
    ret = message_filter(trans, username, flags, &m);
1087
    ret = message_filter(trans, username, learntype, flags, &m);
1064
    if (ret != EX_OK)
1088
    if (ret != EX_OK)
1065
	goto FAIL;
1089
	goto FAIL;
1066
    if (message_write(out_fd, &m) < 0)
1090
    if (message_write(out_fd, &m) < 0)
Lines 1085-1090 Link Here
1085
    }
1109
    }
1086
}
1110
}
1087
1111
1112
1088
void message_cleanup(struct message *m)
1113
void message_cleanup(struct message *m)
1089
{
1114
{
1090
    if (m->out != NULL && m->pre != NULL && m->out != m->pre+m->pre_len)
1115
    if (m->out != NULL && m->pre != NULL && m->out != m->pre+m->pre_len)
Lines 1097-1103 Link Here
1097
}
1122
}
1098
1123
1099
/* Aug 14, 2002 bj: Obsolete! */
1124
/* Aug 14, 2002 bj: Obsolete! */
1100
int process_message(struct transport *tp, char *username, int max_size,
1125
int process_message(struct transport *tp, char *username, int learntype, int max_size,
1101
		    int in_fd, int out_fd, const int my_check_only,
1126
		    int in_fd, int out_fd, const int my_check_only,
1102
		    const int my_safe_fallback)
1127
		    const int my_safe_fallback)
1103
{
1128
{
Lines 1109-1115 Link Here
1109
    if (my_safe_fallback)
1134
    if (my_safe_fallback)
1110
	flags |= SPAMC_SAFE_FALLBACK;
1135
	flags |= SPAMC_SAFE_FALLBACK;
1111
1136
1112
    return message_process(tp, username, max_size, in_fd, out_fd, flags);
1137
    return message_process(tp, username, learntype, max_size, in_fd, out_fd, flags);
1113
}
1138
}
1114
1139
1115
/*
1140
/*
(-)spamassassin-trunk/spamd/spamd.raw (-18 / +175 lines)
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";

Return to bug 1201