First, i'm sorry for the long message below, it's just a c&p from
the README that goes in the patch, but i believe you can get an idea of
what i've done.
Now for the loooong message:
---------- snip ----------
GREYLIST:
In earlier releases, this feature was called GREYLIST and it was provided by
making qmail-smtpd call an external helper which would provide authorization
delivery depending on the helper exit code. That helper is still
provided now
(/var/qmail/bin/qmail-greylist), and is there for those who prefer to have
only a good greylist solution, with MySQL backend, with no extra fuzz.
THE GREYLIST HELPER:
Activation:
Edit the Makefile accordingly and use ENVELOPE_SCANNER env var
set to
/path/to/qmail/bin/qmail-greylist. The database schema is
provided in
/path/to/qmail/doc/dbdef.sql.
All the helper MySQL and TTL parameters and can be set using the
following env vars,
either in the tcp rules for smtpd, or by echo "relaydelay" >
/var/qmail/boot/qmail-smtpd/env/MYSQLDB,
for instance.
MYSQLHOST - host database
MYSQLPORT - database port
MYSQLUSER - database access user
MYSQLPASS - database user password
MYSQLDB - database name
BLOCK_EXPIRE - greylisting period
RECORD_EXPIRE - ttl for greylisted records with only 1 hit
RECORD_EXPIRE_GOOD - ttl for greylisted records with more
than 1 hit
LOCAL_SCAN_DEBUG - turn on debugging
Mysql failure:
If mysql isn't running, the relaydelay database doesn't exist or
the username or
password fails, greylisting will be disabled and messages will
be accepted
according to qmail-ldap defined rules.
Whitelisting:
Although there's the possibility to whitelist by omiting the
ENVELOPE_SCANNER env var
on the tcpserver rules cdb, for a certain netblock, there's also
the possibility to
do so by inserting records into the relaydelay database. There's
a small perl script
(greylist-whitelist.pl) bundled to aid with such task.
Ex:
/var/qmail/doc/qmail-greylist-whitelist.pl 1.2.3.4
Database maintenance:
Database maintenance, i.e. deletion of expired records, has to
be done separatly.
I've bundled a perl script (greylist-cleanup.pl) formerly
submitted by Gerard Earley
to the QMR mailing list.
It's advised that you preform such maintenance once a day,
peharps setting up a cronjob.
Ex:
0 0 * * * root /var/qmail/doc/qmail-greylist-cleanup.pl
SMTPD ACL POLICY:
SMTPD ACL Policy it's a concept used widely in postfix. There are a few
policy servers
available, each with its own level of features and possibilities.
In the case of qmail, and at the envelope level of SMTP conversation,
the use of such
solution could give extra benefit besides the greylisting. Qmail-smtpd
now also passes
the received HELO to the helper, so if using a policy server that supports
HELO checking, such as Policyd (http://policyd.sourceforge.net), it's
also possible
to check and blacklist addresses which use ilegit HELO names (your
server name, for instance).
More, using such a policy server, it's also possible to have a
centralized authorization
service to provide blacklist and whitelists, either based on ip
addresses, networks blocks,
senders recipients and dns names/domains.
The helper is located in /path/to/qmail/bin/qmail-smtpdpol.
In the case of policyd, one can even make use of automatic blacklisting
using smaptraps.
You can even configure if you wish to send permanent or temporary
failures for blacklist
events.
Cool huh?
THE SMTPD POLICY HELPER:
Activation:
Edit the Makefile accordingly and use ENVELOPE_SCANNER env var
set to
/path/to/qmail/bin/qmail-smtpdpol. The policy server can port
can then be defined
by setting the env var POLICYD_SERVER and POLICYD_PORT respectively.
Again, either set those in the tcp rules for smtpd or echo them
to the respective env files.
On failure:
Messaged will be accepted according qmail-ldap defined rules.
Database maintenance:
None. Well, not true in most cases, but that will be done by
whatever ways the SMTPD
policy server has.
GENERIC ENVELOPE SCANNING:
Supporting generic envelope scanners, allows that besides using the
provided helpers,
people can write their own helpers having the following information into
consideration:
The call to the helper will have the following data passed and in the
following order:
sender recipient helo
The helper can choose of course which information to treat, for
instance, while using
qmail-greylist, although the helo is passed on to the helper, it simply
disregards it.
The helper must then return one of the following codes:
0 on success, meaning that mail will be accepted
100 on a permanent reject of mail
101 on a temporary fail, which can be used for greylisting
-1 if there was something wrong in the process (actually
any other value than 0, 100
and 101 will be treated as an error, so the -1
is a should.)
In the case of -1 is returned, qmail-ldap will continue as if there was
no external
envelope scanning and mail is delivered normally.
That's all folks!
-------- snip --------
Note: The qmail-greylist stuff, scripts and database definition, already
reflect the Makefile options.
Comments are welcomed,
Hugo Monteiro.
--
ci.fct.unl.pt:~# cat .signature
Hugo Monteiro
Email : [EMAIL PROTECTED]
Telefone : +351 212948300 Ext.15307
Centro de Informática
Faculdade de Ciências e Tecnologia da
Universidade Nova de Lisboa
Quinta da Torre 2829-516 Caparica Portugal
Telefone: +351 212948596 Fax: +351 212948548
www.ci.fct.unl.pt [EMAIL PROTECTED]
ci.fct.unl.pt:~# _
diff -u -N -r qmail-1.03/dbdef.in qmail-ldap-1.03-20060201-envelope-scan-0.5/dbdef.in
--- qmail-1.03/dbdef.in 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/dbdef.in 2007-01-30 00:44:27.000000000 +0000
@@ -0,0 +1,73 @@
+
+#######################################################################
+# Database definitions
+#######################################################################
+
+# Using Mysql 3.23.2 or later (required for NULLs allowed in indexed fields
+
+CREATE DATABASE %MYSQLDB%;
+grant select,insert,update,delete on %MYSQLDB%.* to %MYSQLUSER%@"%MYSQLHOST%" identified by '%MYSQLPASS%';
+
+USE %MYSQLDB%;
+
+# NOTE: We allow nulls in the 3 "triplet" fields for manual white/black listing. A null indicates that that field
+# should not be considered when looking for a match. Automatic entries will always have all three populated.
+# Note: Since we index the triplet fields, and allow them to be null, this requires at least Mysql 3.23.2 and MYISAM
+# tables.
+create table relaytofrom # Stores settings for each [relay, to, from] triplet
+(
+ id bigint NOT NULL auto_increment, # unique triplet id
+ relay_ip char(16), # sending relay in IPV4 ascii dotted quad notation
+ mail_from varchar(255), # ascii address of sender
+ rcpt_to varchar(255), # the recipient address.
+ block_expires datetime NOT NULL, # the time that an initial block will/did expire
+ record_expires datetime NOT NULL, # the date after which this record is ignored
+
+ blocked_count bigint default 0 NOT NULL, # num of blocked attempts to deliver
+ passed_count bigint default 0 NOT NULL, # num of passed attempts we have allowed
+ aborted_count bigint default 0 NOT NULL, # num of attempts we have passed, but were later aborted
+ origin_type enum("MANUAL","AUTO") NOT NULL, # indicates the origin of this record (auto, or manual)
+ create_time datetime NOT NULL, # timestamp of creation time of this record
+ last_update timestamp NOT NULL, # timestamp of last change to this record (automatic)
+
+ primary key(id),
+ key(relay_ip),
+ key(mail_from(20)), # To keep the index size down, only index first 20 chars
+ key(rcpt_to(20))
+);
+
+create table dns_name # Stores the reverse dns name lookup for records
+(
+ relay_ip varchar(18) NOT NULL,
+ relay_name varchar(255) NOT NULL, # dns name, stored in reversed character order (for index)
+ last_update timestamp NOT NULL, # timestamp of last change to this record (automatic)
+ primary key(relay_ip),
+ key(relay_name(20))
+);
+
+# This table is not used yet, possibly never will be
+create table mail_log # Stores a record for every mail delivery attempt
+(
+ id bigint NOT NULL auto_increment, # unique log entry id
+ relay_ip varchar(16) NOT NULL, # sending relay in IPV4 ascii dotted quad notation
+ relay_name varchar(255), # sending relay dns name
+ dns_mismatch bool NOT NULL, # true if does not match, false if matches or no dns
+ mail_from varchar(255) NOT NULL, # the mail from: address
+ rcpt_to varchar(255) NOT NULL, # the rcpt to: address
+ rcpt_host varchar(80) NOT NULL, # the id (hostname) of the host that generated this row
+ create_time datetime NOT NULL, # timestamp of inserted time, since no updates
+
+ primary key(id),
+ key(relay_ip),
+ key(mail_from(20)),
+ key(rcpt_to(20))
+);
+
+# Example wildcard whitelists for subnets
+insert into relaytofrom values (0,"127.0.0.1" ,NULL,NULL,"0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+insert into relaytofrom values (0,"192.168" ,NULL,NULL,"0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+# Example wildcard whitelist entry for a recieved domain or subdomain
+insert into relaytofrom values (0,NULL,NULL,"sub.domain.com","0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+
diff -u -N -r qmail-1.03/FILES qmail-ldap-1.03-20060201-envelope-scan-0.5/FILES
--- qmail-1.03/FILES 2007-01-30 18:46:06.965641272 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/FILES 2007-01-30 00:46:57.000000000 +0000
@@ -426,6 +426,14 @@
tcp-environ.5
constmap.h
constmap.c
+local_scan.c
+qmail-greylist.c
+qmail-greylist-cleanup.pl.in
+qmail-greylist-whitelist.pl.in
+dbdef.in
+local_scan.h
+qmail-smtpdpol.c
+README.ENVELOPE_SCAN
EXTTODO
Makefile.cdb
POPBEFORESMTP
diff -u -N -r qmail-1.03/hier.c qmail-ldap-1.03-20060201-envelope-scan-0.5/hier.c
--- qmail-1.03/hier.c 2007-01-30 18:46:07.123617256 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/hier.c 2007-01-30 00:46:21.000000000 +0000
@@ -214,6 +214,14 @@
c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+#ifdef ENVELOPE_SCAN
+ c(auto_qmail,"bin","qmail-greylist",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-smtpdpol",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","qmail-greylist-whitelist.pl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","qmail-greylist-cleanup.pl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","README.ENVELOPE_SCAN",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","dbdef.sql",auto_uido,auto_gidq,0644);
+#endif
c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -u -N -r qmail-1.03/install-big.c qmail-ldap-1.03-20060201-envelope-scan-0.5/install-big.c
--- qmail-1.03/install-big.c 2007-01-30 18:46:07.129616344 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/install-big.c 2007-01-30 00:46:29.000000000 +0000
@@ -214,6 +214,14 @@
c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+#ifdef ENVELOPE_SCAN
+ c(auto_qmail,"bin","qmail-greylist",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-smtpdpol",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","qmail-greylist-whitelist.pl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","qmail-greylist-cleanup.pl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"doc","README.ENVELOPE_SCAN",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","dbdef.sql",auto_uido,auto_gidq,0644);
+#endif
c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -u -N -r qmail-1.03/local_scan.c qmail-ldap-1.03-20060201-envelope-scan-0.5/local_scan.c
--- qmail-1.03/local_scan.c 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/local_scan.c 2007-01-30 00:47:27.000000000 +0000
@@ -0,0 +1,287 @@
+/**************
+ * Copyright (c) [EMAIL PROTECTED] 2003
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include "local_scan.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mysql.h>
+
+/************************/
+#define DEFAULT_MYSQLHOST "localhost"
+#define DEFAULT_MYSQLUSER "milter"
+#define DEFAULT_MYSQLPASS "milter"
+#define DEFAULT_MYSQLDB "relaydelay"
+#define DEFAULT_BLOCK_EXPIRE 55 /* minutes until email is accepted */
+#define DEFAULT_RECORD_EXPIRE 500 /* minutes until record expires */
+#define DEFAULT_RECORD_EXPIRE_GOOD 36 /* days until record expires after accepting email */
+#define DEFAULT_LOCAL_SCAN_DEBUG 0 /* set to 1 to enable debugging */
+
+/*************************/
+
+#define SQLCMDSIZE 1024
+
+char *mysqlhost, *mysqluser, *mysqlpass, *mysqldb;
+int block_expire, record_expire, record_expire_good, local_scan_debug;
+
+int mysql_query_wrapper(MYSQL *mysql, char *sqltext)
+{
+ int result;
+
+ result = mysql_query(mysql,sqltext);
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: SQL: ret=%d |%s|\n",result,sqltext);
+ return result;
+}
+
+void AddOrClause(char *subquery,char *in_ipaddr)
+{
+ int loop;
+ char *ptr;
+ char ipaddr[256];
+
+
+ strncpy(ipaddr, in_ipaddr, sizeof(ipaddr));
+ for (loop = 0; loop < 4; loop++) {
+ if (loop)
+ strcat(subquery, " OR ");
+ strcat(subquery , "relay_ip = '" );
+ strcat(subquery ,ipaddr);
+ strcat(subquery , "'");
+ ptr = strrchr(ipaddr,'.'); // strip off the last octet
+ if(ptr)
+ *ptr = '\0';
+ }
+}
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListIP( MYSQL *mysql, int *action)
+{
+ MYSQL_RES *myres;
+ MYSQL_ROW myrow;
+
+ char sql[SQLCMDSIZE], sid[32];
+ int white, black,exists = FALSE;
+
+ /* check for whitelisted sender ip address */
+ sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND rcpt_to IS NULL AND (");
+ AddOrClause(sql, sender_host_address);
+ strcat(sql, ") ORDER BY length(relay_ip)");
+ //#ifdef local_scan_debug
+ // fprintf(stderr,"local_scan: check whitelist: %s\n",sql);
+ //#endif
+ if(!mysql_query_wrapper(mysql,sql)) {
+ myres = mysql_store_result(mysql);
+ while ((myrow = mysql_fetch_row(myres)) != NULL) {
+ exists = TRUE;
+ strncpy(sid,myrow[0],sizeof(sid));
+ black = atoi(myrow[1]);
+ white = atoi(myrow[2]);
+ }
+ mysql_free_result(myres); // free memory used by result
+ }
+ if (exists && white) {
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> xxx (%s) Whitelist IP Accept id = %s \n",sender_address, sender_host_address,sid);
+ *action = LOCAL_SCAN_ACCEPT;
+ }
+ else if (exists && black) {
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> xxx (%s) Blacklist IP Permanent Reject id = %s \n",sender_address, sender_host_address,sid);
+ *action = LOCAL_SCAN_REJECT;
+ }
+
+ return exists;
+}
+
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListDomain( MYSQL *mysql, int *action, int i)
+{
+ MYSQL_RES *myres;
+ MYSQL_ROW myrow;
+
+ char sql[SQLCMDSIZE], sid[32], *indomain;
+ int white, black,exists = FALSE;
+
+ indomain = strrchr(recipients_list[i].address,'@');
+ if(indomain != NULL) {
+ indomain++; /* skip the '@' char */
+
+ /* check for whitelisted receiver domain address */
+ sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND relay_ip IS NULL AND rcpt_to = '%s'",indomain);
+ if(!mysql_query_wrapper(mysql,sql)) {
+ myres = mysql_store_result(mysql);
+ while ((myrow = mysql_fetch_row(myres)) != NULL) {
+ exists = TRUE;
+ strncpy(sid,myrow[0],sizeof(sid));
+ black = atoi(myrow[1]);
+ white = atoi(myrow[2]);
+ }
+ mysql_free_result(myres); // free memory used by result
+ }
+ if (exists && white) {
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) Whitelist Domain Accept id = %s \n",sender_address, recipients_list[i].address, sender_host_address,sid);
+ *action = LOCAL_SCAN_ACCEPT;
+ }
+ else if (exists && black) {
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> xxx (%s) Blacklist Domain Permanent Reject id = %s \n",sender_address, sender_host_address,sid);
+ *action = LOCAL_SCAN_REJECT;
+ }
+ }
+ return exists;
+}
+
+void buildLookupSql(char *sql, int recipIndex)
+{
+ char hostip[32];
+ char *ptr;
+
+ if (FALSE) {
+ /* this first sprintf does an exact match on ipaddr */
+ sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s' AND rcpt_to = '%s' AND relay_ip = '%s' order by block_expires desc",sender_address, recipients_list[recipIndex].address,sender_host_address);
+ }
+ else {
+ /* this second version matches anything in the same /24 subnet */
+ sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s' AND rcpt_to = '%s' AND relay_ip like '",sender_address, recipients_list[recipIndex].address);
+ strcpy(hostip,sender_host_address);
+ /* now remove the last octet and add a '%' */
+ ptr = strrchr(hostip,'.'); // strip off the last octet
+ if(ptr)
+ *ptr = '\0';
+ strcat(sql,hostip);
+ strcat(sql, "%' order by block_expires desc");
+ }
+}
+
+
+int checkGreylist( MYSQL *mysql, int *action, int i)
+{
+ MYSQL_RES *myres;
+ MYSQL_ROW myrow;
+
+ char sql[SQLCMDSIZE], sid[32];
+ int notexpired, exists = FALSE;
+
+ // lookup to see if record exists
+ buildLookupSql(sql,i);
+ if(!mysql_query_wrapper(mysql,sql)) {
+ myres = mysql_store_result(mysql);
+ while ((myrow = mysql_fetch_row(myres)) != NULL) {
+ exists = TRUE;
+ strncpy(sid,myrow[0],sizeof(sid));
+ notexpired = atoi(myrow[1]);
+ //fprintf(stderr,"local_scan: id = %s expire = %d\n",sid,notexpired);
+ }
+ mysql_free_result(myres); // free memory used by result
+ // if it exists and is not record expired and the block_expired passes
+ if (exists && notexpired) {
+ // update expire time to 36 days, and pass count
+ sprintf(sql,"update relaytofrom set record_expires = NOW() + INTERVAL %d DAY, passed_count = passed_count + 1 where id ='%s'",record_expire_good,sid);
+ mysql_query_wrapper(mysql, sql);
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) Exists Accept id = %s expire = %d\n",sender_address,recipients_list[i].address, sender_host_address,sid,notexpired);
+ *action = LOCAL_SCAN_ACCEPT;
+ }
+ else if ( exists && ! notexpired) {
+ // update fail count
+ sprintf(sql,"update relaytofrom set blocked_count = blocked_count + 1 where id = %s",sid);
+ mysql_query_wrapper(mysql, sql);
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) Exists Block id = %s expire = %d\n",sender_address,recipients_list[i].address, sender_host_address,sid,notexpired);
+ *action = LOCAL_SCAN_TEMPREJECT;
+ }
+ else /* doesn't exist */ {
+ // it doesn't exist make and entry
+ sprintf(sql,"insert into relaytofrom values (0,'%s','%s','%s',NOW() + INTERVAL %d MINUTE,NOW() + INTERVAL %d MINUTE,1,0,0,'AUTO',NOW(),NOW())",sender_host_address,sender_address, recipients_list[i].address,block_expire,record_expire);
+ mysql_query_wrapper(mysql,sql);
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) Doesn't Exists Block\n",sender_address,recipients_list[i].address, sender_host_address);
+ *action = LOCAL_SCAN_TEMPREJECT;
+ }
+ }
+}
+
+void get_config(void) {
+
+ char *t;
+
+ mysqlhost = getenv("MYSQLHOST");
+ if (!mysqlhost) {
+ mysqlhost = malloc(strlen(DEFAULT_MYSQLHOST)+1);
+ strcpy(mysqlhost, DEFAULT_MYSQLHOST);
+ }
+ mysqluser = getenv("MYSQLUSER");
+ if (!mysqluser) {
+ mysqluser = malloc(strlen(DEFAULT_MYSQLUSER)+1);
+ strcpy(mysqluser, DEFAULT_MYSQLUSER);
+ }
+ mysqlpass = getenv("MYSQLPASS");
+ if (!mysqlpass) {
+ mysqlpass = malloc(strlen(DEFAULT_MYSQLPASS)+1);
+ strcpy(mysqlpass, DEFAULT_MYSQLPASS);
+ }
+ mysqldb = getenv("MYSQLDB");
+ if (!mysqldb) {
+ mysqldb = malloc(strlen(DEFAULT_MYSQLDB)+1);
+ strcpy(mysqldb, DEFAULT_MYSQLDB);
+ }
+ t = getenv("BLOCK_EXPIRE");
+ if (t) {
+ block_expire = atoi(t);
+ } else {
+ block_expire = DEFAULT_BLOCK_EXPIRE;
+ }
+ t = getenv("RECORD_EXPIRE");
+ if (t) {
+ record_expire = atoi(t);
+ } else {
+ record_expire = DEFAULT_RECORD_EXPIRE;
+ }
+ t = getenv("RECORD_EXPIRE_GOOD");
+ if (t) {
+ record_expire_good = atoi(t);
+ } else {
+ record_expire_good = DEFAULT_RECORD_EXPIRE_GOOD;
+ }
+ t = getenv("LOCAL_SCAN_DEBUG");
+ if (t) {
+ local_scan_debug = atoi(t);
+ } else {
+ local_scan_debug = DEFAULT_LOCAL_SCAN_DEBUG;
+ }
+}
+
+int local_scan(int fd, uschar **return_text)
+{
+ MYSQL *mysql = NULL;
+ int i, ret = LOCAL_SCAN_ACCEPT;
+
+ get_config();
+
+ if(local_scan_debug == 1) fprintf(stderr,"local_scan: protocol = %s %s \n",received_protocol,sender_address);
+
+
+ fd = fd; /* Keep picky compilers happy */
+ return_text = return_text; /* Keep picky compilers happy */
+ mysql = mysql_init(NULL);
+ if (mysql && strcmp(received_protocol,"local") ) {
+ if (mysql_real_connect(mysql,mysqlhost,mysqluser,mysqlpass,mysqldb,0,NULL,0)) {
+ if ( !checkWhiteListIP( mysql, &ret )) { /* check for whitelisted sender ip address */
+ for(i= 0 ; i < recipients_count; i++ ) {
+ if(strlen(sender_host_address) + strlen(sender_address) +
+ strlen(recipients_list[i].address) < SQLCMDSIZE - 200) { /* check to avoid buffer overflows */
+ if(!checkWhiteListDomain( mysql, &ret,i ))
+ checkGreylist(mysql, &ret,i);
+ }
+ }
+ }
+ }
+ }
+ if(mysql) mysql_close(mysql);
+ if(local_scan_debug == 1) fprintf(stderr, "--------\n");
+ return ret;
+}
+
+/* End of local_scan.c */
diff -u -N -r qmail-1.03/local_scan.h qmail-ldap-1.03-20060201-envelope-scan-0.5/local_scan.h
--- qmail-1.03/local_scan.h 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/local_scan.h 2007-01-30 17:58:31.731702808 +0000
@@ -0,0 +1,38 @@
+/**************
+ * Copyright (c) [EMAIL PROTECTED] 2003
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#ifndef __GREYLIST_H__
+#define __GREYLIST_H__
+
+#define FALSE 0
+#define TRUE 1
+#define uschar char
+
+#define LOCAL_SCAN_ACCEPT 0
+#define LOCAL_SCAN_REJECT 100
+#define LOCAL_SCAN_TEMPREJECT 101
+#define SOMETHING_BAD -1
+
+struct recipients_list {
+ char *address;
+};
+
+
+int local_scan(int fd, uschar **return_text);
+
+/* globals used by exim */
+
+extern int recipients_count;
+extern char *received_protocol;
+extern char *sender_host_address;
+extern char *sender_address;
+extern struct recipients_list recipients_list[];
+
+#endif
diff -u -N -r qmail-1.03/Makefile qmail-ldap-1.03-20060201-envelope-scan-0.5/Makefile
--- qmail-1.03/Makefile 2007-01-30 18:46:06.976639600 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/Makefile 2007-01-30 18:47:31.856735864 +0000
@@ -19,6 +19,8 @@
# -DQMQP_COMPRESS to use the QMQP on the fly compression (for clusters)
# -DQUOTATRASH to include the Trash in the quota calculation (normaly it is not)
# -DSMTPEXECCHECK to enable smtp DOS/Windows executable detection
+# -DENVELOPE_SCAN to enable generic envelope scanning. MySQL, Postfix SMTPD Access
+# Policy Server or custom authorization.
#LDAPFLAGS=-DQLDAP_CLUSTER -DEXTERNAL_TODO -DDASH_EXT -DDATA_COMPRESS -DQMQP_COMPRESS -DSMTPEXECCHECK
# Perhaps you have different ldap libraries, change them here
@@ -71,6 +73,21 @@
#SHADOWOPTS=-DPW_SHADOW
# To use shadow passwords under Solaris, uncomment the SHADOWOPTS line.
+# MySQL greylist support
+#MYSQLINCLUDES=-I/usr/include/mysql
+#MYSQLLIBS=-L/usr/lib -lmysqlclient
+#MYSQLHOST="localhost"
+#MYSQLPORT=3306
+#MYSQLUSER="milter"
+#MYSQLPASS="milter"
+#MYSQLDB="relaydelay"
+# minutes until email is accepted
+#BLOCK_EXPIRE=55
+# minutes until record expires
+#RECORD_EXPIRE=500
+# days until record expires after accepting email
+#RECORD_EXPIRE_GOOD=36
+
# to enable the possibility to log and debug imap and pop uncoment the
# next line
#DEBUG=-DDEBUG
@@ -1041,7 +1058,8 @@
predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
-qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
+qmail-smtpd qmail-greylist qmail-smtpdpol \
+sendmail tcp-env qmail-newmrh config config-fast dnscname \
dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
forward preline condredirect bouncesaying except maildirmake \
maildir2mbox maildirwatch qail elq pinq idedit install-big \
@@ -2071,6 +2089,58 @@
error.a fs.a auto_qmail.o dns.o str.a auto_break.o \
`cat dns.lib` `cat socket.lib` $(TLSLIBS) $(ZLIB)
+qmail-greylist: \
+load qmail-greylist.o local_scan.o dbdef.sql qmail-greylist-whitelist.pl \
+qmail-greylist-cleanup.pl
+ ./load qmail-greylist -lz -lm local_scan.o $(MYSQLLIBS)
+
+qmail-greylist.o: \
+compile qmail-greylist.c local_scan.h
+ ./compile qmail-greylist.c
+
+local_scan.o: \
+compile local_scan.c local_scan.h
+ ./compile $(MYSQLINCLUDES) local_scan.c
+
+qmail-greylist-whitelist.pl: \
+qmail-greylist-whitelist.pl.in
+ cat qmail-greylist-whitelist.pl.in > qmail-greylist-whitelist.pl
+ sed -i s/%MYSQLHOST%/${MYSQLHOST}/ qmail-greylist-whitelist.pl
+ sed -i s/%MYSQLPORT%/${MYSQLPORT}/ qmail-greylist-whitelist.pl
+ sed -i s/%MYSQLUSER%/${MYSQLUSER}/ qmail-greylist-whitelist.pl
+ sed -i s/%MYSQLPASS%/${MYSQLPASS}/ qmail-greylist-whitelist.pl
+ sed -i s/%MYSQLDB%/${MYSQLDB}/ qmail-greylist-whitelist.pl
+
+qmail-greylist-cleanup.pl: \
+qmail-greylist-cleanup.pl.in
+ cat qmail-greylist-cleanup.pl.in > qmail-greylist-cleanup.pl
+ sed -i s/%MYSQLHOST%/${MYSQLHOST}/ qmail-greylist-cleanup.pl
+ sed -i s/%MYSQLPORT%/${MYSQLPORT}/ qmail-greylist-cleanup.pl
+ sed -i s/%MYSQLUSER%/${MYSQLUSER}/ qmail-greylist-cleanup.pl
+ sed -i s/%MYSQLPASS%/${MYSQLPASS}/ qmail-greylist-cleanup.pl
+ sed -i s/%MYSQLDB%/${MYSQLDB}/ qmail-greylist-cleanup.pl
+
+dbdef.sql: \
+dbdef.in
+ cat dbdef.in > dbdef.sql
+ sed -i s/%MYSQLHOST%/${MYSQLHOST}/ dbdef.sql
+ sed -i s/%MYSQLPORT%/${MYSQLPORT}/ dbdef.sql
+ sed -i s/%MYSQLUSER%/${MYSQLUSER}/ dbdef.sql
+ sed -i s/%MYSQLPASS%/${MYSQLPASS}/ dbdef.sql
+ sed -i s/%MYSQLDB%/${MYSQLDB}/ dbdef.sql
+ sed -i s/%BLOCK_EXPIRE%/${BLOCK_EXPIRE}/ dbdef.sql
+ sed -i s/%RECORD_EXPIRE%/${RECORD_EXPIRE}/ dbdef.sql
+ sed -i s/%RECORD_EXPIRE_GOOD%/${RECORD_EXPIRE_GOOD}/ dbdef.sql
+
+qmail-smtpdpol: \
+load qmail-smtpdpol.o
+ ./load qmail-smtpdpol
+ strip qmail-smtpdpol
+
+qmail-smtpdpol.o: \
+compile qmail-smtpdpol.c
+ ./compile qmail-smtpdpol.c
+
qmail-smtpd.0: \
qmail-smtpd.8
nroff -man qmail-smtpd.8 > qmail-smtpd.0
@@ -2363,6 +2433,8 @@
qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
+qmail-greylist.c qmail-smtpdpol.c local_scan.c local_scan.h dbdef.in \
+qmail-greylist-cleanup.pl.in qmail-greylist-whitelist.pl.in README.ENVELOPE_SCANNER \
qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
diff -u -N -r qmail-1.03/qmail-greylist.c qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist.c
--- qmail-1.03/qmail-greylist.c 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist.c 2007-01-30 17:42:55.994956416 +0000
@@ -0,0 +1,34 @@
+/**************
+ * Copyright (c) [EMAIL PROTECTED] 2003
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "local_scan.h"
+
+
+char *received_protocol;
+char *sender_host_address;
+char *sender_address;
+struct recipients_list recipients_list[1];
+int recipients_count = 1;
+
+int main(int argc, char *argv[])
+{
+ received_protocol = "notneeded4qmail";
+ sender_host_address = getenv("TCPREMOTEIP");
+ recipients_list[0].address = argv[2];
+ sender_address = argv[1];
+
+ if (argc > 2 && sender_host_address != NULL)
+ return local_scan(1,NULL);
+ else
+ return SOMETHING_BAD;
+}
+
diff -u -N -r qmail-1.03/qmail-greylist-cleanup.pl.in qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist-cleanup.pl.in
--- qmail-1.03/qmail-greylist-cleanup.pl.in 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist-cleanup.pl.in 2007-01-30 00:43:55.000000000 +0000
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant DBD => 'DBI:mysql:%MYSQLDB%:%MYSQLHOST%:%MYSQLPORT%';
+use constant DBUSER => '%MYSQLUSER%';
+use constant DBPASS => '%MYSQLPASS%';
+
+use DBI;
+
+system ("cat /dev/null > /tmp/greylist_dbg.txt");
+
+my $dbh = DBI->connect(DBD,DBUSER,DBPASS) or die "can't connect to db ", $DBI::errstr, ":$!";
+
+$dbh->do("DELETE FROM relaytofrom WHERE record_expires < NOW() - INTERVAL 1 HOUR AND origin_type = 'AUTO'");
+$dbh->do("OPTIMIZE TABLE relaytofrom");
+
+$dbh->disconnect;
+
+exit;
diff -u -N -r qmail-1.03/qmail-greylist-whitelist.pl.in qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist-whitelist.pl.in
--- qmail-1.03/qmail-greylist-whitelist.pl.in 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-greylist-whitelist.pl.in 2007-01-30 00:43:55.000000000 +0000
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant DBD => 'DBI:mysql:%MYSQLDB%:%MYSQLHOST%:%MYSQLPORT%';
+use constant DBUSER => '%MYSQLUSER%';
+use constant DBPASS => '%MYSQLPASS%';
+
+use DBI;
+
+system ("cat /dev/null > /tmp/greylist_dbg.txt");
+
+my $dbh = DBI->connect(DBD,DBUSER,DBPASS) or die "can't connect to db ", $DBI::errstr, ":$!";
+
+$dbh->do("INSERT INTO relaytofrom values (NULL, '$ARGV[0]',NULL,NULL, '0000-00-00 00:00:00', '9999-12-31 23:59:59', 0, 0, 0, 'MANUAL', NOW(), NOW())");
+$dbh->do("OPTIMIZE TABLE relaytofrom");
+
+$dbh->disconnect;
+
+exit;
diff -u -N -r qmail-1.03/qmail-smtpd.c qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-smtpd.c
--- qmail-1.03/qmail-smtpd.c 2007-01-30 18:46:07.296590960 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-smtpd.c 2007-01-30 17:52:59.077273968 +0000
@@ -22,6 +22,10 @@
#include "env.h"
#include "now.h"
#include "exit.h"
+#ifdef ENVELOPE_SCAN
+ #include "fork.h"
+ #include "wait.h"
+#endif
#include "rcpthosts.h"
#include "rbl.h"
#include "timeoutread.h"
@@ -165,6 +169,10 @@
void err_qqt(void) { out("451 qqt failure (#4.3.0)\r\n"); }
void err_dns(void) { out("421 DNS temporary failure at return MX check, try again later (#4.3.0)\r\n"); }
void err_soft(char *s) { out("451 "); out(s); out("\r\n"); logline2(1,"temporary verify error: ", s); }
+#ifdef ENVELOPE_SCAN
+void err_tempfail() { out("421 temporary envelope failure (#4.3.0)\r\n"); }
+void err_permfail() { out("553 sorry, permanent envelope failure (#5.7.1)\r\n"); }
+#endif
void err_bmf(void) { out("553 sorry, your mail was administratively denied. (#5.7.1)\r\n"); }
void err_bmfunknown(void) { out("553 sorry, your mail from a host ["); out(remoteip); out("] without valid reverse DNS was administratively denied (#5.7.1)\r\n"); }
void err_maxrcpt(void) { out("553 sorry, too many recipients (#5.7.1)\r\n"); }
@@ -831,7 +839,40 @@
return 0;
}
+#ifdef ENVELOPE_SCAN
+int envelope_scanner()
+{
+ int child;
+ int wstat;
+
+ char *envelope_scannerarg[] = { env_get("ENVELOPE_SCANNER") , mailfrom.s, addr.s, helohost.s, 0 };
+
+ switch(child = vfork()) {
+ case -1:
+ return 1;
+ case 0:
+ execv(*envelope_scannerarg,envelope_scannerarg);
+ _exit(111);
+ }
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat)) {
+ return 1;
+ }
+
+ switch(wait_exitcode(wstat)) {
+ case 100: /* rejected */
+ err_permfail();
+ return 0;
+ case 101: /* greylisted */
+ err_tempfail();
+ return 0;
+ default: /* something wrong happened */
+ return 1;
+ }
+}
+#endif
+
void smtp_helo(char *arg)
{
smtp_line("250 ");
@@ -1134,6 +1175,9 @@
if (errdisconnect) err_quit();
return;
}
+#ifdef ENVELOPE_SCAN
+ if (env_get("ENVELOPE_SCANNER") && !envelope_scanner()) return;
+#endif
}
++rcptcount;
diff -u -N -r qmail-1.03/qmail-smtpdpol.c qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-smtpdpol.c
--- qmail-1.03/qmail-smtpdpol.c 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/qmail-smtpdpol.c 2007-01-30 18:52:50.463300304 +0000
@@ -0,0 +1,233 @@
+/* qmail-smtpdpol.c
+ * an c frontend to Postfix Access Policy servers to use primary whith qmail
+ *
+ *
+ * Based on code by Anton Lundin <[EMAIL PROTECTED]>
+ * Adapted by Hugo Monteiro <[EMAIL PROTECTED]>
+ *
+ * Licence: GPL 2+
+ */
+
+/*
+ * A postfix smtpd_access_policy call looks like:
+ *
+ * These are what we have and matter at RCPT state
+ *
+ * request=smtpd_access_policy
+ * protocol_name=SMTP
+ * protocol_state=RCPT
+ * [EMAIL PROTECTED]
+ * [EMAIL PROTECTED]
+ * client_address=1.2.3.4
+ * client_name=another.domain.tld
+ * helo_name=some.domain.tld
+ *
+ * The following ones are prior to RCPT or unavailable to us
+ *
+ * queue_id=8045F2AB23
+ * instance=123.456.7
+ * sasl_method=plain
+ * sasl_username=you
+ * sasl_sender=
+ * size=12345
+ * [empty line]
+ *
+ * If greylisted, the policy server response contains
+ * (answer should be treated case insensitive)
+ *
+ * action=DEFER_IF_PERMIT
+ * [empty line]
+ *
+ * If whitelisted, the policy server response contains
+ * (answer should be treated case insensitive)
+ *
+ * action=DUNNO
+ * [empty line]
+ *
+ * If blacklisted, and policy server configured for permanent failure
+ * (answer should be treated case insensitive)
+ *
+ * action=REJECT
+ *
+ *
+ * If any parameter (like helo_name=) is omited from the policy server,
+ * or contains an empty string, for alphanumerical, or 0, for numerical values
+ * then the policy server will disregard that particular parameter.
+ *
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+#define SIZEOF_INT sizeof(int)
+
+#define ANSWER_GREYLIST "DEFER_IF_PERMIT"
+#define ANSWER_GO "DUNNO"
+#define ANSWER_PERM "REJECT"
+#define EXIT_GO 0 /* good to go, accept email */
+#define EXIT_PERM 100 /* permanent reject of mail */
+#define EXIT_TEMP 101 /* tempfail, aka graylist */
+#define EXIT_ERROR 102 /* something went wrong */
+#define GREYLISTED "Greylisted (see http://projects.puremagic.com/greylisting/)"
+#define REJECTED "You have been blacklisted. Contact you system administrator."
+
+/* config*/
+#define POSTGREY %POLICYD_SERVER% /*where it is running, gethostbyname notation*/
+#define PORT %POLICYD_PORT% /* port to connect to postgrey on.*/
+/* end config */
+
+int main(int argc, char *argv[]) {
+ int i, j, sock, size, policyd_port, len ;
+ int keylen=0, valuelen=0;
+ long lpolicyd_port;
+ struct sockaddr_in saddr;
+ struct hostent *hp;
+ char answer[32];
+ char *policyd_server, *policyd_env_port, *end_ptr;
+
+ char *key[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ char *value[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ char *query;
+
+ int keylens[8];
+ int valuelens[8];
+
+ policyd_server = getenv("POLICYD_SERVER");
+ policyd_env_port = getenv("POLICYD_PORT");
+
+ if ((argc < 3 ) || getenv("TCPREMOTEIP") == NULL || policyd_server == NULL || policyd_env_port == NULL ) {
+ fprintf(stderr,"usage: qmail-smtpdpol mailfrom.s addr.s [helohost.s]\n");
+ if ( getenv("TCPREMOTEIP") == NULL || policyd_server == NULL || policyd_env_port == NULL ) {
+ fprintf(stderr,"TCPREMOTEIP, POLICYD_SERVER or POLICYD_PORT not set!\n");
+ }
+ exit(EXIT_ERROR);
+ } else {
+ errno=0;
+ lpolicyd_port = strtol(policyd_env_port, &end_ptr, 0);
+ if ( (errno != 0) || (lpolicyd_port < INT_MIN) || (lpolicyd_port > INT_MAX) || (*end_ptr != '\0')) {
+ fprintf(stderr, "qmail-smtpdpol: invalid POLICYD_PORT port number\n");
+ exit(EXIT_ERROR);
+ } else
+ policyd_port = (int)lpolicyd_port;
+ }
+
+ key[0] = "request=";
+ key[1] = "protocol_name=";
+ key[2] = "protocol_state=";
+ key[3] = "client_address=";
+ key[4] = "client_name=";
+ key[5] = "sender=";
+ key[6] = "recipient=";
+ key[7] = argv[3] != NULL ? "helo_name=" : 0;
+
+ value[0] = "smtpd_access_policy";
+ value[1] = "SMTP";
+ value[2] = "RCPT";
+ value[3] = getenv("TCPREMOTEIP");
+ value[4] = getenv("TCPREMOTEHOST") != NULL ? getenv("TCPREMOTEHOST") : getenv("TCPREMOTEIP");
+ value[5] = argv[1];
+ value[6] = argv[2];
+ value[7] = argv[3] != NULL ? argv[3] : 0;
+
+ for (i=0;i<sizeof(key)/SIZEOF_INT;i++) {
+ if (key[i] != NULL) {
+ keylens[i] = strlen(key[i]);
+ keylen += keylens[i];
+ }
+ }
+
+ for (i=0;i<sizeof(value)/SIZEOF_INT;i++) {
+ if (value[i] != NULL) {
+ valuelens[i] = strlen(value[i]);
+ valuelen += valuelens[i];
+ }
+ }
+
+ len = keylen + valuelen + sizeof(key)/SIZEOF_INT + 2;
+
+ query = malloc(len);
+
+ for (i=j=0;i<sizeof(key)/SIZEOF_INT && j<len;i++) {
+ if (key[i] != NULL && value[i] != NULL) {
+ strcpy(query+j, key[i]);
+ j += keylens[i];
+ strcpy(query+j, value[i]);
+ j += valuelens[i];
+ query[j++] = '\n';
+ }
+ }
+ query[j++] = '\n';
+ query[j++] = '\0';
+
+ /* init so we have all the structs for connect */
+ bzero(&saddr, sizeof (saddr));
+ saddr.sin_family = AF_INET;
+
+ if ((hp = gethostbyname(policyd_server)) == NULL) {
+ perror("gethostbyname");
+ free(query);
+ return EXIT_ERROR;
+ }
+
+ bcopy(hp->h_addr, &saddr.sin_addr, hp->h_length);
+ saddr.sin_port = htons(policyd_port);
+
+ /* get a socket */
+ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ perror("socket");
+ free(query);
+ return EXIT_ERROR;
+ }
+
+ /* connect the socket to the other end */
+ if (connect(sock, (struct sockaddr *)&saddr, sizeof (saddr)) != 0) {
+ perror("connect");
+ free(query);
+ return EXIT_ERROR;
+ }
+
+ /* send the query */
+ if ( send(sock,query,strlen(query),0) != strlen(query)) {
+ perror("send");
+ free(query);
+ exit(EXIT_ERROR);
+ }
+ /* recive the answer from postgrey */
+ size = recv(sock, query, strlen(query), 0);
+
+ close(sock);
+ /* pase out the action=? */
+ if (sscanf(query,"action=%31s",answer) < 1 ) {
+ perror("sscanf");
+ free(query);
+ exit(EXIT_ERROR);
+ }
+
+ free(query);
+
+ /* figure out how to exit */
+ if ( strcasecmp(answer,ANSWER_PERM) == 0) {
+ /* is rejected */
+ exit(EXIT_PERM);
+ } else if ( strcasecmp(answer,ANSWER_GREYLIST) == 0) {
+ /* is greylisted */
+ exit(EXIT_TEMP);
+ } else if ( strcasecmp(answer,ANSWER_GO) == 0) {
+ /* is good to go */
+ exit(EXIT_GO);
+ } else {
+ fprintf(stderr,"Policy server response is strange to me : %s\n",query);
+ exit(EXIT_ERROR);
+ }
+
+ /* shud not happen, but anyway. */
+ exit(EXIT_GO);
+}
diff -u -N -r qmail-1.03/README.ENVELOPE_SCAN qmail-ldap-1.03-20060201-envelope-scan-0.5/README.ENVELOPE_SCAN
--- qmail-1.03/README.ENVELOPE_SCAN 1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/README.ENVELOPE_SCAN 2007-01-30 18:34:37.967384928 +0000
@@ -0,0 +1,138 @@
+AKNOWLEDGEMENTS:
+
+Thanks go to, in no special order:
+
+Evan Harris ([EMAIL PROTECTED]) for the greylisting concept.
[EMAIL PROTECTED] for the mysql greylist engine.
+Joshua Megerman ([EMAIL PROTECTED]) and Bill Shupp ([EMAIL PROTECTED])
+ for the qmail patch for the greylist engine.
+Anton Lundin ([EMAIL PROTECTED]) for the generic ENVELOPE_SCANNER
+ idea, and the base for the Postfix SMTPD Policy helper.
+
+
+GREYLIST:
+
+In earlier releases, this feature was called GREYLIST and it was provided by
+making qmail-smtpd call an external helper which would provide authorization
+delivery depending on the helper exit code. That helper is still provided now
+(/var/qmail/bin/qmail-greylist), and is there for those who prefer to have
+only a good greylist solution, with MySQL backend, with no extra fuzz.
+
+THE GREYLIST HELPER:
+
+Activation:
+ Edit the Makefile accordingly and use ENVELOPE_SCANNER env var set to
+ /path/to/qmail/bin/qmail-greylist. The database schema is provided in
+ /path/to/qmail/doc/dbdef.sql.
+ All the helper MySQL and TTL parameters and can be set using the following env vars,
+ either in the tcp rules for smtpd, or by echo "relaydelay" > /var/qmail/boot/qmail-smtpd/env/MYSQLDB,
+ for instance.
+
+ MYSQLHOST - host database
+ MYSQLPORT - database port
+ MYSQLUSER - database access user
+ MYSQLPASS - database user password
+ MYSQLDB - database name
+ BLOCK_EXPIRE - greylisting period
+ RECORD_EXPIRE - ttl for greylisted records with only 1 hit
+ RECORD_EXPIRE_GOOD - ttl for greylisted records with more than 1 hit
+ LOCAL_SCAN_DEBUG - turn on debugging
+
+
+Mysql failure:
+
+ If mysql isn't running, the relaydelay database doesn't exist or the username or
+ password fails, greylisting will be disabled and messages will be accepted
+ according to qmail-ldap defined rules.
+
+
+Whitelisting:
+
+ Although there's the possibility to whitelist by omiting the ENVELOPE_SCANNER env var
+ on the tcpserver rules cdb, for a certain netblock, there's also the possibility to
+ do so by inserting records into the relaydelay database. There's a small perl script
+ (greylist-whitelist.pl) bundled to aid with such task.
+
+ Ex:
+ /var/qmail/doc/qmail-greylist-whitelist.pl 1.2.3.4
+
+
+Database maintenance:
+
+ Database maintenance, i.e. deletion of expired records, has to be done separatly.
+ I've bundled a perl script (greylist-cleanup.pl) formerly submitted by Gerard Earley
+ to the QMR mailing list.
+ It's advised that you preform such maintenance once a day, peharps setting up a cronjob.
+
+ Ex:
+ 0 0 * * * root /var/qmail/doc/qmail-greylist-cleanup.pl
+
+
+
+SMTPD ACL POLICY:
+
+SMTPD ACL Policy it's a concept used widely in postfix. There are a few policy servers
+available, each with its own level of features and possibilities.
+In the case of qmail, and at the envelope level of SMTP conversation, the use of such
+solution could give extra benefit besides the greylisting. Qmail-smtpd now also passes
+the received HELO to the helper, so if using a policy server that supports
+HELO checking, such as Policyd (http://policyd.sourceforge.net), it's also possible
+to check and blacklist addresses which use ilegit HELO names (your server name, for instance).
+More, using such a policy server, it's also possible to have a centralized authorization
+service to provide blacklist and whitelists, either based on ip addresses, networks blocks,
+senders recipients and dns names/domains.
+The helper is located in /path/to/qmail/bin/qmail-smtpdpol.
+In the case of policyd, one can even make use of automatic blacklisting using smaptraps.
+You can even configure if you wish to send permanent or temporary failures for blacklist
+events.
+Cool huh?
+
+
+THE SMTPD POLICY HELPER:
+
+Activation:
+ Edit the Makefile accordingly and use ENVELOPE_SCANNER env var set to
+ /path/to/qmail/bin/qmail-smtpdpol. The policy server can port can then be defined
+ by setting the env var POLICYD_SERVER and POLICYD_PORT respectively.
+ Again, either set those in the tcp rules for smtpd or echo them to the respective env files.
+
+On failure:
+ Messaged will be accepted according qmail-ldap defined rules.
+
+Database maintenance:
+ None. Well, not true in most cases, but that will be done by whatever ways the SMTPD
+ policy server has.
+
+
+
+
+GENERIC ENVELOPE SCANNING:
+
+Supporting generic envelope scanners, allows that besides using the provided helpers,
+people can write their own helpers having the following information into consideration:
+
+The call to the helper will have the following data passed and in the following order:
+
+sender recipient helo
+
+The helper can choose of course which information to treat, for instance, while using
+qmail-greylist, although the helo is passed on to the helper, it simply disregards it.
+
+The helper must then return one of the following codes:
+
+0 on success, meaning that mail will be accepted
+100 on a permanent reject of mail
+101 on a temporary fail, which can be used for greylisting
+-1 if there was something wrong in the process (actually any other value than 0, 100
+ and 101 will be treated as an error, so the -1 is a should.)
+
+In the case of -1 is returned, qmail-ldap will continue as if there was no external
+envelope scanning and mail is delivered normally.
+
+
+That's all folks!
+
+Comments are welcomed,
+
+Hugo Monteiro <[EMAIL PROTECTED]>
+
diff -u -N -r qmail-1.03/TARGETS qmail-ldap-1.03-20060201-envelope-scan-0.5/TARGETS
--- qmail-1.03/TARGETS 2007-01-30 18:46:07.001635800 +0000
+++ qmail-ldap-1.03-20060201-envelope-scan-0.5/TARGETS 2007-01-30 00:47:02.000000000 +0000
@@ -381,6 +381,14 @@
man
setup
check
+local_scan.o
+qmail-greylist.o
+qmail-greylist
+dbdef.sql
+qmail-greylist-whitelist.pl
+qmail-greylist-cleanup.pl
+qmail-smtpdpol.o
+qmail-smtpdpol
auth_imap
Makefile.cdb-p
auth_imap.o