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.
+ +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 is available in Postfix 3.6 and later.
+ +Specify "info_log_address_format = internal" for backwards +
Specify "info_log_address_format = internal" for backwards compatibility.
Postfix uses the unquoted form internally, because an attacker diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index ec2370e96..f35557304 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -361,10 +361,17 @@ SMTP(8) SMTP(8) Available in Postfix 3.5 and later: - info_log_address_format (external) + info_log_address_format (external) The email address form that will be used in non-debug logging (info, warning, etc.). + Available in Postfix 3.6 and later: + + dnssec_probe (ns:.) + The DNS query type (default: "ns") and DNS query name (default: + ".") that Postfix may use to determine whether DNSSEC is avail- + able. + MIME PROCESSING CONTROLS Available in Postfix version 2.0 and later: diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 95ea21483..bc7c54edf 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -1902,6 +1902,47 @@ 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 +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 is available in Postfix 3.6 and later. .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 diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 6f72db307..ad111897a 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -356,6 +356,11 @@ Available in Postfix 3.5 and later: .IP "\fBinfo_log_address_format (external)\fR" The email address form that will be used in non\-debug logging (info, warning, etc.). +.PP +Available in Postfix 3.6 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 is available. .SH "MIME PROCESSING CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index b87e1eb80..556b4cde7 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -695,6 +695,8 @@ while (<>) { s;\bsmtp_per_record_deadline\b;$&;g; s;\bsmtp_send_dummy_mail_auth\b;$&;g; s;\bsmtp_balance_inet_protocols\b;$&;g; + s;\binfo_log_address_format\b;$&;g; + s;\bdnssec_probe\b;$&;g; s;\bsmtp_tls_connection_reuse\b;$&;g; s;\blmtp_tls_connection_reuse\b;$&;g; s;\bsmtpd_enforce_tls\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index d317f43de..768250b85 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -17842,3 +17842,50 @@ smtpd_sasl_mechanism_filter = /etc/postfix/smtpd_mechs
This feature is available in Postfix 3.6 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.
+ +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 is available in Postfix 3.6 and later.
diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in index aec91e0b5..795f9ba2a 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) @@ -346,6 +346,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 4182aceba..26a346279 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -256,7 +256,12 @@ extern int dns_get_h_errno(void); (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) @@ -321,6 +326,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 flags below */ +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 185f6baf6..61375e23c 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -177,6 +177,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 @@ -510,7 +516,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); /* @@ -557,6 +563,8 @@ static int dns_query(const char *name, int type, unsigned flags, dns_res_state.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 (DNS_GET_H_ERRNO(&dns_res_state) != 0) { if (why) vstring_sprintf(why, "Host or domain name not found. " @@ -610,13 +618,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; @@ -934,7 +937,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); @@ -1022,7 +1027,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", @@ -1059,6 +1064,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); } @@ -1068,6 +1077,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..cc1d5bcc2 --- /dev/null +++ b/postfix/src/dns/dns_sec.c @@ -0,0 +1,149 @@ +/*++ +/* NAME +/* dns_sec 3 +/* SUMMARY +/* DNSSEC validation availability +/* SYNOPSIS +/* #include