Commit fa9d8f2b authored by Matthieu Tanon's avatar Matthieu Tanon

Wrap security event type enum and improve security event management

parent a857bb7c
......@@ -100,6 +100,7 @@ set(ENUMS_HEADER_FILES
chat-room-enums.h
encryption-engine-enums.h
event-log-enums.h
security-event-enums.h
)
set(UTILS_HEADER_FILES
......
......@@ -144,6 +144,13 @@ LINPHONE_PUBLIC const LinphoneAddress *linphone_event_log_get_device_address (co
// ConferenceSecurityEvent.
// -----------------------------------------------------------------------------
/**
* Returns the type of security event.
* @param[in] event_log A #LinphoneEventLog object.
* @return The security event type.
*/
LINPHONE_PUBLIC LinphoneSecurityEventType linphone_event_log_get_security_event_type (const LinphoneEventLog *event_log);
/**
* Returns the faulty device address of a conference security event.
* @param[in] event_log A #LinphoneEventLog object.
......
......@@ -28,6 +28,7 @@
#include "linphone/enums/chat-room-enums.h"
#include "linphone/enums/encryption-engine-enums.h"
#include "linphone/enums/event-log-enums.h"
#include "linphone/enums/security-event-enums.h"
#include "linphone/utils/enum-generator.h"
// =============================================================================
......@@ -262,6 +263,12 @@ L_DECLARE_C_ENUM(ChatRoomState, L_ENUM_VALUES_CHAT_ROOM_STATE);
*/
L_DECLARE_C_ENUM(EventLogType, L_ENUM_VALUES_EVENT_LOG_TYPE);
/**
* #LinphoneSecurityEventType is used to indicate the type of security event.
* @ingroup events
*/
L_DECLARE_C_ENUM(SecurityEventType, L_ENUM_VALUES_SECURITY_EVENT_TYPE);
#ifdef __cplusplus
}
#endif // ifdef __cplusplus
......
/*
* security-event-enums.h
* Copyright (C) 2010-2018 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 _L_SECURITY_EVENT_ENUMS_H_
#define _L_SECURITY_EVENT_ENUMS_H_
// =============================================================================
#define L_ENUM_VALUES_SECURITY_EVENT_TYPE(F) \
F(Null /**< Event is not a security event */) \
F(SecurityLevelDowngraded /**< Chatroom security level downgraded event */) \
F(MultideviceParticipantDetected /**< Encrypting message to multidevice participant event */) \
F(LimeIdentityKeyChanged /**< Lime identity key change changed event */) \
F(ManInTheMiddleDetected /**< Man in the middle detected event */) \
#endif // ifndef _L_SECURITY_EVENT_ENUMS_H_
......@@ -321,6 +321,17 @@ const LinphoneAddress *linphone_event_log_get_device_address (const LinphoneEven
// ConferenceSecurityEvent.
// -----------------------------------------------------------------------------
LINPHONE_PUBLIC LinphoneSecurityEventType linphone_event_log_get_security_event_type (const LinphoneEventLog *event_log) {
if (!isConferenceSecurityType(linphone_event_log_get_type(event_log)))
return LinphoneSecurityEventTypeNull;
const auto securityEvent = static_pointer_cast<const LinphonePrivate::ConferenceSecurityEvent>(
L_GET_CPP_PTR_FROM_C_OBJECT(event_log)
);
LinphoneSecurityEventType eventType = static_cast<LinphoneSecurityEventType>(securityEvent->getSecurityEventType());
return eventType;
}
LINPHONE_PUBLIC LinphoneAddress *linphone_event_log_get_security_event_faulty_device (const LinphoneEventLog *event_log) {
if (!isConferenceSecurityType(linphone_event_log_get_type(event_log)))
return nullptr;
......
......@@ -719,18 +719,30 @@ void ClientGroupChatRoom::onParticipantSetAdmin (const shared_ptr<ConferencePart
void ClientGroupChatRoom::onSecurityEvent (const shared_ptr<ConferenceSecurityEvent> &event) {
L_D();
// Add security events or alerts based on the type of security event
switch (event->getSecurityEventType()) {
case ConferenceSecurityEvent::SecurityEventType::SecurityLevelDowngraded:
// Expected behaviour: nothing more to do
break;
case ConferenceSecurityEvent::SecurityEventType::MultideviceParticipantDetected:
case ConferenceSecurityEvent::SecurityEventType::LimeIdentityKeyChanged:
case ConferenceSecurityEvent::SecurityEventType::ManInTheMiddleDetected:
// Unexpected behaviour: set faulty device PeerDeviceStatus to unsafe
if (getCore()->limeV2Enabled() && event->getFaultyDevice().isValid()) {
LimeV2 *limeV2Engine = static_cast<LimeV2 *>(getCore()->getEncryptionEngine());
limeV2Engine->getLimeManager()->set_peerDeviceStatus(event->getFaultyDevice().asString(), lime::PeerDeviceStatus::unsafe);
// WARNING has no effect if faulty device is not in X3DH database
}
break;
case ConferenceSecurityEvent::SecurityEventType::Null:
// Event is not a security event
break;
}
d->addEvent(event);
LinphoneChatRoom *cr = d->getCChatRoom();
_linphone_chat_room_notify_security_event(cr, L_GET_C_BACK_PTR(event));
// Try to set the faulty device PeerDeviceStatus to unsafe
if (getCore()->limeV2Enabled() && event->getFaultyDevice().isValid()) {
LimeV2 *limeV2Engine = static_cast<LimeV2 *>(getCore()->getEncryptionEngine());
// TODO has no effect if faulty device is unkown to LIMEv2
limeV2Engine->getLimeManager()->set_peerDeviceStatus(event->getFaultyDevice().asString(), lime::PeerDeviceStatus::unsafe);
}
}
void ClientGroupChatRoom::onSubjectChanged (const shared_ptr<ConferenceSubjectEvent> &event, bool isFullState) {
......@@ -762,6 +774,37 @@ void ClientGroupChatRoom::onParticipantDeviceAdded (const shared_ptr<ConferenceP
lWarning() << "Participant " << addr.asString() << " added a device but is not in the list of participants!";
return;
}
shared_ptr<ConferenceSecurityEvent> securityEvent;
if (getCore()->limeV2Enabled()) {
int nbDevice = int(participant->getPrivate()->getDevices().size());
int maxNbDevicesPerParticipant = linphone_config_get_int(linphone_core_get_config(L_GET_C_BACK_PTR(getCore())), "lime", "max_nb_device_per_participant", 1);
// Check if the new participant device is unexpected, in which case a security alert is created
if (nbDevice >= maxNbDevicesPerParticipant) {
lWarning() << "LIMEv2 alert: maximum number of devices exceeded for " << participant->getAddress();
securityEvent = make_shared<ConferenceSecurityEvent>(
time(nullptr),
d->conferenceId,
ConferenceSecurityEvent::SecurityEventType::MultideviceParticipantDetected,
event->getDeviceAddress()
);
// Otherwise check if this new device downgrades the chatroom security level, in which case a security event is created
} else {
LimeV2 *limeV2Engine = static_cast<LimeV2 *>(getCore()->getEncryptionEngine());
lime::PeerDeviceStatus newDeviceStatus = limeV2Engine->getLimeManager()->get_peerDeviceStatus(event->getDeviceAddress().asString());
if (getSecurityLevel() == SecurityLevel::Safe && newDeviceStatus != lime::PeerDeviceStatus::trusted) {
lInfo() << "LIMEv2 chat room security level downgraded by "<< event->getDeviceAddress().asString();
securityEvent = make_shared<ConferenceSecurityEvent>(
time(nullptr),
d->conferenceId,
ConferenceSecurityEvent::SecurityEventType::SecurityLevelDowngraded,
event->getDeviceAddress()
);
}
}
}
participant->getPrivate()->addDevice(event->getDeviceAddress());
if (isFullState)
......@@ -769,20 +812,7 @@ void ClientGroupChatRoom::onParticipantDeviceAdded (const shared_ptr<ConferenceP
d->addEvent(event);
// If LIMEv2 enabled and if too many devices for a participant, throw a local security alert event
int nbDevice = int(participant->getPrivate()->getDevices().size());
int maxNbDevicesPerParticipant = linphone_config_get_int(linphone_core_get_config(L_GET_C_BACK_PTR(getCore())), "lime", "max_nb_device_per_participant", 1);
if (getCore()->limeV2Enabled() && nbDevice > maxNbDevicesPerParticipant) {
lWarning() << "LIMEv2 maximum number of devices exceeded for " << participant->getAddress();
const shared_ptr<ConferenceSecurityEvent> securityEvent = make_shared<ConferenceSecurityEvent>(
time(nullptr),
d->conferenceId,
ConferenceSecurityEvent::SecurityAlertType::MultideviceParticipant,
event->getDeviceAddress()
);
onSecurityAlert(securityEvent);
}
if (securityEvent) onSecurityEvent(securityEvent);
LinphoneChatRoom *cr = d->getCChatRoom();
_linphone_chat_room_notify_participant_device_added(cr, L_GET_C_BACK_PTR(event));
......
......@@ -133,6 +133,15 @@ ChatMessageModifier::Result LimeV2::processOutgoingMessage (const shared_ptr<Cha
const IdentityAddress &peerAddress = chatRoom->getPeerAddress();
shared_ptr<const string> recipientUserId = make_shared<const string>(peerAddress.getAddressWithoutGruu().asString());
// Refuse message in unsafe chatroom if not allowed
if (linphone_config_get_int(linphone_core_get_config(chatRoom->getCore()->getCCore()), "lime", "allow_message_in_unsafe_chatroom", 0) == 0) {
if (chatRoom->getSecurityLevel() == ClientGroupChatRoom::SecurityLevel::Unsafe) {
cout << "Sending encrypted message in an unsafe chatroom" << endl;
lWarning() << "Sending encrypted message in an unsafe chatroom" << endl;
return ChatMessageModifier::Result::Error;
}
}
// Add participants to the recipient list
bool tooManyDevices = FALSE;
int maxNbDevicePerParticipant = linphone_config_get_int(linphone_core_get_config(chatRoom->getCore()->getCCore()), "lime", "max_nb_device_per_participant", 1);
......@@ -159,16 +168,6 @@ ChatMessageModifier::Result LimeV2::processOutgoingMessage (const shared_ptr<Cha
}
if (nbDevice > maxNbDevicePerParticipant) tooManyDevices = TRUE;
// Refuse message in unsafe chatroom if not allowed
if (linphone_config_get_int(linphone_core_get_config(chatRoom->getCore()->getCCore()), "lime", "allow_message_in_unsafe_chatroom", 0) == 0) {
for (const auto &recipient : *recipients) {
if (belleSipLimeManager->get_peerDeviceStatus(recipient.deviceId) == lime::PeerDeviceStatus::unsafe) {
lWarning() << "Sending encrypted message to a chatroom with unsafe participant devices" << endl;
return ChatMessageModifier::Result::Error;
}
}
}
// If too many devices for a participant, throw a local security alert event
if (tooManyDevices) {
lWarning() << "Sending encrypted message to multidevice participant, message rejected";
......@@ -180,8 +179,11 @@ ChatMessageModifier::Result LimeV2::processOutgoingMessage (const shared_ptr<Cha
// If there is at least one security alert don't send a new one
for (const auto &event : eventList) {
if (event->getType() == ConferenceEvent::Type::ConferenceSecurityAlert) {
recentSecurityAlert = true;
if (event->getType() == ConferenceEvent::Type::ConferenceSecurityEvent) {
auto securityEvent = static_pointer_cast<ConferenceSecurityEvent>(event);
if (securityEvent->getSecurityEventType() == ConferenceSecurityEvent::SecurityEventType::MultideviceParticipantDetected) {
recentSecurityAlert = true;
}
}
}
......
......@@ -109,6 +109,8 @@ static void chat_room_security_event (LinphoneChatRoom *cr, const LinphoneEventL
LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core);
switch (linphone_event_log_get_security_event_type(event_log)) {
case LinphoneSecurityEventTypeNull:
break;
case LinphoneSecurityEventTypeSecurityLevelDowngraded:
manager->stat.number_of_SecurityLevelDowngraded++;
break;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment