Compare commits

...

3 Commits

Author SHA1 Message Date
holger krekel
ad4e922d0f snap 2019-09-30 03:14:32 +02:00
holger krekel
a6bd68e16e snap 2019-09-30 01:26:00 +02:00
holger krekel
cbfb391714 first bit 2019-09-30 01:26:00 +02:00
3 changed files with 149 additions and 153 deletions

View File

@@ -116,7 +116,7 @@ impl<'a> MimeParser<'a> {
); );
if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() { if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() {
let (encrypted, signatures, gossipped_addr) = let (signatures, gossipped_addr) =
e2ee::try_decrypt(self.context, self.mimeroot)?; e2ee::try_decrypt(self.context, self.mimeroot)?;
self.encrypted = encrypted; self.encrypted = encrypted;
self.signatures = signatures; self.signatures = signatures;

View File

@@ -37,6 +37,9 @@ use crate::wrapmime::*;
static mut VERSION_CONTENT: [libc::c_char; 13] = static mut VERSION_CONTENT: [libc::c_char; 13] =
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0]; [86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
type SignFingerprint = String;
type GossipAddr = String;
#[derive(Debug)] #[derive(Debug)]
pub struct EncryptHelper { pub struct EncryptHelper {
pub prefer_encrypt: EncryptPreference, pub prefer_encrypt: EncryptPreference,
@@ -264,8 +267,7 @@ impl EncryptHelper {
pub fn try_decrypt( pub fn try_decrypt(
context: &Context, context: &Context,
in_out_message: *mut Mailmime, in_out_message: *mut Mailmime,
) -> Result<(bool, HashSet<String>, HashSet<String>)> { ) -> Result<DecryptInfo> {
// just a pointer into mailmime structure, must not be freed
let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) }; let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) };
ensure!( ensure!(
!in_out_message.is_null() && !imffields.is_null(), !in_out_message.is_null() && !imffields.is_null(),
@@ -275,13 +277,16 @@ pub fn try_decrypt(
let from = wrapmime::get_field_from(imffields)?; let from = wrapmime::get_field_from(imffields)?;
let message_time = wrapmime::get_field_date(imffields)?; let message_time = wrapmime::get_field_date(imffields)?;
let mut peerstate = None;
let autocryptheader = Aheader::from_imffields(&from, imffields); let autocryptheader = Aheader::from_imffields(&from, imffields);
let recipients = mailimf_get_recipients(imffields);
let mut peerstate = None;
if message_time > 0 { if message_time > 0 {
peerstate = Peerstate::from_addr(context, &context.sql, &from); peerstate = Peerstate::from_addr(context, &context.sql, &from);
if let Some(ref mut peerstate) = peerstate { if let Some(ref mut peerstate) = peerstate {
// update Autocrypt peer state as per
// https://autocrypt.org/level1.html#updating-autocrypt-peer-state
if let Some(ref header) = autocryptheader { if let Some(ref header) = autocryptheader {
peerstate.apply_header(&header, message_time); peerstate.apply_header(&header, message_time);
peerstate.save_to_db(&context.sql, false).unwrap(); peerstate.save_to_db(&context.sql, false).unwrap();
@@ -300,7 +305,6 @@ pub fn try_decrypt(
/* possibly perform decryption */ /* possibly perform decryption */
let mut private_keyring = Keyring::default(); let mut private_keyring = Keyring::default();
let mut public_keyring_for_validate = Keyring::default(); let mut public_keyring_for_validate = Keyring::default();
let mut encrypted = false;
let mut signatures = HashSet::default(); let mut signatures = HashSet::default();
let mut gossipped_addr = HashSet::default(); let mut gossipped_addr = HashSet::default();
@@ -324,22 +328,24 @@ pub fn try_decrypt(
} }
let mut gossip_headers = ptr::null_mut(); let mut gossip_headers = ptr::null_mut();
encrypted = decrypt_if_autocrypt_message( let mut decrypt_info = decrypt_if_autocrypt_message(
context, context,
in_out_message, in_out_message,
&private_keyring, &private_keyring,
&public_keyring_for_validate, &public_keyring_for_validate,
&mut signatures,
&mut gossip_headers,
)?; )?;
if !gossip_headers.is_null() { decrypt_info.gossipped_addrs =
gossipped_addr = update_gossip_peerstates(
update_gossip_peerstates(context, message_time, imffields, gossip_headers)?; context,
unsafe { mailimf_fields_free(gossip_headers) }; message_time,
gosimffields, decrypt_info.gossip_headers)?;
if recipients.is_none() {
recipients = Some(
}
} }
} }
} }
Ok((encrypted, signatures, gossipped_addr)) Ok((!signatures.is_empty(), signatures, gossipped_addr))
} }
fn new_data_part( fn new_data_part(
@@ -429,64 +435,39 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
fn update_gossip_peerstates( fn update_gossip_peerstates(
context: &Context, context: &Context,
message_time: i64, message_time: i64,
imffields: *mut mailimf_fields, recipients: &Vec<String>,
gossip_headers: *const mailimf_fields, gossip_header_values: &Vec<String>,
) -> Result<HashSet<String>> { ) -> Result<HashSet<String>> {
// XXX split the parsing from the modification part // XXX split the parsing from the modification part
let mut recipients: Option<HashSet<String>> = None; let recipients = wrapmime::mailimf_get_recipients(imffields);
let mut gossipped_addr: HashSet<String> = Default::default();
for cur_data in unsafe { (*(*gossip_headers).fld_list).into_iter() } { let mut gossipped_addr = HashSet<String>::default();
let field = cur_data as *mut mailimf_field; for value in gossip_header_values.iter() {
if field.is_null() { let gossip_header = Aheader::from_str(&value);
continue;
}
let field = unsafe { *field }; if let Ok(ref header) = gossip_header {
if recipients.as_ref().unwrap().contains(&header.addr) {
if field.fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { let mut peerstate = Peerstate::from_addr(context, &context.sql, &header.addr);
let optional_field = unsafe { field.fld_data.fld_optional_field }; if let Some(ref mut peerstate) = peerstate {
if optional_field.is_null() { peerstate.apply_gossip(header, message_time);
continue; peerstate.save_to_db(&context.sql, false)?;
} } else {
let p = Peerstate::from_gossip(context, header, message_time);
let optional_field = unsafe { *optional_field }; p.save_to_db(&context.sql, true)?;
if !optional_field.fld_name.is_null() peerstate = Some(p);
&& as_str(optional_field.fld_name) == "Autocrypt-Gossip" }
{ if let Some(peerstate) = peerstate {
let value = to_string_lossy(optional_field.fld_value); if peerstate.degrade_event.is_some() {
let gossip_header = Aheader::from_str(&value); handle_degrade_event(context, &peerstate)?;
if let Ok(ref header) = gossip_header {
if recipients.is_none() {
recipients = Some(mailimf_get_recipients(imffields));
}
if recipients.as_ref().unwrap().contains(&header.addr) {
let mut peerstate =
Peerstate::from_addr(context, &context.sql, &header.addr);
if let Some(ref mut peerstate) = peerstate {
peerstate.apply_gossip(header, message_time);
peerstate.save_to_db(&context.sql, false)?;
} else {
let p = Peerstate::from_gossip(context, header, message_time);
p.save_to_db(&context.sql, true)?;
peerstate = Some(p);
}
if let Some(peerstate) = peerstate {
if peerstate.degrade_event.is_some() {
handle_degrade_event(context, &peerstate)?;
}
}
gossipped_addr.insert(header.addr.clone());
} else {
info!(
context,
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
&header.addr,
);
} }
} }
gossipped_addr.insert(header.addr.clone());
} else {
info!(
context,
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.", &header.addr,
);
} }
} }
} }
@@ -494,18 +475,25 @@ fn update_gossip_peerstates(
Ok(gossipped_addr) Ok(gossipped_addr)
} }
#[derive(Debug,Default)]
struct DecryptInfo {
signers: Vec<String>;
gossiped_addrs: HashSet<String>;
was_encrypted: bool;
}
fn decrypt_if_autocrypt_message( fn decrypt_if_autocrypt_message(
context: &Context, context: &Context,
mime_undetermined: *mut Mailmime, mime_undetermined: *mut Mailmime,
private_keyring: &Keyring, private_keyring: &Keyring,
public_keyring_for_validate: &Keyring, public_keyring_for_validate: &Keyring,
ret_valid_signatures: &mut HashSet<String>, ) -> Result<DecryptInfo> {
ret_gossip_headers: *mut *mut mailimf_fields, /* If we detected an Autocrypt-encrypted message and successfully decrypted it
) -> Result<(bool)> { there will be at least one signer in the returned Vec<Signer>.
/* The returned bool is true if we detected an Autocrypt-encrypted
message and successfully decrypted it. Decryption then modifies the Decryption modifies the passed in mime structure in place. It is possible
passed in mime structure in place. The returned bool is false to get a decrypted message yet it is not signed. This should be interpreted
if it was not an Autocrypt message. as not-encrypted (i.e. no lock in UIs) on messages.
Errors are returned for failures related to decryption of AC-messages. Errors are returned for failures related to decryption of AC-messages.
*/ */
@@ -514,116 +502,76 @@ fn decrypt_if_autocrypt_message(
let (mime, encrypted_data_part) = match wrapmime::get_autocrypt_mime(mime_undetermined) { let (mime, encrypted_data_part) = match wrapmime::get_autocrypt_mime(mime_undetermined) {
Err(_) => { Err(_) => {
// not a proper autocrypt message, abort and ignore // not a proper autocrypt message, abort and ignore
return Ok(false); return Ok(DecryptInfo::default());
} }
Ok(res) => res, Ok(res) => res,
}; };
let decrypted_mime = decrypt_part( decrypt_part(
context,
encrypted_data_part, encrypted_data_part,
private_keyring, private_keyring,
public_keyring_for_validate, public_keyring_for_validate,
ret_valid_signatures, )
)?;
// decrypted_mime is a dangling pointer which we now put into mailmime's Ownership
unsafe {
mailmime_substitute(mime, decrypted_mime);
mailmime_free(mime);
}
// finally, let's also return gossip headers
// XXX better return parsed headers so that upstream
// does not need to dive into mmime-stuff again.
unsafe {
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
let mut dummy: libc::size_t = 0;
let mut test: *mut mailimf_fields = ptr::null_mut();
if mailimf_envelope_and_optional_fields_parse(
(*decrypted_mime).mm_mime_start,
(*decrypted_mime).mm_length,
&mut dummy,
&mut test,
) == MAILIMF_NO_ERROR as libc::c_int
&& !test.is_null()
{
*ret_gossip_headers = test;
}
}
}
Ok(true)
} }
fn decrypt_part( fn decrypt_part(
_context: &Context,
mime: *mut Mailmime, mime: *mut Mailmime,
private_keyring: &Keyring, private_keyring: &Keyring,
public_keyring_for_validate: &Keyring, public_keyring_for_validate: &Keyring,
ret_valid_signatures: &mut HashSet<String>, ) -> Result<DecryptInfo> {
) -> Result<*mut Mailmime> {
let mime_data: *mut mailmime_data; let mime_data: *mut mailmime_data;
let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int;
unsafe { unsafe {
mime_data = (*mime).mm_data.mm_single; mime_data = (*mime).mm_data.mm_single;
} }
if !wrapmime::has_decryptable_data(mime_data) { if !wrapmime::has_decryptable_data(mime_data) {
return Ok(ptr::null_mut()); return Ok(DecryptInfo::default())
} }
let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int;
if let Some(enc) = wrapmime::get_mime_transfer_encoding(mime) { if let Some(enc) = wrapmime::get_mime_transfer_encoding(mime) {
mime_transfer_encoding = enc; mime_transfer_encoding = enc;
} }
let data: Vec<u8> = wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?; let data: Vec<u8> = wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
let mut ret_decrypted_mime = ptr::null_mut(); if !has_decrypted_pgp_armor(&data) {
return Ok(DecryptInfo::default())
if has_decrypted_pgp_armor(&data) {
// we should only have one decryption happening
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
let plain = match dc_pgp_pk_decrypt(
&data,
&private_keyring,
&public_keyring_for_validate,
Some(ret_valid_signatures),
) {
Ok(plain) => {
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
plain
}
Err(err) => bail!("could not decrypt: {}", err),
};
let plain_bytes = plain.len();
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index = 0;
let mut decrypted_mime = ptr::null_mut();
if unsafe {
mailmime_parse(
plain_buf as *const _,
plain_bytes,
&mut index,
&mut decrypted_mime,
)
} != MAIL_NO_ERROR as libc::c_int
|| decrypted_mime.is_null()
{
if !decrypted_mime.is_null() {
unsafe { mailmime_free(decrypted_mime) };
}
} else {
// decrypted_mime points into `plain`.
// FIXME(@dignifiedquire): this still leaks memory I believe, as mailmime_free
// does not free the underlying buffer. But for now we have to live with it
std::mem::forget(plain);
ret_decrypted_mime = decrypted_mime;
}
} }
Ok(ret_decrypted_mime) // now we have prepared ourselves to attempt decryption
let mut decrypt_info = DecryptInfo::default();
let plain = match dc_pgp_pk_decrypt(
&data,
&private_keyring,
&public_keyring_for_validate,
Some(decrypt_info.signers),
) {
Ok(plain) => {
decrypt_info.plain = plain;
plain
}
Err(err) => bail!("could not decrypt: {}", err),
};
if let Some(decrypted_mime) = wrapmime::parse_mailmime(data) {
unsafe {
// mailmime_substitute detaches mime from its position in the mime tree
// and puts decrypted_mime in its position. Afterwars mime is dangling.
mailmime_substitute(mime, decrypted_mime);
mailmime_free(mime);
}
// now let's read all gossip-header values
decrypt_info.gossip_addrs = wrapmime::iter_optional_field_values(
decrypted_mime,
b"Autocrypt-Gossip\0" as *const u8 as *const libc::c_char,
)?;
} else {
decrypt_info.parsing_error_after_decryption = true;
}
Ok(decrypt_info)
} }
fn has_decrypted_pgp_armor(input: &[u8]) -> bool { fn has_decrypted_pgp_armor(input: &[u8]) -> bool {

View File

@@ -92,6 +92,25 @@ pub fn has_decryptable_data(mime_data: *mut mailmime_data) -> bool {
} }
} }
pub fn parse_mailmime(input: &Vec<u8>) -> Result<*Mailmime> {
let mut parsed_mime = ptr::null_mut();
let mut index = 0;
let res = unsafe { mailmime_parse(
decrypt_info.plain.as_ptr() as *const _,
decrypt_info.plain.len(),
&mut index,
&mut parsed_mime,
) };
if res == MAIL_NO_ERROR && parsed_mime != ptr::null_mut() {
// mailmime_substitute detaches mime from its position in the mime tree
// and puts decrypted_mime in its position. Afterwars mime is dangling.
Ok(parsed_mime)
} else {
Err("could not parse mime structure");
}
}
pub fn get_field_from(imffields: *mut mailimf_fields) -> Result<String, Error> { pub fn get_field_from(imffields: *mut mailimf_fields) -> Result<String, Error> {
let field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int); let field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } { if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } {
@@ -202,6 +221,35 @@ pub unsafe fn mailimf_find_optional_field(
ptr::null_mut() ptr::null_mut()
} }
pub fn iter_optional_field_values(
header: *const mailimf_fields,
wanted_fld_name: *const libc::c_char,
) -> Result<Vec<String>, Error> {
ensure!(
!header.is_null() && unsafe { !(*header).fld_list.is_null() },
"iter_optional_fields: invalid input"
);
let mut result = Vec::default();
for cur_data in unsafe { (*(*header).fld_list).into_iter() } {
let field: *mut mailimf_field = cur_data as *mut _;
unsafe {
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
let optional_field: *mut mailimf_optional_field =
(*field).fld_data.fld_optional_field;
if !optional_field.is_null()
&& !(*optional_field).fld_name.is_null()
&& !(*optional_field).fld_value.is_null()
&& strcasecmp((*optional_field).fld_name, wanted_fld_name) == 0i32
{
result.push(to_string((*optional_field).fld_value));
}
}
}
}
Ok(result)
}
pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> { pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
/* returned addresses are normalized. */ /* returned addresses are normalized. */
let mut recipients: HashSet<String> = Default::default(); let mut recipients: HashSet<String> = Default::default();