Fix validation of domain-literals in Message_ID: headers. Bug 2805
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 5 Sep 2021 10:32:09 +0000 (11:32 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 5 Sep 2021 11:16:31 +0000 (12:16 +0100)
doc/doc-txt/ChangeLog
src/src/parse.c
src/src/receive.c
test/log/0604
test/scripts/0000-Basic/0604

index 5fa80401f75ebc02e32ced833630337ac510bc7e..463bf6ff6d418a03dc7c8ea2bdf5e78d72380bd8 100644 (file)
@@ -332,6 +332,10 @@ JH/59 DKIM: Fix small-message verification under TLS with chunking. If a
       incorrrectly treated as part of the message body, causing a verification
       fail.
 
+JH/60 Bug 2805: Fix logging of domain-literals in Message_ID: headers. They
+      require looser validation rules than those for 821-level addresses,
+      which only permit IP addresses.
+
 
 Exim version 4.94
 -----------------
index 58f8941990403e6602a6179d18d5995b41fd3ab1..42f1234be8b396b3e6f3450b2be416cf431ec72f 100644 (file)
@@ -224,16 +224,20 @@ If allow_domain_literals is TRUE, a "domain" may also be an IP address enclosed
 in []. Make sure the output is set to the null string if there is a syntax
 error as well as if there is no domain at all.
 
+Optionally, msg_id domain literals ( printable-ascii enclosed in [] )
+are permitted.
+
 Arguments:
   s          current character pointer
   t          where to put the domain
+  msg_id_literals     flag for relaxed domain-literal processing
   errorptr   put error message here on failure (*t will be 0 on exit)
 
 Returns:     new character pointer
 */
 
 static const uschar *
-read_domain(const uschar *s, uschar *t, uschar **errorptr)
+read_domain(const uschar *s, uschar *t, BOOL msg_id_literals, uschar **errorptr)
 {
 uschar *tt = t;
 s = skip_comment(s);
@@ -259,7 +263,11 @@ if (*s == '[')
     t += 5;
     s += 5;
     }
-  while (*s == '.' || *s == ':' || isxdigit(*s)) *t++ = *s++;
+
+  if (msg_id_literals)
+    while (*s >= 33 && *s <= 90 || *s >= 94 && *s <= 126) *t++ = *s++;
+  else
+    while (*s == '.' || *s == ':' || isxdigit(*s)) *t++ = *s++;
 
   if (*s == ']') *t++ = *s++; else
     {
@@ -267,7 +275,7 @@ if (*s == '[')
     *tt = 0;
     }
 
-  if (!allow_domain_literals)
+  if (!allow_domain_literals && !msg_id_literals)
     {
     *errorptr = US"domain literals not allowed";
     *tt = 0;
@@ -500,7 +508,7 @@ BOOL commas = FALSE;
 while (*s == '@')
   {
   *t++ = '@';
-  s = read_domain(s+1, t, errorptr);
+  s = read_domain(s+1, t, FALSE, errorptr);
   if (*t == 0) return s;
   t += Ustrlen((const uschar *)t);
   if (*s != ',') break;
@@ -559,7 +567,7 @@ if (*errorptr == NULL)
       t += Ustrlen((const uschar *)t);
       *t++ = *s++;
       *domainptr = t;
-      s = read_domain(s, t, errorptr);
+      s = read_domain(s, t, FALSE, errorptr);
       }
 return s;
 }
@@ -649,7 +657,7 @@ if (*s != '@' && *s != '<')
   {
   if (*s == 0 || *s == ';')
     {
-    if (*t == 0) FAILED(US"empty address");
+    if (!*t) FAILED(US"empty address");
     endptr = last_comment_position;
     goto PARSE_SUCCEEDED;              /* Bare local part */
     }
@@ -740,7 +748,7 @@ if (*s == '<')
     }
 
   endptr = s;
-  if (*errorptr != NULL) goto PARSE_FAILED;
+  if (*errorptr) goto PARSE_FAILED;
   while (bracket_count-- > 0) if (*s++ != '>')
     {
     *errorptr = s[-1] == 0
@@ -759,14 +767,14 @@ should be the domain. However, for flexibility we allow for a route-address
 not enclosed in <> as well, which is indicated by an empty first local
 part preceding '@'. The source routing is, however, ignored. */
 
-else if (*t == 0)
+else if (!*t)
   {
   uschar *domainptr = yield;
   s = read_route(s, t, errorptr);
-  if (*errorptr != NULL) goto PARSE_FAILED;
+  if (*errorptr) goto PARSE_FAILED;
   *t = 0;         /* Ensure route is ignored - probably overkill */
   s = read_addr_spec(s, t, 0, errorptr, &domainptr);
-  if (*errorptr != NULL) goto PARSE_FAILED;
+  if (*errorptr) goto PARSE_FAILED;
   *domain = domainptr - yield;
   endptr = last_comment_position;
   if (*domain == 0) FAILED(US"domain missing in source-routed address");
@@ -779,8 +787,8 @@ else
   t += Ustrlen((const uschar *)t);
   *t++ = *s++;
   *domain = t - yield;
-  s = read_domain(s, t, errorptr);
-  if (*t == 0) goto PARSE_FAILED;
+  s = read_domain(s, t, TRUE, errorptr);
+  if (!*t) goto PARSE_FAILED;
   endptr = last_comment_position;
   }
 
@@ -789,7 +797,7 @@ through for other cases. Endptr may have been moved over whitespace, so
 move it back past white space if necessary. */
 
 PARSE_SUCCEEDED:
-if (*s != 0)
+if (*s)
   {
   if (f.parse_found_group && *s == ';')
     {
index c2b313c637e5c7be54d6a0e5c25538afba14abbe..5471aa7cb94aa009ccc3c77a0f4cc5cfccc8e17a 100644 (file)
@@ -1663,7 +1663,6 @@ int  process_info_len = Ustrlen(process_info);
 int  error_rc = error_handling == ERRORS_SENDER
        ? errors_sender_rc : EXIT_FAILURE;
 int  header_size = 256;
-int  start, end, domain;
 int  id_resolution = 0;
 int  had_zero = 0;
 int  prevlines_length = 0;
@@ -4084,6 +4083,8 @@ if (  LOGGING(msg_id) && msgid_header
   uschar * old_id;
   BOOL save_allow_domain_literals = allow_domain_literals;
   allow_domain_literals = TRUE;
+  int start, end, domain;
+
   old_id = parse_extract_address(Ustrchr(msgid_header->text, ':') + 1,
     &errmsg, &start, &end, &domain, FALSE);
   allow_domain_literals = save_allow_domain_literals;
index 36354ac561ae77fb242889b5c7aea1ff1908d41e..3a17c10211bae33b739cd6babb8be33c8b25df8a 100644 (file)
@@ -1,10 +1,11 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id=original1235@example.com for a@test.ex
-1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for b@test.ex
-1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id*=E10HmaZ-0005vi-00@myhost.test.ex for c@test.ex
-1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id=original1235@example.com for d@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id=original1235@[foobar] for a2@test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for b@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id*=E10HmbA-0005vi-00@myhost.test.ex for c@test.ex
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss id=original1235@example.com for d@test.ex
 1999-03-02 09:44:33 10HmaX-0005vi-00 ** a@test.ex: Unrouteable address
-1999-03-02 09:44:33 10HmbB-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@myhost.test.ex
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@myhost.test.ex
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaY-0005vi-00 ** b@test.ex: Unrouteable address
-1999-03-02 09:44:33 10HmbC-0005vi-00 <= <> R=10HmaY-0005vi-00 U=EXIMUSER P=local S=sss id*=E10HmbC-0005vi-00@myhost.test.ex for CALLER@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 ** a2@test.ex: Unrouteable address
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= <> R=10HmaY-0005vi-00 U=EXIMUSER P=local S=sss id*=E10HmbD-0005vi-00@myhost.test.ex for CALLER@myhost.test.ex
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
index 196b9dc0c1e976bbfaa28cbfc2a05eca6c88e1aa..9e2e4d16e1bd7b87e502c04177233ee93a6e78c6 100644 (file)
@@ -6,6 +6,15 @@ From: CALLER@test.ex
 To: a@test.ex
 Message-ID: original1235@example.com
 
+body
+****
+#
+# check for 822-rules for domain-listeral in Message_ID:
+exim -DLOG_SELECTOR=+received_recipients -t
+From: CALLER@test.ex
+To: a2@test.ex
+Message-ID: original1235@[foobar]
+
 body
 ****
 #