fix: Don't treat forged outgoing messages as Autocrypt-encrypted

This commit is contained in:
iequidoo
2024-02-10 13:55:51 -03:00
committed by link2xt
parent f0be7daae9
commit 6cee295a5d
4 changed files with 67 additions and 4 deletions

View File

@@ -12,7 +12,7 @@ use crate::authres::{self, DkimResults};
use crate::contact::addr_cmp;
use crate::context::Context;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey};
use crate::key::{self, DcKey, Fingerprint, SignedPublicKey, SignedSecretKey};
use crate::peerstate::Peerstate;
use crate::pgp;
@@ -264,16 +264,22 @@ pub(crate) fn validate_detached_signature<'a, 'b>(
}
}
pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Vec<SignedPublicKey> {
/// Returns public keyring for `peerstate`.
pub(crate) async fn keyring_from_peerstate(
context: &Context,
peerstate: Option<&Peerstate>,
) -> Result<Vec<SignedPublicKey>> {
let mut public_keyring_for_validate = Vec::new();
if let Some(peerstate) = peerstate {
if let Some(key) = &peerstate.public_key {
public_keyring_for_validate.push(key.clone());
} else if let Some(key) = &peerstate.gossip_key {
public_keyring_for_validate.push(key.clone());
} else if context.is_self_addr(&peerstate.addr).await? {
public_keyring_for_validate = key::load_self_public_keyring(context).await?;
}
}
public_keyring_for_validate
Ok(public_keyring_for_validate)
}
/// Applies Autocrypt header to Autocrypt peer state and saves it into the database.
@@ -292,6 +298,7 @@ pub(crate) async fn get_autocrypt_peerstate(
message_time: i64,
allow_change: bool,
) -> Result<Option<Peerstate>> {
let allow_change = allow_change && !context.is_self_addr(from).await?;
let mut peerstate;
// Apply Autocrypt header

View File

@@ -101,6 +101,25 @@ pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPubl
}
}
/// Returns our own public keyring.
pub(crate) async fn load_self_public_keyring(context: &Context) -> Result<Vec<SignedPublicKey>> {
let keys = context
.sql
.query_map(
r#"SELECT public_key
FROM keypairs
ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
(),
|row| row.get::<_, Vec<u8>>(0),
|keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
)
.await?
.into_iter()
.filter_map(|bytes| SignedPublicKey::from_slice(&bytes).log_err(context).ok())
.collect();
Ok(keys)
}
pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecretKey> {
let private_key = context
.sql

View File

@@ -304,7 +304,8 @@ impl MimeMessage {
hop_info += "\n\n";
hop_info += &decryption_info.dkim_results.to_string();
let public_keyring = keyring_from_peerstate(decryption_info.peerstate.as_ref());
let public_keyring =
keyring_from_peerstate(context, decryption_info.peerstate.as_ref()).await?;
let (mail, mut signatures, encrypted) = match tokio::task::block_in_place(|| {
try_decrypt(&mail, &private_keyring, &public_keyring)
}) {

View File

@@ -16,6 +16,7 @@ use crate::message::Message;
use crate::mimeparser::SystemMessage;
use crate::sql::Sql;
use crate::stock_str;
use crate::tools;
/// Type of the public key stored inside the peerstate.
#[derive(Debug)]
@@ -165,6 +166,9 @@ impl Peerstate {
/// Loads peerstate corresponding to the given address from the database.
pub async fn from_addr(context: &Context, addr: &str) -> Result<Option<Peerstate>> {
if context.is_self_addr(addr).await? {
return Ok(Some(Peerstate::get_self_stub(addr)));
}
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \
gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \
verified_key, verified_key_fingerprint, \
@@ -182,6 +186,7 @@ impl Peerstate {
context: &Context,
fingerprint: &Fingerprint,
) -> Result<Option<Peerstate>> {
// NOTE: If it's our key fingerprint, this returns None currently.
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \
gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \
verified_key, verified_key_fingerprint, \
@@ -206,6 +211,9 @@ impl Peerstate {
fingerprint: &Fingerprint,
addr: &str,
) -> Result<Option<Peerstate>> {
if context.is_self_addr(addr).await? {
return Ok(Some(Peerstate::get_self_stub(addr)));
}
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \
gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \
verified_key, verified_key_fingerprint, \
@@ -221,6 +229,34 @@ impl Peerstate {
Self::from_stmt(context, query, (&fp, &addr, &fp)).await
}
/// Returns peerstate stub for self `addr`.
///
/// Needed for [`crate::decrypt::keyring_from_peerstate()`] which returns a keyring of all our
/// pubkeys for such a stub so that we can check if a message is signed by us.
fn get_self_stub(addr: &str) -> Self {
let now = tools::time();
// We can have multiple pubkeys, just make the corresponding fields None.
Self {
addr: addr.to_string(),
last_seen: now,
last_seen_autocrypt: now,
prefer_encrypt: EncryptPreference::Mutual,
public_key: None,
public_key_fingerprint: None,
gossip_key: None,
gossip_key_fingerprint: None,
gossip_timestamp: 0,
verified_key: None,
verified_key_fingerprint: None,
verifier: None,
secondary_verified_key: None,
secondary_verified_key_fingerprint: None,
secondary_verifier: None,
backward_verified_key_id: None,
fingerprint_changed: false,
}
}
async fn from_stmt(
context: &Context,
query: &str,