diff options
author | 2013-09-12 18:20:06 +0200 | |
---|---|---|
committer | 2013-09-12 18:20:06 +0200 | |
commit | aec4d5fa58b4d713b4c0500e2923d494be475908 (patch) | |
tree | 91e34c65dfbf010e96c262a9e2f36f9ebb572265 | |
parent | a couple bzero() added just to be safe (diff) | |
download | OpenSMTPD-aec4d5fa58b4d713b4c0500e2923d494be475908.tar.xz OpenSMTPD-aec4d5fa58b4d713b4c0500e2923d494be475908.zip |
negative rules + doc
-rw-r--r-- | smtpd/parse.y | 49 | ||||
-rw-r--r-- | smtpd/ruleset.c | 31 | ||||
-rw-r--r-- | smtpd/smtpd.conf.5 | 90 | ||||
-rw-r--r-- | smtpd/smtpd.h | 6 | ||||
-rw-r--r-- | smtpd/to.c | 17 |
5 files changed, 142 insertions, 51 deletions
diff --git a/smtpd/parse.y b/smtpd/parse.y index 5bd2c1c9..ba16f323 100644 --- a/smtpd/parse.y +++ b/smtpd/parse.y @@ -134,7 +134,7 @@ typedef struct { %token <v.string> STRING %token <v.number> NUMBER %type <v.table> table -%type <v.number> port auth ssl size expire address_family mask_source +%type <v.number> port auth ssl size expire address_family mask_source negation %type <v.table> tables tablenew tableref destination alias virtual usermapping userbase from sender %type <v.string> pkiname tag tagged listen_helo %% @@ -277,13 +277,14 @@ tag : TAG STRING { | /* empty */ { $$ = NULL; } ; -tagged : TAGGED STRING { - if (($$ = strdup($2)) == NULL) { +tagged : TAGGED negation STRING { + if (($$ = strdup($3)) == NULL) { yyerror("strdup"); - free($2); + free($3); YYERROR; } - free($2); + free($3); + rule->r_nottag = $2; } | /* empty */ { $$ = NULL; } ; @@ -858,19 +859,25 @@ userbase : USERBASE tables { -destination : DOMAIN tables { - struct table *t = $2; +destination : negation DOMAIN tables { + struct table *t = $3; if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { yyerror("invalid use of table \"%s\" as DOMAIN parameter", t->t_name); YYERROR; } - + rule->r_notdestination = $1; $$ = t; } - | LOCAL { $$ = table_find("<localnames>", NULL); } - | ANY { $$ = 0; } + | negation LOCAL { + rule->r_notdestination = $1; + $$ = table_find("<localnames>", NULL); + } + | negation ANY { + rule->r_notdestination = $1; + $$ = 0; + } ; @@ -942,37 +949,43 @@ action : userbase DELIVER TO MAILDIR { } ; -from : FROM tables { - struct table *t = $2; +negation : '!' { $$ = 1; } + | /* empty */ { $$ = 0; } + ; + +from : FROM negation tables { + struct table *t = $3; if (! table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { yyerror("invalid use of table \"%s\" as FROM parameter", t->t_name); YYERROR; } - + rule->r_notsources = $2; $$ = t; } - | FROM ANY { + | FROM negation ANY { $$ = table_find("<anyhost>", NULL); + rule->r_notsources = $2; } - | FROM LOCAL { + | FROM negation LOCAL { $$ = table_find("<localhost>", NULL); + rule->r_notsources = $2; } | /* empty */ { $$ = table_find("<localhost>", NULL); } ; -sender : SENDER tables { - struct table *t = $2; +sender : SENDER negation tables { + struct table *t = $3; if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { yyerror("invalid use of table \"%s\" as SENDER parameter", t->t_name); YYERROR; } - + rule->r_notsenders = $2; $$ = t; } | /* empty */ { $$ = NULL; } diff --git a/smtpd/ruleset.c b/smtpd/ruleset.c index b6589edb..cac817d9 100644 --- a/smtpd/ruleset.c +++ b/smtpd/ruleset.c @@ -47,15 +47,20 @@ ruleset_match(const struct envelope *evp) TAILQ_FOREACH(r, env->sc_rules, r_entry) { - if (r->r_tag[0] != '\0' && strcmp(r->r_tag, evp->tag) != 0) - continue; + if (r->r_tag[0] != '\0') { + ret = strcmp(r->r_tag, evp->tag); + if (ret != 0 && !r->r_nottag) + continue; + if (ret == 0 && r->r_nottag) + continue; + } ret = ruleset_check_source(r->r_sources, ss, evp->flags); if (ret == -1) { errno = EAGAIN; return (NULL); } - if (ret == 0) + if ((ret == 0 && !r->r_notsources) || (ret != 0 && r->r_notsources)) continue; if (r->r_senders) { @@ -64,7 +69,7 @@ ruleset_match(const struct envelope *evp) errno = EAGAIN; return (NULL); } - if (ret == 0) + if ((ret == 0 && !r->r_notsenders) || (ret != 0 && r->r_notsenders)) continue; } @@ -75,19 +80,21 @@ ruleset_match(const struct envelope *evp) errno = EAGAIN; return NULL; } - if (ret) { - if (r->r_desttype == DEST_VDOM && - (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) { - if (! aliases_virtual_check(r->r_mapping, - &evp->rcpt)) { - return NULL; - } + if ((ret == 0 && !r->r_notdestination) || (ret != 0 && r->r_notdestination)) + continue; + + if (r->r_desttype == DEST_VDOM && + (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) { + if (! aliases_virtual_check(r->r_mapping, + &evp->rcpt)) { + return NULL; } - goto matched; } + goto matched; } errno = 0; + log_trace(TRACE_RULES, "no rule matched"); return (NULL); matched: diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5 index d2ee5a2e..777012ec 100644 --- a/smtpd/smtpd.conf.5 +++ b/smtpd/smtpd.conf.5 @@ -80,33 +80,57 @@ The first matching rule decides what action is taken. If no rule matches the message, the default action is to reject the message. .Pp +Whenever \! is used, it will performe a reverse match. +.Pp Following the accept/reject -decision comes the client's IP address filter: +decision comes the optional tag matching: +.Bl -tag -width Ds +.It Xo +.Ic tagged +.Op Ic \! +.Ic tag +.Xc +If specified, the rule will only be matched if the client session was tagged +.Ar tag . +.El +.Pp +Then, comes the client's IP address filter: .Bl -tag -width Ds .It Ic from any Make the rule match regardless of the IP of connecting client. -.It Ic from local +.It Xo +.Ic from +.Op Ic \! +.Ic local +.Xc The rule matches only locally originating connections. This is the default, and may be omitted. -.It Ic from Ar network +.It Xo +.Ic from +.Op Ic \! +.Ic network +.Xc The rule matches if the connection is made from the specified .Ar network , specified in CIDR notation. -.It Ic from Aq Ar table +.It Xo +.Ic from +.Op Ic \! +.Ic table +.Xc The rule matches if the connection is made from a client whose address is declared in the table .Ar table . -.It Ic tagged Ar tag -If specified, the rule will only be matched if the client session was tagged -.Ar tag . -Mails enqueued via the local socket are tagged -.Qq local . .El .Pp In addition, finer filtering may be achieved on the sender if desired: .Bl -tag -width Ds -.It Ic sender Ar senders +.It Xo +.Ic sender +.Op Ic \! +.Ic senders +.Xc If specified, the rule will only be matched if the sender email address is found in the table .Ar senders . @@ -126,7 +150,13 @@ Make the rule match regardless of the domain it is sent to. The .Ar vmap table will be used as the virtual domain mapping. -.It Ic for domain Ar domain Op Ic alias Aq Ar aliases +.It Xo +.Ic for +.Op Ic \! +.Ic domain +.Ar domain +.Op Ic alias Aq Ar aliases +.Xc This rule applies to mail destined for the specified .Ar domain . This parameter supports the @@ -141,7 +171,13 @@ If specified, the table .Ar aliases is used for looking up alternative destinations for addresses in this .Ar domain . -.It Ic for domain Aq Ar domains Op Ic alias Aq Ar aliases +.It Xo +.Ic for +.Op Ic \! +.Ic domain +.Aq Ar domains +.Op Ic alias Aq Ar aliases +.Xc This rule applies to mail destined to domains which are part of the table .Ar domains . .Pp @@ -149,7 +185,13 @@ If specified, the table .Ar aliases is used for looking up alternative destinations for addresses in these .Ar domains . -.It Ic for domain Ar domain Ic virtual Aq Ar users +.It Xo +.Ic for +.Op Ic \! +.Ic domain +.Ar domain +.Ic virtual Aq Ar users +.Xc This rule applies to mail destined for the specified virtual .Ar domain . This parameter supports the @@ -168,7 +210,13 @@ For an example of how to configure the .Ar users table, see .Xr makemap 8 . -.It Ic for domain Ao Ar domains Ac Ic virtual Aq Ar users +.It Xo +.Ic for +.Op Ic \! +.Ic domain +.Ao Ar domains +.Ac Ic virtual Aq Ar users +.Xc This rule applies to mail destined for the virtual domains specified in the table .Ar domains . @@ -180,7 +228,12 @@ For an example of how to configure the .Ar users table, see .Xr makemap 8 . -.It Ic for local Op Ic alias Aq Ar aliases +.It Xo +.Ic for +.Op Ic \! +.Ic local +.Op Ic alias Aq Ar aliases +.Xc This rule applies to mail destined to .Dq localhost and to the default server name. @@ -189,7 +242,12 @@ See the entry for .Pa /etc/mail/mailname below for details of how the server name is determined. -.It Ic for local virtual Aq Ar vmap +.It Xo +.Ic for +.Op Ic \! +.Ic local +.Ic virtual Aq Ar vmap +.Xc This rule applies to mail destined to .Dq localhost and to the default server name. diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h index af10cf26..22568749 100644 --- a/smtpd/smtpd.h +++ b/smtpd/smtpd.h @@ -336,10 +336,16 @@ enum decision { struct rule { TAILQ_ENTRY(rule) r_entry; enum decision r_decision; + uint8_t r_nottag; char r_tag[MAX_TAG_SIZE]; + + uint8_t r_notsources; struct table *r_sources; + + uint8_t r_notsenders; struct table *r_senders; + uint8_t r_notdestination; enum dest_type r_desttype; struct table *r_destination; @@ -511,19 +511,26 @@ rule_to_text(struct rule *r) bzero(buf, sizeof buf); strlcpy(buf, r->r_decision == R_ACCEPT ? "accept" : "reject", sizeof buf); if (r->r_tag[0]) { - strlcat(buf, " on ", sizeof buf); + strlcat(buf, " tagged ", sizeof buf); + if (r->r_nottag) + strlcat(buf, "! ", sizeof buf); strlcat(buf, r->r_tag, sizeof buf); } strlcat(buf, " from ", sizeof buf); + if (r->r_notsources) + strlcat(buf, "! ", sizeof buf); strlcat(buf, r->r_sources->t_name, sizeof buf); + strlcat(buf, " for ", sizeof buf); + if (r->r_notdestination) + strlcat(buf, "! ", sizeof buf); switch (r->r_desttype) { case DEST_DOM: if (r->r_destination == NULL) { - strlcat(buf, " for any", sizeof buf); + strlcat(buf, " any", sizeof buf); break; } - strlcat(buf, " for domain ", sizeof buf); + strlcat(buf, " domain ", sizeof buf); strlcat(buf, r->r_destination->t_name, sizeof buf); if (r->r_mapping) { strlcat(buf, " alias ", sizeof buf); @@ -532,11 +539,11 @@ rule_to_text(struct rule *r) break; case DEST_VDOM: if (r->r_destination == NULL) { - strlcat(buf, " for any virtual ", sizeof buf); + strlcat(buf, " any virtual ", sizeof buf); strlcat(buf, r->r_mapping->t_name, sizeof buf); break; } - strlcat(buf, " for domain ", sizeof buf); + strlcat(buf, " domain ", sizeof buf); strlcat(buf, r->r_destination->t_name, sizeof buf); strlcat(buf, " virtual ", sizeof buf); strlcat(buf, r->r_mapping->t_name, sizeof buf); |