mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
mimeparser: do not try to decrypt partially downloaded messages
This commit is contained in:
126
src/e2ee.rs
126
src/e2ee.rs
@@ -131,6 +131,54 @@ impl EncryptHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies Autocrypt header to Autocrypt peer state.
|
||||||
|
///
|
||||||
|
/// Returns updated peer state.
|
||||||
|
pub(crate) async fn get_autocrypt_peerstate(
|
||||||
|
context: &Context,
|
||||||
|
mail: &ParsedMail<'_>,
|
||||||
|
message_time: i64,
|
||||||
|
) -> Result<Option<Peerstate>> {
|
||||||
|
let from = mail
|
||||||
|
.headers
|
||||||
|
.get_header(HeaderDef::From_)
|
||||||
|
.and_then(|from_addr| mailparse::addrparse_header(from_addr).ok())
|
||||||
|
.and_then(|from| from.extract_single_info())
|
||||||
|
.map(|from| from.addr)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let aheader = match Aheader::from_headers(&from, &mail.headers) {
|
||||||
|
Ok(header) => header,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(context, "Failed to parse Autocrypt header: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let peerstate = Peerstate::from_addr(context, &from).await?;
|
||||||
|
let peerstate = if let Some(aheader) = aheader {
|
||||||
|
if let Some(mut peerstate) = peerstate {
|
||||||
|
peerstate.apply_header(&aheader, message_time);
|
||||||
|
peerstate.save_to_db(&context.sql, false).await?;
|
||||||
|
Some(peerstate)
|
||||||
|
} else {
|
||||||
|
let p = Peerstate::from_header(&aheader, message_time);
|
||||||
|
p.save_to_db(&context.sql, true).await?;
|
||||||
|
Some(p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peerstate
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(peerstate) = peerstate.as_ref() {
|
||||||
|
peerstate
|
||||||
|
.handle_fingerprint_change(context, message_time)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(peerstate)
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to decrypt a message, but only if it is structured as an
|
/// Tries to decrypt a message, but only if it is structured as an
|
||||||
/// Autocrypt message.
|
/// Autocrypt message.
|
||||||
///
|
///
|
||||||
@@ -142,65 +190,15 @@ impl EncryptHelper {
|
|||||||
pub async fn try_decrypt(
|
pub async fn try_decrypt(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mail: &ParsedMail<'_>,
|
mail: &ParsedMail<'_>,
|
||||||
message_time: i64,
|
peerstate: &Option<Peerstate>,
|
||||||
) -> Result<(Option<Vec<u8>>, HashSet<Fingerprint>)> {
|
) -> Result<(Option<Vec<u8>>, HashSet<Fingerprint>)> {
|
||||||
let from = mail
|
|
||||||
.headers
|
|
||||||
.get_header(HeaderDef::From_)
|
|
||||||
.and_then(|from_addr| mailparse::addrparse_header(from_addr).ok())
|
|
||||||
.and_then(|from| from.extract_single_info())
|
|
||||||
.map(|from| from.addr)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut peerstate = Peerstate::from_addr(context, &from).await?;
|
|
||||||
|
|
||||||
// Apply Autocrypt header
|
|
||||||
match Aheader::from_headers(&from, &mail.headers) {
|
|
||||||
Ok(Some(ref header)) => {
|
|
||||||
if let Some(ref mut peerstate) = peerstate {
|
|
||||||
peerstate.apply_header(header, message_time);
|
|
||||||
peerstate.save_to_db(&context.sql, false).await?;
|
|
||||||
} else {
|
|
||||||
let p = Peerstate::from_header(header, message_time);
|
|
||||||
p.save_to_db(&context.sql, true).await?;
|
|
||||||
peerstate = Some(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(err) => warn!(context, "Failed to parse Autocrypt header: {}", err),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possibly perform decryption
|
// Possibly perform decryption
|
||||||
let mut public_keyring_for_validate: Keyring<SignedPublicKey> = Keyring::new();
|
let public_keyring_for_validate = keyring_from_peerstate(&peerstate);
|
||||||
|
|
||||||
if let Some(ref mut peerstate) = peerstate {
|
|
||||||
peerstate
|
|
||||||
.handle_fingerprint_change(context, message_time)
|
|
||||||
.await?;
|
|
||||||
if let Some(key) = &peerstate.public_key {
|
|
||||||
public_keyring_for_validate.add(key.clone());
|
|
||||||
} else if let Some(key) = &peerstate.gossip_key {
|
|
||||||
public_keyring_for_validate.add(key.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (out_mail, signatures) =
|
let (out_mail, signatures) =
|
||||||
match decrypt_if_autocrypt_message(context, mail, public_keyring_for_validate).await? {
|
match decrypt_if_autocrypt_message(context, mail, public_keyring_for_validate).await? {
|
||||||
Some((out_mail, signatures)) => (Some(out_mail), signatures),
|
Some((out_mail, signatures)) => (Some(out_mail), signatures),
|
||||||
None => (None, Default::default()),
|
None => (None, Default::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut peerstate) = peerstate {
|
|
||||||
// If message is not encrypted and it is not a read receipt, degrade encryption.
|
|
||||||
if out_mail.is_none()
|
|
||||||
&& message_time > peerstate.last_seen_autocrypt
|
|
||||||
&& !contains_report(mail)
|
|
||||||
{
|
|
||||||
peerstate.degrade_encryption(message_time);
|
|
||||||
peerstate.save_to_db(&context.sql, false).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((out_mail, signatures))
|
Ok((out_mail, signatures))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,6 +281,18 @@ fn get_attachment_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyring_from_peerstate(peerstate: &Option<Peerstate>) -> Keyring<SignedPublicKey> {
|
||||||
|
let mut public_keyring_for_validate: Keyring<SignedPublicKey> = Keyring::new();
|
||||||
|
if let Some(peerstate) = peerstate.as_ref() {
|
||||||
|
if let Some(key) = &peerstate.public_key {
|
||||||
|
public_keyring_for_validate.add(key.clone());
|
||||||
|
} else if let Some(key) = &peerstate.gossip_key {
|
||||||
|
public_keyring_for_validate.add(key.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public_keyring_for_validate
|
||||||
|
}
|
||||||
|
|
||||||
async fn decrypt_if_autocrypt_message(
|
async fn decrypt_if_autocrypt_message(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mail: &ParsedMail<'_>,
|
mail: &ParsedMail<'_>,
|
||||||
@@ -380,18 +390,6 @@ fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a MIME structure contains a multipart/report part.
|
|
||||||
///
|
|
||||||
/// As reports are often unencrypted, we do not reset the Autocrypt header in
|
|
||||||
/// this case.
|
|
||||||
///
|
|
||||||
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
|
||||||
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
|
||||||
/// that we could use the normal Autocrypt processing.
|
|
||||||
fn contains_report(mail: &ParsedMail<'_>) -> bool {
|
|
||||||
mail.ctype.mimetype == "multipart/report"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ensures a private key exists for the configured user.
|
/// Ensures a private key exists for the configured user.
|
||||||
///
|
///
|
||||||
/// Normally the private key is generated when the first message is
|
/// Normally the private key is generated when the first message is
|
||||||
|
|||||||
@@ -214,8 +214,9 @@ impl MimeMessage {
|
|||||||
let mut mail_raw = Vec::new();
|
let mut mail_raw = Vec::new();
|
||||||
let mut gossiped_addr = Default::default();
|
let mut gossiped_addr = Default::default();
|
||||||
|
|
||||||
let (mail, signatures, warn_empty_signature) =
|
let peerstate = e2ee::get_autocrypt_peerstate(context, &mail, message_time).await?;
|
||||||
match e2ee::try_decrypt(context, &mail, message_time).await {
|
let (mail, signatures, warn_empty_signature) = if partial.is_none() {
|
||||||
|
match e2ee::try_decrypt(context, &mail, &peerstate).await {
|
||||||
Ok((raw, signatures)) => {
|
Ok((raw, signatures)) => {
|
||||||
if let Some(raw) = raw {
|
if let Some(raw) = raw {
|
||||||
// Encrypted, but maybe unsigned message. Only if
|
// Encrypted, but maybe unsigned message. Only if
|
||||||
@@ -267,7 +268,17 @@ impl MimeMessage {
|
|||||||
|
|
||||||
(Ok(decrypted_mail), signatures, true)
|
(Ok(decrypted_mail), signatures, true)
|
||||||
} else {
|
} else {
|
||||||
// Message was not encrypted
|
// Message was not encrypted.
|
||||||
|
// If it is not a read receipt, degrade encryption.
|
||||||
|
if let Some(mut peerstate) = peerstate {
|
||||||
|
if message_time > peerstate.last_seen_autocrypt
|
||||||
|
&& mail.ctype.mimetype != "multipart/report"
|
||||||
|
{
|
||||||
|
peerstate.degrade_encryption(message_time);
|
||||||
|
peerstate.save_to_db(&context.sql, false).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(Ok(mail), signatures, false)
|
(Ok(mail), signatures, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,7 +286,11 @@ impl MimeMessage {
|
|||||||
warn!(context, "decryption failed: {}", err);
|
warn!(context, "decryption failed: {}", err);
|
||||||
(Err(err), Default::default(), true)
|
(Err(err), Default::default(), true)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
// Partial message, do not try to decrypt.
|
||||||
|
(Ok(mail), Default::default(), false)
|
||||||
|
};
|
||||||
|
|
||||||
let mut parser = MimeMessage {
|
let mut parser = MimeMessage {
|
||||||
parts: Vec::new(),
|
parts: Vec::new(),
|
||||||
|
|||||||
Reference in New Issue
Block a user