Fix taint-handling in rDNS name construction
[exim.git] / src / src / host.c
index 30d54b481620c5e76ebc401ee8cc19a1ee0c09e5..3361d59123f33314ba79f70c0ac562513da99b4c 100644 (file)
@@ -138,7 +138,7 @@ if (!slow_lookup_log)
 time_msec = get_time_in_ms();
 retval = dns_lookup(dnsa, name, type, fully_qualified_name);
 if ((time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
-  log_long_lookup(US"name", name, time_msec);
+  log_long_lookup(dns_text_type(type), name, time_msec);
 return retval;
 }
 
@@ -177,7 +177,7 @@ const uschar *lname = name;
 uschar *adds;
 uschar **alist;
 struct hostent *yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 
 DEBUG(D_host_lookup)
@@ -231,7 +231,7 @@ if ((ipa = string_is_ip_address(lname, NULL)) != 0)
 else
   {
   int type = (af == AF_INET)? T_A:T_AAAA;
-  int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL);
+  int rc = dns_lookup_timerwrap(dnsa, lname, type, NULL);
   int count = 0;
 
   lookup_dnssec_authenticated = NULL;
@@ -246,9 +246,9 @@ else
     case DNS_FAIL:    *error_num = NO_RECOVERY; return NULL;
     }
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     count++;
 
   yield = store_get(sizeof(struct hostent), FALSE);
@@ -261,13 +261,13 @@ else
   yield->h_length = alen;
   yield->h_addr_list = CSS alist;
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     {
     int x[4];
     dns_address *da;
-    if (!(da = dns_address_from_rr(&dnsa, rr))) break;
+    if (!(da = dns_address_from_rr(dnsa, rr))) break;
     *alist++ = adds;
     for (int n = host_aton(da->address, x), i = 0; i < n; i++)
       {
@@ -1503,9 +1503,7 @@ Returns:     OK, DEFER, FAIL
 static int
 host_name_lookup_byaddr(void)
 {
-int len;
-uschar *s, *t;
-struct hostent *hosts;
+struct hostent * hosts;
 struct in_addr addr;
 unsigned long time_msec = 0;   /* init to quieten dumb static analysis */
 
@@ -1548,7 +1546,7 @@ hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET);
 if (  slow_lookup_log
    && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log
    )
-  log_long_lookup(US"name", sender_host_address, time_msec);
+  log_long_lookup(US"gethostbyaddr", sender_host_address, time_msec);
 
 /* Failed to look up the host. */
 
@@ -1648,10 +1646,9 @@ int old_pool, rc;
 int sep = 0;
 uschar *save_hostname;
 uschar **aliases;
-uschar buffer[256];
 uschar *ordername;
 const uschar *list = host_lookup_order;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 
 sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE;
@@ -1674,13 +1671,14 @@ if (f.running_in_test_harness &&
 /* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in
 the order specified by the host_lookup_order option. */
 
-while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((ordername = string_nextinlist(&list, &sep, NULL, 0)))
   {
   if (strcmpic(ordername, US"bydns") == 0)
     {
+    uschar * name = dns_build_reverse(sender_host_address);
+
     dns_init(FALSE, FALSE, FALSE);    /* dnssec ctrl by dns_dnssec_ok glbl */
-    dns_build_reverse(sender_host_address, buffer);
-    rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL);
+    rc = dns_lookup_timerwrap(dnsa, name, T_PTR, NULL);
 
     /* The first record we come across is used for the name; others are
     considered to be aliases. We have to scan twice, in order to find out the
@@ -1695,16 +1693,16 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
       int count = 0;
       int old_pool = store_pool;
 
-      sender_host_dnssec = dns_is_secure(&dnsa);
+      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 (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+      for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
            rr;
-           rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+           rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
        count++;
 
       /* Get store for the list of aliases. For compatibility with
@@ -1714,16 +1712,16 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
 
       /* Re-scan and extract the names */
 
-      for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+      for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
            rr;
-           rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+           rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
         {
         uschar * s = store_get(ssize, TRUE);   /* names are tainted */
 
         /* If an overlong response was received, the data will have been
         truncated and dn_expand may fail. */
 
-        if (dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen,
+        if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
              US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
           {
           log_write(0, LOG_MAIN, "host name alias list truncated for %s",
@@ -2034,7 +2032,7 @@ for (int i = 1; i <= times;
 
   if (   slow_lookup_log
       && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
-    log_long_lookup(US"name", host->name, time_msec);
+    log_long_lookup(US"gethostbyname", host->name, time_msec);
 
   if (hostdata == NULL)
     {
@@ -2309,17 +2307,17 @@ for (; i >= 0; i--)
   int type = types[i];
   int randoffset = i == (whichrrs & HOST_FIND_IPV4_FIRST ? 1 : 0)
     ? 500 : 0;  /* Ensures v6/4 sort order */
-  dns_answer dnsa;
+  dns_answer * dnsa = store_get_dns_answer();
   dns_scan dnss;
 
-  int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name);
+  int rc = dns_lookup_timerwrap(dnsa, host->name, type, fully_qualified_name);
   lookup_dnssec_authenticated = !dnssec_request ? NULL
-    : dns_is_secure(&dnsa) ? US"yes" : US"no";
+    : dns_is_secure(dnsa) ? US"yes" : US"no";
 
   DEBUG(D_dns)
     if (  (dnssec_request || dnssec_require)
-       && !dns_is_secure(&dnsa)
-       && dns_is_aa(&dnsa)
+       && !dns_is_secure(dnsa)
+       && dns_is_aa(dnsa)
        )
       debug_printf("DNS lookup of %.256s (A/AAAA) requested AD, but got AA\n", host->name);
 
@@ -2347,7 +2345,7 @@ for (; i >= 0; i--)
 
   if (dnssec_request)
     {
-    if (dns_is_secure(&dnsa))
+    if (dns_is_secure(dnsa))
       {
       DEBUG(D_host_lookup) debug_printf("%s A DNSSEC\n", host->name);
       if (host->dnssec == DS_UNK) /* set in host_find_bydns() */
@@ -2378,11 +2376,11 @@ for (; i >= 0; i--)
 
   fully_qualified_name = NULL;
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     {
-    dns_address * da = dns_address_from_rr(&dnsa, rr);
+    dns_address * da = dns_address_from_rr(dnsa, rr);
 
     DEBUG(D_host_lookup)
       if (!da) debug_printf("no addresses extracted from A6 RR for %s\n",
@@ -2544,7 +2542,7 @@ host_item *h, *last;
 int rc = DNS_FAIL;
 int ind_type = 0;
 int yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 BOOL dnssec_require = dnssec_d
                    && match_isinlist(host->name, CUSS &dnssec_d->require,
@@ -2586,18 +2584,18 @@ if (whichrrs & HOST_FIND_BY_SRV)
 
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup_timerwrap(&dnsa, temp_fully_qualified_name, ind_type,
+  rc = dns_lookup_timerwrap(dnsa, temp_fully_qualified_name, ind_type,
        CUSS &temp_fully_qualified_name);
 
   DEBUG(D_dns)
     if ((dnssec_request || dnssec_require)
-       && !dns_is_secure(&dnsa)
-       && dns_is_aa(&dnsa))
+       && !dns_is_secure(dnsa)
+       && dns_is_aa(dnsa))
       debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name);
 
   if (dnssec_request)
     {
-    if (dns_is_secure(&dnsa))
+    if (dns_is_secure(dnsa))
       { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; }
     else
       { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; }
@@ -2609,7 +2607,7 @@ if (whichrrs & HOST_FIND_BY_SRV)
   /* 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))
+  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);
@@ -2639,18 +2637,18 @@ if (rc != DNS_SUCCEED  &&  whichrrs & HOST_FIND_BY_MX)
   ind_type = T_MX;
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
+  rc = dns_lookup_timerwrap(dnsa, host->name, ind_type, fully_qualified_name);
 
   DEBUG(D_dns)
     if (  (dnssec_request || dnssec_require)
-       && !dns_is_secure(&dnsa)
-       && dns_is_aa(&dnsa))
+       && !dns_is_secure(dnsa)
+       && dns_is_aa(dnsa))
       debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name);
 
   if (dnssec_request)
-    if (dns_is_secure(&dnsa))
+    if (dns_is_secure(dnsa))
       {
-      DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
+      DEBUG(D_host_lookup) debug_printf("%s (MX resp) DNSSEC\n", host->name);
       dnssec = DS_YES; lookup_dnssec_authenticated = US"yes";
       }
     else
@@ -2664,7 +2662,7 @@ if (rc != DNS_SUCCEED  &&  whichrrs & HOST_FIND_BY_MX)
       yield = HOST_FIND_FAILED; goto out;
 
     case DNS_SUCCEED:
-      if (!dnssec_require || dns_is_secure(&dnsa))
+      if (!dnssec_require || dns_is_secure(dnsa))
        break;
       DEBUG(D_host_lookup)
        debug_printf("dnssec fail on MX for %.256s", host->name);
@@ -2758,9 +2756,9 @@ host which is not the primary hostname. */
 
 last = NULL;    /* Indicates that not even the first item is filled yet */
 
-for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
      rr;
-     rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
+     rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
   {
   int precedence, weight;
   int port = PORT_NONE;
@@ -2785,7 +2783,7 @@ for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
 
   /* Get the name of the host pointed to. */
 
-  (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, s,
+  (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
     (DN_EXPAND_ARG4_TYPE)data, sizeof(data));
 
   /* Check that we haven't already got this host on the chain; if we have,
@@ -3156,6 +3154,79 @@ dns_init(FALSE, FALSE, FALSE);   /* clear the dnssec bit for getaddrbyname */
 return yield;
 }
 
+
+
+
+#ifdef SUPPORT_DANE
+/* Lookup TLSA record for host/port.
+Return:  OK            success with dnssec; DANE mode
+         DEFER         Do not use this host now, may retry later
+        FAIL_FORCED    No TLSA record; DANE not usable
+        FAIL           Do not use this connection
+*/
+
+int
+tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
+{
+uschar buffer[300];
+const uschar * fullname = buffer;
+int rc;
+BOOL sec;
+
+/* TLSA lookup string */
+(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
+
+rc = dns_lookup_timerwrap(dnsa, buffer, T_TLSA, &fullname);
+sec = dns_is_secure(dnsa);
+DEBUG(D_transport)
+  debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
+
+switch (rc)
+  {
+  case DNS_AGAIN:
+    return DEFER; /* just defer this TLS'd conn */
+
+  case DNS_SUCCEED:
+    if (sec)
+      {
+      DEBUG(D_transport)
+       {
+       dns_scan dnss;
+       for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+            rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+         if (rr->type == T_TLSA && rr->size > 3)
+           {
+           uint16_t payload_length = rr->size - 3;
+           uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
+
+           sp += sprintf(CS sp, "%d ", *p++); /* usage */
+           sp += sprintf(CS sp, "%d ", *p++); /* selector */
+           sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
+           while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+             sp += sprintf(CS sp, "%02x", *p++);
+
+           debug_printf(" %s\n", s);
+           }
+       }
+      return OK;
+      }
+    log_write(0, LOG_MAIN,
+      "DANE error: TLSA lookup for %s not DNSSEC", host->name);
+    /*FALLTRHOUGH*/
+
+  case DNS_NODATA:     /* no TLSA RR for this lookup */
+  case DNS_NOMATCH:    /* no records at all for this lookup */
+    return dane_required ? FAIL : FAIL_FORCED;
+
+  default:
+  case DNS_FAIL:
+    return dane_required ? FAIL : DEFER;
+  }
+}
+#endif /*SUPPORT_DANE*/
+
+
+
 /*************************************************
 **************************************************
 *             Stand-alone test program           *