X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fdns.c;h=a2f4309937b92dfd922034c7774899b04100f0f8;hb=7eb6c37c5084760c1d1469bd4be652b479a8df55;hp=d5214901d11683a822c8c1801f71997c6527dd5d;hpb=eae0036b2dfac1547351908f77a6154b898c45d6;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/dns.c b/src/src/dns.c index d5214901d..a2f430993 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -10,16 +10,6 @@ #include "exim.h" -/* Function declaration needed for mutual recursion when A6 records -are supported. */ - -#if HAVE_IPV6 -#ifdef SUPPORT_A6 -static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *, - int, uschar *); -#endif -#endif - /************************************************* * Fake DNS resolver * @@ -46,7 +36,7 @@ Returns: length of returned data, or -1 on error (h_errno set) */ static int -fakens_search(uschar *domain, int type, uschar *answerptr, int size) +fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ @@ -159,12 +149,13 @@ the first time we have been here, and set the resolver options. Arguments: qualify_single TRUE to set the RES_DEFNAMES option search_parents TRUE to set the RES_DNSRCH option + use_dnssec TRUE to set the RES_USE_DNSSEC option Returns: nothing */ void -dns_init(BOOL qualify_single, BOOL search_parents) +dns_init(BOOL qualify_single, BOOL search_parents, BOOL use_dnssec) { res_state resp = os_get_dns_resolver_res(); @@ -201,6 +192,41 @@ if (dns_use_edns0 >= 0) dns_use_edns0 ? "" : "un"); #endif +#ifndef DISABLE_DNSSEC +# ifdef RES_USE_DNSSEC +# ifndef RES_USE_EDNS0 +# error Have RES_USE_DNSSEC but not RES_USE_EDNS0? Something hinky ... +# endif +if (use_dnssec) + resp->options |= RES_USE_DNSSEC; +if (dns_dnssec_ok >= 0) + { + if (dns_use_edns0 == 0 && dns_dnssec_ok != 0) + { + DEBUG(D_resolver) + debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); + } + else + { + if (dns_dnssec_ok) + resp->options |= RES_USE_DNSSEC; + else + resp->options &= ~RES_USE_DNSSEC; + DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n", + dns_dnssec_ok ? "on" : "off"); + } + } +# else +if (dns_dnssec_ok >= 0) + DEBUG(D_resolver) + debug_printf("Unable to %sset DNSSEC without resolver support.\n", + dns_dnssec_ok ? "" : "un"); +if (use_dnssec) + DEBUG(D_resolver) + debug_printf("Unable to set DNSSEC without resolver support.\n"); +# endif +#endif /* DISABLE_DNSSEC */ + os_put_dns_resolver_res(resp); } @@ -221,9 +247,9 @@ Returns: nothing */ void -dns_build_reverse(uschar *string, uschar *buffer) +dns_build_reverse(const uschar *string, uschar *buffer) { -uschar *p = string + Ustrlen(string); +const uschar *p = string + Ustrlen(string); uschar *pp = buffer; /* Handle IPv4 address */ @@ -235,7 +261,7 @@ if (Ustrchr(string, ':') == NULL) int i; for (i = 0; i < 4; i++) { - uschar *ppp = p; + const uschar *ppp = p; while (ppp > string && ppp[-1] != '.') ppp--; Ustrncpy(pp, ppp, p - ppp); pp += p - ppp; @@ -394,6 +420,41 @@ return &(dnss->srr); +/************************************************* +* Return whether AD bit set in DNS result * +*************************************************/ + +/* We do not perform DNSSEC work ourselves; if the administrator has installed +a verifying resolver which sets AD as appropriate, though, we'll use that. +(AD = Authentic Data) + +Argument: pointer to dns answer block +Returns: bool indicating presence of AD bit +*/ + +BOOL +dns_is_secure(const dns_answer * dnsa) +{ +#ifdef DISABLE_DNSSEC +DEBUG(D_dns) + debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); +return FALSE; +#else +HEADER *h = (HEADER *)dnsa->answer; +return h->ad ? TRUE : FALSE; +#endif +} + +static void +dns_set_insecure(dns_answer * dnsa) +{ +HEADER * h = (HEADER *)dnsa->answer; +h->ad = 0; +} + + + + /************************************************* * Turn DNS type into text * *************************************************/ @@ -421,6 +482,7 @@ switch(t) case T_SRV: return US"SRV"; case T_NS: return US"NS"; case T_CNAME: return US"CNAME"; + case T_TLSA: return US"TLSA"; default: return US"?"; } } @@ -445,7 +507,7 @@ Returns: the return code */ static int -dns_return(uschar *name, int type, int rc) +dns_return(const uschar * name, int type, int rc) { res_state resp = os_get_dns_resolver_res(); tree_node *node = store_get_perm(sizeof(tree_node) + 290); @@ -484,11 +546,11 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) +dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) { #ifndef STAND_ALONE int rc = -1; -uschar *save; +const uschar *save_domain; #endif res_state resp = os_get_dns_resolver_res(); @@ -532,23 +594,21 @@ For SRV records, we omit the initial _smtp._tcp. components at the start. */ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) { - uschar *checkname = name; + const uschar *checkname = name; int ovector[3*(EXPAND_MAXN+1)]; - if (regex_check_dns_names == NULL) - regex_check_dns_names = - regex_must_compile(check_dns_names_pattern, FALSE, TRUE); + dns_pattern_init(); /* For an SRV lookup, skip over the first two components (the service and protocol names, which both start with an underscore). */ - if (type == T_SRV) + if (type == T_SRV || type == T_TLSA) { while (*checkname++ != '.'); while (*checkname++ != '.'); } - if (pcre_exec(regex_check_dns_names, NULL, CS checkname, Ustrlen(checkname), + if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) { DEBUG(D_dns) @@ -570,22 +630,16 @@ the IP address instead of returning -1 with h_error=HOST_NOT_FOUND. Some nameservers are also believed to do this. It is, of course, contrary to the specification of the DNS, so we lock it out. */ -if (( - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_A || type == T_AAAA) && - string_is_ip_address(name, NULL) != 0) +if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) return DNS_NOMATCH; /* If we are running in the test harness, instead of calling the normal resolver (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ -if (running_in_test_harness) - dnsa->answerlen = fakens_search(name, type, dnsa->answer, MAXPACKET); -else - dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET); +dnsa->answerlen = running_in_test_harness + ? fakens_search(name, type, dnsa->answer, MAXPACKET) + : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); if (dnsa->answerlen > MAXPACKET) { @@ -606,12 +660,12 @@ if (dnsa->answerlen < 0) switch (h_errno) name, dns_text_type(type)); /* Cut this out for various test programs */ - #ifndef STAND_ALONE - save = deliver_domain; - deliver_domain = name; /* set $domain */ - rc = match_isinlist(name, &dns_again_means_nonexist, 0, NULL, NULL, +#ifndef STAND_ALONE + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); - deliver_domain = save; + deliver_domain = save_domain; if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); @@ -621,9 +675,9 @@ if (dnsa->answerlen < 0) switch (h_errno) "DNS_NOMATCH\n", name); return dns_return(name, type, DNS_NOMATCH); - #else /* For stand-alone tests */ +#else /* For stand-alone tests */ return dns_return(name, type, DNS_AGAIN); - #endif +#endif case NO_RECOVERY: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" @@ -684,10 +738,12 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) +dns_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { int i; -uschar *orig_name = name; +const uschar *orig_name = name; +BOOL secure_so_far = TRUE; /* Loop to follow CNAME chains so far, but no further... */ @@ -742,7 +798,12 @@ for (i = 0; i < 10; i++) /* If any data records of the correct type were found, we are done. */ - if (type_rr.data != NULL) return DNS_SUCCEED; + if (type_rr.data != NULL) + { + if (!secure_so_far) /* mark insecure if any element of CNAME chain was */ + dns_set_insecure(dnsa); + return DNS_SUCCEED; + } /* If there are no data records, we need to re-scan the DNS using the domain given in the CNAME record, which should exist (otherwise we should @@ -751,10 +812,13 @@ for (i = 0; i < 10; i++) if (cname_rr.data == NULL) return DNS_FAIL; datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data)); if (datalen < 0) return DNS_FAIL; name = data; + if (!dns_is_secure(dnsa)) + secure_so_far = FALSE; + DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ @@ -793,8 +857,8 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_special_lookup(dns_answer *dnsa, uschar *name, int type, - uschar **fully_qualified_name) +dns_special_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); @@ -807,7 +871,7 @@ root servers. */ if (type == T_ZNS) { - uschar *d = name; + const uschar *d = name; while (d != 0) { int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name); @@ -840,7 +904,7 @@ if (type == T_CSA) rc = dns_lookup(dnsa, srvname, T_SRV, NULL); if (rc == DNS_SUCCEED || rc == DNS_AGAIN) { - if (rc == DNS_SUCCEED) *fully_qualified_name = name; + if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); return rc; } @@ -948,7 +1012,7 @@ if (type == T_CSA) /* Extract the numerical SRV fields (p is incremented) */ p = rr->data; GETSHORT(priority, p); - GETSHORT(weight, p); + GETSHORT(weight, p); weight = weight; /* compiler quietening */ GETSHORT(port, p); /* Check the CSA version number */ @@ -972,164 +1036,6 @@ return DNS_FAIL; -/* Support for A6 records has been commented out since they were demoted to -experimental status at IETF 51. */ - -#if HAVE_IPV6 && defined(SUPPORT_A6) - -/************************************************* -* Search DNS block for prefix RRs * -*************************************************/ - -/* Called from dns_complete_a6() to search an additional section or a main -answer section for required prefix records to complete an IPv6 address obtained -from an A6 record. For each prefix record, a recursive call to dns_complete_a6 -is made, with a new copy of the address so far. - -Arguments: - dnsa the DNS answer block - which RESET_ADDITIONAL or RESET_ANSWERS - name name of prefix record - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - bits number of bits we have already got - bitvec the bits we have already got - -Returns: TRUE if any records were found -*/ - -static BOOL -dns_find_prefix(dns_answer *dnsa, int which, uschar *name, dns_address - ***yptrptr, int bits, uschar *bitvec) -{ -BOOL yield = FALSE; -dns_record *rr; -dns_scan dnss; - -for (rr = dns_next_rr(dnsa, &dnss, which); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - { - uschar cbitvec[16]; - if (rr->type != T_A6 || strcmpic(rr->name, name) != 0) continue; - yield = TRUE; - memcpy(cbitvec, bitvec, sizeof(cbitvec)); - dns_complete_a6(yptrptr, dnsa, rr, bits, cbitvec); - } - -return yield; -} - - - -/************************************************* -* Follow chains of A6 records * -*************************************************/ - -/* A6 records may be incomplete, with pointers to other records containing more -bits of the address. There can be a tree structure, leading to a number of -addresses originating from a single initial A6 record. - -Arguments: - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - dnsa the current DNS answer block - rr the RR we have at present - bits number of bits we have already got - bitvec the bits we have already got - -Returns: nothing -*/ - -static void -dns_complete_a6(dns_address ***yptrptr, dns_answer *dnsa, dns_record *rr, - int bits, uschar *bitvec) -{ -static uschar bitmask[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; -uschar *p = (uschar *)(rr->data); -int prefix_len, suffix_len; -int i, j, k; -uschar *chainptr; -uschar chain[264]; -dns_answer cdnsa; - -/* The prefix length is the first byte. It defines the prefix which is missing -from the data in this record as a number of bits. Zero means this is the end of -a chain. The suffix is the data in this record; only sufficient bytes to hold -it are supplied. There may be zero bytes. We have to ignore trailing bits that -we have already obtained from earlier RRs in the chain. */ - -prefix_len = *p++; /* bits */ -suffix_len = (128 - prefix_len + 7)/8; /* bytes */ - -/* If the prefix in this record is greater than the prefix in the previous -record in the chain, we have to ignore the record (RFC 2874). */ - -if (prefix_len > 128 - bits) return; - -/* In this little loop, the number of bits up to and including the current byte -is held in k. If we have none of the bits in this byte, we can just or it into -the current data. If we have all of the bits in this byte, we skip it. -Otherwise, some masking has to be done. */ - -for (i = suffix_len - 1, j = 15, k = 8; i >= 0; i--) - { - int required = k - bits; - if (required >= 8) bitvec[j] |= p[i]; - else if (required > 0) bitvec[j] |= p[i] & bitmask[required]; - j--; /* I tried putting these in the "for" statement, but gcc muttered */ - k += 8; /* about computed values not being used. */ - } - -/* If the prefix_length is zero, we are at the end of a chain. Build a -dns_address item with the current data, hang it onto the end of the chain, -adjust the hanging pointer, and we are done. */ - -if (prefix_len == 0) - { - dns_address *new = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, bitvec, CS new->address, 50); - new->next = NULL; - **yptrptr = new; - *yptrptr = &(new->next); - return; - } - -/* Prefix length is not zero. Reset the number of bits that we have collected -so far, and extract the chain name. */ - -bits = 128 - prefix_len; -p += suffix_len; - -chainptr = chain; -while ((i = *p++) != 0) - { - if (chainptr != chain) *chainptr++ = '.'; - memcpy(chainptr, p, i); - chainptr += i; - p += i; - } -*chainptr = 0; -chainptr = chain; - -/* Now scan the current DNS response record to see if the additional section -contains the records we want. This processing can be cut out for testing -purposes. */ - -if (dns_find_prefix(dnsa, RESET_ADDITIONAL, chainptr, yptrptr, bits, bitvec)) - return; - -/* No chain records were found in the current DNS response block. Do a new DNS -lookup to try to find these records. This opens up the possibility of DNS -failures. We ignore them at this point; if all branches of the tree fail, there -will be no addresses at the end. */ - -if (dns_lookup(&cdnsa, chainptr, T_A6, NULL) == DNS_SUCCEED) - (void)dns_find_prefix(&cdnsa, RESET_ANSWERS, chainptr, yptrptr, bits, bitvec); -} -#endif /* HAVE_IPV6 && defined(SUPPORT_A6) */ - - /************************************************* @@ -1154,12 +1060,7 @@ dns_address_from_rr(dns_answer *dnsa, dns_record *rr) { dns_address *yield = NULL; -#if HAVE_IPV6 && defined(SUPPORT_A6) -dns_address **yieldptr = &yield; -uschar bitvec[16]; -#else dnsa = dnsa; /* Stop picky compilers warning */ -#endif if (rr->type == T_A) { @@ -1171,14 +1072,6 @@ if (rr->type == T_A) #if HAVE_IPV6 -#ifdef SUPPORT_A6 -else if (rr->type == T_A6) - { - memset(bitvec, 0, sizeof(bitvec)); - dns_complete_a6(&yieldptr, dnsa, rr, 0, bitvec); - } -#endif /* SUPPORT_A6 */ - else { yield = store_get(sizeof(dns_address) + 50); @@ -1190,4 +1083,16 @@ else return yield; } + + +void +dns_pattern_init(void) +{ +if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names) + regex_check_dns_names = + regex_must_compile(check_dns_names_pattern, FALSE, TRUE); +} + +/* vi: aw ai sw=2 +*/ /* End of dns.c */