Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

bypass INPUT_MAIL_FILTER

52 views
Skip to first unread message

Riccardo

unread,
Dec 6, 2006, 8:40:02 AM12/6/06
to
I'm using Sendmail-8.12.10-1.1.1 and smrazor (with milter interface) to
check if mail messages are SPAM. My sendmail.mc contains:
INPUT_MAIL_FILTER(`smrazor', `S=inet:4...@192.168.1.12,
T=C:5s;S:5s;R:20s;E:20s')

I'd like bypassing SMRAZOR milter if messages contain a specific
header, delivering to recipient:

for example if I receive email tagged with "checked out : OK" I don't
want to check this message with SMRAZOR (to know if it's a spam )but
delivering it immediately to recipient.

Can I create a check_rule in sendmail.mc to do it ?

jma...@ttec.com

unread,
Dec 6, 2006, 9:08:42 AM12/6/06
to

Headers can be inserted by parties not under your control, this is an
unsecured backdoor.

You can do this one of two ways.

1) Using a header ruleset, set a macro if the header is this and passed
the rulese and in the milter add code in xxfi_eoh() callback to return
SMFIS_ACCEPT if that macro is set

2) Use the milter_rrres patch and write rulesets that examine each
header sent to the milter. The rulesets can abort the milter and many
other things.

http://www.jmaimon.com/sendmail/#milter-rrres.v15

See cf/README on how to add a header ruleset into your mc file.

Riccardo

unread,
Dec 8, 2006, 8:28:06 AM12/8/06
to
I'm interesting to your solution 1. I' should create an header ruleset
in my sendmail.cf to detect the specific header (e.g. "checked out :
OK") of message.
If header is contained in the mail I don't know how to create MACRO.
I can add code in xxfi_eoh callback (written in C) to return
SMFIS_ACCEPT but I don't know how testing macro existence; in xxfi_eoh
callback I can test macro created in sendmail.cf ?
Where macro is created ? In sendmail.cf ?

Thanks for your time that you spend to answer to my questions.

jma...@ttec.com

unread,
Dec 9, 2006, 7:11:10 PM12/9/06
to

Riccardo wrote:
> I'm interesting to your solution 1. I' should create an header ruleset
> in my sendmail.cf to detect the specific header (e.g. "checked out :
> OK") of message.
> If header is contained in the mail I don't know how to create MACRO.

You will need to learn a bit about sendmail maps and rulesets.

You can read up on from the sendmail source

doc/op

You will also need to configure sendmail to send the macro to the
milter.

Denis Eremenko

unread,
Dec 25, 2006, 11:16:22 PM12/25/06
to

"""Riccardo писал(а):

"""
> I'm using Sendmail-8.12.10-1.1.1 and smrazor (with milter interface) to
> check if mail messages are SPAM. My sendmail.mc contains:
> INPUT_MAIL_FILTER(`smrazor', `S=inet:4...@192.168.1.12,
> T=C:5s;S:5s;R:20s;E:20s')
>
> I'd like bypassing SMRAZOR milter if messages contain a specific
> header, delivering to recipient:

I have similar problem, but i want to skip spam checking by regular
expressions of networks and domains. I extended sendmail and libmilter
for extra command - "skip filter". The idea: milter checks controlled
by some external filter which could command to skip any specific milter
going after himself. Patch quite rough, but works fine for a month (avg
load: about 0.5 msg/min on 2xPPro with 320M RAM). SPAM checker is
amavisd-new+spamassassin.

Here 2 patchsets:
1. sendmail/libmilter itself
2. milter-regex (i'am using it as "dispatcher" doing "skip" commands)

--- include/libmilter/mfapi.h.orig Sat Aug 26 18:42:06 2006
+++ include/libmilter/mfapi.h Wed Nov 15 14:19:51 2006
@@ -464,7 +464,7 @@
** Quarantine an envelope
**
** SMFICTX *ctx; Opaque context structure
-** char *reason: explanation
+** char *reason; explanation
*/

LIBMILTER_API int smfi_quarantine __P((SMFICTX *ctx, char *reason));
@@ -485,6 +485,15 @@
*/

LIBMILTER_API void *smfi_getpriv __P((SMFICTX *));
+
+/*
+** Set milter to skip next time
+**
+** SMFICTX *ctx; Opaque context structure
+** mfi_skipname; name of filter to skip
+*/
+
+LIBMILTER_API int smfi_skip __P((SMFICTX *, char *));

#ifdef __cplusplus
}

--- include/libmilter/mfdef.h.orig Fri Mar 31 04:06:02 2006
+++ include/libmilter/mfdef.h Tue Nov 14 16:12:38 2006
@@ -71,6 +71,7 @@
# define SMFIR_INSHEADER 'i' /* insert header */
# define SMFIR_REPLYCODE 'y' /* reply code etc */
# define SMFIR_QUARANTINE 'q' /* quarantine */
+# define SMFIR_MFISKIP 's' /* skip other filter */

/* What the MTA can send/filter wants in protocol */
# define SMFIP_NOCONNECT 0x00000001L /* MTA should not send connect
info */

--- libmilter/smfi.c.orig Fri Mar 31 04:06:02 2006
+++ libmilter/smfi.c Wed Nov 15 13:08:21 2006
@@ -646,3 +646,45 @@

return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
}
+
+/*
+** SMFI_SKIP -- send "skip filter" message to the MTA to prevent
message
+** from processing by particular filter next time
+**
+** Parameters:
+** ctx -- Opaque context structure
+** mfi_skipname -- name of filter to skip
+**
+** Return value:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_skip(ctx, mfi_skipname)
+ SMFICTX *ctx;
+ char *mfi_skipname;
+{
+ size_t len;
+ int r;
+ char *buf;
+ struct timeval timeout;
+
+ if (mfi_skipname == NULL)
+ return MI_FAILURE;
+
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+
+ len = strlen(mfi_skipname) + 1;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+
+ (void) memcpy(buf, mfi_skipname, len);
+
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_MFISKIP, buf, len);
+
+ free(buf);
+
+ return r;
+}

--- src/milter.c.orig Wed Mar 29 13:34:35 2006
+++ src/milter.c Tue Dec 5 09:25:27 2006
@@ -1900,6 +1900,7 @@
{
char rcmd;
ssize_t rlen;
+ ssize_t sm_i; /* mfi_skipmap counter */
unsigned long skipflag;
#if _FFR_MILTER_NOHDR_RESP
unsigned long norespflag = 0;
@@ -2067,6 +2068,19 @@
m->mf_name, action);
break;

+ case SMFIR_MFISKIP:
+ for (sm_i = 0; InputFilters[sm_i] != NULL; ++sm_i)
+ {
+ if (sm_strcasecmp(InputFilters[sm_i]->mf_name, response) == 0)
+ e->mfi_skipmap[sm_i] = 1;
+ }
+ if (MilterLogLevel > 12)
+ {
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, mfiskip=%s",
+ m->mf_name, action, response);
+ }
+ /* FALLTHROUGH */
+
case SMFIR_CONTINUE:
/* if MAIL command is ok, filter is in message state */
if (command == SMFIC_MAIL)
@@ -2158,7 +2172,20 @@
if (MilterLogLevel > 21)
tn = curtime();

- response = milter_send_command(m, command, data, sz, e, state);
+ if (e->mfi_skipmap[i])
+ {
+ if (MilterLogLevel > 12)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter (%s): skipped", m->mf_name);
+ }
+ continue;
+ }
+ else
+ {
+ response = milter_send_command(m, command, data,
+ sz, e, state);
+ }

if (MilterLogLevel > 21)
{
@@ -3725,7 +3752,7 @@
bool dfopen = false; /* data file open for writing? */
bool newfilter; /* reset on each new filter */
char rcmd;
- int i;
+ int i, sm_i;
int save_errno;
char *response = NULL;
time_t eomsent;
@@ -3762,6 +3789,17 @@
*state = SMFIR_CONTINUE;
newfilter = true;

+ /* check if filter set to be skipped */
+ if (e->mfi_skipmap[i])
+ {
+ if (MilterLogLevel > 12)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter (%s): skipped", m->mf_name);
+ }
+ continue;
+ }
+
/* previous problem? */
if (m->mf_state == SMFS_ERROR)
{
@@ -3879,6 +3917,19 @@
m->mf_name);
*state = rcmd;
m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_MFISKIP:
+ for (sm_i = 0; InputFilters[sm_i] != NULL; ++sm_i)
+ {
+ if (sm_strcasecmp(InputFilters[sm_i]->mf_name, response) == 0)
+ e->mfi_skipmap[sm_i] = 1;
+ }
+ if (MilterLogLevel > 12)
+ {
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, mfiskip=%s",
+ m->mf_name, response);
+ }
break;

case SMFIR_CONTINUE:

--- src/sendmail.h.orig Sat Aug 26 18:42:08 2006
+++ src/sendmail.h Sun Nov 19 22:28:16 2006
@@ -911,6 +911,7 @@
long e_deliver_by; /* deliver by */
int e_dlvr_flag; /* deliver by flag */
SM_RPOOL_T *e_rpool; /* resource pool for this envelope */
+ ssize_t mfi_skipmap[MAXFILTERS]; /* filter map to skip */
};

/* values for e_flags */

--- eval.h.orig Fri Oct 29 21:48:41 2004
+++ eval.h Tue Nov 14 17:03:19 2006
@@ -40,7 +40,7 @@
COND_HEADER, COND_BODY, COND_MAX };
enum { EXPR_AND, EXPR_OR, EXPR_NOT, EXPR_COND };
enum { ACTION_REJECT, ACTION_TEMPFAIL, ACTION_QUARANTINE,
- ACTION_DISCARD, ACTION_ACCEPT };
+ ACTION_DISCARD, ACTION_ACCEPT, ACTION_MFISKIP };

struct expr;

--- parse.y.orig Fri Oct 29 21:48:42 2004
+++ parse.y Wed Nov 15 12:12:02 2006
@@ -75,7 +75,7 @@
%}

%token ERROR STRING
-%token ACCEPT REJECT TEMPFAIL DISCARD QUARANTINE
+%token ACCEPT REJECT TEMPFAIL DISCARD QUARANTINE MFISKIP
%token CONNECT HELO ENVFROM ENVRCPT HEADER BODY
%token AND OR NOT
%type <v.string> STRING
@@ -146,6 +146,14 @@
YYERROR;
}
}
+ | MFISKIP STRING {
+ $$ = create_action(rs, ACTION_MFISKIP, $2);
+ if ($$ == NULL) {
+ yyerror("yyparse: create_action");
+ YYERROR;
+ }
+ free($2);
+ }
;

expr_l : expr {
@@ -287,6 +295,7 @@
{ "envrcpt", ENVRCPT },
{ "header", HEADER },
{ "helo", HELO },
+ { "mfiskip", MFISKIP },
{ "not", NOT },
{ "or", OR },
{ "quarantine", QUARANTINE },

--- milter-regex.c.orig Wed Nov 15 12:23:41 2006
+++ milter-regex.c Wed Nov 15 12:24:49 2006
@@ -180,6 +180,9 @@
smfi_setreply(ctx, RCODE_TEMPFAIL, XCODE_TEMPFAIL,
(char *)action->msg) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
+ if (action->type == ACTION_MFISKIP &&
+ smfi_skip(ctx, (char *)action->msg) != MI_SUCCESS)
+ msg(LOG_ERR, context, "smfi_skip");
return (result);
}

--- milter-regex.8.orig Wed Nov 29 17:12:46 2006
+++ milter-regex.8 Wed Nov 29 17:13:34 2006
@@ -140,6 +140,10 @@
Subsequent matching rules cause the mail to be accepted without
further rule evaluation.
Can be used for whitelist criteria.
+.It mfiskip <milter>
+Matching rules cause specified mail filter to be skipped next time
+for envelope being in process. Milter names set with INPUT_MAIL_FILTER
+1st parameter.
.El
.Pp
A command is followed by one or more expressions, each causing

jma...@ttec.com

unread,
Dec 26, 2006, 8:09:42 AM12/26/06
to

Denis Eremenko wrote:
> """Riccardo ÐÉÓÁÌ(Á):

> I have similar problem, but i want to skip spam checking by regular
> expressions of networks and domains. I extended sendmail and libmilter
> for extra command - "skip filter". The idea: milter checks controlled
> by some external filter which could command to skip any specific milter
> going after himself.

This is an interesting approach, but not very generic.

I would attempt to accomplish the same thing with a sendmail/libmilter
patched by milter-rrres by either

A)

1 - Have the milter call into sendmail with smfi_sm_class() to control
the contents of a sendmail class

2 - Have sendmail control subsequent milters with rulesets that can
compare milter names against the class and abort the milter or skip the
particular command,

B)

1 - Use a regex map in sendmail.mc

2 - Write a ruleset in sendmail.mc that accomplishes your objective

3 - Use milter-rrres patch so that you can control milters with that
ruleset

C)

1 - If for some reason the regex map isnt enough for you, use the
compare map

2 - Write a ruleset in sendmail.mc that accomplishes your objectives

3 - Use milter-rrres patch so that you can control milters with that
ruleset

D)

1 - Use a tag in sendmail access map such as

SkipMilters:127.0.0.1 milter1,milter2,milter3

2 - Write a rulset in sendmail.mc that lookups the access map tags and
sees if {milter_name} macro matches the returned value

3 - Use milter-rrres so you can control the milter with that ruleset.

Here are links to the relevant patches:

http://www.jmaimon.com/sendmail/patches/milter-rrres.v15.tar.gz
http://www.jmaimon.com/sendmail/patches/milter-rrres.Changes.v15.txt
http://www.jmaimon.com/sendmail/#milter-rrres.v15

http://www.jmaimon.com/sendmail/patches/compare-map.v2.81304.patch

http://www.jmaimon.com/sendmail/


If you have an outline for specific ruleset objectives, I may be able
to help you write it.

jma...@ttec.com

unread,
Dec 26, 2006, 9:15:51 AM12/26/06
to

jma...@ttec.com wrote:

> 1 - Have the milter call into sendmail with smfi_sm_class() to control
> the contents of a sendmail class


Oops. Make that a macro. milter-rrres doesnt delete class members
(yet), just adds.

Denis Eremenko

unread,
Dec 27, 2006, 4:40:26 AM12/27/06
to
jma...@ttec.com wrote:
> This is an interesting approach, but not very generic.
>
> I would attempt to accomplish the same thing with a sendmail/libmilter
> patched by milter-rrres by either
...skipped...

I see your point now. You prefer more MTA-centric and ruleset driven
way.
Well, yes, mine was more... straight. Simple and fast to implement.

> If you have an outline for specific ruleset objectives, I may be able
> to help you write it.

This means patching base sendmail anyway. Plus ruleset magic... and
i'am not too good at this. :)

Thanks for help.

Dennis Peterson

unread,
Dec 27, 2006, 10:53:24 PM12/27/06
to
jma...@ttec.com wrote:
> Riccardo wrote:
>> I'm using Sendmail-8.12.10-1.1.1 and smrazor (with milter interface) to
>> check if mail messages are SPAM. My sendmail.mc contains:
>> INPUT_MAIL_FILTER(`smrazor', `S=inet:4...@192.168.1.12,
>> T=C:5s;S:5s;R:20s;E:20s')
>>
>> I'd like bypassing SMRAZOR milter if messages contain a specific
>> header, delivering to recipient:
>>
>> for example if I receive email tagged with "checked out : OK" I don't
>> want to check this message with SMRAZOR (to know if it's a spam )but
>> delivering it immediately to recipient.
>
> Headers can be inserted by parties not under your control, this is an
> unsecured backdoor.

This is not only possible, it has happened. The Habeas haiku header
nonsense comes to mind. Unless this tag is changed often, has a
time-to-live, and never repeats, it sounds like just another bad idea.

dp

0 new messages