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