There is currently no way to force TLS on a relay rule in general, and force certificate checking. Typical use case: a secondary MX needing to relay safely to lower preference MXs.
This diff below allows the "tls" option to be used alone, including on non-smarthost relay rules, to specify that the relay must be using TLS. The "no-verify" keyword becomes optional. Currently, the different cases are as follows: - action <name> relay Standard relaying, using smtp with opportunistic STARTTLS. When using TLS, certificates are not checked. - action <name> relay host <smarthost> Relay through smarthost, using TLS or not, depending on the protocol. When using TLS, certificates are checked. - action <name> relay host <smarthost> tls no-verify Same as above, but certificates are not checked. With the proposed change, we get: - action <name> relay Standard relaying, using smtp with opportunistic STARTTLS. When using TLS, certificates are not checked. - action <name> relay tls Standard relaying, using smtp with mandatory STARTTLS. Certificates are checked. - action <name> relay tls no-verify Same as above, but certificates are not checked. - action <name> relay host <smarthost> Relay through smarthost, using TLS or not, depending on the protocol. For "smtp+tls://" and "smtps://" certificates are checked. For "smtp://" (opportunistic TLS) certificates are not checked. - action <name> relay host <smarthost> tls Relay through smarthost with mandatory TLS. Certificates are checked. The "smtp://" protocol is updated to "smtp+tls://" internally. The "smtp+notls://" protocol is rejected, and no relaying happens. - action <name> relay host <smarthost> tls no-verify Same as above, but certificates are not checked. The differences with the currently allowed syntax are: 1) the "tls no-verify" option on smarthost relay actually forces TLS, 2) a relay with a "smtp://" smarthost and no "tls no-verify" does not require a valid certificate anymore. It is more constistent altogether, and in practice it should not be a problem because most smarthost configurations uses strict TLS. Now, for the secondary MX example, the rule would look like: action "do-backup" relay backup tls Comments? Eric. Index: mta.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/mta.c,v retrieving revision 1.225 diff -u -p -r1.225 mta.c --- mta.c 19 Sep 2018 05:31:12 -0000 1.225 +++ mta.c 21 Sep 2018 08:09:14 -0000 @@ -657,6 +657,23 @@ mta_handle_envelope(struct envelope *evp return; } + if (dispatcher->u.remote.tls_required) { + /* Reject relay if smtp+notls:// is requested */ + if (relayh.tls == RELAY_TLS_NO) { + log_warnx("warn: TLS required for action \"%s\"", + evp->dispatcher); + m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); + m_add_evpid(p_queue, evp->id); + m_add_string(p_queue, "TLS required for relaying"); + m_add_int(p_queue, ESC_OTHER_STATUS); + m_close(p_queue); + return; + } + /* Update smtp:// to smtp+tls:// */ + if (relayh.tls == RELAY_TLS_OPPORTUNISTIC) + relayh.tls = RELAY_TLS_STARTTLS; + } + relay = mta_relay(evp, &relayh); /* ignore if we don't know the limits yet */ if (relay->limits && @@ -1739,7 +1756,7 @@ mta_relay(struct envelope *e, struct rel if (!key.authlabel[0]) key.authlabel = NULL; - if (dispatcher->u.remote.smarthost && + if ((key.tls == RELAY_TLS_STARTTLS || key.tls == RELAY_TLS_SMTPS) && dispatcher->u.remote.tls_noverify == 0) key.flags |= RELAY_TLS_VERIFY; Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v retrieving revision 1.221 diff -u -p -r1.221 parse.y --- parse.y 7 Sep 2018 07:35:31 -0000 1.221 +++ parse.y 21 Sep 2018 08:09:14 -0000 @@ -739,17 +739,21 @@ HELO STRING { dispatcher->u.remote.smarthost = strdup(t->t_name); } -| TLS NO_VERIFY { - if (dispatcher->u.remote.smarthost == NULL) { - yyerror("tls no-verify may not be specified without host on a dispatcher"); +| TLS { + if (dispatcher->u.remote.tls_required == 1) { + yyerror("tls already specified for this dispatcher"); YYERROR; } - if (dispatcher->u.remote.tls_noverify == 1) { - yyerror("tls no-verify already specified for this dispatcher"); + dispatcher->u.remote.tls_required = 1; +} +| TLS NO_VERIFY { + if (dispatcher->u.remote.tls_required == 1) { + yyerror("tls already specified for this dispatcher"); YYERROR; } + dispatcher->u.remote.tls_required = 1; dispatcher->u.remote.tls_noverify = 1; } | AUTH tables { Index: smtpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.conf.5,v retrieving revision 1.204 diff -u -p -r1.204 smtpd.conf.5 --- smtpd.conf.5 10 Sep 2018 12:42:17 -0000 1.204 +++ smtpd.conf.5 21 Sep 2018 08:09:15 -0000 @@ -265,8 +265,13 @@ and .Dq smtps protocols for authentication. Server certificates for those protocols are verified by default. -.It Cm tls no-verify -Do not require a valid certificate for the specified host. +.It Cm tls Op no-verify +Require TLS to be used when relaying, using mandatory STARTTLS by default. +When used with a smarthost, the protocol must not be +.Dq smtp+notls:// . +If +.Op no-verify +is specified, do not require a valid certificate. .It Cm auth Pf < Ar table Ns > Use the mapping .Ar table Index: smtpd.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v retrieving revision 1.561 diff -u -p -r1.561 smtpd.h --- smtpd.h 19 Sep 2018 05:31:12 -0000 1.561 +++ smtpd.h 21 Sep 2018 08:09:15 -0000 @@ -1063,6 +1063,7 @@ struct dispatcher_remote { char *smarthost; char *auth; + int tls_required; int tls_noverify; int backup;