summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-08-10 03:02:37 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-08-10 03:02:42 +0200
commit9e0fc730f3ee59d626b32e194c38794a882b4103 (patch)
treed9de03d2b5129d25d17b8f6cee8dbb54803fac1a
parentdf2f3de89a3659d767f93e98d0774e54845b8b46 (diff)
parentcdcce44f64f658bce83666f2e24a6f52fb8e8a59 (diff)
Merge "Merge remote-tracking branch 'origin/5.13' into dev"
-rw-r--r--src/imports/opcua/opcuaattributeoperand.h1
-rw-r--r--src/imports/opcua/opcuaeventfilter.h1
-rw-r--r--src/imports/opcua/opcuasimpleattributeoperand.h1
-rw-r--r--src/opcua/client/qopcuaclientprivate.cpp17
-rw-r--r--src/opcua/client/qopcuapkiconfiguration.cpp9
-rw-r--r--src/opcua/client/qopcuapkiconfiguration.h1
-rw-r--r--src/opcua/client/qopcuatype.cpp14
-rw-r--r--src/opcua/client/qopcuatype.h1
-rw-r--r--src/plugins/opcua/open62541/qopen62541valueconverter.cpp4
-rw-r--r--src/plugins/opcua/uacpp/quacppbackend.cpp36
-rw-r--r--tests/auto/security/tst_security.cpp25
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);