mail_auth/common/
message.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use mail_parser::{parsers::MessageStream, Address, HeaderName, HeaderValue, Message};
8
9use crate::{arc, common::crypto::HashAlgorithm, dkim, AuthenticatedMessage};
10
11use super::headers::{AuthenticatedHeader, Header, HeaderParser};
12
13impl<'x> AuthenticatedMessage<'x> {
14    pub fn parse(raw_message: &'x [u8]) -> Option<Self> {
15        Self::parse_with_opts(raw_message, true)
16    }
17
18    pub fn from_parsed(parsed: &'x Message<'x>, strict: bool) -> Self {
19        let root = parsed.root_part();
20        let mut message = AuthenticatedMessage {
21            raw_message: parsed.raw_message(),
22            body_offset: root.raw_body_offset(),
23            headers: Vec::with_capacity(root.headers.len()),
24            ..Default::default()
25        };
26
27        for header in root.headers() {
28            let name =
29                &parsed.raw_message[header.offset_field as usize..header.offset_start as usize - 1];
30            let value =
31                &parsed.raw_message[header.offset_start as usize..header.offset_end as usize];
32
33            match &header.name {
34                HeaderName::From => {
35                    message.parse_from(&header.value);
36                }
37                HeaderName::Date => {
38                    message.date_header_present = true;
39                }
40                HeaderName::Received => {
41                    message.received_headers_count += 1;
42                }
43                HeaderName::MessageId => {
44                    message.message_id_header_present = true;
45                }
46                HeaderName::DkimSignature => {
47                    message.parse_dkim(name, value, strict);
48                }
49                HeaderName::ArcAuthenticationResults => {
50                    message.parse_aar(name, value);
51                }
52                HeaderName::ArcSeal => {
53                    message.parse_as(name, value);
54                }
55                HeaderName::ArcMessageSignature => {
56                    message.parse_ams(name, value, strict);
57                }
58                _ => (),
59            }
60
61            message.headers.push((name, value))
62        }
63
64        message.finalize()
65    }
66
67    pub fn parse_with_opts(raw_message: &'x [u8], strict: bool) -> Option<Self> {
68        let mut message = AuthenticatedMessage {
69            raw_message,
70            ..Default::default()
71        };
72
73        let mut headers = HeaderParser::new(raw_message);
74
75        for (header, value) in &mut headers {
76            let name = match header {
77                AuthenticatedHeader::Ds(name) => {
78                    message.parse_dkim(name, value, strict);
79                    name
80                }
81                AuthenticatedHeader::Aar(name) => {
82                    message.parse_aar(name, value);
83                    name
84                }
85                AuthenticatedHeader::Ams(name) => {
86                    message.parse_ams(name, value, strict);
87                    name
88                }
89                AuthenticatedHeader::As(name) => {
90                    message.parse_as(name, value);
91                    name
92                }
93                AuthenticatedHeader::From(name) => {
94                    message.parse_from(&MessageStream::new(value).parse_address());
95                    name
96                }
97                AuthenticatedHeader::Other(name) => name,
98            };
99
100            message.headers.push((name, value));
101        }
102
103        if !message.headers.is_empty() {
104            // Update header counts
105            message.received_headers_count = headers.num_received;
106            message.message_id_header_present = headers.has_message_id;
107            message.date_header_present = headers.has_date;
108
109            // Obtain message body
110            if let Some(offset) = headers.body_offset() {
111                message.body_offset = offset as u32;
112            } else {
113                message.body_offset = raw_message.len() as u32;
114            }
115            Some(message.finalize())
116        } else {
117            None
118        }
119    }
120
121    fn parse_dkim(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
122        let signature = match dkim::Signature::parse(value) {
123            Ok(signature) if signature.l == 0 || !strict => {
124                let ha = HashAlgorithm::from(signature.a);
125                if !self
126                    .body_hashes
127                    .iter()
128                    .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
129                {
130                    self.body_hashes
131                        .push((signature.cb, ha, signature.l, Vec::new()));
132                }
133                Ok(signature)
134            }
135            Ok(_) => Err(crate::Error::SignatureLength),
136            Err(err) => Err(err),
137        };
138
139        self.dkim_headers.push(Header::new(name, value, signature));
140    }
141
142    fn parse_aar(&mut self, name: &'x [u8], value: &'x [u8]) {
143        let results = arc::Results::parse(value);
144        if !self.has_arc_errors {
145            self.has_arc_errors = results.is_err();
146        }
147        self.aar_headers.push(Header::new(name, value, results));
148    }
149
150    fn parse_ams(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
151        let signature = match arc::Signature::parse(value) {
152            Ok(signature) if signature.l == 0 || !strict => {
153                let ha = HashAlgorithm::from(signature.a);
154                if !self
155                    .body_hashes
156                    .iter()
157                    .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
158                {
159                    self.body_hashes
160                        .push((signature.cb, ha, signature.l, Vec::new()));
161                }
162                Ok(signature)
163            }
164            Ok(_) => {
165                self.has_arc_errors = true;
166                Err(crate::Error::SignatureLength)
167            }
168            Err(err) => {
169                self.has_arc_errors = true;
170                Err(err)
171            }
172        };
173
174        self.ams_headers.push(Header::new(name, value, signature));
175    }
176
177    fn parse_as(&mut self, name: &'x [u8], value: &'x [u8]) {
178        let seal = arc::Seal::parse(value);
179        if !self.has_arc_errors {
180            self.has_arc_errors = seal.is_err();
181        }
182        self.as_headers.push(Header::new(name, value, seal));
183    }
184
185    fn parse_from(&mut self, value: &HeaderValue<'x>) {
186        match value {
187            HeaderValue::Address(Address::List(list)) => {
188                self.from.extend(
189                    list.iter()
190                        .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase())),
191                );
192            }
193            HeaderValue::Address(Address::Group(group_list)) => {
194                self.from.extend(group_list.iter().flat_map(|group| {
195                    group
196                        .addresses
197                        .iter()
198                        .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase()))
199                }))
200            }
201            _ => (),
202        }
203    }
204
205    fn finalize(mut self) -> Self {
206        let body = self
207            .raw_message
208            .get(self.body_offset as usize..)
209            .unwrap_or_default();
210
211        // Calculate body hashes
212        for (cb, ha, l, bh) in &mut self.body_hashes {
213            *bh = ha.hash(cb.canonical_body(body, *l)).as_ref().to_vec();
214        }
215
216        // Sort ARC headers
217        if !self.as_headers.is_empty() && !self.has_arc_errors {
218            self.as_headers.sort_unstable_by(|a, b| {
219                a.header
220                    .as_ref()
221                    .unwrap()
222                    .i
223                    .cmp(&b.header.as_ref().unwrap().i)
224            });
225            self.ams_headers.sort_unstable_by(|a, b| {
226                a.header
227                    .as_ref()
228                    .unwrap()
229                    .i
230                    .cmp(&b.header.as_ref().unwrap().i)
231            });
232            self.aar_headers.sort_unstable_by(|a, b| {
233                a.header
234                    .as_ref()
235                    .unwrap()
236                    .i
237                    .cmp(&b.header.as_ref().unwrap().i)
238            });
239        }
240
241        self
242    }
243
244    pub fn received_headers_count(&self) -> usize {
245        self.received_headers_count
246    }
247
248    pub fn has_message_id_header(&self) -> bool {
249        self.message_id_header_present
250    }
251
252    pub fn has_date_header(&self) -> bool {
253        self.date_header_present
254    }
255
256    pub fn raw_message(&self) -> &[u8] {
257        self.raw_message
258    }
259
260    pub fn raw_headers(&self) -> &[u8] {
261        self.raw_message
262            .get(..self.body_offset as usize)
263            .unwrap_or_default()
264    }
265
266    pub fn raw_parsed_headers(&self) -> &[(&[u8], &[u8])] {
267        &self.headers
268    }
269
270    pub fn raw_body(&self) -> &[u8] {
271        self.raw_message
272            .get(self.body_offset as usize..)
273            .unwrap_or_default()
274    }
275
276    pub fn body_offset(&self) -> usize {
277        self.body_offset as usize
278    }
279
280    pub fn froms(&self) -> &[String] {
281        &self.from
282    }
283
284    pub fn from(&self) -> &str {
285        self.from.first().map_or("", |f| f.as_str())
286    }
287}