From: Wietse Venema
This feature is available in Postfix 2.8 and later.
+ + +The DNS query type (default: "ns") and DNS query name (default: +".") that Postfix may use to determine whether DNSSEC validation +is available. +
+ +Background: DNSSEC validation is needed for Postfix DANE support; +this ensures that Postfix receives TLSA records with secure TLS +server certificate info. When DNSSEC validation is unavailable, +mail deliveries using opportunistic DANE will not be protected +by server certificate info in TLSA records, and mail deliveries +using mandatory DANE will not be made at all.
+ +By default, a Postfix process will send a DNSSEC probe after +1) the process made a DNS query that requested DNSSEC validation, +2) the process did not receive a DNSSEC validated response to this +query or to an earlier query, and 3) the process did not already +send a DNSSEC probe.
+ +
When the DNSSEC probe has no response, or when the response is +not DNSSEC validated, Postfix logs a warning that DNSSEC validation +may be unavailable.
+ +Example:
+ ++warning: DNSSEC validation may be unavailable +warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated +warning: reason: dnssec_probe 'ns:.' received no response: Server failure ++ +
Possible reasons why DNSSEC validation may be unavailable:
+ +By default, the DNSSEC probe asks for the DNS root zone NS +records, because resolvers should always have that information +cached. If Postfix runs on a network where the DNS root zone is not +reachable, specify a different probe, or specify an empty dnssec_probe +value to disable the feature.
+ +This feature was backported from Postfix 3.6 to Postfix versions +3.5.9, 3.4.19, 3.3.16. 3.2.21.
+ +The TLS policy for MX hosts with "secure" TLSA records when the nexthop destination security level is dane, but the MX @@ -12024,6 +12084,12 @@ authentication succeeds, it will be logged only as "Trusted", not "Verified", because the MX host name could have been forged.
The default setting for Postfix ≥ 3.6 is "dane" with +"smtp_tls_security_level = dane", otherwise "may". This behavior +was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21. +With earlier +Postfix versions the default setting was always "dane".
+Though with "insecure" MX records an active attacker can compromise SMTP transport security by returning forged MX records, such attacks are "tamper-evident" since any forged MX hostnames diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 8e9e825c8..80c0c992e 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -310,6 +310,13 @@ SMTP(8) SMTP(8) IPv6 addresses, ensure that the Postfix SMTP client can try both address types before it runs into the smtp_mx_address_limit. + Available in Postfix 3.3.16 and later: + + dnssec_probe (ns:.) + The DNS query type (default: "ns") and DNS query name (default: + ".") that Postfix may use to determine whether DNSSEC validation + is available. + MIME PROCESSING CONTROLS Available in Postfix version 2.0 and later: diff --git a/postfix/makedefs b/postfix/makedefs index eacef30b3..a44700e2f 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -226,19 +226,6 @@ case $# in *) echo usage: $0 [system release] 1>&2; exit 1;; esac -case "$SYSTEM" in - Linux) - case "`PATH=/bin:/usr/bin ldd /bin/sh`" in - *-musl-*) - case "$CCARGS" in - *-DNO_DNSSEC*) ;; - *) echo Warning: libc-musl breaks DANE/TLSA security. 1>&2 - echo This build will not support DANE/TLSA. 1>&2 - CCARGS="$CCARGS -DNO_DNSSEC";; - esac;; - esac;; -esac - case "$SYSTEM.$RELEASE" in SCO_SV.3.2) SYSTYPE=SCO5 # Use the native compiler by default diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index cd513529c..4e1272f4e 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -1887,6 +1887,60 @@ The name of the \fBdnsblog\fR(8) service entry in master.cf. This service performs DNS white/blacklist lookups. .PP This feature is available in Postfix 2.8 and later. +.SH dnssec_probe (default: ns:.) +The DNS query type (default: "ns") and DNS query name (default: +".") that Postfix may use to determine whether DNSSEC validation +is available. +.PP +Background: DNSSEC validation is needed for Postfix DANE support; +this ensures that Postfix receives TLSA records with secure TLS +server certificate info. When DNSSEC validation is unavailable, +mail deliveries using \fIopportunistic\fR DANE will not be protected +by server certificate info in TLSA records, and mail deliveries +using \fImandatory\fR DANE will not be made at all. +.PP +By default, a Postfix process will send a DNSSEC probe after +1) the process made a DNS query that requested DNSSEC validation, +2) the process did not receive a DNSSEC validated response to this +query or to an earlier query, and 3) the process did not already +send a DNSSEC probe. +.PP +When the DNSSEC probe has no response, or when the response is +not DNSSEC validated, Postfix logs a warning that DNSSEC validation +may be unavailable. +.PP +Example: +.PP +.nf +.na +.ft C +warning: DNSSEC validation may be unavailable +warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated +warning: reason: dnssec_probe 'ns:.' received no response: Server failure +.fi +.ad +.ft R +.PP +Possible reasons why DNSSEC validation may be unavailable: +.IP \(bu +The local /etc/resolv.conf file specifies a DNS resolver that +does not validate DNSSEC signatures (that's +$queue_directory/etc/resolv.conf when a Postfix daemon runs in a +chroot jail). +.IP \(bu +The local system library does not pass on the "DNSSEC validated" +bit to Postfix, or Postfix does not know how to ask the library to +do that. +.br +.PP +By default, the DNSSEC probe asks for the DNS root zone NS +records, because resolvers should always have that information +cached. If Postfix runs on a network where the DNS root zone is not +reachable, specify a different probe, or specify an empty dnssec_probe +value to disable the feature. +.PP +This feature was backported from Postfix 3.6 to Postfix versions +3.5.9, 3.4.19, 3.3.16. 3.2.21. .SH dont_remove (default: 0) Don't remove queue files and save them to the "saved" mail queue. This is a debugging aid. To inspect the envelope information and @@ -7593,7 +7647,7 @@ ciphers on a per\-destination basis. This feature is available in Postfix 2.6 and later. With earlier Postfix releases only the smtp_tls_mandatory_ciphers parameter is implemented, and opportunistic TLS always uses "export" or better (i.e. all) ciphers. -.SH smtp_tls_dane_insecure_mx_policy (default: dane) +.SH smtp_tls_dane_insecure_mx_policy (default: see "postconf \-d" output) The TLS policy for MX hosts with "secure" TLSA records when the nexthop destination security level is \fBdane\fR, but the MX record was found via an "insecure" MX lookup. The choices are: @@ -7614,6 +7668,12 @@ authentication succeeds, it will be logged only as "Trusted", not "Verified", because the MX host name could have been forged. .br .br +The default setting for Postfix >= 3.6 is "dane" with +"smtp_tls_security_level = dane", otherwise "may". This behavior +was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21. +With earlier +Postfix versions the default setting was always "dane". +.PP Though with "insecure" MX records an active attacker can compromise SMTP transport security by returning forged MX records, such attacks are "tamper\-evident" since any forged MX hostnames diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 0d810e5ad..9a49d3db0 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -291,6 +291,12 @@ Available in Postfix version 3.3 and later: When a remote destination resolves to a combination of IPv4 and IPv6 addresses, ensure that the Postfix SMTP client can try both address types before it runs into the smtp_mx_address_limit. +.PP +Available in Postfix 3.3.16 and later: +.IP "\fBdnssec_probe (ns:.)\fR" +The DNS query type (default: "ns") and DNS query name (default: +".") that Postfix may use to determine whether DNSSEC validation +is available. .SH "MIME PROCESSING CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 86c6e3740..047002cd2 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -690,6 +690,7 @@ while (<>) { s;\bsmtp_per_record_deadline\b;$&;g; s;\bsmtp_send_dummy_mail_auth\b;$&;g; s;\bsmtp_balance_inet_protocols\b;$&;g; + s;\bdnssec_probe\b;$&;g; s;\bsmtpd_enforce_tls\b;$&;g; s;\bsmtpd_sasl_tls_security_options\b;$&;g; s;\bsmtpd_sasl_type\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index d80af287f..04ea741f1 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -16672,7 +16672,7 @@ clients).
This feature is available in Postfix 3.1 and later. -%PARAM smtp_tls_dane_insecure_mx_policy dane +%PARAM smtp_tls_dane_insecure_mx_policy see "postconf -d" outputThe TLS policy for MX hosts with "secure" TLSA records when the nexthop destination security level is dane, but the MX @@ -16696,6 +16696,12 @@ authentication succeeds, it will be logged only as "Trusted", not "Verified", because the MX host name could have been forged. +
The default setting for Postfix ≥ 3.6 is "dane" with +"smtp_tls_security_level = dane", otherwise "may". This behavior +was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21. +With earlier +Postfix versions the default setting was always "dane".
+Though with "insecure" MX records an active attacker can compromise SMTP transport security by returning forged MX records, such attacks are "tamper-evident" since any forged MX hostnames @@ -16849,3 +16855,59 @@ environment variable, or from the UNIX password file.
This feature is available in Postfix 3.3 and later.
+ +%PARAM dnssec_probe ns:. + +The DNS query type (default: "ns") and DNS query name (default: +".") that Postfix may use to determine whether DNSSEC validation +is available. +
+ +Background: DNSSEC validation is needed for Postfix DANE support; +this ensures that Postfix receives TLSA records with secure TLS +server certificate info. When DNSSEC validation is unavailable, +mail deliveries using opportunistic DANE will not be protected +by server certificate info in TLSA records, and mail deliveries +using mandatory DANE will not be made at all.
+ +By default, a Postfix process will send a DNSSEC probe after +1) the process made a DNS query that requested DNSSEC validation, +2) the process did not receive a DNSSEC validated response to this +query or to an earlier query, and 3) the process did not already +send a DNSSEC probe.
+ +
When the DNSSEC probe has no response, or when the response is +not DNSSEC validated, Postfix logs a warning that DNSSEC validation +may be unavailable.
+ +Example:
+ ++warning: DNSSEC validation may be unavailable +warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated +warning: reason: dnssec_probe 'ns:.' received no response: Server failure ++ +
Possible reasons why DNSSEC validation may be unavailable:
+ +By default, the DNSSEC probe asks for the DNS root zone NS +records, because resolvers should always have that information +cached. If Postfix runs on a network where the DNS root zone is not +reachable, specify a different probe, or specify an empty dnssec_probe +value to disable the feature.
+ +This feature was backported from Postfix 3.6 to Postfix versions +3.5.9, 3.4.19, 3.3.16. 3.2.21.
diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in index 1913bc3f0..0af33f4ab 100644 --- a/postfix/src/dns/Makefile.in +++ b/postfix/src/dns/Makefile.in @@ -1,10 +1,10 @@ SHELL = /bin/sh SRCS = dns_lookup.c dns_rr.c dns_strerror.c dns_strtype.c dns_rr_to_pa.c \ dns_sa_to_rr.c dns_rr_eq_sa.c dns_rr_to_sa.c dns_strrecord.c \ - dns_rr_filter.c dns_str_resflags.c + dns_rr_filter.c dns_str_resflags.c dns_sec.c OBJS = dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o dns_rr_to_pa.o \ dns_sa_to_rr.o dns_rr_eq_sa.o dns_rr_to_sa.o dns_strrecord.o \ - dns_rr_filter.o dns_str_resflags.o + dns_rr_filter.o dns_str_resflags.o dns_sec.o HDRS = dns.h TESTSRC = test_dns_lookup.c test_alias_token.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -76,7 +76,7 @@ update: $(LIB_DIR)/$(LIB) $(HDRS) done cd $(INC_DIR); chmod 644 $(HDRS) -test_dns_lookup: test_dns_lookup.c $(LIB) $(LIBS) +test_dns_lookup: test_dns_lookup.c all $(LIB) $(LIBS) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) dns_rr_to_pa: $(LIB) $(LIBS) @@ -354,6 +354,18 @@ dns_sa_to_rr.o: ../../include/vbuf.h dns_sa_to_rr.o: ../../include/vstring.h dns_sa_to_rr.o: dns.h dns_sa_to_rr.o: dns_sa_to_rr.c +dns_sec.o: ../../include/check_arg.h +dns_sec.o: ../../include/mail_params.h +dns_sec.o: ../../include/msg.h +dns_sec.o: ../../include/myaddrinfo.h +dns_sec.o: ../../include/mymalloc.h +dns_sec.o: ../../include/sock_addr.h +dns_sec.o: ../../include/split_at.h +dns_sec.o: ../../include/sys_defs.h +dns_sec.o: ../../include/vbuf.h +dns_sec.o: ../../include/vstring.h +dns_sec.o: dns.h +dns_sec.o: dns_sec.c dns_str_resflags.o: ../../include/check_arg.h dns_str_resflags.o: ../../include/myaddrinfo.h dns_str_resflags.o: ../../include/name_mask.h diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index b8c4c4aa5..3631d2318 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -244,7 +244,12 @@ extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *, (lflags), (ltype)) /* - * Request flags. + * The dns_lookup() rflag that requests DNSSEC validation. + */ +#define DNS_WANT_DNSSEC_VALIDATION(rflags) ((rflags) & RES_USE_DNSSEC) + + /* + * lflags. */ #define DNS_REQ_FLAG_STOP_OK (1<<0) #define DNS_REQ_FLAG_STOP_INVAL (1<<1) @@ -309,6 +314,18 @@ extern int dns_rr_filter_execute(DNS_RR **); */ const char *dns_str_resflags(unsigned long); + /* + * dns_sec.c. + */ +#define DNS_SEC_FLAG_AVAILABLE (1<<0) /* got some DNSSEC validated reply */ +#define DNS_SEC_FLAG_DONT_PROBE (1<<1) /* probe already sent, or disabled */ + +#define DNS_SEC_STATS_SET(flags) (dns_sec_stats |= (flags)) +#define DNS_SEC_STATS_TEST(flags) (dns_sec_stats & (flags)) + +extern int dns_sec_stats; /* See DNS_SEC_FLAG_XXX above */ +extern void dns_sec_probe(int); + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 2ae64836a..68e243ba0 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -169,6 +169,12 @@ /* Pointer to storage for the reply RCODE value. This gives /* more detailed information than DNS_FAIL, DNS_RETRY, etc. /* DIAGNOSTICS +/* If DNSSEC validation is requested but the response is not +/* DNSSEC validated, dns_lookup() will send a one-time probe +/* query as configured with the \fBdnssec_probe\fR configuration +/* parameter, and will log a warning when the probe response +/* was not DNSSEC validated. +/* .PP /* dns_lookup() returns one of the following codes and sets the /* \fIwhy\fR argument accordingly: /* .IP DNS_OK @@ -458,7 +464,7 @@ static int dns_query(const char *name, int type, unsigned flags, */ #define XTRA_FLAGS (RES_USE_EDNS0 | RES_TRUSTAD) - if (flags & RES_USE_DNSSEC) + if (DNS_WANT_DNSSEC_VALIDATION(flags)) flags |= (RES_USE_EDNS0 | RES_TRUSTAD); /* @@ -487,6 +493,8 @@ static int dns_query(const char *name, int type, unsigned flags, _res.options |= saved_options; reply_header = (HEADER *) reply->buf; reply->rcode = reply_header->rcode; + if ((reply->dnssec_ad = !!reply_header->ad) != 0) + DNS_SEC_STATS_SET(DNS_SEC_FLAG_AVAILABLE); if (h_errno != 0) { if (why) vstring_sprintf(why, "Host or domain name not found. " @@ -538,13 +546,8 @@ static int dns_query(const char *name, int type, unsigned flags, /* * Initialize the reply structure. Some structure members are filled on - * the fly while the reply is being parsed. Coerce AD bit to boolean. + * the fly while the reply is being parsed. */ -#if RES_USE_DNSSEC != 0 - reply->dnssec_ad = (flags & RES_USE_DNSSEC) ? !!reply_header->ad : 0; -#else - reply->dnssec_ad = 0; -#endif SET_HAVE_DNS_REPLY_PACKET(reply, len); reply->query_start = reply->buf + sizeof(HEADER); reply->answer_start = 0; @@ -862,7 +865,9 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, CORRUPT(DNS_RETRY); if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK) CORRUPT(status); - if (!valid_rr_name(rr_name, "resource name", fixed.type, reply)) + if (strcmp(orig_name, ".") == 0 && *rr_name == 0) + /* Allow empty response name for root queries. */ ; + else if (!valid_rr_name(rr_name, "resource name", fixed.type, reply)) CORRUPT(DNS_INVAL); if (fqdn) vstring_strcpy(fqdn, rr_name); @@ -950,7 +955,7 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags, /* * The Linux resolver misbehaves when given an invalid domain name. */ - if (!valid_hostname(name, DONT_GRIPE)) { + if (strcmp(name, ".") && !valid_hostname(name, DONT_GRIPE)) { if (why) vstring_sprintf(why, "Name service error for %s: invalid host or domain name", @@ -987,6 +992,10 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags, (void) dns_get_answer(orig_name, &reply, T_SOA, rrlist, fqdn, cname, c_len, &maybe_secure); } + if (DNS_WANT_DNSSEC_VALIDATION(flags) + && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \ + DNS_SEC_FLAG_DONT_PROBE)) + dns_sec_probe(flags); /* XXX Clobbers 'reply' */ return (status); } @@ -996,6 +1005,10 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags, */ status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn, cname, c_len, &maybe_secure); + if (DNS_WANT_DNSSEC_VALIDATION(flags) + && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \ + DNS_SEC_FLAG_DONT_PROBE)) + dns_sec_probe(flags); /* XXX Clobbers 'reply' */ switch (status) { default: if (why) diff --git a/postfix/src/dns/dns_sec.c b/postfix/src/dns/dns_sec.c new file mode 100644 index 000000000..849627e4b --- /dev/null +++ b/postfix/src/dns/dns_sec.c @@ -0,0 +1,144 @@ +/*++ +/* NAME +/* dns_sec 3 +/* SUMMARY +/* DNSSEC validation availability +/* SYNOPSIS +/* #include