mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 13:36:30 +03:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba25f9f77 | ||
|
|
e23704486a | ||
|
|
feee340f4d | ||
|
|
a5cde0d137 | ||
|
|
b08a2b4d2c | ||
|
|
3b6e1b0aae |
@@ -888,6 +888,7 @@ pub unsafe fn mailimf_fields_new(mut fld_list: *mut clist) -> *mut mailimf_field
|
|||||||
(*fields).fld_list = fld_list;
|
(*fields).fld_list = fld_list;
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn mailimf_field_new(
|
pub unsafe fn mailimf_field_new(
|
||||||
mut fld_type: libc::c_int,
|
mut fld_type: libc::c_int,
|
||||||
@@ -947,6 +948,20 @@ pub unsafe fn mailimf_field_new(
|
|||||||
}
|
}
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn mailimf_field_new_subject(fld_subject: *mut mailimf_subject) -> *mut mailimf_field {
|
||||||
|
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
|
||||||
|
field = malloc(::std::mem::size_of::<mailimf_field>() as libc::size_t) as *mut mailimf_field;
|
||||||
|
if field.is_null() {
|
||||||
|
return 0 as *mut mailimf_field;
|
||||||
|
}
|
||||||
|
(*field).fld_type = MAILIMF_FIELD_SUBJECT as libc::c_int;
|
||||||
|
(*field).fld_data.fld_subject = fld_subject;
|
||||||
|
|
||||||
|
field
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn mailimf_orig_date_new(
|
pub unsafe fn mailimf_orig_date_new(
|
||||||
mut dt_date_time: *mut mailimf_date_time,
|
mut dt_date_time: *mut mailimf_date_time,
|
||||||
|
|||||||
@@ -708,6 +708,28 @@ pub unsafe fn mailmime_new(
|
|||||||
return mime;
|
return mime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_new_simple(
|
||||||
|
mut mm_type: libc::c_int,
|
||||||
|
mut mm_mime_fields: *mut mailmime_fields,
|
||||||
|
mut mm_content_type: *mut mailmime_content,
|
||||||
|
mut mm_fields: *mut mailimf_fields,
|
||||||
|
mut mm_msg_mime: *mut Mailmime,
|
||||||
|
) -> *mut Mailmime {
|
||||||
|
mailmime_new(
|
||||||
|
mm_type,
|
||||||
|
std::ptr::null(),
|
||||||
|
0,
|
||||||
|
mm_mime_fields,
|
||||||
|
mm_content_type,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
mm_fields,
|
||||||
|
mm_msg_mime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn mailmime_free(mut mime: *mut Mailmime) {
|
pub unsafe fn mailmime_free(mut mime: *mut Mailmime) {
|
||||||
match (*mime).mm_type {
|
match (*mime).mm_type {
|
||||||
1 => {
|
1 => {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use std::ptr;
|
|||||||
use charset::Charset;
|
use charset::Charset;
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
use libc::{strcmp, strlen, strncmp};
|
use libc::{strcmp, strlen, strncmp};
|
||||||
use mmime::clist::*;
|
|
||||||
use mmime::mailimf::types::*;
|
use mmime::mailimf::types::*;
|
||||||
use mmime::mailimf::*;
|
use mmime::mailimf::*;
|
||||||
use mmime::mailmime::content::*;
|
use mmime::mailmime::content::*;
|
||||||
@@ -21,7 +20,7 @@ use crate::context::Context;
|
|||||||
use crate::dc_simplify::*;
|
use crate::dc_simplify::*;
|
||||||
use crate::dc_strencode::*;
|
use crate::dc_strencode::*;
|
||||||
use crate::dc_tools::*;
|
use crate::dc_tools::*;
|
||||||
use crate::e2ee::*;
|
use crate::e2ee;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::location;
|
use crate::location;
|
||||||
use crate::param::*;
|
use crate::param::*;
|
||||||
@@ -38,7 +37,9 @@ pub struct MimeParser<'a> {
|
|||||||
pub subject: Option<String>,
|
pub subject: Option<String>,
|
||||||
pub is_send_by_messenger: bool,
|
pub is_send_by_messenger: bool,
|
||||||
pub decrypting_failed: bool,
|
pub decrypting_failed: bool,
|
||||||
pub e2ee_helper: E2eeHelper,
|
pub encrypted: bool,
|
||||||
|
pub signatures: HashSet<String>,
|
||||||
|
pub gossipped_addr: HashSet<String>,
|
||||||
pub is_forwarded: bool,
|
pub is_forwarded: bool,
|
||||||
pub reports: Vec<*mut Mailmime>,
|
pub reports: Vec<*mut Mailmime>,
|
||||||
pub is_system_message: SystemMessage,
|
pub is_system_message: SystemMessage,
|
||||||
@@ -92,7 +93,9 @@ impl<'a> MimeParser<'a> {
|
|||||||
subject: None,
|
subject: None,
|
||||||
is_send_by_messenger: false,
|
is_send_by_messenger: false,
|
||||||
decrypting_failed: false,
|
decrypting_failed: false,
|
||||||
e2ee_helper: Default::default(),
|
encrypted: false,
|
||||||
|
signatures: Default::default(),
|
||||||
|
gossipped_addr: Default::default(),
|
||||||
is_forwarded: false,
|
is_forwarded: false,
|
||||||
context,
|
context,
|
||||||
reports: Vec::new(),
|
reports: Vec::new(),
|
||||||
@@ -113,7 +116,11 @@ 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() {
|
||||||
self.e2ee_helper.try_decrypt(self.context, self.mimeroot)?;
|
let (encrypted, signatures, gossipped_addr) =
|
||||||
|
e2ee::try_decrypt(self.context, self.mimeroot)?;
|
||||||
|
self.encrypted = encrypted;
|
||||||
|
self.signatures = signatures;
|
||||||
|
self.gossipped_addr = gossipped_addr;
|
||||||
self.parse_mime_recursive(self.mimeroot);
|
self.parse_mime_recursive(self.mimeroot);
|
||||||
|
|
||||||
if let Some(field) = self.lookup_field("Subject") {
|
if let Some(field) = self.lookup_field("Subject") {
|
||||||
@@ -794,9 +801,9 @@ impl<'a> MimeParser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn do_add_single_part(&mut self, mut part: Part) {
|
fn do_add_single_part(&mut self, mut part: Part) {
|
||||||
if self.e2ee_helper.encrypted && self.e2ee_helper.signatures.len() > 0 {
|
if self.encrypted && self.signatures.len() > 0 {
|
||||||
part.param.set_int(Param::GuranteeE2ee, 1);
|
part.param.set_int(Param::GuranteeE2ee, 1);
|
||||||
} else if self.e2ee_helper.encrypted {
|
} else if self.encrypted {
|
||||||
part.param.set_int(Param::ErroneousE2ee, 0x2);
|
part.param.set_int(Param::ErroneousE2ee, 0x2);
|
||||||
}
|
}
|
||||||
self.parts.push(part);
|
self.parts.push(part);
|
||||||
@@ -1204,50 +1211,61 @@ pub unsafe fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result<Vec<u8>, E
|
|||||||
Err(format_err!("Failed to to decode"))
|
Err(format_err!("Failed to to decode"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe 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();
|
||||||
|
|
||||||
for cur in (*(*imffields).fld_list).into_iter() {
|
for cur in unsafe { (*(*imffields).fld_list).into_iter() } {
|
||||||
let fld = cur as *mut mailimf_field;
|
let fld = cur as *mut mailimf_field;
|
||||||
|
|
||||||
let fld_to: *mut mailimf_to;
|
let fld_to: *mut mailimf_to;
|
||||||
let fld_cc: *mut mailimf_cc;
|
let fld_cc: *mut mailimf_cc;
|
||||||
|
|
||||||
let mut addr_list: *mut mailimf_address_list = ptr::null_mut();
|
let mut addr_list: *mut mailimf_address_list = ptr::null_mut();
|
||||||
|
if fld.is_null() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fld = unsafe { *fld };
|
||||||
|
|
||||||
// TODO match on enums /rtn
|
// TODO match on enums /rtn
|
||||||
match (*fld).fld_type {
|
match fld.fld_type {
|
||||||
13 => {
|
13 => {
|
||||||
fld_to = (*fld).fld_data.fld_to;
|
fld_to = unsafe { fld.fld_data.fld_to };
|
||||||
if !fld_to.is_null() {
|
if !fld_to.is_null() {
|
||||||
addr_list = (*fld_to).to_addr_list
|
addr_list = unsafe { (*fld_to).to_addr_list };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14 => {
|
14 => {
|
||||||
fld_cc = (*fld).fld_data.fld_cc;
|
fld_cc = unsafe { fld.fld_data.fld_cc };
|
||||||
if !fld_cc.is_null() {
|
if !fld_cc.is_null() {
|
||||||
addr_list = (*fld_cc).cc_addr_list
|
addr_list = unsafe { (*fld_cc).cc_addr_list };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !addr_list.is_null() {
|
if !addr_list.is_null() {
|
||||||
for cur2 in (*(*addr_list).ad_list).into_iter() {
|
for cur2 in unsafe { &(*(*addr_list).ad_list) } {
|
||||||
let adr = cur2 as *mut mailimf_address;
|
let adr = cur2 as *mut mailimf_address;
|
||||||
|
|
||||||
if !adr.is_null() {
|
if adr.is_null() {
|
||||||
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
continue;
|
||||||
mailimf_get_recipients_add_addr(&mut recipients, (*adr).ad_data.ad_mailbox);
|
}
|
||||||
} else if (*adr).ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
let adr = unsafe { *adr };
|
||||||
let group: *mut mailimf_group = (*adr).ad_data.ad_group;
|
|
||||||
if !group.is_null() && !(*group).grp_mb_list.is_null() {
|
if adr.ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||||
for cur3 in (*(*(*group).grp_mb_list).mb_list).into_iter() {
|
mailimf_get_recipients_add_addr(&mut recipients, unsafe {
|
||||||
mailimf_get_recipients_add_addr(
|
adr.ad_data.ad_mailbox
|
||||||
&mut recipients,
|
});
|
||||||
cur3 as *mut mailimf_mailbox,
|
} else if adr.ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
||||||
);
|
let group = unsafe { adr.ad_data.ad_group };
|
||||||
}
|
if !group.is_null() && unsafe { !(*group).grp_mb_list.is_null() } {
|
||||||
|
for cur3 in unsafe { &(*(*(*group).grp_mb_list).mb_list) } {
|
||||||
|
mailimf_get_recipients_add_addr(
|
||||||
|
&mut recipients,
|
||||||
|
cur3 as *mut mailimf_mailbox,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1266,30 +1284,26 @@ fn mailimf_get_recipients_add_addr(recipients: &mut HashSet<String>, mb: *mut ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*the result is a pointer to mime, must not be freed*/
|
/*the result is a pointer to mime, must not be freed*/
|
||||||
pub unsafe fn mailimf_find_field(
|
pub fn mailimf_find_field(
|
||||||
header: *mut mailimf_fields,
|
header: *mut mailimf_fields,
|
||||||
wanted_fld_type: libc::c_int,
|
wanted_fld_type: libc::c_int,
|
||||||
) -> *mut mailimf_field {
|
) -> *mut mailimf_field {
|
||||||
if header.is_null() || (*header).fld_list.is_null() {
|
if header.is_null() {
|
||||||
return ptr::null_mut();
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
let mut cur1: *mut clistiter = (*(*header).fld_list).first;
|
|
||||||
while !cur1.is_null() {
|
let header = unsafe { (*header) };
|
||||||
let field: *mut mailimf_field = (if !cur1.is_null() {
|
if header.fld_list.is_null() {
|
||||||
(*cur1).data
|
return ptr::null_mut();
|
||||||
} else {
|
}
|
||||||
ptr::null_mut()
|
|
||||||
}) as *mut mailimf_field;
|
for cur in unsafe { &(*header.fld_list) } {
|
||||||
|
let field = cur as *mut mailimf_field;
|
||||||
if !field.is_null() {
|
if !field.is_null() {
|
||||||
if (*field).fld_type == wanted_fld_type {
|
if unsafe { (*field).fld_type } == wanted_fld_type {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur1 = if !cur1.is_null() {
|
|
||||||
(*cur1).next
|
|
||||||
} else {
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
|
|||||||
@@ -610,7 +610,7 @@ unsafe fn add_parts(
|
|||||||
let icnt = mime_parser.parts.len();
|
let icnt = mime_parser.parts.len();
|
||||||
let mut txt_raw = None;
|
let mut txt_raw = None;
|
||||||
|
|
||||||
let is_ok = context
|
context
|
||||||
.sql
|
.sql
|
||||||
.prepare(
|
.prepare(
|
||||||
"INSERT INTO msgs \
|
"INSERT INTO msgs \
|
||||||
@@ -697,13 +697,10 @@ unsafe fn add_parts(
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.is_ok();
|
.map_err(|err| {
|
||||||
|
cleanup(mime_in_reply_to, mime_references);
|
||||||
if !is_ok {
|
err
|
||||||
// i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record
|
})?;
|
||||||
cleanup(mime_in_reply_to, mime_references);
|
|
||||||
bail!("Cannot write DB.");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
context,
|
context,
|
||||||
@@ -1609,10 +1606,7 @@ fn check_verified_properties(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let contact = Contact::load_from_db(context, from_id)?;
|
let contact = Contact::load_from_db(context, from_id)?;
|
||||||
|
|
||||||
ensure!(
|
ensure!(mimeparser.encrypted, "This message is not encrypted.");
|
||||||
mimeparser.e2ee_helper.encrypted,
|
|
||||||
"This message is not encrypted."
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure, the contact is verified
|
// ensure, the contact is verified
|
||||||
// and the message is signed with a verified key of the sender.
|
// and the message is signed with a verified key of the sender.
|
||||||
@@ -1633,7 +1627,7 @@ fn check_verified_properties(
|
|||||||
|
|
||||||
if let Some(peerstate) = peerstate {
|
if let Some(peerstate) = peerstate {
|
||||||
ensure!(
|
ensure!(
|
||||||
peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures),
|
peerstate.has_verified_key(&mimeparser.signatures),
|
||||||
"The message was sent with non-verified encryption."
|
"The message was sent with non-verified encryption."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1660,7 +1654,7 @@ fn check_verified_properties(
|
|||||||
let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
|
let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
|
||||||
|
|
||||||
// mark gossiped keys (if any) as verified
|
// mark gossiped keys (if any) as verified
|
||||||
if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
|
if mimeparser.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
|
||||||
let peerstate = peerstate.as_mut().unwrap();
|
let peerstate = peerstate.as_mut().unwrap();
|
||||||
|
|
||||||
// if we're here, we know the gossip key is verified:
|
// if we're here, we know the gossip key is verified:
|
||||||
|
|||||||
@@ -217,26 +217,26 @@ pub(crate) fn dc_str_to_color(s: impl AsRef<str>) -> u32 {
|
|||||||
|
|
||||||
/* date/time tools */
|
/* date/time tools */
|
||||||
/* the result is UTC or DC_INVALID_TIMESTAMP */
|
/* the result is UTC or DC_INVALID_TIMESTAMP */
|
||||||
pub(crate) unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
pub(crate) fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||||
let sec = (*date_time).dt_sec;
|
assert!(!date_time.is_null());
|
||||||
let min = (*date_time).dt_min;
|
let dt = unsafe { *date_time };
|
||||||
let hour = (*date_time).dt_hour;
|
|
||||||
let day = (*date_time).dt_day;
|
let sec = dt.dt_sec;
|
||||||
let month = (*date_time).dt_month;
|
let min = dt.dt_min;
|
||||||
let year = (*date_time).dt_year;
|
let hour = dt.dt_hour;
|
||||||
|
let day = dt.dt_day;
|
||||||
|
let month = dt.dt_month;
|
||||||
|
let year = dt.dt_year;
|
||||||
|
|
||||||
let ts = chrono::NaiveDateTime::new(
|
let ts = chrono::NaiveDateTime::new(
|
||||||
chrono::NaiveDate::from_ymd(year, month as u32, day as u32),
|
chrono::NaiveDate::from_ymd(year, month as u32, day as u32),
|
||||||
chrono::NaiveTime::from_hms(hour as u32, min as u32, sec as u32),
|
chrono::NaiveTime::from_hms(hour as u32, min as u32, sec as u32),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (zone_hour, zone_min) = if (*date_time).dt_zone >= 0 {
|
let (zone_hour, zone_min) = if dt.dt_zone >= 0 {
|
||||||
((*date_time).dt_zone / 100, (*date_time).dt_zone % 100)
|
(dt.dt_zone / 100, dt.dt_zone % 100)
|
||||||
} else {
|
} else {
|
||||||
(
|
(-(-dt.dt_zone / 100), -(-dt.dt_zone % 100))
|
||||||
-(-(*date_time).dt_zone / 100),
|
|
||||||
-(-(*date_time).dt_zone % 100),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
|
ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
|
||||||
|
|||||||
627
src/e2ee.rs
627
src/e2ee.rs
@@ -1,11 +1,10 @@
|
|||||||
//! End-to-end encryption support.
|
//! End-to-end encryption support.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use libc::{strcmp, strlen, strncmp};
|
use libc::strlen;
|
||||||
use mmime::clist::*;
|
use mmime::clist::*;
|
||||||
use mmime::mailimf::types::*;
|
use mmime::mailimf::types::*;
|
||||||
use mmime::mailimf::types_helper::*;
|
use mmime::mailimf::types_helper::*;
|
||||||
@@ -18,6 +17,7 @@ use mmime::mailmime::*;
|
|||||||
use mmime::mailprivacy_prepare_mime;
|
use mmime::mailprivacy_prepare_mime;
|
||||||
use mmime::mmapstring::*;
|
use mmime::mmapstring::*;
|
||||||
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
use crate::aheader::*;
|
use crate::aheader::*;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
@@ -47,12 +47,12 @@ pub struct EncryptHelper {
|
|||||||
|
|
||||||
impl EncryptHelper {
|
impl EncryptHelper {
|
||||||
pub fn new(context: &Context) -> Result<EncryptHelper> {
|
pub fn new(context: &Context) -> Result<EncryptHelper> {
|
||||||
let e2ee = context.sql.get_config_int(&context, "e2ee_enabled");
|
let prefer_encrypt = context
|
||||||
let prefer_encrypt = if 0 != e2ee.unwrap_or_default() {
|
.sql
|
||||||
EncryptPreference::Mutual
|
.get_config_int(&context, "e2ee_enabled")
|
||||||
} else {
|
.and_then(EncryptPreference::from_i32)
|
||||||
EncryptPreference::NoPreference
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
let addr = match context.get_config(Config::ConfiguredAddr) {
|
let addr = match context.get_config(Config::ConfiguredAddr) {
|
||||||
None => {
|
None => {
|
||||||
bail!("addr not configured!");
|
bail!("addr not configured!");
|
||||||
@@ -61,6 +61,7 @@ impl EncryptHelper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let public_key = load_or_generate_self_public_key(context, &addr)?;
|
let public_key = load_or_generate_self_public_key(context, &addr)?;
|
||||||
|
|
||||||
Ok(EncryptHelper {
|
Ok(EncryptHelper {
|
||||||
prefer_encrypt,
|
prefer_encrypt,
|
||||||
addr,
|
addr,
|
||||||
@@ -83,11 +84,13 @@ impl EncryptHelper {
|
|||||||
mut in_out_message: *mut Mailmime,
|
mut in_out_message: *mut Mailmime,
|
||||||
imffields_unprotected: *mut mailimf_fields,
|
imffields_unprotected: *mut mailimf_fields,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
// libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||||
We just expect the root as being given to this function. */
|
// We just expect the root as being given to this function.
|
||||||
if in_out_message.is_null() || unsafe { !(*in_out_message).mm_parent.is_null() } {
|
ensure!(
|
||||||
bail!("corrupted inputs");
|
!in_out_message.is_null() && unsafe { (*in_out_message).mm_parent.is_null() },
|
||||||
}
|
"corrupted inputs"
|
||||||
|
);
|
||||||
|
|
||||||
if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -106,13 +109,14 @@ impl EncryptHelper {
|
|||||||
None => {
|
None => {
|
||||||
let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
||||||
if e2ee_guaranteed {
|
if e2ee_guaranteed {
|
||||||
bail!("{}", msg);
|
return Err(format_err!("{}", msg));
|
||||||
} else {
|
} else {
|
||||||
info!(context, "{}", msg);
|
info!(context, "{}", msg);
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
||||||
info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
@@ -136,30 +140,24 @@ impl EncryptHelper {
|
|||||||
let sign_key = {
|
let sign_key = {
|
||||||
keyring.add_ref(&self.public_key);
|
keyring.add_ref(&self.public_key);
|
||||||
let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
||||||
if key.is_none() {
|
ensure!(key.is_some(), "no own private key found");
|
||||||
bail!("no own private key found")
|
|
||||||
}
|
|
||||||
key
|
key
|
||||||
};
|
};
|
||||||
|
|
||||||
/* encrypt message */
|
// encrypt message
|
||||||
unsafe {
|
unsafe {
|
||||||
mailprivacy_prepare_mime(in_out_message);
|
mailprivacy_prepare_mime(in_out_message);
|
||||||
let mut part_to_encrypt: *mut Mailmime =
|
let mut part_to_encrypt = (*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||||
(*in_out_message).mm_data.mm_message.mm_msg_mime;
|
|
||||||
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||||
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
|
let imffields_encrypted = mailimf_fields_new_empty();
|
||||||
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
|
|
||||||
let message_to_encrypt: *mut Mailmime = mailmime_new(
|
// mailmime_new_message_data() calls mailmime_fields_new_with_version()
|
||||||
|
// which would add the unwanted MIME-Version:-header
|
||||||
|
let message_to_encrypt = mailmime_new_simple(
|
||||||
MAILMIME_MESSAGE as libc::c_int,
|
MAILMIME_MESSAGE as libc::c_int,
|
||||||
ptr::null(),
|
|
||||||
0 as libc::size_t,
|
|
||||||
mailmime_fields_new_empty(),
|
mailmime_fields_new_empty(),
|
||||||
mailmime_get_content_message(),
|
mailmime_get_content_message(),
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
imffields_encrypted,
|
imffields_encrypted,
|
||||||
part_to_encrypt,
|
part_to_encrypt,
|
||||||
);
|
);
|
||||||
@@ -168,12 +166,13 @@ impl EncryptHelper {
|
|||||||
wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* memoryhole headers: move some headers into encrypted part */
|
// memoryhole headers: move some headers into encrypted part
|
||||||
// XXX note we can't use clist's into_iter() because the loop body also removes items
|
// XXX note we can't use clist's into_iter() because the loop body also removes items
|
||||||
let mut cur: *mut clistiter = (*(*imffields_unprotected).fld_list).first;
|
let mut cur = (*(*imffields_unprotected).fld_list).first;
|
||||||
while !cur.is_null() {
|
while !cur.is_null() {
|
||||||
let field: *mut mailimf_field = (*cur).data as *mut mailimf_field;
|
let field = (*cur).data as *mut mailimf_field;
|
||||||
let mut move_to_encrypted = false;
|
let mut move_to_encrypted = false;
|
||||||
|
|
||||||
if !field.is_null() {
|
if !field.is_null() {
|
||||||
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||||
move_to_encrypted = true;
|
move_to_encrypted = true;
|
||||||
@@ -189,6 +188,7 @@ impl EncryptHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if move_to_encrypted {
|
if move_to_encrypted {
|
||||||
mailimf_fields_add(imffields_encrypted, field);
|
mailimf_fields_add(imffields_encrypted, field);
|
||||||
cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
||||||
@@ -196,48 +196,24 @@ impl EncryptHelper {
|
|||||||
cur = (*cur).next;
|
cur = (*cur).next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let subject: *mut mailimf_subject = mailimf_subject_new("...".strdup());
|
|
||||||
mailimf_fields_add(
|
let subject = mailimf_subject_new("...".strdup());
|
||||||
imffields_unprotected,
|
mailimf_fields_add(imffields_unprotected, mailimf_field_new_subject(subject));
|
||||||
mailimf_field_new(
|
|
||||||
MAILIMF_FIELD_SUBJECT as libc::c_int,
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
subject,
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
wrapmime::append_ct_param(
|
wrapmime::append_ct_param(
|
||||||
(*part_to_encrypt).mm_content_type,
|
(*part_to_encrypt).mm_content_type,
|
||||||
"protected-headers",
|
"protected-headers",
|
||||||
"v1",
|
"v1",
|
||||||
)?;
|
)?;
|
||||||
let plain: *mut MMAPString =
|
let plain = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||||
mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
let mut col = 0;
|
||||||
let mut col: libc::c_int = 0i32;
|
|
||||||
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||||
mailmime_free(message_to_encrypt);
|
mailmime_free(message_to_encrypt);
|
||||||
if (*plain).str_0.is_null() || (*plain).len <= 0 {
|
|
||||||
bail!("could not write/allocate");
|
ensure!(
|
||||||
}
|
!(*plain).str_0.is_null() && (*plain).len > 0,
|
||||||
|
"could not write/allocate"
|
||||||
|
);
|
||||||
|
|
||||||
let ctext = dc_pgp_pk_encrypt(
|
let ctext = dc_pgp_pk_encrypt(
|
||||||
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
||||||
@@ -246,165 +222,148 @@ impl EncryptHelper {
|
|||||||
);
|
);
|
||||||
mmap_string_free(plain);
|
mmap_string_free(plain);
|
||||||
|
|
||||||
if let Ok(ctext_v) = ctext {
|
let ctext_v = ctext?;
|
||||||
/* create MIME-structure that will contain the encrypted text */
|
|
||||||
let mut encrypted_part: *mut Mailmime = new_data_part(
|
|
||||||
ptr::null_mut(),
|
|
||||||
0 as libc::size_t,
|
|
||||||
"multipart/encrypted",
|
|
||||||
MAILMIME_MECHANISM_BASE64,
|
|
||||||
)?;
|
|
||||||
let content: *mut mailmime_content = (*encrypted_part).mm_content_type;
|
|
||||||
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
|
||||||
let version_mime: *mut Mailmime = new_data_part(
|
|
||||||
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
|
||||||
strlen(VERSION_CONTENT.as_mut_ptr()),
|
|
||||||
"application/pgp-encrypted",
|
|
||||||
MAILMIME_MECHANISM_7BIT,
|
|
||||||
)?;
|
|
||||||
mailmime_smart_add_part(encrypted_part, version_mime);
|
|
||||||
|
|
||||||
// we assume that ctext_v is not dropped until the end
|
// create MIME-structure that will contain the encrypted text
|
||||||
// of this if-scope
|
let mut encrypted_part = new_data_part(
|
||||||
let ctext_part: *mut Mailmime = new_data_part(
|
ptr::null_mut(),
|
||||||
ctext_v.as_ptr() as *mut libc::c_void,
|
0 as libc::size_t,
|
||||||
ctext_v.len(),
|
"multipart/encrypted",
|
||||||
"application/octet-stream",
|
MAILMIME_MECHANISM_BASE64,
|
||||||
MAILMIME_MECHANISM_7BIT,
|
)?;
|
||||||
)?;
|
let content = (*encrypted_part).mm_content_type;
|
||||||
mailmime_smart_add_part(encrypted_part, ctext_part);
|
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
|
||||||
(*encrypted_part).mm_parent = in_out_message;
|
let version_mime = new_data_part(
|
||||||
let gossiped = !&gossip_headers.is_empty();
|
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||||
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||||
Ok(true)
|
"application/pgp-encrypted",
|
||||||
} else {
|
MAILMIME_MECHANISM_7BIT,
|
||||||
bail!("encryption failed")
|
)?;
|
||||||
}
|
mailmime_smart_add_part(encrypted_part, version_mime);
|
||||||
|
|
||||||
|
// we assume that ctext_v is not dropped until the end
|
||||||
|
// of this if-scope
|
||||||
|
let ctext_part = new_data_part(
|
||||||
|
ctext_v.as_ptr() as *mut libc::c_void,
|
||||||
|
ctext_v.len(),
|
||||||
|
"application/octet-stream",
|
||||||
|
MAILMIME_MECHANISM_7BIT,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||||
|
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||||
|
(*encrypted_part).mm_parent = in_out_message;
|
||||||
|
let gossiped = !&gossip_headers.is_empty();
|
||||||
|
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
pub fn try_decrypt(
|
||||||
pub struct E2eeHelper {
|
context: &Context,
|
||||||
// for decrypting only
|
in_out_message: *mut Mailmime,
|
||||||
pub encrypted: bool,
|
) -> Result<(bool, HashSet<String>, HashSet<String>)> {
|
||||||
pub signatures: HashSet<String>,
|
let mut encrypted = false;
|
||||||
pub gossipped_addr: HashSet<String>,
|
let mut signatures = HashSet::default();
|
||||||
}
|
let mut gossipped_addr = HashSet::default();
|
||||||
|
|
||||||
impl E2eeHelper {
|
// just a pointer into mailmime structure, must not be freed
|
||||||
pub unsafe fn try_decrypt(
|
let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) };
|
||||||
&mut self,
|
let mut message_time = 0;
|
||||||
context: &Context,
|
let mut from = None;
|
||||||
in_out_message: *mut Mailmime,
|
let mut private_keyring = Keyring::default();
|
||||||
) -> Result<()> {
|
let mut public_keyring_for_validate = Keyring::default();
|
||||||
/*just a pointer into mailmime structure, must not be freed*/
|
let mut gossip_headers = ptr::null_mut();
|
||||||
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
|
|
||||||
let mut message_time = 0;
|
|
||||||
let mut from = None;
|
|
||||||
let mut private_keyring = Keyring::default();
|
|
||||||
let mut public_keyring_for_validate = Keyring::default();
|
|
||||||
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
|
|
||||||
|
|
||||||
// XXX do wrapmime:: helper for the next block
|
// XXX do wrapmime:: helper for the next block
|
||||||
if !(in_out_message.is_null() || imffields.is_null()) {
|
if !(in_out_message.is_null() || imffields.is_null()) {
|
||||||
let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||||
|
|
||||||
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
|
if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } {
|
||||||
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
|
let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list };
|
||||||
}
|
from = mailimf_find_first_addr(mb_list);
|
||||||
|
}
|
||||||
|
|
||||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||||
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
|
if !field.is_null() && unsafe { !(*field).fld_data.fld_orig_date.is_null() } {
|
||||||
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
|
let orig_date = unsafe { (*field).fld_data.fld_orig_date };
|
||||||
if !orig_date.is_null() {
|
|
||||||
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
|
if !orig_date.is_null() {
|
||||||
if message_time != 0 && message_time > time() {
|
let dt = unsafe { (*orig_date).dt_date_time };
|
||||||
message_time = time()
|
message_time = dc_timestamp_from_date(dt);
|
||||||
}
|
if message_time != 0 && message_time > time() {
|
||||||
|
message_time = time()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut peerstate = None;
|
}
|
||||||
let autocryptheader = from
|
let mut peerstate = None;
|
||||||
.as_ref()
|
let autocryptheader = from
|
||||||
.and_then(|from| Aheader::from_imffields(from, imffields));
|
.as_ref()
|
||||||
if message_time > 0 {
|
.and_then(|from| Aheader::from_imffields(from, imffields));
|
||||||
if let Some(ref from) = from {
|
if message_time > 0 {
|
||||||
peerstate = Peerstate::from_addr(context, &context.sql, from);
|
if let Some(ref from) = from {
|
||||||
|
peerstate = Peerstate::from_addr(context, &context.sql, from);
|
||||||
|
|
||||||
if let Some(ref mut peerstate) = peerstate {
|
if let Some(ref mut peerstate) = peerstate {
|
||||||
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)?;
|
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||||
} else if message_time > peerstate.last_seen_autocrypt
|
} else if message_time > peerstate.last_seen_autocrypt
|
||||||
&& !contains_report(in_out_message)
|
&& !contains_report(in_out_message)
|
||||||
{
|
{
|
||||||
peerstate.degrade_encryption(message_time);
|
peerstate.degrade_encryption(message_time);
|
||||||
peerstate.save_to_db(&context.sql, false)?;
|
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||||
}
|
|
||||||
} else if let Some(ref header) = autocryptheader {
|
|
||||||
let p = Peerstate::from_header(context, header, message_time);
|
|
||||||
p.save_to_db(&context.sql, true)?;
|
|
||||||
peerstate = Some(p);
|
|
||||||
}
|
}
|
||||||
|
} else if let Some(ref header) = autocryptheader {
|
||||||
|
let p = Peerstate::from_header(context, header, message_time);
|
||||||
|
p.save_to_db(&context.sql, true).unwrap();
|
||||||
|
peerstate = Some(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* load private key for decryption */
|
}
|
||||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
/* load private key for decryption */
|
||||||
if let Some(self_addr) = self_addr {
|
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||||
if private_keyring.load_self_private_for_decrypting(
|
if let Some(self_addr) = self_addr {
|
||||||
|
if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) {
|
||||||
|
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
||||||
|
peerstate =
|
||||||
|
Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default());
|
||||||
|
}
|
||||||
|
if let Some(ref peerstate) = peerstate {
|
||||||
|
if peerstate.degrade_event.is_some() {
|
||||||
|
handle_degrade_event(context, &peerstate)?;
|
||||||
|
}
|
||||||
|
if let Some(ref key) = peerstate.gossip_key {
|
||||||
|
public_keyring_for_validate.add_ref(key);
|
||||||
|
}
|
||||||
|
if let Some(ref key) = peerstate.public_key {
|
||||||
|
public_keyring_for_validate.add_ref(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted = decrypt_if_autocrypt_message(
|
||||||
context,
|
context,
|
||||||
self_addr,
|
in_out_message,
|
||||||
&context.sql,
|
&private_keyring,
|
||||||
) {
|
&public_keyring_for_validate,
|
||||||
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
&mut signatures,
|
||||||
peerstate =
|
&mut gossip_headers,
|
||||||
Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default());
|
)?;
|
||||||
}
|
if !gossip_headers.is_null() {
|
||||||
if let Some(ref peerstate) = peerstate {
|
gossipped_addr =
|
||||||
if peerstate.degrade_event.is_some() {
|
update_gossip_peerstates(context, message_time, imffields, gossip_headers)?;
|
||||||
handle_degrade_event(context, &peerstate)?;
|
|
||||||
}
|
|
||||||
if let Some(ref key) = peerstate.gossip_key {
|
|
||||||
public_keyring_for_validate.add_ref(key);
|
|
||||||
}
|
|
||||||
if let Some(ref key) = peerstate.public_key {
|
|
||||||
public_keyring_for_validate.add_ref(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match decrypt_if_autocrypt_message(
|
|
||||||
context,
|
|
||||||
in_out_message,
|
|
||||||
&private_keyring,
|
|
||||||
&public_keyring_for_validate,
|
|
||||||
&mut self.signatures,
|
|
||||||
&mut gossip_headers,
|
|
||||||
) {
|
|
||||||
Ok(res) => {
|
|
||||||
self.encrypted = res;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
bail!("failed to decrypt: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !gossip_headers.is_null() {
|
|
||||||
self.gossipped_addr = update_gossip_peerstates(
|
|
||||||
context,
|
|
||||||
message_time,
|
|
||||||
imffields,
|
|
||||||
gossip_headers,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !gossip_headers.is_null() {
|
|
||||||
mailimf_fields_free(gossip_headers);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
if !gossip_headers.is_null() {
|
||||||
|
unsafe { mailimf_fields_free(gossip_headers) };
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((encrypted, signatures, gossipped_addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_data_part(
|
fn new_data_part(
|
||||||
@@ -414,31 +373,34 @@ fn new_data_part(
|
|||||||
default_encoding: u32,
|
default_encoding: u32,
|
||||||
) -> Result<*mut Mailmime> {
|
) -> Result<*mut Mailmime> {
|
||||||
let content = new_content_type(&content_type)?;
|
let content = new_content_type(&content_type)?;
|
||||||
unsafe {
|
let mut encoding = ptr::null_mut();
|
||||||
let mut encoding: *mut mailmime_mechanism = ptr::null_mut();
|
if wrapmime::content_type_needs_encoding(content) {
|
||||||
if wrapmime::content_type_needs_encoding(content) {
|
encoding = unsafe { mailmime_mechanism_new(default_encoding as i32, ptr::null_mut()) };
|
||||||
encoding = mailmime_mechanism_new(default_encoding as i32, ptr::null_mut());
|
ensure!(!encoding.is_null(), "failed to create encoding");
|
||||||
ensure!(!encoding.is_null(), "failed to create encoding");
|
|
||||||
}
|
|
||||||
let mime_fields = mailmime_fields_new_with_data(
|
|
||||||
encoding,
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
);
|
|
||||||
ensure!(!mime_fields.is_null(), "internal mime error");
|
|
||||||
|
|
||||||
let mime = mailmime_new_empty(content, mime_fields);
|
|
||||||
ensure!(!mime.is_null(), "internal mime error");
|
|
||||||
|
|
||||||
if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int {
|
|
||||||
if !data.is_null() && data_bytes > 0 {
|
|
||||||
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(mime);
|
|
||||||
}
|
}
|
||||||
|
let mime_fields = {
|
||||||
|
unsafe {
|
||||||
|
mailmime_fields_new_with_data(
|
||||||
|
encoding,
|
||||||
|
ptr::null_mut(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ensure!(!mime_fields.is_null(), "internal mime error");
|
||||||
|
|
||||||
|
let mime = unsafe { mailmime_new_empty(content, mime_fields) };
|
||||||
|
ensure!(!mime.is_null(), "internal mime error");
|
||||||
|
|
||||||
|
if unsafe { (*mime).mm_type } == MAILMIME_SINGLE as libc::c_int {
|
||||||
|
if !data.is_null() && data_bytes > 0 {
|
||||||
|
unsafe { mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load public key from database or generate a new one.
|
/// Load public key from database or generate a new one.
|
||||||
@@ -488,7 +450,7 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn update_gossip_peerstates(
|
fn update_gossip_peerstates(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
message_time: i64,
|
message_time: i64,
|
||||||
imffields: *mut mailimf_fields,
|
imffields: *mut mailimf_fields,
|
||||||
@@ -498,21 +460,27 @@ unsafe fn update_gossip_peerstates(
|
|||||||
let mut recipients: Option<HashSet<String>> = None;
|
let mut recipients: Option<HashSet<String>> = None;
|
||||||
let mut gossipped_addr: HashSet<String> = Default::default();
|
let mut gossipped_addr: HashSet<String> = Default::default();
|
||||||
|
|
||||||
for cur_data in (*(*gossip_headers).fld_list).into_iter() {
|
for cur_data in unsafe { (*(*gossip_headers).fld_list).into_iter() } {
|
||||||
let field: *mut mailimf_field = cur_data as *mut _;
|
let field = cur_data as *mut mailimf_field;
|
||||||
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
if field.is_null() {
|
||||||
let optional_field = (*field).fld_data.fld_optional_field;
|
continue;
|
||||||
if !optional_field.is_null()
|
}
|
||||||
&& !(*optional_field).fld_name.is_null()
|
|
||||||
&& strcasecmp(
|
let field = unsafe { *field };
|
||||||
(*optional_field).fld_name,
|
|
||||||
b"Autocrypt-Gossip\x00" as *const u8 as *const libc::c_char,
|
if field.fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||||
) == 0i32
|
let optional_field = unsafe { field.fld_data.fld_optional_field };
|
||||||
|
if optional_field.is_null() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let optional_field = unsafe { *optional_field };
|
||||||
|
if !optional_field.fld_name.is_null()
|
||||||
|
&& as_str(optional_field.fld_name) == "Autocrypt-Gossip"
|
||||||
{
|
{
|
||||||
let value = CStr::from_ptr((*optional_field).fld_value)
|
let value = to_string_lossy(optional_field.fld_value);
|
||||||
.to_str()
|
let gossip_header = Aheader::from_str(&value);
|
||||||
.unwrap();
|
|
||||||
let gossip_header = Aheader::from_str(value);
|
|
||||||
if let Ok(ref header) = gossip_header {
|
if let Ok(ref header) = gossip_header {
|
||||||
if recipients.is_none() {
|
if recipients.is_none() {
|
||||||
recipients = Some(mailimf_get_recipients(imffields));
|
recipients = Some(mailimf_get_recipients(imffields));
|
||||||
@@ -582,16 +550,15 @@ fn decrypt_if_autocrypt_message(
|
|||||||
public_keyring_for_validate,
|
public_keyring_for_validate,
|
||||||
ret_valid_signatures,
|
ret_valid_signatures,
|
||||||
)?;
|
)?;
|
||||||
/* decrypted_mime is a dangling pointer which we now put into
|
// decrypted_mime is a dangling pointer which we now put into mailmime's Ownership
|
||||||
mailmime's Ownership */
|
|
||||||
unsafe {
|
unsafe {
|
||||||
mailmime_substitute(mime, decrypted_mime);
|
mailmime_substitute(mime, decrypted_mime);
|
||||||
mailmime_free(mime);
|
mailmime_free(mime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* finally, let's also return gossip headers
|
// finally, let's also return gossip headers
|
||||||
XXX better return parsed headers so that upstream
|
// XXX better return parsed headers so that upstream
|
||||||
does not need to dive into mmime-stuff again. */
|
// does not need to dive into mmime-stuff again.
|
||||||
unsafe {
|
unsafe {
|
||||||
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
|
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
|
||||||
let mut dummy: libc::size_t = 0;
|
let mut dummy: libc::size_t = 0;
|
||||||
@@ -604,11 +571,12 @@ fn decrypt_if_autocrypt_message(
|
|||||||
) == MAILIMF_NO_ERROR as libc::c_int
|
) == MAILIMF_NO_ERROR as libc::c_int
|
||||||
&& !test.is_null()
|
&& !test.is_null()
|
||||||
{
|
{
|
||||||
*ret_gossip_headers = test
|
*ret_gossip_headers = test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(true);
|
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_part(
|
fn decrypt_part(
|
||||||
@@ -634,74 +602,75 @@ fn decrypt_part(
|
|||||||
|
|
||||||
let (decoded_data, decoded_data_bytes) =
|
let (decoded_data, decoded_data_bytes) =
|
||||||
wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
|
wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
|
||||||
/* encrypted, non-NULL decoded data in decoded_data now ...
|
|
||||||
Note that we need to take care of freeing decoded_data ourself,
|
// encrypted, non-NULL decoded data in decoded_data now ...
|
||||||
after encryption has been attempted.
|
// Note that we need to take care of freeing decoded_data ourself,
|
||||||
*/
|
// after encryption has been attempted.
|
||||||
let mut ret_decrypted_mime = ptr::null_mut();
|
let mut ret_decrypted_mime = ptr::null_mut();
|
||||||
|
|
||||||
unsafe {
|
ensure!(!decoded_data.is_null(), "Missing data");
|
||||||
if has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int) {
|
let data = unsafe { std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes) };
|
||||||
/* we should only have one decryption happening */
|
if has_decrypted_pgp_armor(data) {
|
||||||
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
|
// we should only have one decryption happening
|
||||||
|
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
|
||||||
|
|
||||||
let plain = match dc_pgp_pk_decrypt(
|
let plain = match dc_pgp_pk_decrypt(
|
||||||
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes),
|
data,
|
||||||
&private_keyring,
|
&private_keyring,
|
||||||
&public_keyring_for_validate,
|
&public_keyring_for_validate,
|
||||||
Some(ret_valid_signatures),
|
Some(ret_valid_signatures),
|
||||||
) {
|
) {
|
||||||
Ok(plain) => {
|
Ok(plain) => {
|
||||||
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
||||||
plain
|
plain
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
mmap_string_unref(decoded_data);
|
unsafe { mmap_string_unref(decoded_data) };
|
||||||
bail!("could not decrypt: {}", err)
|
bail!("could not decrypt: {}", err)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let plain_bytes = plain.len();
|
let plain_bytes = plain.len();
|
||||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||||
|
|
||||||
let mut index: libc::size_t = 0;
|
let mut index = 0;
|
||||||
let mut decrypted_mime: *mut Mailmime = ptr::null_mut();
|
let mut decrypted_mime = ptr::null_mut();
|
||||||
if mailmime_parse(
|
if unsafe {
|
||||||
|
mailmime_parse(
|
||||||
plain_buf as *const _,
|
plain_buf as *const _,
|
||||||
plain_bytes,
|
plain_bytes,
|
||||||
&mut index,
|
&mut index,
|
||||||
&mut decrypted_mime,
|
&mut decrypted_mime,
|
||||||
) != MAIL_NO_ERROR as libc::c_int
|
)
|
||||||
|| decrypted_mime.is_null()
|
} != MAIL_NO_ERROR as libc::c_int
|
||||||
{
|
|| decrypted_mime.is_null()
|
||||||
if !decrypted_mime.is_null() {
|
{
|
||||||
mailmime_free(decrypted_mime);
|
if !decrypted_mime.is_null() {
|
||||||
}
|
unsafe { mailmime_free(decrypted_mime) };
|
||||||
} else {
|
|
||||||
ret_decrypted_mime = 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);
|
std::mem::forget(plain);
|
||||||
|
ret_decrypted_mime = decrypted_mime;
|
||||||
}
|
}
|
||||||
mmap_string_unref(decoded_data);
|
|
||||||
}
|
}
|
||||||
|
unsafe { mmap_string_unref(decoded_data) };
|
||||||
|
|
||||||
Ok(ret_decrypted_mime)
|
Ok(ret_decrypted_mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: libc::c_int) -> bool {
|
fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||||
let str_end: *const libc::c_uchar = (str__ as *const libc::c_uchar).offset(str_bytes as isize);
|
if let Some(index) = input.iter().position(|b| *b > b' ') {
|
||||||
let mut p: *const libc::c_uchar = str__ as *const libc::c_uchar;
|
if input.len() - index > 26 {
|
||||||
while p < str_end {
|
let start = index;
|
||||||
if *p as libc::c_int > ' ' as i32 {
|
let end = start + 27;
|
||||||
break;
|
|
||||||
|
return &input[start..end] == b"-----BEGIN PGP MESSAGE-----";
|
||||||
}
|
}
|
||||||
p = p.offset(1isize);
|
|
||||||
str_bytes -= 1
|
|
||||||
}
|
}
|
||||||
str_bytes > 27i32
|
|
||||||
&& strncmp(
|
false
|
||||||
p as *const libc::c_char,
|
|
||||||
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
|
|
||||||
27,
|
|
||||||
) == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a MIME structure contains a multipart/report part.
|
/// Check if a MIME structure contains a multipart/report part.
|
||||||
@@ -712,29 +681,31 @@ unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: lib
|
|||||||
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
||||||
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
||||||
/// that we could use the normal Autocrypt processing.
|
/// that we could use the normal Autocrypt processing.
|
||||||
unsafe fn contains_report(mime: *mut Mailmime) -> bool {
|
fn contains_report(mime: *mut Mailmime) -> bool {
|
||||||
if (*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
assert!(!mime.is_null());
|
||||||
if (*(*(*mime).mm_content_type).ct_type).tp_type
|
let mime = unsafe { *mime };
|
||||||
== MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
|
||||||
&& (*(*(*(*mime).mm_content_type).ct_type)
|
if mime.mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||||
.tp_data
|
let tp_type = unsafe { (*(*mime.mm_content_type).ct_type).tp_type };
|
||||||
.tp_composite_type)
|
let ct_type =
|
||||||
.ct_type
|
unsafe { (*(*(*mime.mm_content_type).ct_type).tp_data.tp_composite_type).ct_type };
|
||||||
== MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
|
||||||
&& strcmp(
|
if tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||||
(*(*mime).mm_content_type).ct_subtype,
|
&& ct_type == MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
||||||
b"report\x00" as *const u8 as *const libc::c_char,
|
&& as_str(unsafe { (*mime.mm_content_type).ct_subtype }) == "report"
|
||||||
) == 0i32
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for cur_data in (*(*(*mime).mm_mime_fields).fld_list).into_iter() {
|
|
||||||
|
for cur_data in unsafe { (*(*mime.mm_mime_fields).fld_list).into_iter() } {
|
||||||
if contains_report(cur_data as *mut Mailmime) {
|
if contains_report(cur_data as *mut Mailmime) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int {
|
} else if mime.mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||||
if contains_report((*mime).mm_data.mm_message.mm_msg_mime) {
|
let m = unsafe { mime.mm_data.mm_message.mm_msg_mime };
|
||||||
|
|
||||||
|
if contains_report(m) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -869,4 +840,22 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
|||||||
assert_eq!(res0.unwrap(), res1.unwrap());
|
assert_eq!(res0.unwrap(), res1.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_has_decrypted_pgp_armor() {
|
||||||
|
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||||
|
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||||
|
|
||||||
|
let data = b" \n-----BEGIN PGP MESSAGE-----";
|
||||||
|
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||||
|
|
||||||
|
let data = b" -----BEGIN PGP MESSAGE---";
|
||||||
|
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||||
|
|
||||||
|
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||||
|
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||||
|
|
||||||
|
let data = b"blas";
|
||||||
|
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ pub fn handle_securejoin_handshake(
|
|||||||
could_not_establish_secure_connection(
|
could_not_establish_secure_connection(
|
||||||
context,
|
context,
|
||||||
contact_chat_id,
|
contact_chat_id,
|
||||||
if mimeparser.e2ee_helper.encrypted {
|
if mimeparser.encrypted {
|
||||||
"No valid signature."
|
"No valid signature."
|
||||||
} else {
|
} else {
|
||||||
"Not encrypted."
|
"Not encrypted."
|
||||||
@@ -693,17 +693,16 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> Res
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRef<str>) -> bool {
|
fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRef<str>) -> bool {
|
||||||
if !mimeparser.e2ee_helper.encrypted {
|
if !mimeparser.encrypted {
|
||||||
warn!(mimeparser.context, "Message not encrypted.",);
|
warn!(mimeparser.context, "Message not encrypted.",);
|
||||||
false
|
false
|
||||||
} else if mimeparser.e2ee_helper.signatures.len() <= 0 {
|
} else if mimeparser.signatures.len() <= 0 {
|
||||||
warn!(mimeparser.context, "Message not signed.",);
|
warn!(mimeparser.context, "Message not signed.",);
|
||||||
false
|
false
|
||||||
} else if expected_fingerprint.as_ref().is_empty() {
|
} else if expected_fingerprint.as_ref().is_empty() {
|
||||||
warn!(mimeparser.context, "Fingerprint for comparison missing.",);
|
warn!(mimeparser.context, "Fingerprint for comparison missing.",);
|
||||||
false
|
false
|
||||||
} else if !mimeparser
|
} else if !mimeparser
|
||||||
.e2ee_helper
|
|
||||||
.signatures
|
.signatures
|
||||||
.contains(expected_fingerprint.as_ref())
|
.contains(expected_fingerprint.as_ref())
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user