]>
Commit | Line | Data |
---|---|---|
16a62dd6 AM |
1 | From 889894461aa958da4604299acc161c29e2aa603c Mon Sep 17 00:00:00 2001 |
2 | From: Jeremy Harris <jgh146exb@wizmail.org> | |
3 | Date: Sun, 5 Sep 2021 11:32:09 +0100 | |
4 | Subject: [PATCH] Fix validation of domain-literals in Message_ID: headers. | |
5 | Bug 2805 | |
6 | ||
7 | --- | |
8 | src/src/parse.c | 34 +++++++++++++++++++++------------- | |
9 | src/src/receive.c | 3 ++- | |
10 | 5 files changed, 43 insertions(+), 20 deletions(-) | |
11 | ||
12 | diff --git a/src/src/parse.c b/src/src/parse.c | |
13 | index 58f8941..42f1234 100644 | |
14 | --- a/src/src/parse.c | |
15 | +++ b/src/src/parse.c | |
16 | @@ -224,16 +224,20 @@ If allow_domain_literals is TRUE, a "domain" may also be an IP address enclosed | |
17 | in []. Make sure the output is set to the null string if there is a syntax | |
18 | error as well as if there is no domain at all. | |
19 | ||
20 | +Optionally, msg_id domain literals ( printable-ascii enclosed in [] ) | |
21 | +are permitted. | |
22 | + | |
23 | Arguments: | |
24 | s current character pointer | |
25 | t where to put the domain | |
26 | + msg_id_literals flag for relaxed domain-literal processing | |
27 | errorptr put error message here on failure (*t will be 0 on exit) | |
28 | ||
29 | Returns: new character pointer | |
30 | */ | |
31 | ||
32 | static const uschar * | |
33 | -read_domain(const uschar *s, uschar *t, uschar **errorptr) | |
34 | +read_domain(const uschar *s, uschar *t, BOOL msg_id_literals, uschar **errorptr) | |
35 | { | |
36 | uschar *tt = t; | |
37 | s = skip_comment(s); | |
38 | @@ -259,7 +263,11 @@ if (*s == '[') | |
39 | t += 5; | |
40 | s += 5; | |
41 | } | |
42 | - while (*s == '.' || *s == ':' || isxdigit(*s)) *t++ = *s++; | |
43 | + | |
44 | + if (msg_id_literals) | |
45 | + while (*s >= 33 && *s <= 90 || *s >= 94 && *s <= 126) *t++ = *s++; | |
46 | + else | |
47 | + while (*s == '.' || *s == ':' || isxdigit(*s)) *t++ = *s++; | |
48 | ||
49 | if (*s == ']') *t++ = *s++; else | |
50 | { | |
51 | @@ -267,7 +275,7 @@ if (*s == '[') | |
52 | *tt = 0; | |
53 | } | |
54 | ||
55 | - if (!allow_domain_literals) | |
56 | + if (!allow_domain_literals && !msg_id_literals) | |
57 | { | |
58 | *errorptr = US"domain literals not allowed"; | |
59 | *tt = 0; | |
60 | @@ -500,7 +508,7 @@ BOOL commas = FALSE; | |
61 | while (*s == '@') | |
62 | { | |
63 | *t++ = '@'; | |
64 | - s = read_domain(s+1, t, errorptr); | |
65 | + s = read_domain(s+1, t, FALSE, errorptr); | |
66 | if (*t == 0) return s; | |
67 | t += Ustrlen((const uschar *)t); | |
68 | if (*s != ',') break; | |
69 | @@ -559,7 +567,7 @@ if (*errorptr == NULL) | |
70 | t += Ustrlen((const uschar *)t); | |
71 | *t++ = *s++; | |
72 | *domainptr = t; | |
73 | - s = read_domain(s, t, errorptr); | |
74 | + s = read_domain(s, t, FALSE, errorptr); | |
75 | } | |
76 | return s; | |
77 | } | |
78 | @@ -649,7 +657,7 @@ if (*s != '@' && *s != '<') | |
79 | { | |
80 | if (*s == 0 || *s == ';') | |
81 | { | |
82 | - if (*t == 0) FAILED(US"empty address"); | |
83 | + if (!*t) FAILED(US"empty address"); | |
84 | endptr = last_comment_position; | |
85 | goto PARSE_SUCCEEDED; /* Bare local part */ | |
86 | } | |
87 | @@ -740,7 +748,7 @@ if (*s == '<') | |
88 | } | |
89 | ||
90 | endptr = s; | |
91 | - if (*errorptr != NULL) goto PARSE_FAILED; | |
92 | + if (*errorptr) goto PARSE_FAILED; | |
93 | while (bracket_count-- > 0) if (*s++ != '>') | |
94 | { | |
95 | *errorptr = s[-1] == 0 | |
96 | @@ -759,14 +767,14 @@ should be the domain. However, for flexibility we allow for a route-address | |
97 | not enclosed in <> as well, which is indicated by an empty first local | |
98 | part preceding '@'. The source routing is, however, ignored. */ | |
99 | ||
100 | -else if (*t == 0) | |
101 | +else if (!*t) | |
102 | { | |
103 | uschar *domainptr = yield; | |
104 | s = read_route(s, t, errorptr); | |
105 | - if (*errorptr != NULL) goto PARSE_FAILED; | |
106 | + if (*errorptr) goto PARSE_FAILED; | |
107 | *t = 0; /* Ensure route is ignored - probably overkill */ | |
108 | s = read_addr_spec(s, t, 0, errorptr, &domainptr); | |
109 | - if (*errorptr != NULL) goto PARSE_FAILED; | |
110 | + if (*errorptr) goto PARSE_FAILED; | |
111 | *domain = domainptr - yield; | |
112 | endptr = last_comment_position; | |
113 | if (*domain == 0) FAILED(US"domain missing in source-routed address"); | |
114 | @@ -779,8 +787,8 @@ else | |
115 | t += Ustrlen((const uschar *)t); | |
116 | *t++ = *s++; | |
117 | *domain = t - yield; | |
118 | - s = read_domain(s, t, errorptr); | |
119 | - if (*t == 0) goto PARSE_FAILED; | |
120 | + s = read_domain(s, t, TRUE, errorptr); | |
121 | + if (!*t) goto PARSE_FAILED; | |
122 | endptr = last_comment_position; | |
123 | } | |
124 | ||
125 | @@ -789,7 +797,7 @@ through for other cases. Endptr may have been moved over whitespace, so | |
126 | move it back past white space if necessary. */ | |
127 | ||
128 | PARSE_SUCCEEDED: | |
129 | -if (*s != 0) | |
130 | +if (*s) | |
131 | { | |
132 | if (f.parse_found_group && *s == ';') | |
133 | { | |
134 | diff --git a/src/src/receive.c b/src/src/receive.c | |
135 | index c2b313c..5471aa7 100644 | |
136 | --- a/src/src/receive.c | |
137 | +++ b/src/src/receive.c | |
138 | @@ -1663,7 +1663,6 @@ int process_info_len = Ustrlen(process_info); | |
139 | int error_rc = error_handling == ERRORS_SENDER | |
140 | ? errors_sender_rc : EXIT_FAILURE; | |
141 | int header_size = 256; | |
142 | -int start, end, domain; | |
143 | int id_resolution = 0; | |
144 | int had_zero = 0; | |
145 | int prevlines_length = 0; | |
146 | @@ -4084,6 +4083,8 @@ if ( LOGGING(msg_id) && msgid_header | |
147 | uschar * old_id; | |
148 | BOOL save_allow_domain_literals = allow_domain_literals; | |
149 | allow_domain_literals = TRUE; | |
150 | + int start, end, domain; | |
151 | + | |
152 | old_id = parse_extract_address(Ustrchr(msgid_header->text, ':') + 1, | |
153 | &errmsg, &start, &end, &domain, FALSE); | |
154 | allow_domain_literals = save_allow_domain_literals; | |
155 |