spam.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the BSD package *
*Copyright (c) 1978-2012 The Regents of the University of California an*
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* conditions are met: *
* *
* 1. Redistributions of source code must retain the above *
* copyright notice, this list of conditions and the *
* following disclaimer. *
* *
* 2. Redistributions in binary form must reproduce the above *
* copyright notice, this list of conditions and the *
* materials provided with the distribution. *
* *
* 3. Neither the name of The Regents of the University of California*
* names of its contributors may be used to endorse or *
* promote products derived from this software without *
* specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in *
* distribution. *
* 3. Neither the name of the University nor the names of its *
* contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
* SUCH DAMAGE. *
* *
* Kurt Shoens (UCB) *
* gsf *
* *
***********************************************************************/
#pragma prototyped
/*
* Mail -- a mail program
*
* spam heuristics
*/
#include "mailx.h"
#if _PACKAGE_ast
#include <tm.h>
#endif
#define SPAM_DEFAULT (SPAM_advertisement|SPAM_delay_spam|SPAM_external_spam|SPAM_from_spam|SPAM_message_id_spam|SPAM_received_forged|SPAM_received_unknown|SPAM_subject_spam|SPAM_to_spam)
{
"advertisement", SPAM_advertisement,
"authentication_outsider", SPAM_authentication_outsider,
"authentication_protocol", SPAM_authentication_protocol,
"authentication_warning", SPAM_authentication_warning,
"content_multipart_related", SPAM_content_multipart_related,
"content_text_html", SPAM_content_text_html,
"delay_spam", SPAM_delay_spam,
"external_spam", SPAM_external_spam,
"from_forged", SPAM_from_forged,
"from_spam", SPAM_from_spam,
"message_id_spam", SPAM_message_id_spam,
"mime_autoconverted", SPAM_mime_autoconverted,
"received_forged", SPAM_received_forged,
"received_unknown", SPAM_received_unknown,
"subject_spam", SPAM_subject_spam,
"to_spam", SPAM_to_spam,
};
/*
* Trap spamtest variable assignment.
*/
void
{
register char* s;
register char* t;
register int n;
register const struct lab* p;
long test;
s = (char*)value;
if (!isdigit(*s)) {
do {
for (t = s; *t && *t != ',' && *t != '|'; t++);
if (n = t - s)
for (p = spamtest;; p++) {
if (!strncasecmp(s, "clear", n))
test = 0;
else if (!strncasecmp(s, "default", n))
test = SPAM_DEFAULT;
else
break;
}
if (!strncasecmp(s, p->name, n)) {
break;
}
}
s = t;
} while (*s++);
}
}
/*
* Return 1 if the intersection of the <,><space> separated
* address strings a and b is not empty.
*/
static int
addrmatch(const char* a, const char* b)
{
register char* ap;
register char* ae;
register char* bp;
register char* be;
register char* tp;
register int many = 0;
register int host;
ap = (char*)a;
for (;;)
{
ap++;
*ae = 0;
bp = (char*)b;
for (;;)
{
bp++;
{
*be = 0;
many = 1;
}
if (TRACING('x'))
bp++;
for (;;)
{
{
if (ae)
*ae = ',';
if (be)
*be = ',';
else if (many)
return 0;
return 1;
}
break;
}
if (!be)
break;
*be++ = ',';
}
if (!ae)
break;
*ae++ = ',';
}
return 0;
}
/*
* Return 1 if hosts in list a are part of the address list in b.
*/
static int
hostmatch(const char* a, const char* b)
{
register char* ap;
register char* ae;
register char* ad;
register char* bp;
register char* be;
int local = 1;
ap = (char*)a;
for (;;)
{
*ae = 0;
ad = 0;
bp = (char*)b;
for (;;)
{
bp++;
*be = 0;
local = 0;
{
local = 0;
bp++;
if (TRACING('x'))
goto hit;
if (ad)
{
goto hit;
{
bp++;
goto hit;
}
}
}
if (!be)
break;
*bp++ = ',';
}
if (!ae)
return local;
*ap++ = ',';
}
hit:
if (be)
*be = ',';
if (ae)
*ae = ',';
return 1;
}
/*
* Return 1 if string a contains any words whose prefixes
* ignorecase match the <,><space> separated lower case
* prefix list in b.
*/
static int
wordmatch(const char* a, const char* b)
{
register char* ab;
register char* ap;
register char* am;
register char* bb;
register char* be;
register char* bm;
register int u;
register int l;
bb = (char*)b;
for (;;)
{
bb++;
l = *bb;
u = toupper(l);
do
{
{
for (;;)
{
{
if (TRACING('x'))
return 1;
}
{
am++;
bm++;
}
{
if (!*am++)
break;
}
else
break;
}
}
} while (*++ap);
if (*be == 0)
break;
}
return 0;
}
/*
* Return 1 if user a is part of the <,><space> separated address strings in b.
*/
static int
{
register char* ap;
register char* ae;
register char* ad;
register char* bp;
register char* be;
register char* td;
if (!*a || !*b)
return 0;
ap = (char*)a;
while (*ap)
{
ap++;
*ae = 0;
{
if (ae)
*ae = ',';
return 1;
}
bp = (char*)b;
for (;;)
{
bp++;
*be = 0;
if (TRACING('x'))
if (*bp == 0)
/* skip */;
else if (*bp == '@')
{
bp++;
goto hit;
}
goto hit;
else if (ad)
{
*ad = 0;
{
*ad = '@';
if (TRACING('x'))
goto hit;
}
{
*ad = '@';
goto hit;
}
*ad = '@';
}
if (!be)
{
if (ae)
*ae = ',';
break;
}
*be++ = ',';
}
if (!ae)
return 0;
*ae++ = ',';
}
hit:
if (ae)
*ae = ',';
if (be)
*be = ',';
return 1;
}
/*
* check if s came from inside the domain
*/
static int
{
register int n;
if (!e)
e = s + strlen(s);
do
{
n = e - ++s;
return 1;
} while (s = strchr(s, '.'));
return -1;
}
/*
* Return 1 if it looks like we've been spammed.
*/
int
{
char* s;
char* t;
char* e;
char* to;
char* local;
char* domain2;
unsigned long q;
unsigned long x;
unsigned long d;
int n;
int me;
int ok;
int no;
int ours;
int ours2;
int fromours;
long test;
{
if (TRACING('x'))
note(0, "spam: To: header missing");
return 1;
}
test = 0;
{
if (TRACING('x'))
note(0, "spam: sender `%s'", t);
return 0;
test |= SPAM_from_spam;
}
{
q = 0;
{
else
{
domain2 = 0;
ours2 = 0;
}
}
else
{
domain2 = 0;
}
if (TRACING('z'))
{
if (TRACING('h'))
if ((*t == 'X' || *t == 'x') && *(t + 1) == '-')
t += 2;
if (*t == 'A' || *t == 'a')
{
{
if (TRACING('x'))
note(0, "spam: advertisement header");
}
else if (!strcasecmp(t, "Authentication-Warning"))
{
{
*t++ = 0;
if (streq(t, "-f"))
{
if ((t = strrchr(pp.data, ' ')) && streq(t + 1, "using") && !(*t = 0) && (t = strrchr(pp.data, ' ')) && insider(t + 1, NiL, 0, state.var.domain, ours, domain2, ours2))
}
else if (streq(t, "protocol"))
}
}
}
else if (*t == 'C' || *t == 'c')
{
if (!strcasecmp(t, "Cc"))
{
if (TRACING('x'))
note(0, "spam: cc `%s'", t);
me = 1;
}
else if (!strcasecmp(t, "Content-Type"))
{
if (TRACING('x'))
note(0, "spam: content-type `%s'", t);
}
}
else if (*t == 'F' || *t == 'f')
{
if (!strcasecmp(t, "From"))
{
if (s = strchr(t, ' '))
*s = 0;
if (TRACING('x'))
note(0, "spam: from `%s'", t);
return 0;
test |= SPAM_from_spam;
if (fromours >= 0)
}
}
else if (*t == 'M' || *t == 'm')
{
if (!strcasecmp(t, "Message-Id"))
{
if (TRACING('x'))
note(0, "spam: message-id `%s'", t);
if (!*t)
}
else if (!strcasecmp(t, "Mime-Autoconverted"))
{
if (TRACING('x'))
note(0, "spam: mime autoconverted");
}
}
else if (*t == 'R' || *t == 'r')
{
if (!strcasecmp(t, "Received"))
{
{
if (*t == 'u')
{
{
if (TRACING('x'))
note(0, "spam: unknown host name");
}
}
{
{
if (TRACING('x'))
note(0, "spam: forged");
}
{
n = 0;
s = t;
for (s = t + 5; *s == ' '; s++);
while (e = strchr(s, ' '))
{
{
n = 1;
break;
}
break;
}
if (!n)
{
ours = 0;
if (TRACING('x'))
}
}
}
}
#if _PACKAGE_ast
{
while (*++s && isspace(*s));
if (*s)
{
if (q == 0)
q = x;
else if (((q > x) ? (q - x) : (x - q)) > d)
{
if (TRACING('x'))
note(0, "spam: delay %ld", (q > x) ? (q - x) : (x - q));
test |= SPAM_delay_spam;
}
q = x;
}
}
#endif
}
}
else if (*t == 'S' || *t == 's')
{
if (!strcasecmp(t, "Spam-Flag"))
{
if (*t == 'Y' || *t == 'y' || *t == '1')
{
if (TRACING('x'))
note(0, "spam: external spam check hit");
}
}
else if (!strcasecmp(t, "Subject"))
{
while (n = *s++)
{
break;
}
{
}
}
}
else if (*t == 'T' || *t == 't')
{
if (!strcasecmp(t, "To"))
{
do
{
if (e = strchr(s, ','))
*e++ = 0;
if (TRACING('x'))
note(0, "spam: to `%s'", t);
if (*t == 0)
{
test |= SPAM_to_spam;
break;
}
me = 1;
{
if (TRACING('x'))
note(0, "spam: spamtook `%s'", t);
ok++;
}
{
if (TRACING('x'))
note(0, "spam: spamto `%s'", t);
no++;
}
} while (s = e);
}
}
}
test |= SPAM_from_forged;
const struct lab* p;
char buf[1024];
s = buf;
e = s + sizeof(buf) - 1;
{
for (t = (char*)p->name; *t && s < e; *s++ = *t++);
if (s < e)
*s++ = '|';
}
if (s > buf)
s--;
*s = 0;
}
return 1;
return 0;
if (me)
return 0;
return 1;
}
{
{
if (TRACING('x'))
note(0, "spam: To: header missing");
return 1;
}
{
if (TRACING('y'))
return 0;
}
{
if (TRACING('y'))
return 0;
}
return 1;
}
return 0;
}