From a9d55dec9275c472d7eea2ff79304eda1aefd3be Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 18 Mar 2019 07:11:19 +0140 Subject: [PATCH] Add support for sending encrypted message in MUC --- src/event/client_events.c | 23 +++++-- src/omemo/omemo.c | 137 +++++++++++++++++++++++++------------- src/omemo/omemo.h | 2 +- src/xmpp/message.c | 14 ++-- src/xmpp/xmpp.h | 2 +- 5 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index 79ed88f6..1488174a 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -218,7 +218,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else { char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); chat_log_msg_out(chatwin->barejid, plugin_msg); @@ -238,7 +238,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else { gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); if (!handled) { @@ -261,7 +261,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -286,7 +286,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -334,10 +334,25 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const return; } +#ifdef HAVE_OMEMO + if (mucwin->is_omemo) { + omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); + } else { + message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + } + + plugins_post_room_message_send(mucwin->roomjid, plugin_msg); + free(plugin_msg); + return; +#endif + +#ifndef HAVE_OMEMO message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); plugins_post_room_message_send(mucwin->roomjid, plugin_msg); free(plugin_msg); + return; +#endif } void diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index f7a305ee..3fb52b08 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -168,6 +169,7 @@ omemo_init(void) void omemo_on_connect(ProfAccount *account) { + GError *error = NULL; char *omemodir = files_get_data_path(DIR_OMEMO); GString *basedir = g_string_new(omemodir); free(omemodir); @@ -194,18 +196,26 @@ omemo_on_connect(ProfAccount *account) } } + omemo_devicelist_subscribe(); + omemo_ctx.identity_keyfile = g_key_file_new(); - if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + omemo_ctx.sessions_keyfile = g_key_file_new(); + + if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { load_identity(); omemo_generate_short_term_crypto_materials(account); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message); + return; } - omemo_ctx.sessions_keyfile = g_key_file_new(); - if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + error = NULL; + if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { load_sessions(); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message); } - omemo_devicelist_subscribe(); } void @@ -533,22 +543,15 @@ out: } gboolean -omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) +omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc) { int res; gboolean ret = FALSE; Jid *jid = jid_create(connection_get_fulljid()); GList *keys = NULL; - GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); - if (!recipient_device_id) { - goto out; - } - GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); - /* TODO generate fresh AES-GCM materials */ - /* TODO encrypt message */ unsigned char *key; unsigned char *iv; unsigned char *ciphertext; @@ -574,37 +577,72 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean memcpy(key_tag, key, AES128_GCM_KEY_LENGTH); memcpy(key_tag + AES128_GCM_KEY_LENGTH, tag, AES128_GCM_TAG_LENGTH); - GList *device_ids_iter; - for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { - int res; - ciphertext_message *ciphertext; - session_cipher *cipher; - signal_protocol_address address = { - .name = chatwin->barejid, - .name_len = strlen(chatwin->barejid), - .device_id = GPOINTER_TO_INT(device_ids_iter->data) - }; - - res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); - if (res != 0) { - log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); - continue; + GList *recipients = NULL; + if (muc) { + ProfMucWin *mucwin = (ProfMucWin *)win; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + GList *roster = muc_roster(mucwin->roomjid); + GList *iter; + for (iter = roster; iter != NULL; iter = iter->next) { + Occupant *occupant = (Occupant *)iter->data; + Jid *jid = jid_create(occupant->jid); + if (!jid->barejid) { + log_warning("OMEMO: missing barejid for MUC %s occupant %s", mucwin->roomjid, occupant->nick); + } else { + recipients = g_list_append(recipients, strdup(jid->barejid)); + } + jid_destroy(jid); } - - res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); - if (res != 0) { - log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); - continue; - } - signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); - omemo_key_t *key = malloc(sizeof(omemo_key_t)); - key->data = signal_buffer_data(buffer); - key->length = signal_buffer_len(buffer); - key->device_id = GPOINTER_TO_INT(device_ids_iter->data); - key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; - keys = g_list_append(keys, key); + } else { + ProfChatWin *chatwin = (ProfChatWin *)win; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + recipients = g_list_append(recipients, strdup(chatwin->barejid)); } + GList *device_ids_iter; + + GList *recipients_iter; + for (recipients_iter = recipients; recipients_iter != NULL; recipients_iter = recipients_iter->next) { + GList *recipient_device_id = NULL; + recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, recipients_iter->data); + if (!recipient_device_id) { + log_warning("OMEMO: cannot find device ids for %s", recipients_iter->data); + continue; + } + + for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + int res; + ciphertext_message *ciphertext; + session_cipher *cipher; + signal_protocol_address address = { + .name = recipients_iter->data, + .name_len = strlen(recipients_iter->data), + .device_id = GPOINTER_TO_INT(device_ids_iter->data) + }; + + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); + continue; + } + + res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); + if (res != 0) { + log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); + continue; + } + signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + key->data = signal_buffer_data(buffer); + key->length = signal_buffer_len(buffer); + key->device_id = GPOINTER_TO_INT(device_ids_iter->data); + key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; + keys = g_list_append(keys, key); + } + } + + g_list_free_full(recipients, free); + for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { int res; ciphertext_message *ciphertext; @@ -635,14 +673,23 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean keys = g_list_append(keys, key); } - char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt); - chat_log_omemo_msg_out(chatwin->barejid, message); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + if (muc) { + ProfMucWin *mucwin = (ProfMucWin *)win; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + char *id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE); + free(id); + } else { + ProfChatWin *chatwin = (ProfChatWin *)win; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, message); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + free(id); + } ret = TRUE; out: jid_destroy(jid); - free(id); g_list_free_full(keys, free); free(ciphertext); gcry_free(key); @@ -659,7 +706,7 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, const unsigned char *const payload, size_t payload_len, gboolean muc) { unsigned char *plaintext = NULL; - Jid *sender; + Jid *sender = NULL; Jid *from = jid_create(from_jid); if (!from) { log_error("Invalid jid %s", from_jid); diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index b56b50a5..a8af1a66 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,5 +40,5 @@ void omemo_start_muc_sessions(const char *const roomjid); void omemo_start_device_session(const char *const jid, uint32_t device_id, GList *prekeys, uint32_t signed_prekey_id, const unsigned char *const signed_prekey, size_t signed_prekey_len, const unsigned char *const signature, size_t signature_len, const unsigned char *const identity_key, size_t identity_key_len); gboolean omemo_loaded(void); -gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); +gboolean omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc); char * omemo_on_message_recv(const char *const from, uint32_t sid, const unsigned char *const iv, size_t iv_len, GList *keys, const unsigned char *const payload, size_t payload_len, gboolean muc); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 8fbaedd9..47a21438 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -318,13 +318,19 @@ char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, - gboolean request_receipt) + gboolean request_receipt, gboolean muc) { char *state = chat_session_get_state(jid); xmpp_ctx_t * const ctx = connection_get_ctx(); - char *id = connection_create_stanza_id("msg"); - - xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); + char *id; + xmpp_stanza_t *message; + if (muc) { + id = connection_create_stanza_id("muc"); + message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, jid, id); + } else { + id = connection_create_stanza_id("msg"); + message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); + } xmpp_stanza_t *encrypted = xmpp_stanza_new(ctx); xmpp_stanza_set_name(encrypted, "encrypted"); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index f2eec6c7..6675369a 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -140,7 +140,7 @@ char* message_send_chat(const char *const barejid, const char *const msg, const gboolean request_receipt); char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt); char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt); -char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt); +char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); void message_send_groupchat_subject(const char *const roomjid, const char *const subject);