[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