diff --git a/src/chat.rs b/src/chat.rs index 3a6a67dc0..908b879fc 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -854,7 +854,7 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> { /// However, this does not imply, the message really reached the recipient - /// sending may be delayed eg. due to network problems. However, from your /// view, you're done with the message. Sooner or later it will find its way. -pub unsafe fn send_msg<'a>( +pub fn send_msg<'a>( context: &'a Context, chat_id: u32, msg: &mut Message<'a>, @@ -872,7 +872,7 @@ pub unsafe fn send_msg<'a>( } ensure!( - job_send_msg(context, msg.id) != 0, + unsafe { job_send_msg(context, msg.id) } != 0, "Failed to initiate send job" ); @@ -1394,7 +1394,7 @@ pub unsafe fn add_contact_to_chat(context: &Context, chat_id: u32, contact_id: u // TODO should return bool /rtn #[allow(non_snake_case)] -pub unsafe fn add_contact_to_chat_ex( +pub fn add_contact_to_chat_ex( context: &Context, chat_id: u32, contact_id: u32, @@ -1407,7 +1407,7 @@ pub unsafe fn add_contact_to_chat_ex( if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL { return 0; } - let mut msg = dc_msg_new_untyped(context); + let mut msg = unsafe { dc_msg_new_untyped(context) }; reset_gossiped_timestamp(context, chat_id); let contact = contact.unwrap(); diff --git a/src/configure/mod.rs b/src/configure/mod.rs index 070e38e35..2d387c5a5 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -33,7 +33,7 @@ macro_rules! progress { // connect pub unsafe fn configure(context: &Context) { - if 0 != dc_has_ongoing(context) { + if dc_has_ongoing(context) { warn!( context, 0, "There is already another ongoing process running.", @@ -43,6 +43,7 @@ pub unsafe fn configure(context: &Context) { job_kill_action(context, Action::ConfigureImap); job_add(context, Action::ConfigureImap, 0, Params::new(), 0); } + /// Check if the context is already configured. pub fn dc_is_configured(context: &Context) -> libc::c_int { if context @@ -56,17 +57,6 @@ pub fn dc_is_configured(context: &Context) -> libc::c_int { 0 } } -/// Check if there is an ongoing process. -unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int { - let s_a = context.running_state.clone(); - let s = s_a.read().unwrap(); - - if s.ongoing_running || !s.shall_stop_ongoing { - 1 - } else { - 0 - } -} /******************************************************************************* * Configure JOB @@ -80,7 +70,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) { let mut ongoing_allocated_here = false; let mut param_autoconfig: Option = None; - if !(0 == dc_alloc_ongoing(context)) { + if dc_alloc_ongoing(context) { ongoing_allocated_here = true; if !context.sql.is_open() { error!(context, 0, "Cannot configure, database not opened.",); @@ -591,7 +581,45 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) { progress!(context, (if success { 1000 } else { 0 })); } -/* File Structure like in C: */ +/******************************************************************************* + * Ongoing process allocation/free/check + ******************************************************************************/ + +pub fn dc_alloc_ongoing(context: &Context) -> bool { + if dc_has_ongoing(context) { + warn!( + context, + 0, "There is already another ongoing process running.", + ); + + false + } else { + let s_a = context.running_state.clone(); + let mut s = s_a.write().unwrap(); + + s.ongoing_running = true; + s.shall_stop_ongoing = false; + + true + } +} + +pub fn dc_free_ongoing(context: &Context) { + let s_a = context.running_state.clone(); + let mut s = s_a.write().unwrap(); + + s.ongoing_running = false; + s.shall_stop_ongoing = true; +} + + +fn dc_has_ongoing(context: &Context) -> bool { + let s_a = context.running_state.clone(); + let s = s_a.read().unwrap(); + + s.ongoing_running || !s.shall_stop_ongoing +} + /******************************************************************************* * Connect to configured account @@ -624,35 +652,6 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_ * Configure a Context ******************************************************************************/ -/// Request an ongoing process to start. -/// Returns 0=process started, 1=not started, there is running another process -pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int { - if 0 != dc_has_ongoing(context) { - warn!( - context, - 0, "There is already another ongoing process running.", - ); - return 0; - } - let s_a = context.running_state.clone(); - let mut s = s_a.write().unwrap(); - - s.ongoing_running = true; - s.shall_stop_ongoing = false; - - 1 -} - -/// Frees the process allocated with dc_alloc_ongoing() - independently of dc_shall_stop_ongoing. -/// If dc_alloc_ongoing() fails, this function MUST NOT be called. -pub unsafe fn dc_free_ongoing(context: &Context) { - let s_a = context.running_state.clone(); - let mut s = s_a.write().unwrap(); - - s.ongoing_running = false; - s.shall_stop_ongoing = true; -} - /// Signal an ongoing process to stop. pub fn dc_stop_ongoing_process(context: &Context) { let s_a = context.running_state.clone(); diff --git a/src/dc_imex.rs b/src/dc_imex.rs index 3bacdbd28..1882b4302 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -103,7 +103,7 @@ pub unsafe fn dc_imex_has_backup( pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { let mut setup_file_name: *mut libc::c_char = ptr::null_mut(); let mut msg: Message; - if dc_alloc_ongoing(context) == 0 { + if !dc_alloc_ongoing(context) { return std::ptr::null_mut(); } let setup_code = dc_create_setup_code(context); @@ -502,11 +502,9 @@ pub unsafe fn dc_normalize_setup_code( pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) { let mut ok_to_continue = true; let mut success: libc::c_int = 0; - let mut ongoing_allocated_here: libc::c_int = 0; let what: libc::c_int; - if !(0 == dc_alloc_ongoing(context)) { - ongoing_allocated_here = 1; + if dc_alloc_ongoing(context) { what = job.param.get_int(Param::Cmd).unwrap_or_default(); let param1_s = job.param.get(Param::Arg).unwrap_or_default(); let param1 = CString::yolo(param1_s); @@ -564,9 +562,6 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) { } } } - } - - if 0 != ongoing_allocated_here { dc_free_ongoing(context); } context.call_cb( diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 5beb71daa..ff7d19db1 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -24,7 +24,7 @@ use crate::types::*; pub const NON_ALPHANUMERIC_WITHOUT_DOT: &AsciiSet = &NON_ALPHANUMERIC.remove(b'.'); -pub unsafe fn dc_get_securejoin_qr(context: &Context, group_chat_id: uint32_t) -> Option { +pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: uint32_t) -> Option { /* ========================================================= ==== Alice - the inviter side ==== ==== Step 1 in "Setup verified contact" protocol ==== @@ -117,158 +117,159 @@ fn get_self_fingerprint(context: &Context) -> Option { None } -pub unsafe fn dc_join_securejoin(context: &Context, qr: &str) -> uint32_t { +pub fn dc_join_securejoin(context: &Context, qr: &str) -> uint32_t { + let cleanup = + |context: &Context, contact_chat_id: u32, ongoing_allocated: bool, join_vg: bool| { + let mut bob = context.bob.write().unwrap(); + bob.expects = 0; + let ret_chat_id = if bob.status == DC_BOB_SUCCESS { + if join_vg { + chat::get_chat_id_by_grpid( + context, + bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(), + ) + .0 + } else { + contact_chat_id + } + } else { + 0 + }; + bob.qr_scan = None; + + if ongoing_allocated { + dc_free_ongoing(context); + } + ret_chat_id as uint32_t + }; /* ========================================================== ==== Bob - the joiner's side ===== ==== Step 2 in "Setup verified contact" protocol ===== ========================================================== */ - let ongoing_allocated: libc::c_int; let mut contact_chat_id: uint32_t = 0; let mut join_vg: bool = false; info!(context, 0, "Requesting secure-join ...",); ensure_secret_key_exists(context).ok(); - ongoing_allocated = dc_alloc_ongoing(context); - - if ongoing_allocated != 0 { - let qr_scan = check_qr(context, &qr); - if qr_scan.state != LotState::QrAskVerifyContact - && qr_scan.state != LotState::QrAskVerifyGroup - { - error!(context, 0, "Unknown QR code.",); - } else { - contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default(); - if contact_chat_id == 0 { - error!(context, 0, "Unknown contact.",); - } else if !(context - .running_state - .clone() + if !dc_alloc_ongoing(context) { + return cleanup(&context, contact_chat_id, false, join_vg); + } + let qr_scan = check_qr(context, &qr); + if qr_scan.state != LotState::QrAskVerifyContact && qr_scan.state != LotState::QrAskVerifyGroup + { + error!(context, 0, "Unknown QR code.",); + return cleanup(&context, contact_chat_id, true, join_vg); + } + contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default(); + if contact_chat_id == 0 { + error!(context, 0, "Unknown contact.",); + return cleanup(&context, contact_chat_id, true, join_vg); + } + if check_exit(context) { + return cleanup(&context, contact_chat_id, true, join_vg); + } + join_vg = qr_scan.get_state() == LotState::QrAskVerifyGroup; + { + let mut bob = context.bob.write().unwrap(); + bob.status = 0; + bob.qr_scan = Some(qr_scan); + } + if fingerprint_equals_sender( + context, + context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .fingerprint + .as_ref() + .unwrap(), + contact_chat_id, + ) { + info!(context, 0, "Taking protocol shortcut."); + context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; + context.call_cb( + Event::SECUREJOIN_JOINER_PROGRESS, + chat_id_2_contact_id(context, contact_chat_id) as uintptr_t, + 400i32 as uintptr_t, + ); + let own_fingerprint = get_self_fingerprint(context).unwrap(); + send_handshake_msg( + context, + contact_chat_id, + if join_vg { + "vg-request-with-auth" + } else { + "vc-request-with-auth" + }, + context + .bob .read() .unwrap() - .shall_stop_ongoing) - { - join_vg = qr_scan.get_state() == LotState::QrAskVerifyGroup; - { - let mut bob = context.bob.write().unwrap(); - bob.status = 0; - bob.qr_scan = Some(qr_scan); - } - if fingerprint_equals_sender( - context, - context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .fingerprint - .as_ref() - .unwrap(), - contact_chat_id, - ) { - info!(context, 0, "Taking protocol shortcut."); - context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; - context.call_cb( - Event::SECUREJOIN_JOINER_PROGRESS, - chat_id_2_contact_id(context, contact_chat_id) as uintptr_t, - 400i32 as uintptr_t, - ); - let own_fingerprint = get_self_fingerprint(context).unwrap(); - send_handshake_msg( - context, - contact_chat_id, - if join_vg { - "vg-request-with-auth" - } else { - "vc-request-with-auth" - }, - context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .auth - .as_ref() - .unwrap() - .to_string(), - Some(own_fingerprint), - if join_vg { - context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .text2 - .as_ref() - .unwrap() - .to_string() - } else { - "".to_string() - }, - ); - } else { - context.bob.write().unwrap().expects = DC_VC_AUTH_REQUIRED; - send_handshake_msg( - context, - contact_chat_id, - if join_vg { "vg-request" } else { "vc-request" }, - context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .invitenumber - .as_ref() - .unwrap(), - None, - "", - ); - } - - // Bob -> Alice - while !(context - .running_state - .clone() + .qr_scan + .as_ref() + .unwrap() + .auth + .as_ref() + .unwrap() + .to_string(), + Some(own_fingerprint), + if join_vg { + context + .bob .read() .unwrap() - .shall_stop_ongoing) - { - std::thread::sleep(std::time::Duration::new(0, 3_000_000)); - } - } - } - } - let mut bob = context.bob.write().unwrap(); - bob.expects = 0; - let ret_chat_id = if bob.status == 1 { - if join_vg { - chat::get_chat_id_by_grpid( - context, - bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(), - ) - .0 - } else { - contact_chat_id - } + .qr_scan + .as_ref() + .unwrap() + .text2 + .as_ref() + .unwrap() + .to_string() + } else { + "".to_string() + }, + ); } else { - 0 - }; - bob.qr_scan = None; - - if 0 != ongoing_allocated { - dc_free_ongoing(context); + context.bob.write().unwrap().expects = DC_VC_AUTH_REQUIRED; + send_handshake_msg( + context, + contact_chat_id, + if join_vg { "vg-request" } else { "vc-request" }, + context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .invitenumber + .as_ref() + .unwrap(), + None, + "", + ); } - ret_chat_id as uint32_t + + // Bob -> Alice + while !check_exit(&context) { + std::thread::sleep(std::time::Duration::new(0, 3_000_000)); + } + cleanup(&context, contact_chat_id, true, join_vg) } -unsafe fn send_handshake_msg( +fn check_exit(context: &Context) -> bool { + context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing +} + +fn send_handshake_msg( context: &Context, contact_chat_id: uint32_t, step: &str, @@ -276,7 +277,7 @@ unsafe fn send_handshake_msg( fingerprint: Option, grpid: impl AsRef, ) { - let mut msg = dc_msg_new_untyped(context); + let mut msg = unsafe { dc_msg_new_untyped(context) }; msg.type_0 = Viewtype::Text; msg.text = Some(format!("Secure-Join: {}", step)); msg.hidden = true; @@ -340,448 +341,385 @@ fn fingerprint_equals_sender( } /* library private: secure-join */ -pub unsafe fn dc_handle_securejoin_handshake( +pub fn dc_handle_securejoin_handshake( context: &Context, mimeparser: &dc_mimeparser_t, contact_id: uint32_t, ) -> libc::c_int { - let mut ok_to_continue: bool; - let step: String; - let join_vg: bool; let own_fingerprint: String; - let contact_chat_id: u32; - let contact_chat_id_blocked: Blocked; - let mut grpid = "".to_string(); - let mut ret: libc::c_int = 0i32; - if !(contact_id <= DC_CONTACT_ID_LAST_SPECIAL) { - step = lookup_field(mimeparser, "Secure-Join"); - if !step.is_empty() { - info!( - context, - 0, ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step, - ); - join_vg = step.starts_with("vg-"); - let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not) - .unwrap_or_default(); - contact_chat_id = id; - contact_chat_id_blocked = bl; + if contact_id <= DC_CONTACT_ID_LAST_SPECIAL { + return 0; + } + let step = lookup_field(mimeparser, "Secure-Join"); + if step.is_empty() { + return 0; + } + info!( + context, + 0, ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step, + ); + let (contact_chat_id, contact_chat_id_blocked) = + chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default(); - if Blocked::Not != contact_chat_id_blocked { - chat::unblock(context, contact_chat_id); + if contact_chat_id_blocked != Blocked::Not { + chat::unblock(context, contact_chat_id); + } + let mut ret: libc::c_int = DC_HANDSHAKE_STOP_NORMAL_PROCESSING; + let join_vg = step.starts_with("vg-"); + match step.as_str() { + "vg-request" | "vc-request" => { + /* ========================================================= + ==== Alice - the inviter side ==== + ==== Step 3 in "Setup verified contact" protocol ==== + ========================================================= */ + // this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet) + // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it, + // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here. + // verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code + let invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber"); + if invitenumber == "" { + warn!(context, 0, "Secure-join denied (invitenumber missing).",); + return ret; } - ret = DC_HANDSHAKE_STOP_NORMAL_PROCESSING; - if step == "vg-request" || step == "vc-request" { - /* ========================================================= - ==== Alice - the inviter side ==== - ==== Step 3 in "Setup verified contact" protocol ==== - ========================================================= */ - // this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet) - // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it, - // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here. - // verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code - let invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber"); - if invitenumber == "" { - warn!(context, 0, "Secure-join denied (invitenumber missing).",); - ok_to_continue = false; - } else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, &invitenumber) { - warn!(context, 0, "Secure-join denied (bad invitenumber).",); - ok_to_continue = false; - } else { - info!(context, 0, "Secure-join requested.",); + if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, &invitenumber) { + warn!(context, 0, "Secure-join denied (bad invitenumber).",); + return ret; + } + info!(context, 0, "Secure-join requested.",); - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 300i32 as uintptr_t, - ); - send_handshake_msg( - context, - contact_chat_id, - if join_vg { - "vg-auth-required" - } else { - "vc-auth-required" - }, - "", - None, - "", - ); - ok_to_continue = true; - } - } else if step == "vg-auth-required" || step == "vc-auth-required" { - let cond = { - let bob = context.bob.read().unwrap(); - let scan = bob.qr_scan.as_ref(); - scan.is_none() - || bob.expects != DC_VC_AUTH_REQUIRED - || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup - }; - - if cond { - warn!(context, 0, "auth-required message out of sync.",); - // no error, just aborted somehow or a mail from another handshake - ok_to_continue = false; - } else { - let scanned_fingerprint_of_alice = context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .fingerprint - .as_ref() - .unwrap() - .to_string(); - - let auth = context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .auth - .as_ref() - .unwrap() - .to_string(); - if join_vg { - grpid = context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .text2 - .as_ref() - .unwrap() - .to_string(); - } - - if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) { - could_not_establish_secure_connection( - context, - contact_chat_id, - if mimeparser.e2ee_helper.encrypted { - "No valid signature." - } else { - "Not encrypted." - }, - ); - end_bobs_joining(context, DC_BOB_ERROR); - ok_to_continue = false; - } else if !fingerprint_equals_sender( - context, - &scanned_fingerprint_of_alice, - contact_chat_id, - ) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on joiner-side.", - ); - end_bobs_joining(context, DC_BOB_ERROR); - ok_to_continue = false; - } else { - info!(context, 0, "Fingerprint verified.",); - own_fingerprint = get_self_fingerprint(context).unwrap(); - context.call_cb( - Event::SECUREJOIN_JOINER_PROGRESS, - contact_id as uintptr_t, - 400i32 as uintptr_t, - ); - context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; - - send_handshake_msg( - context, - contact_chat_id, - if join_vg { - "vg-request-with-auth" - } else { - "vc-request-with-auth" - }, - auth, - Some(own_fingerprint), - grpid, - ); - ok_to_continue = true; - } - } - } else if step == "vg-request-with-auth" || step == "vc-request-with-auth" { - /* ============================================================ - ==== Alice - the inviter side ==== - ==== Steps 5+6 in "Setup verified contact" protocol ==== - ==== Step 6 in "Out-of-band verified groups" protocol ==== - ============================================================ */ - // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob - let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint"); - if fingerprint.is_empty() { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint not provided.", - ); - ok_to_continue = false; - } else if !encrypted_and_signed(mimeparser, &fingerprint) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Auth not encrypted.", - ); - ok_to_continue = false; - } else if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on inviter-side.", - ); - ok_to_continue = false; - } else { - info!(context, 0, "Fingerprint verified.",); - // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code - let auth_0 = lookup_field(mimeparser, "Secure-Join-Auth"); - if auth_0.is_empty() { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Auth not provided.", - ); - ok_to_continue = false; - } else if !dc_token_exists(context, DC_TOKEN_AUTH, &auth_0) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Auth invalid.", - ); - ok_to_continue = false; - } else if mark_peer_as_verified(context, fingerprint).is_err() { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on inviter-side.", - ); - ok_to_continue = false; - } else { - Contact::scaleup_origin_by_id( - context, - contact_id, - Origin::SecurejoinInvited, - ); - info!(context, 0, "Auth verified.",); - secure_connection_established(context, contact_chat_id); - context.call_cb( - Event::CONTACTS_CHANGED, - contact_id as uintptr_t, - 0i32 as uintptr_t, - ); - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 600i32 as uintptr_t, - ); - if join_vg { - grpid = lookup_field(mimeparser, "Secure-Join-Group"); - let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &grpid); - if group_chat_id == 0 { - error!(context, 0, "Chat {} not found.", &grpid); - ok_to_continue = false; - } else { - chat::add_contact_to_chat_ex( - context, - group_chat_id, - contact_id, - 0x1i32, - ); - ok_to_continue = true; - } - } else { - send_handshake_msg( - context, - contact_chat_id, - "vc-contact-confirm", - "", - None, - "", - ); - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 1000i32 as uintptr_t, - ); - ok_to_continue = true; - } - } - } - } else if step == "vg-member-added" || step == "vc-contact-confirm" { + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 300i32 as uintptr_t, + ); + send_handshake_msg( + context, + contact_chat_id, if join_vg { - ret = DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING; - } - if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM { - info!(context, 0, "Message belongs to a different handshake.",); - ok_to_continue = false; + "vg-auth-required" } else { - let cond = { - let bob = context.bob.read().unwrap(); - let scan = bob.qr_scan.as_ref(); - scan.is_none() - || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup - }; - if cond { - warn!( - context, - 0, "Message out of sync or belongs to a different handshake.", - ); - ok_to_continue = false; - } else { - let scanned_fingerprint_of_alice = context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .fingerprint - .as_ref() - .unwrap() - .to_string(); + "vc-auth-required" + }, + "", + None, + "", + ); + } + "vg-auth-required" | "vc-auth-required" => { + let cond = { + let bob = context.bob.read().unwrap(); + let scan = bob.qr_scan.as_ref(); + scan.is_none() + || bob.expects != DC_VC_AUTH_REQUIRED + || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup + }; - if join_vg { - grpid = context - .bob - .read() - .unwrap() - .qr_scan - .as_ref() - .unwrap() - .text2 - .as_ref() - .unwrap() - .to_string(); - } + if cond { + warn!(context, 0, "auth-required message out of sync.",); + // no error, just aborted somehow or a mail from another handshake + return ret; + } + let scanned_fingerprint_of_alice = context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .fingerprint + .as_ref() + .unwrap() + .to_string(); - let vg_expect_encrypted = if join_vg { - let (_, is_verified_group, _) = - chat::get_chat_id_by_grpid(context, grpid); - // when joining a non-verified group - // the vg-member-added message may be unencrypted - // when not all group members have keys or prefer encryption. - // So only expect encryption if this is a verified group - is_verified_group - } else { - true - }; - if vg_expect_encrypted { - if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Contact confirm message not encrypted.", - ); - end_bobs_joining(context, DC_BOB_ERROR); - ok_to_continue = false; - } else { - ok_to_continue = true; - } - } else { - ok_to_continue = true; - } - if ok_to_continue { - if mark_peer_as_verified(context, &scanned_fingerprint_of_alice) - .is_err() - { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on joiner-side.", - ); - ok_to_continue = false; - } else { - Contact::scaleup_origin_by_id( - context, - contact_id, - Origin::SecurejoinJoined, - ); - context.call_cb( - Event::CONTACTS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - if join_vg { - if !addr_equals_self( - context, - lookup_field(mimeparser, "Chat-Group-Member-Added"), - ) { - info!( - context, - 0, - "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)." - ); - ok_to_continue = false; - } else { - ok_to_continue = true; - } - } else { - ok_to_continue = true; - } - if ok_to_continue { - secure_connection_established(context, contact_chat_id); - context.bob.write().unwrap().expects = 0; - if join_vg { - send_handshake_msg( - context, - contact_chat_id, - "vg-member-added-received", - "", - None, - "", - ); - } - end_bobs_joining(context, DC_BOB_SUCCESS); - ok_to_continue = true; - } - } - } - } - } - } else if step == "vg-member-added-received" { - /* ============================================================ - ==== Alice - the inviter side ==== - ==== Step 8 in "Out-of-band verified groups" protocol ==== - ============================================================ */ - if let Ok(contact) = Contact::get_by_id(context, contact_id) { - if contact.is_verified() == VerifiedStatus::Unverified { - warn!(context, 0, "vg-member-added-received invalid.",); - ok_to_continue = false; + let auth = context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .auth + .as_ref() + .unwrap() + .to_string(); + if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) { + could_not_establish_secure_connection( + context, + contact_chat_id, + if mimeparser.e2ee_helper.encrypted { + "No valid signature." } else { - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 800i32 as uintptr_t, - ); - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 1000i32 as uintptr_t, - ); - ok_to_continue = true; - } + "Not encrypted." + }, + ); + end_bobs_joining(context, DC_BOB_ERROR); + return ret; + } + if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Fingerprint mismatch on joiner-side.", + ); + end_bobs_joining(context, DC_BOB_ERROR); + return ret; + } + info!(context, 0, "Fingerprint verified.",); + own_fingerprint = get_self_fingerprint(context).unwrap(); + context.call_cb( + Event::SECUREJOIN_JOINER_PROGRESS, + contact_id as uintptr_t, + 400i32 as uintptr_t, + ); + context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; + + let grpid = if join_vg { + context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .text2 + .as_ref() + .unwrap() + .to_string() + } else { + "".to_string() + }; + + send_handshake_msg( + context, + contact_chat_id, + if join_vg { + "vg-request-with-auth" } else { - warn!(context, 0, "vg-member-added-received invalid.",); - ok_to_continue = false; + "vc-request-with-auth" + }, + auth, + Some(own_fingerprint), + grpid, + ); + } + "vg-request-with-auth" | "vc-request-with-auth" => { + /* ============================================================ + ==== Alice - the inviter side ==== + ==== Steps 5+6 in "Setup verified contact" protocol ==== + ==== Step 6 in "Out-of-band verified groups" protocol ==== + ============================================================ */ + // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob + let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint"); + if fingerprint.is_empty() { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Fingerprint not provided.", + ); + return ret; + } + if !encrypted_and_signed(mimeparser, &fingerprint) { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Auth not encrypted.", + ); + return ret; + } + if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Fingerprint mismatch on inviter-side.", + ); + return ret; + } + info!(context, 0, "Fingerprint verified.",); + // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code + let auth_0 = lookup_field(mimeparser, "Secure-Join-Auth"); + if auth_0.is_empty() { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Auth not provided.", + ); + return ret; + } + if !dc_token_exists(context, DC_TOKEN_AUTH, &auth_0) { + could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid."); + return ret; + } + if mark_peer_as_verified(context, fingerprint).is_err() { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Fingerprint mismatch on inviter-side.", + ); + return ret; + } + Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited); + info!(context, 0, "Auth verified.",); + secure_connection_established(context, contact_chat_id); + context.call_cb( + Event::CONTACTS_CHANGED, + contact_id as uintptr_t, + 0i32 as uintptr_t, + ); + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 600i32 as uintptr_t, + ); + if join_vg { + let grpid = lookup_field(mimeparser, "Secure-Join-Group"); + let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &grpid); + if group_chat_id == 0 { + error!(context, 0, "Chat {} not found.", &grpid); + return ret; + } else { + chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, 0x1i32); } } else { - ok_to_continue = true; - } - if ok_to_continue { - if ret == DC_HANDSHAKE_STOP_NORMAL_PROCESSING { - ret |= DC_HANDSHAKE_ADD_DELETE_JOB; - } + send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, ""); + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 1000i32 as uintptr_t, + ); } } - } + "vg-member-added" | "vc-contact-confirm" => { + if join_vg { + ret = DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING; + } + if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM { + info!(context, 0, "Message belongs to a different handshake.",); + return ret; + } + let cond = { + let bob = context.bob.read().unwrap(); + let scan = bob.qr_scan.as_ref(); + scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup + }; + if cond { + warn!( + context, + 0, "Message out of sync or belongs to a different handshake.", + ); + return ret; + } + let scanned_fingerprint_of_alice = context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .fingerprint + .as_ref() + .unwrap() + .to_string(); + let vg_expect_encrypted = if join_vg { + let grpid = context + .bob + .read() + .unwrap() + .qr_scan + .as_ref() + .unwrap() + .text2 + .as_ref() + .unwrap() + .to_string(); + let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, grpid); + // when joining a non-verified group + // the vg-member-added message may be unencrypted + // when not all group members have keys or prefer encryption. + // So only expect encryption if this is a verified group + is_verified_group + } else { + true + }; + if vg_expect_encrypted + && !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) + { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Contact confirm message not encrypted.", + ); + end_bobs_joining(context, DC_BOB_ERROR); + return ret; + } + + if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() { + could_not_establish_secure_connection( + context, + contact_chat_id, + "Fingerprint mismatch on joiner-side.", + ); + return ret; + } + Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); + context.call_cb( + Event::CONTACTS_CHANGED, + 0i32 as uintptr_t, + 0i32 as uintptr_t, + ); + if join_vg + && !addr_equals_self(context, lookup_field(mimeparser, "Chat-Group-Member-Added")) + { + info!(context, 0, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."); + return ret; + } + secure_connection_established(context, contact_chat_id); + context.bob.write().unwrap().expects = 0; + if join_vg { + send_handshake_msg( + context, + contact_chat_id, + "vg-member-added-received", + "", + None, + "", + ); + } + end_bobs_joining(context, DC_BOB_SUCCESS); + } + "vg-member-added-received" => { + /* ============================================================ + ==== Alice - the inviter side ==== + ==== Step 8 in "Out-of-band verified groups" protocol ==== + ============================================================ */ + if let Ok(contact) = Contact::get_by_id(context, contact_id) { + if contact.is_verified() == VerifiedStatus::Unverified { + warn!(context, 0, "vg-member-added-received invalid.",); + return ret; + } + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 800i32 as uintptr_t, + ); + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 1000i32 as uintptr_t, + ); + } else { + warn!(context, 0, "vg-member-added-received invalid.",); + return ret; + } + } + _ => { + warn!(context, 0, "invalid step: {}", step); + } + } + if ret == DC_HANDSHAKE_STOP_NORMAL_PROCESSING { + ret |= DC_HANDSHAKE_ADD_DELETE_JOB; + } ret } @@ -790,7 +728,7 @@ fn end_bobs_joining(context: &Context, status: libc::c_int) { dc_stop_ongoing_process(context); } -unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) { +fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) { let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id); let contact = Contact::get_by_id(context, contact_id); let addr = if let Ok(ref contact) = contact { @@ -807,20 +745,22 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint ); } -unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> String { +fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> String { let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key); - let mut value: *const libc::c_char = ptr::null(); - if field.is_null() - || (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int - || (*field).fld_data.fld_optional_field.is_null() - || { - value = (*(*field).fld_data.fld_optional_field).fld_value; - value.is_null() + unsafe { + let mut value: *const libc::c_char = ptr::null(); + if field.is_null() + || (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int + || (*field).fld_data.fld_optional_field.is_null() + || { + value = (*(*field).fld_data.fld_optional_field).fld_value; + value.is_null() + } + { + return String::from(""); } - { - return String::from(""); + as_str(value).to_string() } - as_str(value).to_string() } fn could_not_establish_secure_connection( @@ -864,23 +804,20 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Res * Tools: Misc. ******************************************************************************/ -unsafe fn encrypted_and_signed( +fn encrypted_and_signed( mimeparser: &dc_mimeparser_t, expected_fingerprint: impl AsRef, ) -> bool { if !mimeparser.e2ee_helper.encrypted { warn!(mimeparser.context, 0, "Message not encrypted.",); - return false; - } - if mimeparser.e2ee_helper.signatures.len() <= 0 { + false + } else if mimeparser.e2ee_helper.signatures.len() <= 0 { warn!(mimeparser.context, 0, "Message not signed.",); - return false; - } - if expected_fingerprint.as_ref().is_empty() { + false + } else if expected_fingerprint.as_ref().is_empty() { warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",); - return false; - } - if !mimeparser + false + } else if !mimeparser .e2ee_helper .signatures .contains(expected_fingerprint.as_ref()) @@ -891,13 +828,13 @@ unsafe fn encrypted_and_signed( "Message does not match expected fingerprint {}.", expected_fingerprint.as_ref(), ); - return false; + false + } else { + true } - - true } -pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) { +pub fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) { // - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal // - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes // together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother diff --git a/src/location.rs b/src/location.rs index 66444d948..11569e223 100644 --- a/src/location.rs +++ b/src/location.rs @@ -220,7 +220,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) { msg.text = Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)); msg.param.set_int(Param::Cmd, 8); - unsafe { chat::send_msg(context, chat_id, &mut msg).unwrap() }; + chat::send_msg(context, chat_id, &mut msg).unwrap(); } else if 0 == seconds && is_sending_locations_before { let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); @@ -607,7 +607,7 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) { msg.hidden = true; msg.param.set_int(Param::Cmd, 9); // TODO: handle cleanup on error - unsafe { chat::send_msg(context, chat_id as u32, &mut msg).unwrap() }; + chat::send_msg(context, chat_id as u32, &mut msg).unwrap(); } Ok(()) },