X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fhost.c;h=00524f41631db22ebf98bde88f75fe37dab4ab56;hp=73f359348e34bfcac729960845119fd322651fa3;hb=071c51f70266916a7be153ce67c0045beb58b841;hpb=0a49a7a4f1090b6f1ce1d0f9d969804c9226b53e;ds=sidebyside diff --git a/src/src/host.c b/src/src/host.c index 73f359348..00524f416 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/host.c,v 1.31 2009/11/16 19:50:37 nm4 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or @@ -70,7 +68,7 @@ sprintf(addr, "%d.%d.%d.%d", very good for the uses to which it is put. When running the regression tests, start with a fixed seed. -If you need better, see pseudo_random_number() which is potentially stronger, +If you need better, see vaguely_random_number() which is potentially stronger, if a crypto library is available, but might end up just calling this instead. Arguments: @@ -222,6 +220,8 @@ else int rc = dns_lookup(&dnsa, lname, type, NULL); int count = 0; + lookup_dnssec_authenticated = NULL; + switch(rc) { case DNS_SUCCEED: break; @@ -1179,19 +1179,15 @@ host_is_tls_on_connect_port(int port) { int sep = 0; uschar buffer[32]; -uschar *list = tls_on_connect_ports; +uschar *list = tls_in.on_connect_ports; uschar *s; +uschar *end; -if (tls_on_connect) return TRUE; +if (tls_in.on_connect) return TRUE; -while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) - { - uschar *end; - int lport = Ustrtol(s, &end, 10); - if (*end != 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "tls_on_connect_ports " - "contains \"%s\", which is not a port number: exim abandoned", s); - if (lport == port) return TRUE; - } +while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + if (Ustrtol(s, &end, 10) == port) + return TRUE; return FALSE; } @@ -1599,7 +1595,7 @@ dns_record *rr; dns_answer dnsa; dns_scan dnss; -host_lookup_deferred = host_lookup_failed = FALSE; +sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE; HDEBUG(D_host_lookup) debug_printf("looking up host name for %s\n", sender_host_address); @@ -1624,7 +1620,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) { if (strcmpic(ordername, US"bydns") == 0) { - dns_init(FALSE, FALSE); + dns_init(FALSE, FALSE, FALSE); /* dnssec ctrl by dns_dnssec_ok glbl */ dns_build_reverse(sender_host_address, buffer); rc = dns_lookup(&dnsa, buffer, T_PTR, NULL); @@ -1641,6 +1637,13 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) int count = 0; int old_pool = store_pool; + /* Ideally we'd check DNSSEC both forward and reverse, but we use the + gethost* routines for forward, so can't do that unless/until we rewrite. */ + sender_host_dnssec = dns_is_secure(&dnsa); + DEBUG(D_dns) + debug_printf("Reverse DNS security status: %s\n", + sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified"); + store_pool = POOL_PERM; /* Save names in permanent storage */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); @@ -1914,7 +1917,8 @@ if (running_in_test_harness) some circumstances when the get..byname() function actually calls the DNS. */ dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0, - (flags & HOST_FIND_SEARCH_PARENTS) != 0); + (flags & HOST_FIND_SEARCH_PARENTS) != 0, + FALSE); /*XXX dnssec? */ /* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both kinds of address, so go round the loop twice. Note that we have ensured that @@ -2057,6 +2061,7 @@ for (i = 1; i <= times; host->port = PORT_NONE; host->status = hstatus_unknown; host->why = hwhy_unknown; + host->dnssec = DS_UNK; last = host; } @@ -2072,6 +2077,7 @@ for (i = 1; i <= times; next->port = PORT_NONE; next->status = hstatus_unknown; next->why = hwhy_unknown; + next->dnssec = DS_UNK; next->last_try = 0; next->next = last->next; last->next = next; @@ -2190,6 +2196,7 @@ Arguments: fully_qualified_name if not NULL, return fully qualified name here if the contents are different (i.e. it must be preset to something) + dnnssec_require if TRUE check the DNS result AD bit Returns: HOST_FIND_FAILED couldn't find A record HOST_FIND_AGAIN try again later @@ -2199,7 +2206,8 @@ Returns: HOST_FIND_FAILED couldn't find A record static int set_address_from_dns(host_item *host, host_item **lastptr, - uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name) + uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name, + BOOL dnssec_requested, BOOL dnssec_require) { dns_record *rr; host_item *thishostlast = NULL; /* Indicates not yet filled in anything */ @@ -2260,6 +2268,8 @@ for (; i >= 0; i--) dns_scan dnss; int rc = dns_lookup(&dnsa, host->name, type, fully_qualified_name); + lookup_dnssec_authenticated = !dnssec_requested ? NULL + : dns_is_secure(&dnsa) ? US"yes" : US"no"; /* We want to return HOST_FIND_AGAIN if one of the A, A6, or AAAA lookups fails or times out, but not if another one succeeds. (In the early @@ -2282,6 +2292,12 @@ for (; i >= 0; i--) if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE; continue; } + if (dnssec_require && !dns_is_secure(&dnsa)) + { + log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on %s for %.256s", + i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name); + continue; + } /* Lookup succeeded: fill in the given host item with the first non-ignored address found; create additional items for any others. A single A6 record @@ -2428,6 +2444,8 @@ Arguments: srv_service when SRV used, the service name srv_fail_domains DNS errors for these domains => assume nonexist mx_fail_domains DNS errors for these domains => assume nonexist + dnssec_request_domains => make dnssec request + dnssec_require_domains => ditto and nonexist failures fully_qualified_name if not NULL, return fully-qualified name removed set TRUE if local host was removed from the list @@ -2445,6 +2463,7 @@ Returns: HOST_FIND_FAILED Failed to find the host or domain; int host_find_bydns(host_item *host, uschar *ignore_target_hosts, int whichrrs, uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains, + uschar *dnssec_request_domains, uschar *dnssec_require_domains, uschar **fully_qualified_name, BOOL *removed) { host_item *h, *last; @@ -2454,6 +2473,12 @@ int ind_type = 0; int yield; dns_answer dnsa; dns_scan dnss; +BOOL dnssec_require = match_isinlist(host->name, &dnssec_require_domains, + 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; +BOOL dnssec_request = dnssec_require + || match_isinlist(host->name, &dnssec_request_domains, + 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; +dnssec_status_t dnssec; /* Set the default fully qualified name to the incoming name, initialize the resolver if necessary, set up the relevant options, and initialize the flag @@ -2461,7 +2486,9 @@ that gets set for DNS syntax check errors. */ if (fully_qualified_name != NULL) *fully_qualified_name = host->name; dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0, - (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0); + (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0, + dnssec_request + ); host_find_failed_syntax = FALSE; /* First, if requested, look for SRV records. The service name is given; we @@ -2482,20 +2509,37 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0) the input name, pass back the new original domain, without the prepended magic. */ + dnssec = DS_UNK; + lookup_dnssec_authenticated = NULL; rc = dns_lookup(&dnsa, buffer, ind_type, &temp_fully_qualified_name); + + if (dnssec_request) + { + if (dns_is_secure(&dnsa)) + { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } + else + { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } + } + if (temp_fully_qualified_name != buffer && fully_qualified_name != NULL) *fully_qualified_name = temp_fully_qualified_name + prefix_length; /* On DNS failures, we give the "try again" error unless the domain is listed as one for which we continue. */ + if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(&dnsa)) + { + log_write(L_host_lookup_failed, LOG_MAIN, + "dnssec fail on SRV for %.256s", host->name); + rc = DNS_FAIL; + } if (rc == DNS_FAIL || rc == DNS_AGAIN) { #ifndef STAND_ALONE if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) != OK) #endif - return HOST_FIND_AGAIN; + { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " "(domain in srv_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); } @@ -2511,17 +2555,41 @@ listed as one for which we continue. */ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0) { ind_type = T_MX; + dnssec = DS_UNK; + lookup_dnssec_authenticated = NULL; rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name); - if (rc == DNS_NOMATCH) return HOST_FIND_FAILED; - if (rc == DNS_FAIL || rc == DNS_AGAIN) + + if (dnssec_request) { - #ifndef STAND_ALONE - if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) - #endif - return HOST_FIND_AGAIN; - DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " - "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); + if (dns_is_secure(&dnsa)) + { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } + else + { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } + } + + switch (rc) + { + case DNS_NOMATCH: + yield = HOST_FIND_FAILED; goto out; + + case DNS_SUCCEED: + if (!dnssec_require || dns_is_secure(&dnsa)) + break; + log_write(L_host_lookup_failed, LOG_MAIN, + "dnssec fail on MX for %.256s", host->name); + rc = DNS_FAIL; + /*FALLTRHOUGH*/ + + case DNS_FAIL: + case DNS_AGAIN: + #ifndef STAND_ALONE + if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN, + TRUE, NULL) != OK) + #endif + { yield = HOST_FIND_AGAIN; goto out; } + DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " + "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); + break; } } @@ -2534,14 +2602,25 @@ if (rc != DNS_SUCCEED) if ((whichrrs & HOST_FIND_BY_A) == 0) { DEBUG(D_host_lookup) debug_printf("Address records are not being sought\n"); - return HOST_FIND_FAILED; + yield = HOST_FIND_FAILED; + goto out; } last = host; /* End of local chainlet */ host->mx = MX_NONE; host->port = PORT_NONE; + dnssec = DS_UNK; + lookup_dnssec_authenticated = NULL; rc = set_address_from_dns(host, &last, ignore_target_hosts, FALSE, - fully_qualified_name); + fully_qualified_name, dnssec_request, dnssec_require); + + if (dnssec_request) + { + if (dns_is_secure(&dnsa)) + { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } + else + { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } + } /* If one or more address records have been found, check that none of them are local. Since we know the host items all have their IP addresses @@ -2568,7 +2647,8 @@ if (rc != DNS_SUCCEED) } } - return rc; + yield = rc; + goto out; } /* We have found one or more MX or SRV records. Sort them according to @@ -2611,9 +2691,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); the same precedence to sort randomly. */ if (ind_type == T_MX) - { weight = random_number(500); - } /* SRV records are specified with a port and a weight. The weight is used in a special algorithm. However, to start with, we just use it to order the @@ -2677,6 +2755,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); host->sort_key = precedence * 1000 + weight; host->status = hstatus_unknown; host->why = hwhy_unknown; + host->dnssec = dnssec; last = host; } @@ -2693,6 +2772,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); next->sort_key = sort_key; next->status = hstatus_unknown; next->why = hwhy_unknown; + next->dnssec = dnssec; next->last_try = 0; /* Handle the case when we have to insert before the first item. */ @@ -2752,7 +2832,8 @@ if (ind_type == T_SRV) if (host == last && host->name[0] == 0) { DEBUG(D_host_lookup) debug_printf("the single SRV record is \".\"\n"); - return HOST_FIND_FAILED; + yield = HOST_FIND_FAILED; + goto out; } DEBUG(D_host_lookup) @@ -2862,12 +2943,14 @@ otherwise invalid host names obtained from MX or SRV records can cause trouble if they happen to match something local. */ yield = HOST_FIND_FAILED; /* Default yield */ -dns_init(FALSE, FALSE); /* Disable qualify_single and search_parents */ +dns_init(FALSE, FALSE, /* Disable qualify_single and search_parents */ + dnssec_request || dnssec_require); for (h = host; h != last->next; h = h->next) { if (h->address != NULL) continue; /* Inserted by a multihomed host */ - rc = set_address_from_dns(h, &last, ignore_target_hosts, allow_mx_to_ip, NULL); + rc = set_address_from_dns(h, &last, ignore_target_hosts, allow_mx_to_ip, + NULL, dnssec_request, dnssec_require); if (rc != HOST_FOUND) { h->status = hstatus_unusable; @@ -2976,6 +3059,9 @@ DEBUG(D_host_lookup) } } +out: + +dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ return yield; } @@ -2997,6 +3083,8 @@ int whichrrs = HOST_FIND_BY_MX | HOST_FIND_BY_A; BOOL byname = FALSE; BOOL qualify_single = TRUE; BOOL search_parents = FALSE; +BOOL request_dnssec = FALSE; +BOOL require_dnssec = FALSE; uschar **argv = USS cargv; uschar buffer[256]; @@ -3016,7 +3104,7 @@ if (argc > 1) primary_hostname = argv[1]; /* So that debug level changes can be done first */ -dns_init(qualify_single, search_parents); +dns_init(qualify_single, search_parents, FALSE); printf("Testing host lookup\n"); printf("> "); @@ -3042,10 +3130,14 @@ while (Ufgets(buffer, 256, stdin) != NULL) whichrrs = HOST_FIND_BY_SRV | HOST_FIND_BY_MX; else if (Ustrcmp(buffer, "srv+mx+a") == 0) whichrrs = HOST_FIND_BY_SRV | HOST_FIND_BY_MX | HOST_FIND_BY_A; - else if (Ustrcmp(buffer, "qualify_single") == 0) qualify_single = TRUE; + else if (Ustrcmp(buffer, "qualify_single") == 0) qualify_single = TRUE; else if (Ustrcmp(buffer, "no_qualify_single") == 0) qualify_single = FALSE; - else if (Ustrcmp(buffer, "search_parents") == 0) search_parents = TRUE; + else if (Ustrcmp(buffer, "search_parents") == 0) search_parents = TRUE; else if (Ustrcmp(buffer, "no_search_parents") == 0) search_parents = FALSE; + else if (Ustrcmp(buffer, "request_dnssec") == 0) request_dnssec = TRUE; + else if (Ustrcmp(buffer, "no_request_dnssec") == 0) request_dnssec = FALSE; + else if (Ustrcmp(buffer, "require_dnssec") == 0) require_dnssec = TRUE; + else if (Ustrcmp(buffer, "no_reqiret_dnssec") == 0) require_dnssec = FALSE; else if (Ustrcmp(buffer, "test_harness") == 0) running_in_test_harness = !running_in_test_harness; else if (Ustrcmp(buffer, "ipv6") == 0) disable_ipv6 = !disable_ipv6; @@ -3078,11 +3170,12 @@ while (Ufgets(buffer, 256, stdin) != NULL) if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS; - rc = byname? - host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE) - : - host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL, - &fully_qualified_name, NULL); + rc = byname + ? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE) + : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL, + request_dnssec ? &h.name : NULL, + require_dnssec ? &h.name : NULL, + &fully_qualified_name, NULL); if (rc == HOST_FIND_FAILED) printf("Failed\n"); else if (rc == HOST_FIND_AGAIN) printf("Again\n"); @@ -3141,4 +3234,6 @@ return 0; } #endif /* STAND_ALONE */ +/* vi: aw ai sw=2 +*/ /* End of host.c */