Skip to main content

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 super::headers::{AuthenticatedHeader, Header, HeaderParser};
8#[cfg(feature = "arc")]
9use crate::arc;
10use crate::{AuthenticatedMessage, Error, common::crypto::HashAlgorithm, dkim, dkim2};
11use mail_parser::{Address, HeaderName, HeaderValue, Message, parsers::MessageStream};
12
13const EXPECTED_HEADER_COUNT: usize = 32;
14
15impl<'x> AuthenticatedMessage<'x> {
16    pub fn parse(raw_message: &'x [u8]) -> Option<Self> {
17        Self::parse_with_opts(raw_message, None, true)
18    }
19
20    pub fn from_parsed<'y>(parsed: &'y Message<'x>, raw_message: &'x [u8], strict: bool) -> Self {
21        let root = parsed.root_part();
22        let mut message = AuthenticatedMessage {
23            raw_message,
24            body_offset: root.raw_body_offset(),
25            headers: Vec::with_capacity(root.headers.len()),
26            ..Default::default()
27        };
28
29        for header in root.headers() {
30            let name = &raw_message[header.offset_field as usize..header.offset_start as usize - 1];
31            let value = &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                #[cfg(feature = "arc")]
50                HeaderName::ArcAuthenticationResults => {
51                    message.parse_aar(name, value);
52                }
53                #[cfg(feature = "arc")]
54                HeaderName::ArcSeal => {
55                    message.parse_as(name, value);
56                }
57                #[cfg(feature = "arc")]
58                HeaderName::ArcMessageSignature => {
59                    message.parse_ams(name, value, strict);
60                }
61                HeaderName::Dkim2Signature => {
62                    message.parse_dkim2_signature(name, value);
63                }
64                HeaderName::MessageInstance => {
65                    message.parse_dkim2_instance(name, value);
66                }
67                _ => (),
68            }
69
70            message.headers.push((name, value))
71        }
72
73        message.finalize()
74    }
75
76    pub fn parse_with_opts(
77        raw_message: &'x [u8],
78        prepend_headers: Option<&'x [u8]>,
79        strict: bool,
80    ) -> Option<Self> {
81        let mut message = AuthenticatedMessage {
82            raw_message,
83            headers: Vec::with_capacity(EXPECTED_HEADER_COUNT),
84            ..Default::default()
85        };
86
87        if let Some(headers) = prepend_headers {
88            message.parse_headers(headers, strict);
89        }
90
91        let body_offset = message.parse_headers(raw_message, strict);
92
93        if !message.headers.is_empty() {
94            // Obtain message body
95            if let Some(offset) = body_offset {
96                message.body_offset = offset as u32;
97            } else {
98                message.body_offset = raw_message.len() as u32;
99            }
100            Some(message.finalize())
101        } else {
102            None
103        }
104    }
105
106    fn parse_headers(&mut self, headers: &'x [u8], strict: bool) -> Option<usize> {
107        let mut headers = HeaderParser::new(headers);
108
109        for (header, value) in &mut headers {
110            let name = match header {
111                AuthenticatedHeader::Ds(name) => {
112                    self.parse_dkim(name, value, strict);
113                    name
114                }
115                AuthenticatedHeader::D2s(name) => {
116                    self.parse_dkim2_signature(name, value);
117                    name
118                }
119                AuthenticatedHeader::D2i(name) => {
120                    self.parse_dkim2_instance(name, value);
121                    name
122                }
123                #[cfg(feature = "arc")]
124                AuthenticatedHeader::Aar(name) => {
125                    self.parse_aar(name, value);
126                    name
127                }
128                #[cfg(feature = "arc")]
129                AuthenticatedHeader::Ams(name) => {
130                    self.parse_ams(name, value, strict);
131                    name
132                }
133                #[cfg(feature = "arc")]
134                AuthenticatedHeader::As(name) => {
135                    self.parse_as(name, value);
136                    name
137                }
138                AuthenticatedHeader::From(name) => {
139                    self.parse_from(&MessageStream::new(value).parse_address());
140                    name
141                }
142                AuthenticatedHeader::Other(name) => name,
143            };
144
145            self.headers.push((name, value));
146        }
147
148        // Update header counts
149        self.received_headers_count += headers.num_received;
150        self.message_id_header_present |= headers.has_message_id;
151        self.date_header_present |= headers.has_date;
152
153        headers.body_offset()
154    }
155
156    fn parse_dkim(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
157        match dkim::Signature::parse(value) {
158            Ok(signature) if signature.l == 0 || !strict => {
159                let ha = HashAlgorithm::from(signature.a);
160                if !self
161                    .body_hashes
162                    .iter()
163                    .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
164                {
165                    self.body_hashes
166                        .push((signature.cb, ha, signature.l, Vec::new()));
167                }
168                self.dkim_headers.push(Header::new(name, value, signature));
169            }
170            Ok(_) => {
171                self.push_dkim_error(name, value, Error::Dkim(dkim::DkimError::SignatureLength));
172            }
173            Err(err) => self.push_dkim_error(name, value, err),
174        }
175    }
176
177    fn parse_dkim2_signature(&mut self, name: &'x [u8], value: &'x [u8]) {
178        match dkim2::Signature::parse(value) {
179            Ok(signature) => self
180                .dkim2_signatures
181                .push(Header::new(name, value, signature)),
182            Err(err) => self.push_dkim2_error(name, value, err),
183        }
184    }
185
186    fn parse_dkim2_instance(&mut self, name: &'x [u8], value: &'x [u8]) {
187        match dkim2::MessageInstance::parse(value) {
188            Ok(instance) => self
189                .dkim2_instances
190                .push(Header::new(name, value, instance)),
191            Err(err) => self.push_dkim2_error(name, value, err),
192        }
193    }
194
195    #[cfg(feature = "arc")]
196    fn parse_aar(&mut self, name: &'x [u8], value: &'x [u8]) {
197        match arc::Results::parse(value) {
198            Ok(results) => self.aar_headers.push(Header::new(name, value, results)),
199            Err(err) => self.push_arc_error(name, value, err),
200        }
201    }
202
203    #[cfg(feature = "arc")]
204    fn parse_ams(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
205        match arc::Signature::parse(value) {
206            Ok(signature) if signature.l == 0 || !strict => {
207                let ha = HashAlgorithm::from(signature.a);
208                if !self
209                    .body_hashes
210                    .iter()
211                    .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
212                {
213                    self.body_hashes
214                        .push((signature.cb, ha, signature.l, Vec::new()));
215                }
216                self.ams_headers.push(Header::new(name, value, signature));
217            }
218            Ok(_) => {
219                self.push_arc_error(name, value, Error::Arc(arc::ArcError::SignatureLength));
220            }
221            Err(err) => self.push_arc_error(name, value, err),
222        }
223    }
224
225    #[cfg(feature = "arc")]
226    fn parse_as(&mut self, name: &'x [u8], value: &'x [u8]) {
227        match arc::Seal::parse(value) {
228            Ok(seal) => self.as_headers.push(Header::new(name, value, seal)),
229            Err(err) => self.push_arc_error(name, value, err),
230        }
231    }
232
233    fn push_dkim_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
234        self.has_dkim_errors = true;
235        self.errors.push(Header::new(name, value, err));
236    }
237
238    fn push_dkim2_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
239        self.has_dkim2_errors = true;
240        self.errors.push(Header::new(name, value, err));
241    }
242
243    #[cfg(feature = "arc")]
244    fn push_arc_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
245        self.has_arc_errors = true;
246        self.errors.push(Header::new(name, value, err));
247    }
248
249    fn parse_from(&mut self, value: &HeaderValue<'x>) {
250        match value {
251            HeaderValue::Address(Address::List(list)) => {
252                self.from.extend(
253                    list.iter()
254                        .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase())),
255                );
256            }
257            HeaderValue::Address(Address::Group(group_list)) => {
258                self.from.extend(group_list.iter().flat_map(|group| {
259                    group
260                        .addresses
261                        .iter()
262                        .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase()))
263                }))
264            }
265            _ => (),
266        }
267    }
268
269    fn finalize(mut self) -> Self {
270        let body = self
271            .raw_message
272            .get(self.body_offset as usize..)
273            .unwrap_or_default();
274
275        // Calculate body hashes
276        for (cb, ha, l, bh) in &mut self.body_hashes {
277            *bh = ha.hash(cb.canonical_body(body, *l)).as_ref().to_vec();
278        }
279
280        // Sort ARC headers
281        #[cfg(feature = "arc")]
282        if !self.as_headers.is_empty() && !self.has_arc_errors {
283            self.as_headers.sort_unstable_by_key(|h| h.header.i);
284            self.ams_headers.sort_unstable_by_key(|h| h.header.i);
285            self.aar_headers.sort_unstable_by_key(|h| h.header.i);
286        }
287
288        // Sort DKIM2 signatures and instances
289        if !self.has_dkim2_errors {
290            self.dkim2_signatures.sort_unstable_by_key(|h| h.header.i);
291            self.dkim2_instances.sort_unstable_by_key(|h| h.header.m);
292        }
293
294        self
295    }
296
297    pub fn received_headers_count(&self) -> usize {
298        self.received_headers_count
299    }
300
301    pub fn has_message_id_header(&self) -> bool {
302        self.message_id_header_present
303    }
304
305    pub fn has_date_header(&self) -> bool {
306        self.date_header_present
307    }
308
309    pub fn raw_message(&self) -> &[u8] {
310        self.raw_message
311    }
312
313    pub fn raw_headers(&self) -> &[u8] {
314        self.raw_message
315            .get(..self.body_offset as usize)
316            .unwrap_or_default()
317    }
318
319    pub fn raw_parsed_headers(&self) -> &[(&[u8], &[u8])] {
320        &self.headers
321    }
322
323    pub fn raw_body(&self) -> &[u8] {
324        self.raw_message
325            .get(self.body_offset as usize..)
326            .unwrap_or_default()
327    }
328
329    pub fn body_offset(&self) -> usize {
330        self.body_offset as usize
331    }
332
333    pub fn froms(&self) -> &[String] {
334        &self.from
335    }
336
337    pub fn from(&self) -> &str {
338        self.from.first().map_or("", |f| f.as_str())
339    }
340}