Select Git revision
securityevaluationmodel.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
securityevaluationmodel.cpp 36.49 KiB
/****************************************************************************
* Copyright (C) 2013-2016 by Savoir-faire Linux *
* Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "securityevaluationmodel.h"
//Qt
#include <QtCore/QIdentityProxyModel>
#include <QtCore/QTimer>
//Ring
#include "account.h"
#include "certificatemodel.h"
#include "globalinstances.h"
#include "interfaces/pixmapmanipulatori.h"
#include "private/securityevaluationmodel_p.h"
#include "securityflaw.h"
#include "private/securityflaw_p.h"
#include "private/certificate_p.h"
#include <QtAlgorithms>
const QString SecurityEvaluationModelPrivate::messages[enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>()] = {
/*SRTP_ENABLED */QObject::tr("Your media streams are not encrypted, please enable SDES"),
/*TLS_ENABLED */QObject::tr("TLS is disabled, the negotiation won't be encrypted. Your communication will be vulnerable to "
"snooping"),
/*CERTIFICATE_MATCH */QObject::tr("Your certificate and authority don't match, if your certificate require an authority, it won't work"),
/*OUTGOING_SERVER_MATCH */QObject::tr("The outgoing server specified doesn't match the hostname or the one included in the certificate"),
/*VERIFY_INCOMING_ENABLED */QObject::tr("The \"verify incoming certificate\" option is disabled, this leave you vulnerable to man in the middle attack"),
/*VERIFY_ANSWER_ENABLED */QObject::tr("The \"verify answer certificate\" option is disabled, this leave you vulnerable to man in the middle attack"),
/*REQUIRE_CERTIFICATE_ENABLED */QObject::tr("None of your certificate provide a private key, this is required. Please select a private key"
" or use a certificate with one built-in"),
/* NOT_MISSING_CERTIFICATE */QObject::tr("No certificate has been provided. This is, for now, unsupported by Ring"),
/* NOT_MISSING_AUTHORITY */QObject::tr("No certificate authority is provided, it won't be possible to validate if the answer certificates are valid. Some account may also not work."),
};
static const QString s1 = QObject::tr("Your certificate is expired, please contact your system administrator.");
static const QString s2 = QObject::tr("Your certificate is self signed. This break the chain of trust.");
const TypedStateMachine< SecurityEvaluationModel::SecurityLevel , SecurityEvaluationModel::AccountSecurityChecks >
SecurityEvaluationModelPrivate::maximumSecurityLevel = {{
/* SRTP_ENABLED */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* TLS_ENABLED */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* CERTIFICATE_MATCH */ SecurityEvaluationModel::SecurityLevel::WEAK ,
/* OUTGOING_SERVER_MATCH */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* VERIFY_INCOMING_ENABLED */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* VERIFY_ANSWER_ENABLED */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* REQUIRE_CERTIFICATE_ENABLED */ SecurityEvaluationModel::SecurityLevel::WEAK ,
/* NOT_MISSING_CERTIFICATE */ SecurityEvaluationModel::SecurityLevel::WEAK ,
/* NOT_MISSING_AUTHORITY */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
}};
const TypedStateMachine< SecurityEvaluationModel::Severity , SecurityEvaluationModel::AccountSecurityChecks >
SecurityEvaluationModelPrivate::flawSeverity = {{
/* SRTP_ENABLED */ SecurityEvaluationModel::Severity::ISSUE ,
/* TLS_ENABLED */ SecurityEvaluationModel::Severity::ISSUE ,
/* CERTIFICATE_MATCH */ SecurityEvaluationModel::Severity::ERROR ,
/* OUTGOING_SERVER_MATCH */ SecurityEvaluationModel::Severity::WARNING ,
/* VERIFY_INCOMING_ENABLED */ SecurityEvaluationModel::Severity::ISSUE ,
/* VERIFY_ANSWER_ENABLED */ SecurityEvaluationModel::Severity::ISSUE ,
/* REQUIRE_CERTIFICATE_ENABLED */ SecurityEvaluationModel::Severity::ISSUE ,
/* NOT_MISSING_CERTIFICATE */ SecurityEvaluationModel::Severity::WARNING ,
/* NOT_MISSING_AUTHORITY */ SecurityEvaluationModel::Severity::INFORMATION ,
}};
const TypedStateMachine< SecurityEvaluationModel::SecurityLevel , Certificate::Checks > SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel = {{
/* HAS_PRIVATE_KEY */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* EXPIRED */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* STRONG_SIGNING */ SecurityEvaluationModel::SecurityLevel::WEAK ,
/* NOT_SELF_SIGNED */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* KEY_MATCH */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* PRIVATE_KEY_STORAGE_PERMISSION */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* PUBLIC_KEY_STORAGE_PERMISSION */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* PRIVATE_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* PUBLIC_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* PRIVATE_KEY_STORAGE_LOCATION */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
/* PUBLIC_KEY_STORAGE_LOCATION */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
/* PRIVATE_KEY_SELINUX_ATTRIBUTES */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
/* PUBLIC_KEY_SELINUX_ATTRIBUTES */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
/* EXIST */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* VALID */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* VALID_AUTHORITY */ SecurityEvaluationModel::SecurityLevel::MEDIUM ,
/* KNOWN_AUTHORITY */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE , //TODO figure out of the impact of this
/* NOT_REVOKED */ SecurityEvaluationModel::SecurityLevel::WEAK ,
/* AUTHORITY_MATCH */ SecurityEvaluationModel::SecurityLevel::NONE ,
/* EXPECTED_OWNER */ SecurityEvaluationModel::SecurityLevel::MEDIUM , //TODO figure out of the impact of this
/* ACTIVATED */ SecurityEvaluationModel::SecurityLevel::MEDIUM , //TODO figure out of the impact of this
}};
static const Matrix1D<Certificate::Checks, bool> relevantWithoutPrivateKey = {
{ Certificate::Checks::HAS_PRIVATE_KEY , false },
{ Certificate::Checks::EXPIRED , true },
{ Certificate::Checks::STRONG_SIGNING , true },
{ Certificate::Checks::NOT_SELF_SIGNED , true },
{ Certificate::Checks::KEY_MATCH , false },
{ Certificate::Checks::PRIVATE_KEY_STORAGE_PERMISSION , true },
{ Certificate::Checks::PUBLIC_KEY_STORAGE_PERMISSION , true },
{ Certificate::Checks::PRIVATE_KEY_DIRECTORY_PERMISSIONS, true },
{ Certificate::Checks::PUBLIC_KEY_DIRECTORY_PERMISSIONS , true },
{ Certificate::Checks::PRIVATE_KEY_STORAGE_LOCATION , true },
{ Certificate::Checks::PUBLIC_KEY_STORAGE_LOCATION , true },
{ Certificate::Checks::PRIVATE_KEY_SELINUX_ATTRIBUTES , true },
{ Certificate::Checks::PUBLIC_KEY_SELINUX_ATTRIBUTES , true },
{ Certificate::Checks::EXIST , true },
{ Certificate::Checks::VALID , true },
{ Certificate::Checks::VALID_AUTHORITY , true },
{ Certificate::Checks::KNOWN_AUTHORITY , true },
{ Certificate::Checks::NOT_REVOKED , true },
{ Certificate::Checks::AUTHORITY_MATCH , true },
{ Certificate::Checks::EXPECTED_OWNER , true },
{ Certificate::Checks::ACTIVATED , true },
};
const TypedStateMachine< SecurityEvaluationModel::Severity , Certificate::Checks > SecurityEvaluationModelPrivate::certificateFlawSeverity = {{
/* HAS_PRIVATE_KEY */ SecurityEvaluationModel::Severity::ERROR ,
/* EXPIRED */ SecurityEvaluationModel::Severity::WARNING ,
/* STRONG_SIGNING */ SecurityEvaluationModel::Severity::ISSUE ,
/* NOT_SELF_SIGNED */ SecurityEvaluationModel::Severity::WARNING ,
/* KEY_MATCH */ SecurityEvaluationModel::Severity::ERROR ,
/* PRIVATE_KEY_STORAGE_PERMISSION */ SecurityEvaluationModel::Severity::WARNING ,
/* PUBLIC_KEY_STORAGE_PERMISSION */ SecurityEvaluationModel::Severity::WARNING ,
/* PRIVATE_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::Severity::WARNING ,
/* PUBLIC_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::Severity::WARNING ,
/* PRIVATE_KEY_STORAGE_LOCATION */ SecurityEvaluationModel::Severity::INFORMATION ,
/* PUBLIC_KEY_STORAGE_LOCATION */ SecurityEvaluationModel::Severity::INFORMATION ,
/* PRIVATE_KEY_SELINUX_ATTRIBUTES */ SecurityEvaluationModel::Severity::INFORMATION ,
/* PUBLIC_KEY_SELINUX_ATTRIBUTES */ SecurityEvaluationModel::Severity::INFORMATION ,
/* EXIST */ SecurityEvaluationModel::Severity::ERROR ,
/* VALID */ SecurityEvaluationModel::Severity::ERROR ,
/* VALID_AUTHORITY */ SecurityEvaluationModel::Severity::WARNING ,
/* KNOWN_AUTHORITY */ SecurityEvaluationModel::Severity::WARNING ,
/* NOT_REVOKED */ SecurityEvaluationModel::Severity::ISSUE ,
/* AUTHORITY_MATCH */ SecurityEvaluationModel::Severity::ISSUE ,
/* EXPECTED_OWNER */ SecurityEvaluationModel::Severity::WARNING ,
/* ACTIVATED */ SecurityEvaluationModel::Severity::WARNING ,
}};
/**
* This class add a prefix in front of Qt::DisplayRole to add a disambiguation
* when there is multiple certificates in the same SecurityEvaluationModel and
* also add new roles such as the severity, BackgroundRole and DecorationRole
*/
class PrefixAndSeverityProxyModel : public QIdentityProxyModel
{
Q_OBJECT
public:
explicit PrefixAndSeverityProxyModel(const QString& prefix,QAbstractItemModel* parent);
virtual QModelIndex index ( int row , int column, const QModelIndex& parent ) const override;
virtual QVariant data ( const QModelIndex& index , int role ) const override;
virtual int columnCount ( const QModelIndex& parent ) const override;
//Attributes
QString m_Name;
};
/**
* This model transform accounts attributes into security checks to validate if
* some options reduce the security level.
*/
class AccountChecksModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum class Columns {
MESSAGE ,
SOURCE ,
RESULT ,
COUNT__
};
AccountChecksModel(const Account* a);
//Model functions
virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override;
virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const override;
virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const override;
virtual Qt::ItemFlags flags ( const QModelIndex& index ) const override;
virtual bool setData ( const QModelIndex& index, const QVariant &value, int role) override;
virtual QHash<int,QByteArray> roleNames() const override;
//Helpers
void update();
private:
//Attributes
const Account* m_pAccount;
Matrix1D<SecurityEvaluationModel::AccountSecurityChecks, Certificate::CheckValues> m_lCachedResults;
};
/**
* This model take multiple listModels and append them one after the other
*
* the trick for high performance is to known at compile time the sizes
*/
class CombinaisonProxyModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit CombinaisonProxyModel( QAbstractItemModel* publicCert ,
QAbstractItemModel* caCert ,
QAbstractItemModel* account ,
QObject* parent
);
//Model functions
virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override;
virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const override;
virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const override;
virtual Qt::ItemFlags flags ( const QModelIndex& index ) const override;
virtual bool setData ( const QModelIndex& index, const QVariant &value, int role) override;
virtual QHash<int,QByteArray> roleNames() const override;
private:
//Attributes
QVector<QAbstractItemModel*> m_lSources;
///All source model, in order
enum Src {
CA = 0, /*!< Rows allocated for the certificate authority */
PK = 1, /*!< Rows allocated for the public certificate */
AC = 2, /*!< Rows allocated for the account settions */
ER = 3, /*!< TODO Rows allocated for runtime error */
};
///This model expect a certain size, get each sections size
constexpr static const short sizes[] = {
enum_class_size< Certificate :: Checks > (),
enum_class_size< Certificate :: Checks > (),
enum_class_size< SecurityEvaluationModel :: AccountSecurityChecks > (),
};
///Get the combined size
constexpr inline static int totalSize() {
return sizes[CA] + sizes[PK] + sizes[AC]+1;
}
///Get a model index from a value
constexpr inline static int toModelIdx(const int value) {
return (value >= sizes[CA] + sizes[PK] ? AC : (
value >= sizes[PK] ? PK :
CA ) );
}
//Compute the correct index.row() offset
constexpr inline static int fromFinal(const int value) {
return (value >= sizes[CA] + sizes[PK] ? value - sizes[CA] - sizes[PK] : (
value >= sizes[PK] ? value - sizes[CA] :
value ) );
}
};
constexpr const short CombinaisonProxyModel::sizes[];
///Create a callback map for signals to avoid a large switch(){} in the code
static const Matrix1D<SecurityEvaluationModel::Severity, void(SecurityEvaluationModel::*)()> m_lSignalMap = {{
/* UNSUPPORTED */ nullptr ,
/* INFORMATION */ &SecurityEvaluationModel::informationCountChanged ,
/* WARN1NG */ &SecurityEvaluationModel::warningCountChanged ,
/* ISSUE */ &SecurityEvaluationModel::issueCountChanged ,
/* ERROR */ &SecurityEvaluationModel::errorCountChanged ,
/* FATAL_WARNING */ &SecurityEvaluationModel::fatalWarningCountChanged,
}};
SecurityEvaluationModelPrivate::SecurityEvaluationModelPrivate(Account* account, SecurityEvaluationModel* parent) :
QObject(parent),q_ptr(parent), m_pAccount(account),m_isScheduled(false),
m_CurrentSecurityLevel(SecurityEvaluationModel::SecurityLevel::NONE),m_pAccChecks(nullptr),
m_SeverityCount{
/* UNSUPPORTED */ 0,
/* INFORMATION */ 0,
/* WARN1NG */ 0,
/* ISSUE */ 0,
/* ERROR */ 0,
/* FATAL_WARNING */ 0,
}
{
//Make sure the security level is updated if something change
QObject::connect(parent,&SecurityEvaluationModel::layoutChanged , this,&SecurityEvaluationModelPrivate::update);
QObject::connect(parent,&SecurityEvaluationModel::dataChanged , this,&SecurityEvaluationModelPrivate::update);
QObject::connect(parent,&SecurityEvaluationModel::rowsInserted , this,&SecurityEvaluationModelPrivate::update);
QObject::connect(parent,&SecurityEvaluationModel::rowsRemoved , this,&SecurityEvaluationModelPrivate::update);
QObject::connect(parent,&SecurityEvaluationModel::modelReset , this,&SecurityEvaluationModelPrivate::update);
}
/*******************************************************************************
* *
* PrefixAndSeverityProxyModel *
* *
******************************************************************************/
PrefixAndSeverityProxyModel::PrefixAndSeverityProxyModel(const QString& prefix, QAbstractItemModel* parent) :
QIdentityProxyModel(parent),m_Name(prefix)
{
setSourceModel(parent);
}
///It insert a second column with the source name
int PrefixAndSeverityProxyModel::columnCount( const QModelIndex& parent) const
{
Q_UNUSED(parent)
return parent.isValid() ? 0 : 3;
}
QModelIndex PrefixAndSeverityProxyModel::index( int row, int column, const QModelIndex& parent) const
{
if (column == 2)
return createIndex(row,column);
return QIdentityProxyModel::index(row,column,parent);
}
///Map items and add elements
QVariant PrefixAndSeverityProxyModel::data(const QModelIndex& index, int role) const
{
if (index.isValid()) {
Certificate::Checks c = Certificate::Checks::HAS_PRIVATE_KEY;
if (QIdentityProxyModel::data(index,(int)CertificateModel::Role::isCheck).toBool() == true)
c = qvariant_cast<Certificate::Checks>(QIdentityProxyModel::data(index,(int)CertificateModel::Role::check));
else if (index.column() != 2) //That column doesn't exist in the source, the won't exist
return QVariant();
switch (index.column()) {
case (int)AccountChecksModel::Columns::MESSAGE:
switch(role) {
case Qt::DecorationRole:
return GlobalInstances::pixmapManipulator().securityIssueIcon(index);
case (int)SecurityEvaluationModel::Role::Severity:
return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
case (int)SecurityEvaluationModel::Role::SecurityLevel:
return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
}
break;
//
case (int)AccountChecksModel::Columns::SOURCE: {
switch(role) {
case Qt::DisplayRole:
return m_Name;
case (int)SecurityEvaluationModel::Role::Severity:
return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
case (int)SecurityEvaluationModel::Role::SecurityLevel:
return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
}
return QVariant();
}
break;
//Map source column 1 to 2
case (int)AccountChecksModel::Columns::RESULT: {
const QModelIndex& srcIdx = sourceModel()->index(index.row(),1);
c = qvariant_cast<Certificate::Checks>(srcIdx.data((int)CertificateModel::Role::check));
switch(role) {
case (int)SecurityEvaluationModel::Role::Severity:
return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
case (int)SecurityEvaluationModel::Role::SecurityLevel:
return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
}
return srcIdx.data(role);
}
}
}
return QIdentityProxyModel::data(index,role);
}
/*******************************************************************************
* *
* AccountChecksModel *
* *
******************************************************************************/
AccountChecksModel::AccountChecksModel(const Account* a) : QAbstractTableModel(const_cast<Account*>(a)), m_pAccount(a)
{
update();
}
QVariant AccountChecksModel::data( const QModelIndex& index, int role ) const
{
if ((!index.isValid())
|| (index.row() < 0)
|| (index.row() >= enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>())
)
return QVariant();
const SecurityEvaluationModel::AccountSecurityChecks f = static_cast<SecurityEvaluationModel::AccountSecurityChecks>(index.row());
switch(role) {
case (int)SecurityEvaluationModel::Role::Severity:
return QVariant::fromValue(
m_lCachedResults[f] == Certificate::CheckValues::UNSUPPORTED ?
SecurityEvaluationModel::Severity::UNSUPPORTED : SecurityEvaluationModelPrivate::flawSeverity[f]
);
case (int)SecurityEvaluationModel::Role::SecurityLevel:
return QVariant::fromValue(
//If the check is unsupported then using "COMPLETE" won't affect the algorithm output
// if n < current then n else current end will always be "current" when n == maximum
m_lCachedResults[f] == Certificate::CheckValues::UNSUPPORTED ?
SecurityEvaluationModel::SecurityLevel::COMPLETE : SecurityEvaluationModelPrivate::maximumSecurityLevel[f]
);
}
switch (index.column()) {
case (int)AccountChecksModel::Columns::MESSAGE:
switch(role) {
case Qt::DisplayRole:
return SecurityEvaluationModelPrivate::messages[index.row()];
case Qt::DecorationRole:
return GlobalInstances::pixmapManipulator().securityIssueIcon(index);
};
break;
case (int)AccountChecksModel::Columns::SOURCE:
switch(role) {
case Qt::DisplayRole:
return tr("Configuration");
};
break;
case (int)AccountChecksModel::Columns::RESULT:
switch(role) {
case Qt::DisplayRole:
if (m_lCachedResults[f] != Certificate::CheckValues::UNSUPPORTED)
return m_lCachedResults[f] == Certificate::CheckValues::PASSED ? true : false;
break;
};
break;
};
return QVariant();
}
int AccountChecksModel::rowCount( const QModelIndex& parent ) const
{
return parent.isValid() ? 0 : enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>();
}
int AccountChecksModel::columnCount( const QModelIndex& parent ) const
{
return parent.isValid() ? 0 : enum_class_size<AccountChecksModel::Columns>();
}
Qt::ItemFlags AccountChecksModel::flags( const QModelIndex& index) const
{
Q_UNUSED(index)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool AccountChecksModel::setData( const QModelIndex& index, const QVariant &value, int role)
{
Q_UNUSED(index)
Q_UNUSED(value)
Q_UNUSED(role )
return false;
}
QHash<int,QByteArray> AccountChecksModel::roleNames() const
{
return {};
}
//This could have been an inlined function too
#define SET_CHECK_VALUE(check,condition) {Certificate::CheckValues c = Certificate::CheckValues::UNSUPPORTED;\
bool isSet = m_lCachedResults.isSet(check); if (isSet) c = m_lCachedResults[check]; m_lCachedResults.setAt( check ,\
condition? Certificate::CheckValues::PASSED : Certificate::CheckValues::FAILED);\
changed |= (!isSet) || c != m_lCachedResults[check];}
void AccountChecksModel::update()
{
bool changed = false;
// AccountSecurityChecks::SRTP_DISABLED
SET_CHECK_VALUE(SecurityEvaluationModel::AccountSecurityChecks::SRTP_ENABLED ,
m_pAccount->isSrtpEnabled () || m_pAccount->protocol() == Account::Protocol::RING
);
// AccountSecurityChecks::TLS_DISABLED
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::TLS_ENABLED ,
m_pAccount->isTlsEnabled ()
);
// AccountSecurityChecks::CERTIFICATE_MISMATCH
m_lCachedResults.setAt( SecurityEvaluationModel::AccountSecurityChecks::CERTIFICATE_MATCH ,
Certificate::CheckValues::UNSUPPORTED); //TODO
// AccountSecurityChecks::OUTGOING_SERVER_MISMATCH
m_lCachedResults.setAt( SecurityEvaluationModel::AccountSecurityChecks::OUTGOING_SERVER_MATCH ,
Certificate::CheckValues::UNSUPPORTED); //TODO
// AccountSecurityChecks::VERIFY_INCOMING_DISABLED
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::VERIFY_INCOMING_ENABLED ,
m_pAccount->isTlsVerifyServer ()
);
// AccountSecurityChecks::VERIFY_ANSWER_DISABLED
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::VERIFY_ANSWER_ENABLED ,
m_pAccount->isTlsVerifyClient ()
);
// AccountSecurityChecks::REQUIRE_CERTIFICATE_DISABLED
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::REQUIRE_CERTIFICATE_ENABLED ,
m_pAccount->isTlsRequireClientCertificate()
);
// AccountSecurityChecks::MISSING_CERTIFICATE
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::NOT_MISSING_CERTIFICATE ,
m_pAccount->tlsCertificate ()
);
// AccountSecurityChecks::MISSING_AUTHORITY
SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::NOT_MISSING_AUTHORITY ,
m_pAccount->tlsCaListCertificate ()
);
if (changed)
emit dataChanged(index(0,2),index(rowCount()-1,2));
}
#undef SET_CHECK_VALUE
/*******************************************************************************
* *
* CombinaisonProxyModel *
* *
******************************************************************************/
CombinaisonProxyModel::CombinaisonProxyModel(QAbstractItemModel* publicCert,
QAbstractItemModel* caCert ,
QAbstractItemModel* account ,
QObject* parent )
: QAbstractTableModel(parent), m_lSources({publicCert,caCert,account})
{
for (int i = 0; i < m_lSources.size(); i++) {
const QAbstractItemModel* m = m_lSources[i];
if (m) {
connect(m, &QAbstractItemModel::dataChanged, [this,i](const QModelIndex& tl, const QModelIndex& br) {
int offset =0;
for (int j = 0; j < i;j++)
offset += sizes[j];
emit this->dataChanged(this->index(offset+tl.row(), br.column()), this->index(offset+br.row(), br.column()));
});
}
}
}
QVariant CombinaisonProxyModel::data( const QModelIndex& index, int role) const
{
const QAbstractItemModel* src = m_lSources[toModelIdx(index.row())];
//Role::Severity will give ::UNSUPPORTED (aka, 0) if a model is missing
//this is done on purpose
//All "groups" will have empty items for unsupported checks
return index.isValid() && src ? src->index(fromFinal(index.row()),index.column()).data(role) : QVariant();
}
int CombinaisonProxyModel::rowCount( const QModelIndex& parent) const
{
return parent.isValid() ? 0 : totalSize();
}
int CombinaisonProxyModel::columnCount( const QModelIndex& parent) const
{
return parent.isValid() ? 0 : 3;
}
Qt::ItemFlags CombinaisonProxyModel::flags( const QModelIndex& index) const
{
Q_UNUSED(index)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool CombinaisonProxyModel::setData( const QModelIndex& index, const QVariant &value, int role)
{
Q_UNUSED(index)
Q_UNUSED(value)
Q_UNUSED(role )
return false;
}
QHash<int,QByteArray> CombinaisonProxyModel::roleNames() const
{
return {};
}
/*******************************************************************************
* *
* SecurityEvaluationModel *
* *
******************************************************************************/
SecurityEvaluationModel::SecurityEvaluationModel(Account* account) : QSortFilterProxyModel(account),
d_ptr(new SecurityEvaluationModelPrivate(account,this))
{
Certificate* caCert = d_ptr->m_pAccount->tlsCaListCertificate ();
Certificate* pkCert = d_ptr->m_pAccount->tlsCertificate ();
SecurityEvaluationModelPrivate::getCertificateSeverityProxy(caCert);
SecurityEvaluationModelPrivate::getCertificateSeverityProxy(pkCert);
d_ptr->m_pAccChecks = new AccountChecksModel(account);
d_ptr->update();
setSourceModel(new CombinaisonProxyModel(pkCert ? pkCert->d_ptr->m_pSeverityProxy : nullptr, caCert ? caCert->d_ptr->m_pSeverityProxy : nullptr, d_ptr->m_pAccChecks,this));
setSortRole((int)Role::Severity);
}
SecurityEvaluationModel::~SecurityEvaluationModel()
{
delete d_ptr;
}
bool SecurityEvaluationModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
const QModelIndex& idx = sourceModel()->index(source_row,0,source_parent);
const QModelIndex& idx2 = sourceModel()->index(source_row,2,source_parent);
const Severity s = qvariant_cast<Severity>(idx.data((int)SecurityEvaluationModel::Role::Severity));
return s != Severity::UNSUPPORTED && idx2.data(Qt::DisplayRole).toBool() == false;
}
QHash<int,QByteArray> SecurityEvaluationModel::roleNames() const
{
static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
static bool initRoles = false;
if (!initRoles) {
initRoles = true;
roles[(int)Role::Severity] = "Severity";
}
return roles;
}
void SecurityEvaluationModelPrivate::update()
{
//As this can be called multiple time, only perform the checks once per event loop cycle
if (!m_isScheduled) {
m_pAccChecks->update();
#if QT_VERSION >= 0x050400
QTimer::singleShot(0,this,&SecurityEvaluationModelPrivate::updateReal);
m_isScheduled = true;
#else //Too bad for Qt < 5.3 users
updateReal();
#endif
}
}
QAbstractItemModel* SecurityEvaluationModelPrivate::getCertificateSeverityProxy(Certificate* c)
{
if (!c)
return nullptr;
if (!c->d_ptr->m_pSeverityProxy)
c->d_ptr->m_pSeverityProxy = new PrefixAndSeverityProxyModel(tr("Authority" ),c->checksModel());
return c->d_ptr->m_pSeverityProxy;
}
SecurityEvaluationModel::SecurityLevel SecurityEvaluationModelPrivate::maxSecurityLevel(QAbstractItemModel* m, int* counter)
{
typedef SecurityEvaluationModel::Severity Severity ;
typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
SecurityLevel maxLevel = SecurityLevel::COMPLETE;
for (int i=0; i < m->rowCount();i++) {
const QModelIndex& idx = m->index(i,0);
const Severity severity = qvariant_cast<Severity>(
idx.data((int) SecurityEvaluationModel::Role::Severity)
);
//Ignore items without severity
const QVariant levelVariant = idx.data((int) SecurityEvaluationModel::Role::SecurityLevel );
const SecurityLevel level = levelVariant.canConvert<SecurityLevel>() ? qvariant_cast<SecurityLevel>(
levelVariant
) : maxLevel;
//Increment the count
if (counter)
counter[static_cast<int>(severity)]++;
const bool forceIgnore = idx.data((int)CertificateModel::Role::requirePrivateKey).toBool();
//Update the maximum level
maxLevel = level < maxLevel && !forceIgnore ? level : maxLevel;
}
return maxLevel;
}
void SecurityEvaluationModelPrivate::updateReal()
{
typedef SecurityEvaluationModel::Severity Severity ;
typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
int countCache[enum_class_size<SecurityEvaluationModel::Severity>()];
//Reset the counter
for (const Severity s : EnumIterator<Severity>()) {
countCache [(int)s] = m_SeverityCount[(int)s];
m_SeverityCount[(int)s] = 0 ;
}
SecurityLevel maxLevel = maxSecurityLevel(q_ptr, m_SeverityCount);
//Notify
for (const Severity s : EnumIterator<Severity>()) {
if (countCache[(int)s] != m_SeverityCount[(int)s] && m_lSignalMap[s])
(q_ptr->*m_lSignalMap[s])();
}
//Update the security level
if (m_CurrentSecurityLevel != maxLevel) {
m_CurrentSecurityLevel = maxLevel;
emit q_ptr->securityLevelChanged();
}
m_isScheduled = false;
}
QModelIndex SecurityEvaluationModel::getIndex(const SecurityFlaw* flaw)
{
return index(flaw->d_ptr->m_Row,0);
}
QList<SecurityFlaw*> SecurityEvaluationModel::currentFlaws()
{
return d_ptr->m_lCurrentFlaws;
}
SecurityEvaluationModel::SecurityLevel SecurityEvaluationModel::securityLevel() const
{
return d_ptr->m_CurrentSecurityLevel;
}
SecurityEvaluationModel::SecurityLevel SecurityEvaluationModelPrivate::certificateSecurityLevel(const Certificate* c, bool forceIgnorePrivateKey)
{
typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
SecurityLevel maxLevelWithPriv = SecurityLevel::COMPLETE;
SecurityLevel maxLevelWithoutPriv = SecurityLevel::COMPLETE;
const bool ignorePrivateKey = forceIgnorePrivateKey || (c->requirePrivateKey() == false);
if (c->d_ptr->m_hasLoadedSecurityLevel) {
if (ignorePrivateKey)
return c->d_ptr->m_SecurityLevelWithoutPriv;
else
return c->d_ptr->m_SecurityLevelWithPriv;
}
for (const Certificate::Checks check : EnumIterator<Certificate::Checks>()) {
const bool relevant = relevantWithoutPrivateKey[check];
if (c->checkResult(check) == Certificate::CheckValues::FAILED) {
if (relevant) {
const SecurityLevel checkLevel = maximumCertificateSecurityLevel[check];
maxLevelWithoutPriv = checkLevel < maxLevelWithoutPriv ? checkLevel : maxLevelWithoutPriv;
}
const SecurityLevel checkLevel = maximumCertificateSecurityLevel[check];
maxLevelWithPriv = checkLevel < maxLevelWithPriv ? checkLevel : maxLevelWithPriv;
}
}
c->d_ptr->m_hasLoadedSecurityLevel = true;
c->d_ptr->m_SecurityLevelWithoutPriv = maxLevelWithoutPriv;
c->d_ptr->m_SecurityLevelWithPriv = maxLevelWithPriv;
return ignorePrivateKey ? c->d_ptr->m_SecurityLevelWithoutPriv : c->d_ptr->m_SecurityLevelWithPriv;
}
//Map the array to getters
int SecurityEvaluationModel::informationCount () const
{ return d_ptr->m_SeverityCount[ (int)Severity::INFORMATION ]; }
int SecurityEvaluationModel::warningCount () const
{ return d_ptr->m_SeverityCount[ (int)Severity::WARNING ]; }
int SecurityEvaluationModel::issueCount () const
{ return d_ptr->m_SeverityCount[ (int)Severity::ISSUE ]; }
int SecurityEvaluationModel::errorCount () const
{ return d_ptr->m_SeverityCount[ (int)Severity::ERROR ]; }
int SecurityEvaluationModel::fatalWarningCount () const
{ return d_ptr->m_SeverityCount[ (int)Severity::FATAL_WARNING ]; }
#include <securityevaluationmodel.moc>