DNS: more hardening against crafted responses
[exim.git] / src / src / host.c
index 874e19a08dfdc273260fd4d9e852fa4bef1f5820..ce7ca2bab1098d6f5167cd485dd46c42d0451163 100644 (file)
@@ -5,7 +5,7 @@
 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
-/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or
 directly via the DNS. When IPv6 is supported, getipnodebyname() and
@@ -600,35 +600,38 @@ return depends on whether sender_fullhost and sender_ident are set or not:
   ident set, no host  => U=ident
   ident set, host set => H=sender_fullhost U=ident
 
-Use taint-unchecked routines on the assumption we'll never expand the results.
-
 Arguments:
   useflag   TRUE if first item to be flagged (H= or U=); if there are two
               items, the second is always flagged
 
-Returns:    pointer to a string in big_buffer
+Returns:    pointer to an allocated string
 */
 
 uschar *
 host_and_ident(BOOL useflag)
 {
+gstring * g = NULL;
+
 if (!sender_fullhost)
-  string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
-     sender_ident ? sender_ident : US"unknown");
+  {
+  if (useflag)
+    g = string_catn(g, US"U=", 2);
+  g = string_cat(g, sender_ident ? sender_ident : US"unknown");
+  }
 else
   {
-  uschar * flag = useflag ? US"H=" : US"";
-  uschar * iface = US"";
+  if (useflag)
+    g = string_catn(g, US"H=", 2);
+  g = string_cat(g, sender_fullhost);
   if (LOGGING(incoming_interface) && interface_address)
-    iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
+    g = string_fmt_append(g, " I=[%s]:%d", interface_address, interface_port);
   if (sender_ident)
-    string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s",
-      flag, sender_fullhost, iface, sender_ident);
-  else
-    string_format_nt(big_buffer, big_buffer_size, "%s%s%s",
-      flag, sender_fullhost, iface);
+    g = string_fmt_append(g, " U=%s", sender_ident);
   }
-return big_buffer;
+if (LOGGING(connection_id))
+  g = string_fmt_append(g, " Ci=%lu", connection_id);
+gstring_release_unused(g);
+return string_from_gstring(g);
 }
 
 #endif   /* STAND_ALONE */
@@ -824,9 +827,9 @@ Returns:     pointer to character string
 */
 
 uschar *
-host_ntoa(int type, const void *arg, uschar *buffer, int *portptr)
+host_ntoa(int type, const void * arg, uschar * buffer, int * portptr)
 {
-uschar *yield;
+uschar * yield;
 
 /* The new world. It is annoying that we have to fish out the address from
 different places in the block, depending on what kind of address it is. It
@@ -912,7 +915,7 @@ Returns:     the number of ints used
 */
 
 int
-host_aton(const uschar *address, int *bin)
+host_aton(const uschar * address, int * bin)
 {
 int x[4];
 int v4offset = 0;
@@ -924,13 +927,10 @@ supported. */
 
 if (Ustrchr(address, ':') != NULL)
   {
-  const uschar *p = address;
-  const uschar *component[8];
+  const uschar * p = address;
+  const uschar * component[8];
   BOOL ipv4_ends = FALSE;
-  int ci = 0;
-  int nulloffset = 0;
-  int v6count = 8;
-  int i;
+  int ci = 0, nulloffset = 0, v6count = 8, i;
 
   /* If the address starts with a colon, it will start with two colons.
   Just lose the first one, which will leave a null first component. */
@@ -942,7 +942,7 @@ if (Ustrchr(address, ':') != NULL)
   overlooked; to guard against that happening again, check here and crash if
   there are too many components. */
 
-  while (*p != 0 && *p != '%')
+  while (*p && *p != '%')
     {
     int len = Ustrcspn(p, ":%");
     if (len == 0) nulloffset = ci;
@@ -2081,11 +2081,11 @@ so we pass that back. */
 if (!host->address)
   {
   uschar *msg =
-    #ifndef STAND_ALONE
+#ifndef STAND_ALONE
     !message_id[0] && smtp_in
       ? string_sprintf("no IP address found for host %s (during %s)", host->name,
           smtp_get_connection_info()) :
-    #endif
+#endif
     string_sprintf("no IP address found for host %s", host->name);
 
   HDEBUG(D_host_lookup) debug_printf("%s\n", msg);
@@ -2725,6 +2725,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
   const uschar * s = rr->data; /* MUST be unsigned for GETSHORT */
   uschar data[256];
 
+  if (rr_bad_size(rr, sizeof(uint16_t))) continue;
   GETSHORT(precedence, s);      /* Pointer s is advanced */
 
   /* For MX records, we use a random "weight" which causes multiple records of
@@ -2737,6 +2738,8 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
     /* 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
     records of equal priority (precedence). */
+
+    if (rr_bad_increment(rr, s, 2 * sizeof(uint16_t))) continue;
     GETSHORT(weight, s);
     GETSHORT(port, s);
     }