diff options
author | Eric Faurot <eric@faurot.net> | 2013-09-12 18:40:55 +0200 |
---|---|---|
committer | Eric Faurot <eric@faurot.net> | 2013-09-12 18:40:55 +0200 |
commit | 1ce72ae6c50b7c10faaa4d92a4c65769584efe58 (patch) | |
tree | 5eca8128d79fe597e093d4c2ab007973e8c14d92 | |
parent | Merge branch 'master' into portable (diff) | |
parent | Merge branch 'master' of ssh.poolp.org:/git/opensmtpd (diff) | |
download | OpenSMTPD-1ce72ae6c50b7c10faaa4d92a4c65769584efe58.tar.xz OpenSMTPD-1ce72ae6c50b7c10faaa4d92a4c65769584efe58.zip |
Merge branch 'master' into portableopensmtpd-201309121848p1
-rw-r--r-- | smtpd/lka.c | 18 | ||||
-rw-r--r-- | smtpd/parse.y | 126 | ||||
-rw-r--r-- | smtpd/ruleset.c | 31 | ||||
-rw-r--r-- | smtpd/smtp.c | 1 | ||||
-rw-r--r-- | smtpd/smtp_session.c | 64 | ||||
-rw-r--r-- | smtpd/smtpd.conf.5 | 122 | ||||
-rw-r--r-- | smtpd/smtpd.h | 9 | ||||
-rw-r--r-- | smtpd/to.c | 17 |
8 files changed, 288 insertions, 100 deletions
diff --git a/smtpd/lka.c b/smtpd/lka.c index 80114e5f..b3292795 100644 --- a/smtpd/lka.c +++ b/smtpd/lka.c @@ -109,6 +109,24 @@ lka_imsg(struct mproc *p, struct imsg *imsg) lka_session(reqid, &evp); return; + case IMSG_LKA_HELO: + m_msg(&m, imsg); + m_get_id(&m, &reqid); + m_get_string(&m, &tablename); + m_get_sockaddr(&m, (struct sockaddr *)&ss); + m_end(&m); + + ret = lka_addrname(tablename, (struct sockaddr*)&ss, + &addrname); + + m_create(p, IMSG_LKA_HELO, 0, 0, -1); + m_add_id(p, reqid); + m_add_int(p, ret); + if (ret == LKA_OK) + m_add_string(p, addrname.name); + m_close(p); + return; + case IMSG_LKA_SSL_INIT: req_ca_cert = imsg->data; resp_ca_cert.reqid = req_ca_cert->reqid; diff --git a/smtpd/parse.y b/smtpd/parse.y index f0022371..0d5708ed 100644 --- a/smtpd/parse.y +++ b/smtpd/parse.y @@ -104,16 +104,16 @@ struct mta_limits *limits; static struct ssl *pki_ssl; static void config_listener(struct listener *, const char *, const char *, - const char *, in_port_t, const char *, uint16_t, const char *); + const char *, in_port_t, const char *, uint16_t, const char *, struct table *); struct listener *host_v4(const char *, in_port_t); struct listener *host_v6(const char *, in_port_t); int host_dns(const char *, const char *, const char *, struct listenerlist *, int, in_port_t, const char *, - uint16_t, const char *); + uint16_t, const char *, struct table *); int host(const char *, const char *, const char *, - struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *); + struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *, struct table *); int interface(const char *, int, const char *, const char *, - struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *); + struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *, struct table *); void set_localaddrs(void); int delaytonum(char *); int is_if_in_group(const char *, const char *); @@ -133,16 +133,16 @@ typedef struct { %token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED MAXSCHEDULERINFLIGHT LISTEN ON ANY PORT EXPIRE %token TABLE SECURE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6 -%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HELO +%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HOSTNAMES %token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI %token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHPARAMS %token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER MASK_SOURCE VERIFY %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.table> table listen_hostnames +%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 +%type <v.string> pkiname tag tagged listen_hostname %% grammar : /* empty */ @@ -283,13 +283,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; } ; @@ -335,7 +336,19 @@ address_family : INET4 { $$ = AF_INET; } | /* empty */ { $$ = AF_UNSPEC; } ; -listen_helo : HOSTNAME STRING { $$ = $2; } +listen_hostname : HOSTNAME STRING { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + +listen_hostnames: HOSTNAMES tables { + struct table *t = $2; + if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { + yyerror("invalid use of table \"%s\" as " + "HOSTNAMES parameter", t->t_name); + YYERROR; + } + $$ = t; + } | /* empty */ { $$ = NULL; } ; @@ -413,11 +426,11 @@ opt_relay_common: AS STRING { strlcpy(rule->r_value.relayhost.sourcetable, t->t_name, sizeof rule->r_value.relayhost.sourcetable); } - | HELO tables { + | HOSTNAMES tables { struct table *t = $2; if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { yyerror("invalid use of table \"%s\" as " - "HELO parameter", t->t_name); + "HOSTNAMES parameter", t->t_name); YYERROR; } strlcpy(rule->r_value.relayhost.helotable, t->t_name, @@ -566,7 +579,7 @@ main : BOUNCEWARN { } limits | LISTEN { bzero(&l, sizeof l); - } ON STRING address_family port ssl pkiname auth tag listen_helo mask_source { + } ON STRING address_family port ssl pkiname auth tag listen_hostname listen_hostnames mask_source { char *ifx = $4; int family = $5; in_port_t port = $6; @@ -574,8 +587,9 @@ main : BOUNCEWARN { char *pki = $8; uint16_t auth = $9; char *tag = $10; - char *helo = $11; - uint16_t masksrc = $12; + char *hostname = $11; + struct table *hostnametable = $12; + uint16_t masksrc = $13; uint16_t flags; if (port != 0 && ssl == F_SSL) { @@ -602,9 +616,9 @@ main : BOUNCEWARN { if (port == 0) { if (ssl & F_SMTPS) { if (! interface(ifx, family, tag, pki, conf->sc_listeners, - MAX_LISTEN, 465, l.authtable, F_SMTPS|flags, helo)) { + MAX_LISTEN, 465, l.authtable, F_SMTPS|flags, hostname, hostnametable)) { if (host(ifx, tag, pki, conf->sc_listeners, - MAX_LISTEN, 465, l.authtable, ssl|flags, helo) <= 0) { + MAX_LISTEN, 465, l.authtable, ssl|flags, hostname, hostnametable) <= 0) { yyerror("invalid virtual ip or interface: %s", ifx); YYERROR; } @@ -612,9 +626,9 @@ main : BOUNCEWARN { } if (! ssl || (ssl & ~F_SMTPS)) { if (! interface(ifx, family, tag, pki, conf->sc_listeners, - MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|flags, helo)) { + MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|flags, hostname, hostnametable)) { if (host(ifx, tag, pki, conf->sc_listeners, - MAX_LISTEN, 25, l.authtable, ssl|flags, helo) <= 0) { + MAX_LISTEN, 25, l.authtable, ssl|flags, hostname, hostnametable) <= 0) { yyerror("invalid virtual ip or interface: %s", ifx); YYERROR; } @@ -623,9 +637,9 @@ main : BOUNCEWARN { } else { if (! interface(ifx, family, tag, pki, conf->sc_listeners, - MAX_LISTEN, port, l.authtable, ssl|auth, helo)) { + MAX_LISTEN, port, l.authtable, ssl|auth, hostname, hostnametable)) { if (host(ifx, tag, pki, conf->sc_listeners, - MAX_LISTEN, port, l.authtable, ssl|flags, helo) <= 0) { + MAX_LISTEN, port, l.authtable, ssl|flags, hostname, hostnametable) <= 0) { yyerror("invalid virtual ip or interface: %s", ifx); YYERROR; } @@ -864,19 +878,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; + } ; @@ -948,37 +968,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; } @@ -1102,8 +1128,8 @@ lookup(char *s) { "filter", FILTER }, { "for", FOR }, { "from", FROM }, - { "helo", HELO }, { "hostname", HOSTNAME }, + { "hostnames", HOSTNAMES }, { "include", INCLUDE }, { "inet4", INET4 }, { "inet6", INET6 }, @@ -1667,14 +1693,14 @@ symget(const char *nam) static void config_listener(struct listener *h, const char *name, const char *tag, const char *pki, in_port_t port, const char *authtable, uint16_t flags, - const char *helo) + const char *hostname, struct table *hostnametable) { h->fd = -1; h->port = port; h->flags = flags; - if (helo == NULL) - helo = conf->sc_hostname; + if (hostname == NULL) + hostname = conf->sc_hostname; h->ssl = NULL; h->ssl_cert_name[0] = '\0'; @@ -1686,7 +1712,9 @@ config_listener(struct listener *h, const char *name, const char *tag, if (tag != NULL) (void)strlcpy(h->tag, tag, sizeof(h->tag)); - (void)strlcpy(h->helo, helo, sizeof(h->helo)); + (void)strlcpy(h->hostname, hostname, sizeof(h->hostname)); + if (hostnametable) + (void)strlcpy(h->hostnametable, hostnametable->t_name, sizeof(h->hostnametable)); } struct listener * @@ -1738,7 +1766,7 @@ host_v6(const char *s, in_port_t port) int host_dns(const char *s, const char *tag, const char *pki, struct listenerlist *al, int max, in_port_t port, const char *authtable, - uint16_t flags, const char *helo) + uint16_t flags, const char *hostname, struct table *hostnametable) { struct addrinfo hints, *res0, *res; int error, cnt = 0; @@ -1783,7 +1811,7 @@ host_dns(const char *s, const char *tag, const char *pki, sin6->sin6_port = port; } - config_listener(h, s, tag, pki, port, authtable, flags, helo); + config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable); TAILQ_INSERT_HEAD(al, h, entry); cnt++; @@ -1799,7 +1827,7 @@ host_dns(const char *s, const char *tag, const char *pki, int host(const char *s, const char *tag, const char *pki, struct listenerlist *al, int max, in_port_t port, const char *authtable, uint16_t flags, - const char *helo) + const char *hostname, struct table *hostnametable) { struct listener *h; @@ -1812,18 +1840,18 @@ host(const char *s, const char *tag, const char *pki, struct listenerlist *al, h = host_v6(s, port); if (h != NULL) { - config_listener(h, s, tag, pki, port, authtable, flags, helo); + config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable); TAILQ_INSERT_HEAD(al, h, entry); return (1); } - return (host_dns(s, tag, pki, al, max, port, authtable, flags, helo)); + return (host_dns(s, tag, pki, al, max, port, authtable, flags, hostname, hostnametable)); } int interface(const char *s, int family, const char *tag, const char *pki, struct listenerlist *al, int max, in_port_t port, const char *authtable, - uint16_t flags, const char *helo) + uint16_t flags, const char *hostname, struct table *hostnametable) { struct ifaddrs *ifap, *p; struct sockaddr_in *sain; @@ -1871,7 +1899,7 @@ interface(const char *s, int family, const char *tag, const char *pki, continue; } - config_listener(h, s, tag, pki, port, authtable, flags, helo); + config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable); ret = 1; TAILQ_INSERT_HEAD(al, h, entry); } diff --git a/smtpd/ruleset.c b/smtpd/ruleset.c index 5f76e305..e523e80c 100644 --- a/smtpd/ruleset.c +++ b/smtpd/ruleset.c @@ -49,15 +49,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) { @@ -66,7 +71,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; } @@ -77,19 +82,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/smtp.c b/smtpd/smtp.c index 6d1bcbfb..7dc2d9d4 100644 --- a/smtpd/smtp.c +++ b/smtpd/smtp.c @@ -69,6 +69,7 @@ smtp_imsg(struct mproc *p, struct imsg *imsg) switch (imsg->hdr.type) { case IMSG_DNS_PTR: case IMSG_LKA_EXPAND_RCPT: + case IMSG_LKA_HELO: case IMSG_LKA_AUTHENTICATE: case IMSG_LKA_SSL_INIT: case IMSG_LKA_SSL_VERIFY: diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c index 031361d8..646fa0c4 100644 --- a/smtpd/smtp_session.c +++ b/smtpd/smtp_session.c @@ -116,6 +116,7 @@ struct smtp_session { struct listener *listener; struct sockaddr_storage ss; char hostname[SMTPD_MAXHOSTNAMELEN]; + char smtpname[SMTPD_MAXHOSTNAMELEN]; int flags; int phase; @@ -156,6 +157,7 @@ struct smtp_session { static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *); static void smtp_session_init(void); static void smtp_connected(struct smtp_session *); +static void smtp_send_banner(struct smtp_session *); static void smtp_mfa_response(struct smtp_session *, int, uint32_t, const char *); static void smtp_io(struct io *, int); @@ -191,6 +193,7 @@ static struct { int code; const char *cmd; } commands[] = { }; static struct tree wait_lka_ptr; +static struct tree wait_lka_helo; static struct tree wait_lka_rcpt; static struct tree wait_mfa_response; static struct tree wait_mfa_data; @@ -208,6 +211,7 @@ smtp_session_init(void) if (!init) { tree_init(&wait_lka_ptr); + tree_init(&wait_lka_helo); tree_init(&wait_lka_rcpt); tree_init(&wait_mfa_response); tree_init(&wait_mfa_data); @@ -249,6 +253,8 @@ smtp_session(struct listener *listener, int sock, s->state = STATE_NEW; s->phase = PHASE_INIT; + strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname)); + /* For local enqueueing, the hostname is already set */ if (hostname) { s->flags |= SF_AUTHENTICATED; @@ -275,7 +281,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) void *ssl; char user[SMTPD_MAXLOGNAME]; struct msg m; - const char *line; + const char *line, *helo; uint64_t reqid, evpid; uint32_t code, msgid; int status, success, dnserror; @@ -320,6 +326,20 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) io_reload(&s->io); return; + case IMSG_LKA_HELO: + m_msg(&m, imsg); + m_get_id(&m, &reqid); + s = tree_xpop(&wait_lka_helo, reqid); + m_get_int(&m, &status); + if (status == LKA_OK) { + m_get_string(&m, &helo); + strlcpy(s->smtpname, helo, sizeof(s->smtpname)); + } + m_end(&m); + smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME); + io_reload(&s->io); + return; + case IMSG_MFA_SMTP_DATA: m_msg(&m, imsg); m_get_id(&m, &reqid); @@ -387,7 +407,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) ss_to_text(&s->ss)); } fprintf(s->ofile, "by %s (%s) with %sSMTP%s%s id %08x;\n", - s->listener->helo, + s->smtpname, SMTPD_NAME, s->flags & SF_EHLO ? "E" : "", s->flags & SF_SECURE ? "S" : "", @@ -635,8 +655,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code, tree_xset(&wait_ssl_init, s->id, s); return; } - smtp_reply(s, SMTPD_BANNER, s->listener->helo, SMTPD_NAME); - io_reload(&s->io); + smtp_send_banner(s); return; case IMSG_MFA_REQ_HELO: @@ -651,7 +670,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code, smtp_enter_state(s, STATE_HELO); smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you", (s->flags & SF_EHLO) ? '-' : ' ', - s->listener->helo, + s->smtpname, s->evp.helo, ss_to_text(&s->ss)); @@ -783,9 +802,8 @@ smtp_io(struct io *io, int evt) if (s->listener->flags & F_SMTPS) { stat_increment("smtp.smtps", 1); - smtp_reply(s, SMTPD_BANNER, s->listener->helo, - SMTPD_NAME); io_set_write(&s->io); + smtp_send_banner(s); } else { stat_increment("smtp.tls", 1); @@ -1081,7 +1099,7 @@ smtp_command(struct smtp_session *s, char *line) smtp_message_reset(s, 1); if (smtp_mailaddr(&s->evp.sender, args, 1, &args, - s->listener->helo) == 0) { + s->smtpname) == 0) { smtp_reply(s, "553 Sender address syntax error"); break; } @@ -1109,7 +1127,7 @@ smtp_command(struct smtp_session *s, char *line) } if (smtp_mailaddr(&s->evp.rcpt, args, 0, &args, - s->listener->helo) == 0) { + s->smtpname) == 0) { smtp_reply(s, "553 Recipient address syntax error"); break; @@ -1345,6 +1363,34 @@ smtp_connected(struct smtp_session *s) smtp_wait_mfa(s, IMSG_MFA_REQ_CONNECT); } +static void +smtp_send_banner(struct smtp_session *s) +{ + struct sockaddr_storage ss; + struct sockaddr *sa; + socklen_t sa_len; + + if (s->listener->hostnametable[0]) { + sa_len = sizeof(ss); + sa = (struct sockaddr *)&ss; + if (getsockname(s->io.sock, sa, &sa_len) == -1) { + log_warn("warn: getsockname()"); + } + else { + m_create(p_lka, IMSG_LKA_HELO, 0, 0, -1); + m_add_id(p_lka, s->id); + m_add_string(p_lka, s->listener->hostnametable); + m_add_sockaddr(p_lka, sa); + m_close(p_lka); + tree_xset(&wait_lka_helo, s->id, s); + return; + } + } + + smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME); + io_reload(&s->io); +} + void smtp_enter_state(struct smtp_session *s, int newstate) { diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5 index 33588caf..69b100c7 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. @@ -243,7 +301,7 @@ This parameter may use conversion specifiers that are expanded before use .Op Ic backup Op Ar mx .Op Ic as Ar address .Op Ic source Ar source -.Op Ic helo Ar names +.Op Ic hostnames Ar names .Xc Mail is relayed. The routing decision is based on the DNS system. @@ -277,7 +335,7 @@ If the parameter is specified, .Xr smtpd 8 will explicitly bind to an address found in the table referenced by -.Ar table +.Ar source when connecting to the relay. If the table contains more than one address, they are picked in turn each time a new connection is opened. @@ -286,7 +344,7 @@ By default, when connecting to a remote server, .Xr smtpd 8 advertises its default server name. A -.Ic helo +.Ic hostnames parameter may be specified to advertise an alternate hostname. Table .Ar names @@ -301,7 +359,7 @@ when connected to the remote server. .Op Ic auth Aq Ar auth .Op Ic as Ar address .Op Ic source Ar source -.Op Ic helo Ar names +.Op Ic hostnames Ar names .Xc Mail is relayed through the specified .Ar host @@ -380,7 +438,7 @@ By default, when connecting to a remote server, .Xr smtpd 8 advertises its default server name. A -.Ic helo +.Ic hostnames parameter may be specified to advertise an alternate hostname. Table .Ar names @@ -442,6 +500,7 @@ to MXs for this domain. .Op Ic auth | auth-optional .Op Ic tag Ar tag .Op Ic hostname Ar hostname +.Op Ic hostnames Ar names .Op Ic mask-source .Ek .Xc @@ -486,8 +545,9 @@ a key and Diffie-Hellman parameters .Ao Ar name Ac Ns .dh are searched for. -A certificate authority may be appended to the .crt -file to create a certificate chain. +A certificate chain, which may include an intermediate +and/or Certificate Authority certificate, may be appended to +the .crt file to create a certificate chain. If no .Ic certificate is specified, @@ -500,8 +560,12 @@ and .Pa fxp0.dh . If no DH parameters are provided, smtpd will use built-in parameters. +A Certificate Authority certificate can be placed in a .ca file +to have it available to OpenSMTPD. Creation of certificates is documented in -.Xr starttls 8 . +.Xr starttls 8 , +and creation of Diffie-Hellman parameters is documented in +.Xr openssl 1 . .Pp If the .Ic auth @@ -531,6 +595,16 @@ If the parameter is used, then it will be used in the greeting banner instead of the default server name. .Pp +The +.Ic hostnames +parameter allows to override the server name for specific addresses. +Table +.Ar names +contains a mapping of IP addresses to hostnames and +.Xr smtpd 8 +will use the host name that matches the address on which the connection arrives +if it is found in the mapping. +.Pp If the .Ic mask-source parameter is used, then the listener will skip the "from" part diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h index 7d24b9b6..c6602b12 100644 --- a/smtpd/smtpd.h +++ b/smtpd/smtpd.h @@ -364,10 +364,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; @@ -525,7 +531,8 @@ struct listener { void *ssl_ctx; char tag[MAX_TAG_SIZE]; char authtable[SMTPD_MAXLINESIZE]; - char helo[SMTPD_MAXHOSTNAMELEN]; + char hostname[SMTPD_MAXHOSTNAMELEN]; + char hostnametable[SMTPD_MAXPATHLEN]; TAILQ_ENTRY(listener) entry; }; @@ -537,19 +537,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); @@ -558,11 +565,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); |