diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-08-10 03:02:37 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-08-10 03:02:42 +0200 |
commit | 9e0fc730f3ee59d626b32e194c38794a882b4103 (patch) | |
tree | d9de03d2b5129d25d17b8f6cee8dbb54803fac1a | |
parent | df2f3de89a3659d767f93e98d0774e54845b8b46 (diff) | |
parent | cdcce44f64f658bce83666f2e24a6f52fb8e8a59 (diff) |
Merge "Merge remote-tracking branch 'origin/5.13' into dev"
-rw-r--r-- | src/imports/opcua/opcuaattributeoperand.h | 1 | ||||
-rw-r--r-- | src/imports/opcua/opcuaeventfilter.h | 1 | ||||
-rw-r--r-- | src/imports/opcua/opcuasimpleattributeoperand.h | 1 | ||||
-rw-r--r-- | src/opcua/client/qopcuaclientprivate.cpp | 17 | ||||
-rw-r--r-- | src/opcua/client/qopcuapkiconfiguration.cpp | 9 | ||||
-rw-r--r-- | src/opcua/client/qopcuapkiconfiguration.h | 1 | ||||
-rw-r--r-- | src/opcua/client/qopcuatype.cpp | 14 | ||||
-rw-r--r-- | src/opcua/client/qopcuatype.h | 1 | ||||
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541valueconverter.cpp | 4 | ||||
-rw-r--r-- | src/plugins/opcua/uacpp/quacppbackend.cpp | 36 | ||||
-rw-r--r-- | tests/auto/security/tst_security.cpp | 25 |
11 files changed, 86 insertions, 24 deletions
diff --git a/src/imports/opcua/opcuaattributeoperand.h b/src/imports/opcua/opcuaattributeoperand.h index de2f332..7637311 100644 --- a/src/imports/opcua/opcuaattributeoperand.h +++ b/src/imports/opcua/opcuaattributeoperand.h @@ -40,6 +40,7 @@ #include "opcuaoperandbase.h" #include <QOpcUaAttributeOperand> #include <QQmlListProperty> +#include <QVector> QT_BEGIN_NAMESPACE diff --git a/src/imports/opcua/opcuaeventfilter.h b/src/imports/opcua/opcuaeventfilter.h index 2ef0f9f..5f863eb 100644 --- a/src/imports/opcua/opcuaeventfilter.h +++ b/src/imports/opcua/opcuaeventfilter.h @@ -42,6 +42,7 @@ #include "opcuasimpleattributeoperand.h" #include "opcuafilterelement.h" #include <QOpcUaMonitoringParameters> +#include <QVector> QT_BEGIN_NAMESPACE diff --git a/src/imports/opcua/opcuasimpleattributeoperand.h b/src/imports/opcua/opcuasimpleattributeoperand.h index 06072fe..ba35679 100644 --- a/src/imports/opcua/opcuasimpleattributeoperand.h +++ b/src/imports/opcua/opcuasimpleattributeoperand.h @@ -40,6 +40,7 @@ #include "opcuaoperandbase.h" #include <QOpcUaSimpleAttributeOperand> #include <QQmlListProperty> +#include <QVector> QT_BEGIN_NAMESPACE diff --git a/src/opcua/client/qopcuaclientprivate.cpp b/src/opcua/client/qopcuaclientprivate.cpp index 1e4c0ae..dfec694 100644 --- a/src/opcua/client/qopcuaclientprivate.cpp +++ b/src/opcua/client/qopcuaclientprivate.cpp @@ -125,6 +125,23 @@ QOpcUaClientPrivate::~QOpcUaClientPrivate() void QOpcUaClientPrivate::connectToEndpoint(const QOpcUaEndpointDescription &endpoint) { + // Some pre-connection checks + if (QOpcUa::isSecurePolicy(endpoint.securityPolicy())) { + // We are going to connect to a secure endpoint + + if (!m_pkiConfig.isPkiValid()) { + qCWarning(QT_OPCUA) << "Can not connect to a secure endpoint without a valid PKI setup."; + setStateAndError(m_state, QOpcUaClient::AccessDenied); + return; + } + + if (!m_pkiConfig.isKeyAndCertificateFileSet()) { + qCWarning(QT_OPCUA) << "Can not connect to a secure endpoint without a client certificate."; + setStateAndError(m_state, QOpcUaClient::AccessDenied); + return; + } + } + m_endpoint = endpoint; m_impl->connectToEndpoint(endpoint); } diff --git a/src/opcua/client/qopcuapkiconfiguration.cpp b/src/opcua/client/qopcuapkiconfiguration.cpp index e0ae67e..4f6f906 100644 --- a/src/opcua/client/qopcuapkiconfiguration.cpp +++ b/src/opcua/client/qopcuapkiconfiguration.cpp @@ -267,4 +267,13 @@ bool QOpcUaPkiConfiguration::isPkiValid() const !trustListDirectory().isEmpty(); } +/*! + Returns true if the private key file and client certificate file are set. +*/ +bool QOpcUaPkiConfiguration::isKeyAndCertificateFileSet() const +{ + return !clientCertificateFile().isEmpty() && + !privateKeyFile().isEmpty(); +} + QT_END_NAMESPACE diff --git a/src/opcua/client/qopcuapkiconfiguration.h b/src/opcua/client/qopcuapkiconfiguration.h index 8c0fe7f..1e54fae 100644 --- a/src/opcua/client/qopcuapkiconfiguration.h +++ b/src/opcua/client/qopcuapkiconfiguration.h @@ -78,6 +78,7 @@ public: QOpcUaApplicationIdentity applicationIdentity() const; bool isPkiValid() const; // Bad name, open for better ideas + bool isKeyAndCertificateFileSet() const; private: QSharedDataPointer<QOpcUaPkiConfigurationData> data; diff --git a/src/opcua/client/qopcuatype.cpp b/src/opcua/client/qopcuatype.cpp index c9e1519..039a389 100644 --- a/src/opcua/client/qopcuatype.cpp +++ b/src/opcua/client/qopcuatype.cpp @@ -875,5 +875,19 @@ QOpcUa::Types QOpcUa::opcUaDataTypeToQOpcUaType(const QString &type) return QOpcUa::Undefined; } +/*! + \since QtOpcUa 5.14 + + Returns \c true if a security policy is a secure policy. +*/ +bool QOpcUa::isSecurePolicy(const QString &securityPolicy) +{ + return securityPolicy == QLatin1String("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15") || + securityPolicy == QLatin1String("http://opcfoundation.org/UA/SecurityPolicy#Basic256") || + securityPolicy == QLatin1String("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256") || + securityPolicy == QLatin1String("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep") || + securityPolicy == QLatin1String("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss"); +} + QT_END_NAMESPACE diff --git a/src/opcua/client/qopcuatype.h b/src/opcua/client/qopcuatype.h index 0969b1e..8e83770 100644 --- a/src/opcua/client/qopcuatype.h +++ b/src/opcua/client/qopcuatype.h @@ -473,6 +473,7 @@ Q_ENUM_NS(ErrorCategory) Q_OPCUA_EXPORT bool isSuccessStatus(QOpcUa::UaStatusCode statusCode); Q_OPCUA_EXPORT QOpcUa::ErrorCategory errorCategory(QOpcUa::UaStatusCode statusCode); Q_OPCUA_EXPORT QString statusToString(QOpcUa::UaStatusCode statusCode); +Q_OPCUA_EXPORT bool isSecurePolicy(const QString &securityPolicy); // NodeId helpers Q_OPCUA_EXPORT QString nodeIdFromString(quint16 ns, const QString &identifier); diff --git a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp index 15e5b91..d044c5c 100644 --- a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp +++ b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp @@ -661,6 +661,10 @@ void createExtensionObject(QByteArray &data, const UA_NodeId &typeEncodingId, UA { UA_ExtensionObject obj; UA_ExtensionObject_init(&obj); + + if (!data.isEmpty() && encoding == QOpcUaExtensionObject::Encoding::NoBody) + qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Data for extension object provided but will not be encoded because encoding format is set to skip the body"; + if (encoding != QOpcUaExtensionObject::Encoding::NoBody) { obj.encoding = static_cast<UA_ExtensionObjectEncoding>(encoding); obj.content.encoded.body.data = reinterpret_cast<UA_Byte *>(data.data()); diff --git a/src/plugins/opcua/uacpp/quacppbackend.cpp b/src/plugins/opcua/uacpp/quacppbackend.cpp index bff66a8..580d52d 100644 --- a/src/plugins/opcua/uacpp/quacppbackend.cpp +++ b/src/plugins/opcua/uacpp/quacppbackend.cpp @@ -261,16 +261,9 @@ void UACppAsyncBackend::connectToEndpoint(const QOpcUaEndpointDescription &endpo sessionSecurityInfo.messageSecurityMode = static_cast<OpcUa_MessageSecurityMode>(endpoint.securityMode()); } - if (authInfo.authenticationType() == QOpcUaUserTokenPolicy::TokenType::Anonymous) { - // nothing to do - } else if (authInfo.authenticationType() == QOpcUaUserTokenPolicy::TokenType::Username) { - const auto credentials = authInfo.authenticationData().value<QPair<QString, QString>>(); - UaString username(credentials.first.toUtf8().constData()); - UaString password(credentials.second.toUtf8().constData()); - sessionSecurityInfo.setUserPasswordUserIdentity(username, password); - if (m_disableEncryptedPasswordCheck) - sessionSecurityInfo.disableEncryptedPasswordCheck = OpcUa_True; - } else if (authInfo.authenticationType() == QOpcUaUserTokenPolicy::TokenType::Certificate) { + if (QOpcUa::isSecurePolicy(endpoint.securityPolicy())) { + // We are going to connect to a secure endpoint + // try to load the client certificate const UaString certificateFilePath(pkiConfig.clientCertificateFile().toUtf8()); const UaString privateKeyFilePath(pkiConfig.privateKeyFile().toUtf8()); @@ -278,7 +271,13 @@ void UACppAsyncBackend::connectToEndpoint(const QOpcUaEndpointDescription &endpo QFile certFile(certificateFilePath.toUtf8()); if (certFile.open(QIODevice::ReadOnly)) { + const auto data = certFile.read(1000); certFile.close(); + if (data.contains("--BEGIN CERTIFICATE")) { + qCWarning(QT_OPCUA_PLUGINS_UACPP) << "The UACPP backend supports only DER encoded certificates."; + emit stateAndOrErrorChanged(QOpcUaClient::Disconnected, QOpcUaClient::AccessDenied); + return; + } } else { qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Failed to load certificate: " << pkiConfig.clientCertificateFile(); result = OpcUa_BadNotFound; @@ -321,7 +320,20 @@ void UACppAsyncBackend::connectToEndpoint(const QOpcUaEndpointDescription &endpo qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Failed to connect using certificate authentication: " << QString::fromUtf8(result.toString().toUtf8()); return; } + } // end secure endpoint + + if (authInfo.authenticationType() == QOpcUaUserTokenPolicy::TokenType::Anonymous) { + // nothing to do + } else if (authInfo.authenticationType() == QOpcUaUserTokenPolicy::TokenType::Username) { + const auto credentials = authInfo.authenticationData().value<QPair<QString, QString>>(); + UaString username(credentials.first.toUtf8().constData()); + UaString password(credentials.second.toUtf8().constData()); + sessionSecurityInfo.setUserPasswordUserIdentity(username, password); + if (m_disableEncryptedPasswordCheck) + sessionSecurityInfo.disableEncryptedPasswordCheck = OpcUa_True; } else { + // QOpcUaUserTokenPolicy::TokenType::Certificate is currently unsupported + emit stateAndOrErrorChanged(QOpcUaClient::Disconnected, QOpcUaClient::UnsupportedAuthenticationInformation); qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Failed to connect: Selected authentication type" << authInfo.authenticationType() << "is not supported."; @@ -334,6 +346,10 @@ void UACppAsyncBackend::connectToEndpoint(const QOpcUaEndpointDescription &endpo if (result.isNotGood()) { emit stateAndOrErrorChanged(QOpcUaClient::Disconnected, QOpcUaClient::AccessDenied); qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Failed to connect:" << QString::fromUtf8(result.toString().toUtf8()); + + if (result.code() == OpcUa_BadEncodingLimitsExceeded && QOpcUa::isSecurePolicy(endpoint.securityPolicy())) + qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Reason may be not using a DER encoded client certificate"; + return; } } diff --git a/tests/auto/security/tst_security.cpp b/tests/auto/security/tst_security.cpp index 9460e46..436f495 100644 --- a/tests/auto/security/tst_security.cpp +++ b/tests/auto/security/tst_security.cpp @@ -145,11 +145,11 @@ private slots: void initTestCase(); void cleanupTestCase(); - defineDataMethod(connectAndDisconnectUsingCertificate_data) - void connectAndDisconnectUsingCertificate(); + defineDataMethod(connectAndDisconnectSecureUnencryptedKey_data) + void connectAndDisconnectSecureUnencryptedKey(); - defineDataMethod(connectAndDisconnectUsingEncryptedPassword_data) - void connectAndDisconnectUsingEncryptedPassword(); + defineDataMethod(connectAndDisconnectSecureEncryptedKey_data) + void connectAndDisconnectSecureEncryptedKey(); private: QString envOrDefault(const char *env, QString def) @@ -244,7 +244,7 @@ void Tst_QOpcUaSecurity::initTestCase() // Select first non-None security policy for (const auto &endpoint : qAsConst(desc)) { - if (!endpoint.securityPolicy().endsWith("#None")) { + if (QOpcUa::isSecurePolicy(endpoint.securityPolicy())) { m_endpoints.append(endpoint); qDebug() << endpoint.securityPolicy(); } @@ -254,7 +254,7 @@ void Tst_QOpcUaSecurity::initTestCase() } } -void Tst_QOpcUaSecurity::connectAndDisconnectUsingCertificate() +void Tst_QOpcUaSecurity::connectAndDisconnectSecureUnencryptedKey() { QFETCH(QString, backend); QFETCH(QOpcUaEndpointDescription, endpoint); @@ -262,9 +262,6 @@ void Tst_QOpcUaSecurity::connectAndDisconnectUsingCertificate() QScopedPointer<QOpcUaClient> client(m_opcUa.createClient(backend)); QVERIFY2(client, QString("Loading backend failed: %1").arg(backend).toLatin1().data()); - if (!client->supportedUserTokenTypes().contains(QOpcUaUserTokenPolicy::TokenType::Certificate)) - QSKIP(QString("This test is skipped because backend %1 does not support certificate authentication").arg(client->backend()).toLatin1().constData()); - const QString pkidir = m_pkiData->path(); QOpcUaPkiConfiguration pkiConfig; pkiConfig.setClientCertificateFile(pkidir + "/own/certs/tst_security.der"); @@ -276,7 +273,7 @@ void Tst_QOpcUaSecurity::connectAndDisconnectUsingCertificate() const auto identity = pkiConfig.applicationIdentity(); QOpcUaAuthenticationInformation authInfo; - authInfo.setCertificateAuthentication(); + authInfo.setUsernameAuthentication("user1", "password"); client->setAuthenticationInformation(authInfo); client->setApplicationIdentity(identity); @@ -316,7 +313,7 @@ void Tst_QOpcUaSecurity::connectAndDisconnectUsingCertificate() QCOMPARE(connectSpy.at(1).at(0), QOpcUaClient::Disconnected); } -void Tst_QOpcUaSecurity::connectAndDisconnectUsingEncryptedPassword() +void Tst_QOpcUaSecurity::connectAndDisconnectSecureEncryptedKey() { QFETCH(QString, backend); QFETCH(QOpcUaEndpointDescription, endpoint); @@ -324,8 +321,8 @@ void Tst_QOpcUaSecurity::connectAndDisconnectUsingEncryptedPassword() QScopedPointer<QOpcUaClient> client(m_opcUa.createClient(backend)); QVERIFY2(client, QString("Loading backend failed: %1").arg(backend).toLatin1().data()); - if (!client->supportedUserTokenTypes().contains(QOpcUaUserTokenPolicy::TokenType::Certificate)) - QSKIP(QString("This test is skipped because backend %1 does not support certificate authentication").arg(client->backend()).toLatin1().constData()); + if (client->backend() == QLatin1String("open62541")) + QSKIP(QString("This test is skipped because backend %1 does not support encrypted keys").arg(client->backend()).toLatin1().constData()); const QString pkidir = m_pkiData->path(); QOpcUaPkiConfiguration pkiConfig; @@ -338,7 +335,7 @@ void Tst_QOpcUaSecurity::connectAndDisconnectUsingEncryptedPassword() const auto identity = pkiConfig.applicationIdentity(); QOpcUaAuthenticationInformation authInfo; - authInfo.setCertificateAuthentication(); + authInfo.setUsernameAuthentication("user1", "password"); client->setAuthenticationInformation(authInfo); client->setApplicationIdentity(identity); |