[Postfixbuch-users] Fallstudie: Missbrauch des Dienstes durch Spammer - Die Lösung
Achim Lammerts
ml-pbu at admin.syntaxys.net
So Sep 3 15:14:56 CEST 2006
Hallo Liste,
wie ich gemerkt habe, ist C ja garnicht so schwer ;-) Ich hab mir den
policyd insoweit gepatcht, daß er nur noch in Aktion tritt, wenn er
einen SASL user vom Postfix übermittelt bekommt. Das heißt, nur dann
greift er auf die db zu, ansonsten gibt er mit 'nem DUNNO an Postfix
zurück. Das ganze bezieht sich auf die reine Nutzung des policyd als
sender throttle und ist vielleicht etwas quick'n'dirty, aber es läuft
so, wie ich es brauche. Wer will, kann's weiter verwenden, die
gepatchten Stellen sind markiert (/* Start Patch by Achim Lammerts,
2006-09-03 */):
**************************
in der throttle.c:
**************************
#include "policyd.h"
/*
*
*
* Policy Daemon
*
* policy daemon is used in conjuction with postfix to combat spam.
*
* Copyright (C) 2004 Cami Sardinha (cami at mweb.co.za)
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*
*/
/*
* function: throttle_check
* purpose: throttle users based on SASL info or envelope FROM
* return: 0 for new record, 1 for update
*/
int
throttle_check (unsigned int fd)
{
/* Start Patch by Achim Lammerts, 2006-09-03 */
if(strlen(triplet_array[fd][4]) > 0)
{
/* End Patch by Achim Lammerts, 2006-09-03 */
unsigned int tnum = 0;
unsigned int tresult = 0;
mysql_optarray[fd][0] = 0;
if(DEBUG > 0)
logmessage("DEBUG: fd: %d checking throttle\n", fd);
/* build up & execute query */
if(SENDER_THROTTLE_HOST == 1)
{
tnum = 1;
snprintf(mysqlquery_array[fd], 512,
"SELECT _from,_count_max,_count_cur,_date,_quota_cur,_quota_max,"
" _time_limit,_mail_size,_count_tot,_rcpt_max,_rcpt_cur,_rcpt_tot,"
" _log_warn, _log_panic, _abuse_tot"
" FROM throttle WHERE _from='%s' OR _from='%s' OR _from='%s' OR
_from='%s'"
" ORDER BY _priority DESC LIMIT 1",
host_array[fd][2], host_array[fd][3], host_array[fd][4],
host_array[fd][5]);
/* Start Patch by Achim Lammerts, 2006-09-03 */
} else if(SENDER_THROTTLE_SASL == 1) {
/* } else if((SENDER_THROTTLE_SASL == 1) && (triplet_array[fd][4][0] !=
0x00)) { */
/* End Patch by Achim Lammerts, 2006-09-03 */
tnum = 2;
snprintf(mysqlquery_array[fd], 512,
"SELECT _from,_count_max,_count_cur,_date,_quota_cur,_quota_max,"
"
_time_limit,_mail_size,_count_tot,_rcpt_max,_rcpt_cur,_rcpt_tot,"
" _log_warn, _log_panic, _abuse_tot"
" FROM throttle WHERE _from='%s'", triplet_array[fd][4]);
} else {
tnum = 3;
snprintf(mysqlquery_array[fd], 512,
"SELECT _from,_count_max,_count_cur,_date,_quota_cur,_quota_max,"
" _time_limit,_mail_size,_count_tot,_rcpt_max,_rcpt_cur,_rcpt_tot,"
" _log_warn, _log_panic, _abuse_tot"
" FROM throttle WHERE _from='%s' OR _from='@%s'"
" ORDER BY _priority DESC LIMIT 1",
triplet_array[fd][1], host_array[fd][7]);
}
if(db_charquery(fd) == -1) return(db_failure(fd, "throttle"));
/* max messages is disabled in database, fall back to config default */
if(atol(mysqlchar_array[fd][1]) == 0)
snprintf(mysqlchar_array[fd][1], sizeof(mysqlchar_array[fd][1]),
"%d", SENDERMSGLIMIT);
/* max user quota is disabled in database, fall back to config defaults */
if(atol(mysqlchar_array[fd][5]) == 0)
snprintf(mysqlchar_array[fd][5], sizeof(mysqlchar_array[fd][5]),
"%d", SENDERQUOTALIMIT);
/* max time limit is disabled in database, fall back to config defaults */
if(atol(mysqlchar_array[fd][6]) == 0)
snprintf(mysqlchar_array[fd][6], sizeof(mysqlchar_array[fd][6]),
"%d", SENDERTIMELIMIT);
/* max message size is disabled in database, fall back to config
defaults */
if(atol(mysqlchar_array[fd][7]) == 0)
snprintf(mysqlchar_array[fd][7], sizeof(mysqlchar_array[fd][7]),
"%d", SENDERMSGSIZE);
/* max rcpt limit is disabled in database, fall back to config defaults */
if(atol(mysqlchar_array[fd][9]) == 0)
snprintf(mysqlchar_array[fd][9], sizeof(mysqlchar_array[fd][9]),
"%d", SENDERRCPTLIMIT);
/* check postfix policy instance */
snprintf(mysqlquery_array[fd], 512,
"SELECT COUNT(_instance) from throttle_from_instance \
WHERE _instance='%s'", triplet_array[fd][6]);
if(db_optquery(fd) == -1) return(db_failure(fd, "throttle"));
/* is instance recorded? */
if(mysql_optarray[fd][0] == 0)
{
int expire=0;
/* its not, so record it */
if(SENDERTIMELIMIT > 0)
expire=timenow;
snprintf(mysqlquery_array[fd], 512,
"INSERT DELAYED INTO throttle_from_instance (_instance,_expire)
VALUES ('%s',%d)",
triplet_array[fd][6], expire);
if(db_doquery(fd) == -1) return(db_failure(fd, "throttle"));
instance_inc[fd] = 1;
} else {
instance_inc[fd] = 0;
}
/* prepare attributes & thresholds */
/* count, quota, rcpt */
tquota[fd] = atof(mysqlchar_array[fd][4]) /
atof(mysqlchar_array[fd][5]) * 100;
tcount[fd] = atof(mysqlchar_array[fd][2]) /
atof(mysqlchar_array[fd][1]) * 100;
trcpt[fd] = atof(mysqlchar_array[fd][10]) /
atof(mysqlchar_array[fd][9]) * 100;
/* catch wierd ones */
if(DEBUG >= 4)
logmessage("DEBUG: fd: %d: tquota[fd]: %d, tcount[fd]: %d,
trcpt[fd]: %d\n",
fd, tquota[fd], tcount[fd], trcpt[fd]);
/* highest percentage always wins.. mmm.. ugly stuff*/
if(tquota[fd] >= tcount[fd] && tquota[fd] >= trcpt[fd]) {
tresult = tquota[fd];
if (DEBUG >= 4) logmessage("tquota[fd] won\n"); }
if(tcount[fd] >= tquota[fd] && tcount[fd] >= trcpt[fd]) {
tresult = tcount[fd];
if (DEBUG >= 4) logmessage("tquota[fd] won\n"); }
if(trcpt[fd] >= tcount[fd] && trcpt[fd] >= tquota[fd]) {
tresult = trcpt[fd];
if (DEBUG >= 4) logmessage("tquota[fd] won\n"); }
if(DEBUG >= 4)
logmessage("DEBUG: fd: %d: tresult: %d\n", fd, tresult);
/* percentage won, set attribute accordingly */
switch (tresult)
{
case 0 ... 49: tattrib_array[fd][0] = 'a'; break;
case 50 ... 89: tattrib_array[fd][0] = 'w'; break;
case 90 ... 900000: tattrib_array[fd][0] = 'p'; break;
/* allow for big percentage overshoot */
default:
logmessage("fatal: throttle_check(): invalid tresult: %d\n", tresult);
return (-1);
}
/* we selectively choose which throttle module we want */
switch(tnum)
{
case 1:
return(throttle_host(fd));
case 2:
return(throttle_sasl(fd));
case 3:
return(throttle_from(fd));
default:
logmessage("fatal: throttle_check(): no tnum\n");
return (-1);
}
/* Start Patch by Achim Lammerts, 2006-09-03 */
}
else
{
if(DEBUG > 0)
logmessage("DEBUG: No SASL user");
}
/* End Patch by Achim Lammerts, 2006-09-03 */
return (0); /* never reached */
}
/* EOF */
**************************
in der throttle_sasl.c:
**************************
#include "policyd.h"
/*
*
*
* Policy Daemon
*
* policy daemon is used in conjuction with postfix to combat spam.
*
* Copyright (C) 2004 Cami Sardinha (cami at mweb.co.za)
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*
*/
/*
* function: throttle_sasl
* purpose: throttle users based on SASL info
* return: 0 for new record, 1 for update
*/
int
throttle_sasl (unsigned int fd)
{
/* Start Patch by Achim Lammerts, 2006-09-03 */
if(strlen(triplet_array[fd][4]) > 0)
{
/* End Patch by Achim Lammerts, 2006-09-03 */
if(DEBUG > 0)
logmessage("DEBUG: fd: %d checking throttle-sasl\n", fd);
/* user is not in the database */
if(strlen(mysqlchar_array[fd][0]) < 2)
{
if(atol(triplet_array[fd][3]) >= atol(mysqlchar_array[fd][7]))
goto abuse;
logmessage("rcpt=%lu, throttle=new(a), host=%s, from=%s, to=%s,
size=%d/%d, "
"quota=%d/%d, count=1/%d(1), rcpt=1/%d(1), threshold=0%|0%|0%,
sasl_username=%s\n",
rcpt_count, /* recipient count */
host_array[fd][2], /* host */
triplet_array[fd][1], /* from */
triplet_array[fd][2], /* to */
atol(triplet_array[fd][3]), /* size_cur */
atol(mysqlchar_array[fd][7]), /* size_max */
atol(triplet_array[fd][3]), /* quota_cur */
atol(mysqlchar_array[fd][5]), /* quota_max */
atol(mysqlchar_array[fd][1]), /* count_max */
atol(mysqlchar_array[fd][9]), /* rcpt_max */
triplet_array[fd][4] /* sasl_username */
);
/* build up & execute query */
snprintf(mysqlquery_array[fd], 512,
"INSERT DELAYED INTO throttle "
"(_date,_from,_quota_cur,_quota_max,_rcpt_max,_mail_size,_count_max,
_time_limit)"
" VALUES (%d, '%s', %d, %ld, %ld, %ld, %ld, %ld)",
timenow,
triplet_array[fd][4],
atoi(triplet_array[fd][3]),
atol(mysqlchar_array[fd][5]),
atol(mysqlchar_array[fd][9]),
atol(mysqlchar_array[fd][7]),
atol(mysqlchar_array[fd][1]),
atol(mysqlchar_array[fd][6]));
if(db_doquery(fd) == -1) return(db_failure(fd, "throttle"));
/* sender does not exist in the database, insert and allow */
return (0);
}
/* if sender is sending a message bigger than allowed, reject */
if(atol(triplet_array[fd][3]) >= atol(mysqlchar_array[fd][7]))
{
abuse:
logmessage("rcpt=%lu, throttle=abuse(f), host=%s, from=%s, to=%s,
size=%d/%d, "
"quota=%d/%d, count=%d/%d(%d), rcpt=%d/%d(%d),
threshold=%d%|%d%|%d%, sasl_username=%s\n",
rcpt_count, /* recipient count */
host_array[fd][2], /* host */
triplet_array[fd][1], /* from */
triplet_array[fd][2], /* to */
atol(triplet_array[fd][3]), /* size_cur */
atol(mysqlchar_array[fd][7]), /* size_max */
atol(mysqlchar_array[fd][4]), /* quota_cur */
atol(mysqlchar_array[fd][5]), /* quota_max */
atol(mysqlchar_array[fd][2]), /* count_cur */
atol(mysqlchar_array[fd][1]), /* count_max */
atol(mysqlchar_array[fd][8]), /* count_tot */
atol(mysqlchar_array[fd][10]), /* rcpt_cur */
atol(mysqlchar_array[fd][9]), /* rcpt_max */
atol(mysqlchar_array[fd][11]), /* rcpt_tot */
tquota[fd], /* quota percentage */
tcount[fd], /* count percentage */
trcpt[fd], /* rcpt percentage */
triplet_array[fd][4] /* sasl_username */
);
return (-3);
}
/* if time has expired, clear quota for size+message count */
if(timenow > (unsigned
int)(atol(mysqlchar_array[fd][6])+atol(mysqlchar_array[fd][3])))
{
logmessage("rcpt=%lu, throttle=clear(a), host=%s, from=%s, to=%s,
size=%d/%d, "
"quota=%d/%d, count=1/%d(%d), rcpt=1/%d(%d), threshold=0%|0%|0%,
sasl_username=%s\n",
rcpt_count, /* recipient count */
host_array[fd][2], /* host */
triplet_array[fd][1], /* from */
triplet_array[fd][2], /* to */
atol(triplet_array[fd][3]), /* size_cur */
atol(mysqlchar_array[fd][7]), /* size_max */
atoi(triplet_array[fd][3]), /* quota_cur */
atol(mysqlchar_array[fd][5]), /* quota_max */
atol(mysqlchar_array[fd][1]), /* count_max */
atol(mysqlchar_array[fd][8]), /* count_tot */
atol(mysqlchar_array[fd][9]), /* rcpt_max */
atol(mysqlchar_array[fd][11])+1, /* rcpt_tot */
triplet_array[fd][4] /* sasl_username */
);
/* build up & execute query */
snprintf(mysqlquery_array[fd], 512,
"UPDATE throttle SET"
" _rcpt_cur=1,"
" _rcpt_tot=_rcpt_tot+1,"
" _date=%d,"
" _quota_cur=%d,"
" _count_cur=1,"
" _count_tot=_count_tot+%d,"
" _abuse_tot=_abuse_tot+_abuse_cur,"
" _abuse_cur=0"
" WHERE _from='%s'",
timenow,
atoi(triplet_array[fd][3]),
instance_inc[fd],
triplet_array[fd][4]);
if(db_doquery(fd) == -1) return(db_failure(fd, "throttle"));
/* counter reset because of expiry, allow mail */
return (0);
}
/* if the sender is past his quota and the timeout has not expired */
/* then reject the message */
if(atol(mysqlchar_array[fd][2]) >= atol(mysqlchar_array[fd][1]) || /*
count */
atol(mysqlchar_array[fd][4]) >= atol(mysqlchar_array[fd][5]) || /*
quota */
atol(mysqlchar_array[fd][10]) >= atol(mysqlchar_array[fd][9])) /*
rcpt max */
{
if((instance_inc[fd] == 0) && (atol(mysqlchar_array[fd][10]) <
atol(mysqlchar_array[fd][9])))
goto update;
logmessage("rcpt=%lu, throttle=abuse(f), host=%s, from=%s, to=%s,
size=%d/%d, "
"quota=%d/%d, count=%d/%d(%d), rcpt=%d/%d(%d),
threshold=%d%|%d%|%d%, sasl_username=%s\n",
rcpt_count, /* recipient count */
host_array[fd][2], /* host */
triplet_array[fd][1], /* from */
triplet_array[fd][2], /* to */
atol(triplet_array[fd][3]), /* size_cur */
atol(mysqlchar_array[fd][7]), /* size_max */
atol(mysqlchar_array[fd][4]), /* quota_cur */
atol(mysqlchar_array[fd][5]), /* quota_max */
atol(mysqlchar_array[fd][2]), /* count_cur */
atol(mysqlchar_array[fd][1]), /* count_max */
atol(mysqlchar_array[fd][8]), /* count_tot */
atol(mysqlchar_array[fd][10]), /* rcpt_cur */
atol(mysqlchar_array[fd][9]), /* rcpt_max */
atol(mysqlchar_array[fd][11]), /* rcpt_tot */
tquota[fd], /* quota percentage */
tcount[fd], /* count percentage */
trcpt[fd], /* rcpt percentage */
triplet_array[fd][4] /* sasl_username */
);
/* build up & execute query */
snprintf(mysqlquery_array[fd], 512,
"UPDATE throttle SET"
" _abuse_cur=1"
" WHERE _from='%s'",
triplet_array[fd][1]);
if(db_doquery(fd) == -1) return(db_failure(fd, "throttle"));
return (-5);
}
update:
/* if the sender has not reached his quota, increase count */
logmessage("rcpt=%lu, throttle=update(%c), host=%s, from=%s, to=%s,
size=%d/%d, "
"quota=%d/%d, count=%d/%d(%d), rcpt=%d/%d(%d),
threshold=%d%|%d%|%d%, sasl_username=%s\n",
rcpt_count, /*
recipient count */
tattrib_array[fd][0], /*
attribute state */
host_array[fd][2], /*
host */
triplet_array[fd][1], /*
from */
triplet_array[fd][2], /*
to */
atol(triplet_array[fd][3]), /*
size_cur */
atol(mysqlchar_array[fd][7]), /*
size_max */
atol(mysqlchar_array[fd][4])+atol(triplet_array[fd][3]), /*
quota_cur */
atol(mysqlchar_array[fd][5]), /*
quota_max */
atol(mysqlchar_array[fd][2])+instance_inc[fd], /*
count_cur */
atol(mysqlchar_array[fd][1]), /*
count_max */
atol(mysqlchar_array[fd][8])+instance_inc[fd], /*
count_tot */
atol(mysqlchar_array[fd][10])+1, /*
rcpt_cur */
atol(mysqlchar_array[fd][9]), /*
rcpt_max */
atol(mysqlchar_array[fd][11])+1, /*
rcpt_tot */
tquota[fd], /* quota
percentage */
tcount[fd], /* count
percentage */
trcpt[fd], /* rcpt
percentage */
triplet_array[fd][4] /*
sasl_username */
);
/* build up & execute query */
snprintf(mysqlquery_array[fd], 512,
"UPDATE throttle SET"
" _rcpt_cur=_rcpt_cur+1,"
" _rcpt_tot=_rcpt_tot+1,"
" _quota_cur=_quota_cur+%ld,"
" _count_cur=_count_cur+%d,"
" _count_tot=_count_tot+%d,"
" _abuse_cur=0"
" WHERE _from='%s'",
atol(triplet_array[fd][3]),
instance_inc[fd],
instance_inc[fd],
triplet_array[fd][4]);
if(db_doquery(fd) == -1) return(db_failure(fd, "throttle"));
/* Start Patch by Achim Lammerts, 2006-09-03 */
}
else
{
if(DEBUG > 0)
logmessage("DEBUG: No SASL user");
}
/* End Patch by Achim Lammerts, 2006-09-03 */
return (0); /* never reached */
}
/* EOF */
Mehr Informationen über die Mailingliste Postfixbuch-users