diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index c3279b34b..12e41c9d4 100644 --- a/coreapi/CMakeLists.txt +++ b/coreapi/CMakeLists.txt @@ -46,6 +46,7 @@ set(LINPHONE_HEADER_FILES linphonepresence.h linphone_proxy_config.h linphone_tunnel.h + im_encryption_engine.h lpc2xml.h lpconfig.h nat_policy.h @@ -95,6 +96,7 @@ set(LINPHONE_SOURCE_FILES_C info.c ldap/ldapprovider.c lime.c + im_encryption_engine.c linphonecall.c linphonecore.c linphone_tunnel_config.c diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index a35031e3c..ce008a496 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -40,6 +40,7 @@ linphone_include_HEADERS=\ linphonepresence.h \ linphone_proxy_config.h \ linphone_tunnel.h \ + im_encryption_engine.h \ lpc2xml.h \ lpconfig.h \ nat_policy.h \ @@ -80,6 +81,7 @@ liblinphone_la_SOURCES=\ localplayer.c \ lpc2xml.c \ lime.c lime.h\ + im_encryption_engine.c \ lpconfig.c \ lsd.c \ message_storage.c \ diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index 533453da4..ccead1c76 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -70,13 +70,6 @@ static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) { && strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0; } -static bool_t is_cipher_xml(belle_sip_header_content_type_t* content_type) { - return (strcmp("xml",belle_sip_header_content_type_get_type(content_type))==0 - && strcmp("cipher",belle_sip_header_content_type_get_subtype(content_type))==0) - - || (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 - && strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0); -} static bool_t is_external_body(belle_sip_header_content_type_t* content_type) { return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0; @@ -107,62 +100,28 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve bool_t cipher_xml=FALSE; bool_t rcs_filetransfer=FALSE; uint8_t *decryptedMessage = NULL; + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + int retval = -1; from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t); - if (content_type){ - - /* check if we have a xml/cipher message to be decrypted */ - if ((cipher_xml=is_cipher_xml(content_type))) { - /* access the zrtp cache to get keys needed to decipher the message */ - LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - FILE *CACHEFD = NULL; - if (lc->zrtp_secrets_cache != NULL) CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+"); - if (CACHEFD == NULL) { - ms_warning("Unable to access ZRTP ZID cache to decrypt message"); - goto error; - } else { - size_t cacheSize; - char *cacheString; - int retval; - xmlDocPtr cacheXml; - - cacheString=ms_load_file_content(CACHEFD, &cacheSize); - if (!cacheString){ - ms_warning("Unable to load content of ZRTP ZID cache to decrypt message"); - goto error; - } - cacheString[cacheSize] = '\0'; - cacheSize += 1; - fclose(CACHEFD); - cacheXml = xmlParseDoc((xmlChar*)cacheString); - ms_free(cacheString); - retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), &decryptedMessage); - if (retval != 0) { - ms_warning("Unable to decrypt message, reason : %s - op [%p]", lime_error_code_to_string(retval), op); - free(decryptedMessage); - xmlFreeDoc(cacheXml); - errcode = 488; - goto error; - } else { - /* dump updated cache to a string */ - xmlChar *xmlStringOutput; - int xmlStringLength; - xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0); - /* write it to the cache file */ - CACHEFD = fopen(lc->zrtp_secrets_cache, "wb+"); - if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){ - ms_warning("Fail to write cache"); - } - xmlFree(xmlStringOutput); - fclose(CACHEFD); - } - - xmlFreeDoc(cacheXml); + if (content_type) { + LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc); + if (imee) { + LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); + LinphoneImEncryptionEngineIncomingMessageCb cb_process_incoming_message = linphone_im_encryption_engine_cbs_get_process_incoming_message(imee_cbs); + if (cb_process_incoming_message) { + retval = cb_process_incoming_message(lc, belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type), + belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), (char **)&decryptedMessage); } - } + cipher_xml = retval >= 0; + if (retval > 0) { + errcode = retval; + goto error; + } + external_body=is_external_body(content_type); plain_text=is_plain_text(content_type); rcs_filetransfer = is_rcs_filetransfer(content_type); diff --git a/coreapi/im_encryption_engine.c b/coreapi/im_encryption_engine.c new file mode 100644 index 000000000..cf6a34530 --- /dev/null +++ b/coreapi/im_encryption_engine.c @@ -0,0 +1,71 @@ +/* +ImEncryptionEgine.c +Copyright (C) 2016 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "linphonecore.h" +#include "im_encryption_engine.h" + +struct _LinphoneImEncryptionEngineCbs { + void *user_data; + LinphoneImEncryptionEngineIncomingMessageCb process_incoming_message; +}; + +struct _LinphoneImEncryptionEngine { + void *user_data; + LinphoneImEncryptionEngineCbs *callbacks; +}; + +LinphoneImEncryptionEngineCbs *linphone_im_encryption_engine_cbs_new(void) { + LinphoneImEncryptionEngineCbs *cbs = ms_new0(LinphoneImEncryptionEngineCbs, 1); + return cbs; +} + +void linphone_im_encryption_engine_cbs_destory(LinphoneImEncryptionEngineCbs *cbs) { + ms_free(cbs); +} + +void *linphone_im_encryption_engine_cbs_get_user_data(const LinphoneImEncryptionEngineCbs *cbs) { + return cbs->user_data; +} + +void linphone_im_encryption_engine_cbs_set_user_data(LinphoneImEncryptionEngineCbs *cbs, void *data) { + cbs->user_data = data; +} + +LinphoneImEncryptionEngine *linphone_im_encryption_engine_new(void) { + LinphoneImEncryptionEngine *imee = ms_new0(LinphoneImEncryptionEngine, 1); + imee->callbacks = linphone_im_encryption_engine_cbs_new(); + return imee; +} + +void linphone_im_encryption_engine_destory(LinphoneImEncryptionEngine *imee) { + if (imee->callbacks) linphone_im_encryption_engine_cbs_destory(imee->callbacks); + ms_free(imee); +} + +LinphoneImEncryptionEngineCbs* linphone_im_encryption_engine_get_callbacks(const LinphoneImEncryptionEngine *imee) { + return imee->callbacks; +} + +LinphoneImEncryptionEngineIncomingMessageCb linphone_im_encryption_engine_cbs_get_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs) { + return cbs->process_incoming_message; +} + +void linphone_im_encryption_engine_cbs_set_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIncomingMessageCb cb) { + cbs->process_incoming_message = cb; +} \ No newline at end of file diff --git a/coreapi/im_encryption_engine.h b/coreapi/im_encryption_engine.h new file mode 100644 index 000000000..7ab571dbe --- /dev/null +++ b/coreapi/im_encryption_engine.h @@ -0,0 +1,53 @@ +/* +ImEncryptionEgine.h +Copyright (C) 2016 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef IM_ENCRYPTION_ENGINE_H +#define IM_ENCRYPTION_ENGINE_H + +#include + +#ifndef LINPHONE_PUBLIC +#define LINPHONE_PUBLIC MS2_PUBLIC +#endif + +typedef int (*LinphoneImEncryptionEngineIncomingMessageCb)(LinphoneCore* lc, const char* content_type, const char* content_subtype, const char* body, char** decrypted_body); + +typedef struct _LinphoneImEncryptionEngineCbs LinphoneImEncryptionEngineCbs; + +typedef struct _LinphoneImEncryptionEngine LinphoneImEncryptionEngine; + +LINPHONE_PUBLIC LinphoneImEncryptionEngineCbs *linphone_im_encryption_engine_cbs_new(void); + +LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_destory(LinphoneImEncryptionEngineCbs *cbs); + +LINPHONE_PUBLIC void *linphone_im_encryption_engine_cbs_get_user_data(const LinphoneImEncryptionEngineCbs *cbs); + +LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_user_data(LinphoneImEncryptionEngineCbs *cbs, void *data); + +LINPHONE_PUBLIC LinphoneImEncryptionEngine *linphone_im_encryption_engine_new(void); + +LINPHONE_PUBLIC void linphone_im_encryption_engine_destory(LinphoneImEncryptionEngine *imee); + +LINPHONE_PUBLIC LinphoneImEncryptionEngineCbs* linphone_im_encryption_engine_get_callbacks(const LinphoneImEncryptionEngine *imee); + +LINPHONE_PUBLIC LinphoneImEncryptionEngineIncomingMessageCb linphone_im_encryption_engine_cbs_get_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs); + +LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIncomingMessageCb cb); + +#endif /* IM_ENCRYPTION_ENGINE_H */ \ No newline at end of file diff --git a/coreapi/lime.c b/coreapi/lime.c index 063331bd3..833161818 100644 --- a/coreapi/lime.c +++ b/coreapi/lime.c @@ -23,7 +23,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #endif #ifdef HAVE_LIME -#include "linphonecore.h" #include "bctoolbox/crypto.h" /** @@ -812,6 +811,70 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_ return 0; } +static bool_t is_cipher_xml(const char* content_type, const char *content_subtype) { + return (strcmp("xml",content_type)==0 + && strcmp("cipher",content_subtype)==0) + || (strcmp("application",content_type)==0 + && strcmp("cipher.vnd.gsma.rcs-ft-http+xml",content_subtype)==0); +} + +int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, const char* content_type, const char* content_subtype, const char* body, char** decrypted_body) { + int errcode = -1; + /* check if we have a xml/cipher message to be decrypted */ + if (is_cipher_xml(content_type, content_subtype)) { + /* access the zrtp cache to get keys needed to decipher the message */ + FILE *CACHEFD = NULL; + const char *zrtp_secrets_cache = linphone_core_get_zrtp_secrets_file(lc); + errcode = 0; + if (zrtp_secrets_cache != NULL) CACHEFD = fopen(zrtp_secrets_cache, "rb+"); + if (CACHEFD == NULL) { + ms_warning("Unable to access ZRTP ZID cache to decrypt message"); + errcode = 500; + return errcode; + } else { + size_t cacheSize; + char *cacheString; + int retval; + xmlDocPtr cacheXml; + + cacheString=ms_load_file_content(CACHEFD, &cacheSize); + if (!cacheString){ + ms_warning("Unable to load content of ZRTP ZID cache to decrypt message"); + errcode = 500; + return errcode; + } + cacheString[cacheSize] = '\0'; + cacheSize += 1; + fclose(CACHEFD); + cacheXml = xmlParseDoc((xmlChar*)cacheString); + ms_free(cacheString); + retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)body, (uint8_t **)decrypted_body); + if (retval != 0) { + ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval)); + free(decrypted_body); + xmlFreeDoc(cacheXml); + errcode = 488; + return errcode; + } else { + /* dump updated cache to a string */ + xmlChar *xmlStringOutput; + int xmlStringLength; + xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0); + /* write it to the cache file */ + CACHEFD = fopen(zrtp_secrets_cache, "wb+"); + if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){ + ms_warning("Fail to write cache"); + } + xmlFree(xmlStringOutput); + fclose(CACHEFD); + } + + xmlFreeDoc(cacheXml); + } + } + return errcode; +} + #else /* HAVE_LIME */ @@ -837,6 +900,9 @@ int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) { int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) { return LIME_NOT_ENABLED; } +int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, const char* content_type, const char* content_subtype, const char* body, char** decrypted_body) { + return 500; +} #endif /* HAVE_LIME */ char *lime_error_code_to_string(int errorCode) { diff --git a/coreapi/lime.h b/coreapi/lime.h index 277f57cbe..f33622812 100644 --- a/coreapi/lime.h +++ b/coreapi/lime.h @@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include +#include "linphonecore.h" #include #ifndef LINPHONE_PUBLIC @@ -203,4 +204,7 @@ LINPHONE_PUBLIC char *lime_error_code_to_string(int errorCode); * @return TRUE if Lime is available, FALSE if not */ LINPHONE_PUBLIC bool_t lime_is_available(void); + +int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, const char* content_type, const char* content_subtype, const char* body, char** decrypted_body); + #endif /* LIME_H */ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e98633ca7..6bd808c56 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1950,6 +1950,18 @@ void linphone_core_enable_lime(LinphoneCore *lc, LinphoneLimeState val){ if (linphone_core_ready(lc)){ lp_config_set_int(lc->config,"sip","lime",val); } + + if (val != LinphoneLimeDisabled) { + LinphoneImEncryptionEngine *imee = linphone_im_encryption_engine_new(); + LinphoneImEncryptionEngineCbs *cbs = linphone_im_encryption_engine_get_callbacks(imee); + linphone_im_encryption_engine_cbs_set_process_incoming_message(cbs, lime_im_encryption_engine_process_incoming_message_cb); + lc->im_encryption_engine = imee; + } else { + if (lc->im_encryption_engine) { + linphone_im_encryption_engine_destory(lc->im_encryption_engine); + lc->im_encryption_engine = NULL; + } + } } bool_t linphone_core_lime_available(const LinphoneCore *lc){ @@ -7996,3 +8008,11 @@ const char *linphone_core_get_tls_key_path(const LinphoneCore *lc) { const char *tls_key_path = lp_config_get_string(lc->config, "sip", "client_cert_key", NULL); return tls_key_path; } + +void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncryptionEngine *imee) { + lc->im_encryption_engine = imee; +} + +LinphoneImEncryptionEngine *linphone_core_get_im_encryption_engine(const LinphoneCore *lc) { + return lc->im_encryption_engine; +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 4eb5335f6..fbdff8de4 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -432,6 +432,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy); #include "nat_policy.h" #include "xmlrpc.h" #include "conference.h" +#include "im_encryption_engine.h" #else #include "linphone/buffer.h" #include "linphone/call_log.h" @@ -442,6 +443,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy); #include "linphone/nat_policy.h" #include "linphone/xmlrpc.h" #include "linphone/conference.h" +#include "linphone/im_encryption_engine.h" #endif LINPHONE_PUBLIC LinphoneAddress * linphone_address_new(const char *addr); @@ -4728,6 +4730,10 @@ LINPHONE_PUBLIC const char *linphone_core_get_tls_cert_path(const LinphoneCore * */ LINPHONE_PUBLIC const char *linphone_core_get_tls_key_path(const LinphoneCore *lc); +LINPHONE_PUBLIC void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncryptionEngine *imee); + +LINPHONE_PUBLIC LinphoneImEncryptionEngine * linphone_core_get_im_encryption_engine(const LinphoneCore *lc); + #include "ringtoneplayer.h" #ifdef __cplusplus diff --git a/coreapi/private.h b/coreapi/private.h index f8fe6cda6..c1339826c 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1071,6 +1071,8 @@ struct _LinphoneCore /*default resource list server*/ LinphoneAddress *default_rls_addr; + + LinphoneImEncryptionEngine *im_encryption_engine; }; diff --git a/tester/message_tester.c b/tester/message_tester.c index 3c49bd180..f326bc893 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -809,6 +809,10 @@ static void lime_text_message(void) { linphone_chat_room_send_message(chat_room,"Bla bla bla bla"); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1)); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedLegacy,1)); + BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message); + if (marie->stat.last_received_chat_message) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marie->stat.last_received_chat_message), "Bla bla bla bla"); + } BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity)); /* TODO : check the msg arrived correctly deciphered */