diff options
author | Rainer Keller <Rainer.Keller@qt.io> | 2019-02-04 16:14:25 +0100 |
---|---|---|
committer | Rainer Keller <Rainer.Keller@qt.io> | 2019-03-06 15:35:04 +0000 |
commit | 19073534dff44d76e8d0c51358bdc0e4520d6907 (patch) | |
tree | a6db4b8186ad48134a8e7179d55b26b14d12e9bc | |
parent | dee609343270c17b9ff132860eda822eb01028ed (diff) |
tests: Add security extensions to testserver
Change-Id: I3f463f290d86f38dbf7fd7ae2a67d643b31113d1
Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
-rw-r--r-- | LICENSE-CC0 | 110 | ||||
-rw-r--r-- | src/opcua/doc/src/security.qdoc | 2 | ||||
-rw-r--r-- | tests/auto/declarative/DiscoveryTest.qml | 5 | ||||
-rw-r--r-- | tests/auto/qopcuaclient/data.qrc | 5 | ||||
-rw-r--r-- | tests/auto/qopcuaclient/qopcuaclient.pro | 6 | ||||
-rw-r--r-- | tests/auto/qopcuaclient/tst_client.cpp | 9 | ||||
-rw-r--r-- | tests/open62541-testserver/certs.qrc | 6 | ||||
-rw-r--r-- | tests/open62541-testserver/open62541-testserver.pro | 17 | ||||
-rw-r--r-- | tests/open62541-testserver/pki/own/certs/open62541-testserver.der | bin | 0 -> 1031 bytes | |||
-rw-r--r-- | tests/open62541-testserver/pki/own/private/open62541-testserver.der | bin | 0 -> 1190 bytes | |||
-rw-r--r-- | tests/open62541-testserver/pki/trusted/certs/.gitkeep | 0 | ||||
-rw-r--r-- | tests/open62541-testserver/qt_attribution.json | 23 | ||||
-rw-r--r-- | tests/open62541-testserver/security_addon.cpp | 121 | ||||
-rw-r--r-- | tests/open62541-testserver/security_addon.h | 23 | ||||
-rw-r--r-- | tests/open62541-testserver/testserver.cpp | 163 | ||||
-rw-r--r-- | tests/open62541-testserver/testserver.h | 4 |
16 files changed, 489 insertions, 5 deletions
diff --git a/LICENSE-CC0 b/LICENSE-CC0 new file mode 100644 index 0000000..ad7fdad --- /dev/null +++ b/LICENSE-CC0 @@ -0,0 +1,110 @@ +Creative Commons CCZero 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/src/opcua/doc/src/security.qdoc b/src/opcua/doc/src/security.qdoc index 8575cce..4fc8e5d 100644 --- a/src/opcua/doc/src/security.qdoc +++ b/src/opcua/doc/src/security.qdoc @@ -65,7 +65,7 @@ \code [ req ] default_bits = 2048 - default_md = sha512 + default_md = sha256 distinguished_name = subject req_extensions = req_ext x509_extensions = req_ext diff --git a/tests/auto/declarative/DiscoveryTest.qml b/tests/auto/declarative/DiscoveryTest.qml index c5e09bc..9a64365 100644 --- a/tests/auto/declarative/DiscoveryTest.qml +++ b/tests/auto/declarative/DiscoveryTest.qml @@ -256,7 +256,10 @@ Item { compare(endpointsCountSpy2.count, 1); compare(endpointsStatusSpy2.count, 2); compare(endpointsChangedSpy2.count, 2); - compare(myEndpoints2.count, 1); + if (SERVER_SUPPORTS_SECURITY) + compare(myEndpoints2.count, 5); + else + compare(myEndpoints2.count, 1); verify(myEndpoints2.at(0).endpointUrl.startsWith("opc.tcp://")); compare(myEndpoints2.at(0).securityPolicyUri, "http://opcfoundation.org/UA/SecurityPolicy#None"); diff --git a/tests/auto/qopcuaclient/data.qrc b/tests/auto/qopcuaclient/data.qrc new file mode 100644 index 0000000..eba56cb --- /dev/null +++ b/tests/auto/qopcuaclient/data.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>../../open62541-testserver/pki/own/certs/open62541-testserver.der</file> + </qresource> +</RCC> diff --git a/tests/auto/qopcuaclient/qopcuaclient.pro b/tests/auto/qopcuaclient/qopcuaclient.pro index 930b54d..62c50c7 100644 --- a/tests/auto/qopcuaclient/qopcuaclient.pro +++ b/tests/auto/qopcuaclient/qopcuaclient.pro @@ -12,3 +12,9 @@ HEADERS += \ INCLUDEPATH += \ $$PWD/../../common + +RESOURCES += data.qrc + +# This tries to check if the server supports security +QT += opcua_private +qtConfig(mbedtls): DEFINES += SERVER_SUPPORTS_SECURITY diff --git a/tests/auto/qopcuaclient/tst_client.cpp b/tests/auto/qopcuaclient/tst_client.cpp index 15edffc..1b39781 100644 --- a/tests/auto/qopcuaclient/tst_client.cpp +++ b/tests/auto/qopcuaclient/tst_client.cpp @@ -734,7 +734,16 @@ void Tst_QOpcUaClient::requestEndpoints() QCOMPARE(desc[0].transportProfileUri(), QStringLiteral("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary")); QCOMPARE(desc[0].securityLevel(), 0); QCOMPARE(desc[0].securityMode(), QOpcUaEndpointDescription::MessageSecurityMode::None); +#ifdef SERVER_SUPPORTS_SECURITY + QFile file(":/open62541-testserver/pki/own/certs/open62541-testserver.der"); + QVERIFY(file.open(QFile::ReadOnly)); + const auto serverCertificate = file.readAll(); + QVERIFY(serverCertificate.size() > 0); + file.close(); + QCOMPARE(desc[0].serverCertificate(), serverCertificate); +#else QCOMPARE(desc[0].serverCertificate(), QByteArray()); +#endif QCOMPARE(desc[0].userIdentityTokens().size(), 2); QCOMPARE(desc[0].userIdentityTokens()[0].policyId(), QStringLiteral("open62541-anonymous-policy")); diff --git a/tests/open62541-testserver/certs.qrc b/tests/open62541-testserver/certs.qrc new file mode 100644 index 0000000..c0eb8a2 --- /dev/null +++ b/tests/open62541-testserver/certs.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>pki/own/private/open62541-testserver.der</file> + <file>pki/own/certs/open62541-testserver.der</file> + </qresource> +</RCC> diff --git a/tests/open62541-testserver/open62541-testserver.pro b/tests/open62541-testserver/open62541-testserver.pro index 68a0069..5e9bb14 100644 --- a/tests/open62541-testserver/open62541-testserver.pro +++ b/tests/open62541-testserver/open62541-testserver.pro @@ -11,6 +11,10 @@ CONFIG += c++11 console QT += opcua-private qtConfig(open62541):!qtConfig(system-open62541) { + qtConfig(mbedtls):{ + QMAKE_USE_PRIVATE += mbedtls + DEFINES += UA_ENABLE_ENCRYPTION + } include($$PWD/../../src/3rdparty/open62541.pri) } else { QMAKE_USE_PRIVATE += open62541 @@ -18,6 +22,13 @@ qtConfig(open62541):!qtConfig(system-open62541) { win32: DESTDIR = ./ +# This file can only be compiled in case of TLS support +qtConfig(mbedtls) { + # Use custom compiler from src/3rdparty/open62541.pri to hide warning caused by + # including open62541.h + OPEN62541_SOURCES += security_addon.cpp +} + SOURCES += \ main.cpp \ testserver.cpp \ @@ -26,4 +37,8 @@ SOURCES += \ HEADERS += \ - testserver.h + testserver.h \ + security_addon.h \ + +RESOURCES += certs.qrc + diff --git a/tests/open62541-testserver/pki/own/certs/open62541-testserver.der b/tests/open62541-testserver/pki/own/certs/open62541-testserver.der Binary files differnew file mode 100644 index 0000000..c8bad3d --- /dev/null +++ b/tests/open62541-testserver/pki/own/certs/open62541-testserver.der diff --git a/tests/open62541-testserver/pki/own/private/open62541-testserver.der b/tests/open62541-testserver/pki/own/private/open62541-testserver.der Binary files differnew file mode 100644 index 0000000..f396b44 --- /dev/null +++ b/tests/open62541-testserver/pki/own/private/open62541-testserver.der diff --git a/tests/open62541-testserver/pki/trusted/certs/.gitkeep b/tests/open62541-testserver/pki/trusted/certs/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/open62541-testserver/pki/trusted/certs/.gitkeep diff --git a/tests/open62541-testserver/qt_attribution.json b/tests/open62541-testserver/qt_attribution.json new file mode 100644 index 0000000..7536c1c --- /dev/null +++ b/tests/open62541-testserver/qt_attribution.json @@ -0,0 +1,23 @@ +{ + "Id": "open62541", + "Name": "Open62541", + "QDocModule": "qtopcua", + "QtUsage": "Used in the Qt OPC UA module to build the test server.", + "Description": "Open62541 is an open source implementation of the OPC UA protocol.", + "Homepage": "https://open62541.org/", + "Version": "0.3", + "License": "Creative Commons CCZero 1.0 Universal License", + "LicenseId": "CC0-1.0", + "LicenseFile": "../../LICENSE-CC0", + "Copyright": "Bauer, Maximilian Ebrahimi + Reza <reza.ebrahimi.dev (at) gmail.com> + Graube, Markus <markus.graube (at) tu-dresden.de> + Gruener, Sten <s.gruener (at) plt.rwth-aachen.de> + Iatrou, Chris Paul <chris_paul.iatrou (at) tu-dresden.de> + Jeromin, Holger Palm, Florian <f.palm (at) plt.rwth-aachen.de> + Pfrommer, Julius <julius.pfrommer (at) iosb.fraunhofer.edu> + Profanter, Stefan <profanter (at) fortiss.org> + Stalder, Thomas <t.stalder (at) bluetimeconcept.ch> + Urbas, Leon <leon.urbas (at) tu-dresden.de> + " +} diff --git a/tests/open62541-testserver/security_addon.cpp b/tests/open62541-testserver/security_addon.cpp new file mode 100644 index 0000000..99a8809 --- /dev/null +++ b/tests/open62541-testserver/security_addon.cpp @@ -0,0 +1,121 @@ +/* This work is licensed under a Creative Commons CCZero 1.0 Universal License. + * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. + * + * Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB + * Copyright 2017 (c) Julian Grothoff + * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB + * Copyright 2017 (c) Stefan Profanter, fortiss GmbH + * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA + * Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG + */ + +// +// This code was mostly taken from the open62541 project from file plugins/ua_config_default.c +// + +#include "security_addon.h" + +UA_StatusCode createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint, const UA_ByteString localCertificate) +{ + UA_EndpointDescription_init(&endpoint->endpointDescription); + + UA_SecurityPolicy_None(&endpoint->securityPolicy, NULL, localCertificate, conf->logger); + endpoint->endpointDescription.securityMode = UA_MESSAGESECURITYMODE_NONE; + endpoint->endpointDescription.securityPolicyUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None"); + endpoint->endpointDescription.transportProfileUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"); + + /* Enable all login mechanisms from the access control plugin */ + UA_StatusCode retval = UA_Array_copy(conf->accessControl.userTokenPolicies, + conf->accessControl.userTokenPoliciesSize, + (void **)&endpoint->endpointDescription.userIdentityTokens, + &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); + if (retval != UA_STATUSCODE_GOOD) + return retval; + endpoint->endpointDescription.userIdentityTokensSize = conf->accessControl.userTokenPoliciesSize; + + UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate); + UA_ApplicationDescription_copy(&conf->applicationDescription, + &endpoint->endpointDescription.server); + + return UA_STATUSCODE_GOOD; +} + +UA_StatusCode +createSecurityPolicyBasic128Rsa15Endpoint(UA_ServerConfig *const conf, + UA_Endpoint *endpoint, + UA_MessageSecurityMode securityMode, + const UA_ByteString localCertificate, + const UA_ByteString localPrivateKey) { + UA_EndpointDescription_init(&endpoint->endpointDescription); + + UA_StatusCode retval = + UA_SecurityPolicy_Basic128Rsa15(&endpoint->securityPolicy, &conf->certificateVerification, + localCertificate, localPrivateKey, conf->logger); + if (retval != UA_STATUSCODE_GOOD) { + endpoint->securityPolicy.deleteMembers(&endpoint->securityPolicy); + return retval; + } + + endpoint->endpointDescription.securityMode = securityMode; + endpoint->endpointDescription.securityPolicyUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"); + endpoint->endpointDescription.transportProfileUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"); + + /* Enable all login mechanisms from the access control plugin */ + retval = UA_Array_copy(conf->accessControl.userTokenPolicies, + conf->accessControl.userTokenPoliciesSize, + (void **)&endpoint->endpointDescription.userIdentityTokens, + &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); + if (retval != UA_STATUSCODE_GOOD) + return retval; + endpoint->endpointDescription.userIdentityTokensSize = + conf->accessControl.userTokenPoliciesSize; + + UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate); + UA_ApplicationDescription_copy(&conf->applicationDescription, + &endpoint->endpointDescription.server); + + return UA_STATUSCODE_GOOD; +} + +UA_StatusCode +createSecurityPolicyBasic256Sha256Endpoint(UA_ServerConfig *const conf, + UA_Endpoint *endpoint, + UA_MessageSecurityMode securityMode, + const UA_ByteString localCertificate, + const UA_ByteString localPrivateKey) { + UA_EndpointDescription_init(&endpoint->endpointDescription); + + UA_StatusCode retval = + UA_SecurityPolicy_Basic256Sha256(&endpoint->securityPolicy, &conf->certificateVerification, localCertificate, + localPrivateKey, conf->logger); + if (retval != UA_STATUSCODE_GOOD) { + endpoint->securityPolicy.deleteMembers(&endpoint->securityPolicy); + return retval; + } + + endpoint->endpointDescription.securityMode = securityMode; + endpoint->endpointDescription.securityPolicyUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"); + endpoint->endpointDescription.transportProfileUri = + UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"); + + /* Enable all login mechanisms from the access control plugin */ + retval = UA_Array_copy(conf->accessControl.userTokenPolicies, + conf->accessControl.userTokenPoliciesSize, + (void **)&endpoint->endpointDescription.userIdentityTokens, + &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); + if (retval != UA_STATUSCODE_GOOD) + return retval; + endpoint->endpointDescription.userIdentityTokensSize = + conf->accessControl.userTokenPoliciesSize; + + UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate); + UA_ApplicationDescription_copy(&conf->applicationDescription, + &endpoint->endpointDescription.server); + + return UA_STATUSCODE_GOOD; +} diff --git a/tests/open62541-testserver/security_addon.h b/tests/open62541-testserver/security_addon.h new file mode 100644 index 0000000..455092f --- /dev/null +++ b/tests/open62541-testserver/security_addon.h @@ -0,0 +1,23 @@ + +/* This work is licensed under a Creative Commons CCZero 1.0 Universal License. + * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. + * + * Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB + * Copyright 2017 (c) Julian Grothoff + * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB + * Copyright 2017 (c) Stefan Profanter, fortiss GmbH + * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA + * Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG + */ + +// +// This code was taken from the open62541 project from file plugins/ua_config_default.c +// + +#include "open62541.h" + +UA_StatusCode createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint, const UA_ByteString localCertificate); +UA_StatusCode createSecurityPolicyBasic128Rsa15Endpoint(UA_ServerConfig *const conf, UA_Endpoint *endpoint, UA_MessageSecurityMode securityMode, + const UA_ByteString localCertificate, const UA_ByteString localPrivateKey); +UA_StatusCode createSecurityPolicyBasic256Sha256Endpoint(UA_ServerConfig *const conf, UA_Endpoint *endpoint, UA_MessageSecurityMode securityMode, + const UA_ByteString localCertificate, const UA_ByteString localPrivateKey); diff --git a/tests/open62541-testserver/testserver.cpp b/tests/open62541-testserver/testserver.cpp index b0edba9..2e79d72 100644 --- a/tests/open62541-testserver/testserver.cpp +++ b/tests/open62541-testserver/testserver.cpp @@ -42,15 +42,24 @@ #include <QtCore/QDebug> #include <QtCore/QLoggingCategory> #include <QtCore/QUuid> +#include <QDir> +#include <QFile> + +#if defined UA_ENABLE_ENCRYPTION +#include "security_addon.h" +#endif #include <cstring> QT_BEGIN_NAMESPACE +const UA_UInt16 portNumber = 43344; + // Node ID conversion is included from the open62541 plugin but warnings from there should be logged // using qt.opcua.testserver instead of qt.opcua.plugins.open62541 for usage in the test server Q_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541, "qt.opcua.testserver") + TestServer::TestServer(QObject *parent) : QObject(parent) { m_timer.setInterval(30); @@ -65,15 +74,165 @@ TestServer::~TestServer() UA_ServerConfig_delete(m_config); } -bool TestServer::init() +bool TestServer::createInsecureServerConfig() +{ + m_config = UA_ServerConfig_new_minimal(portNumber, nullptr); + return m_config != nullptr; +} + +#if defined UA_ENABLE_ENCRYPTION +static UA_ByteString loadFile(const QString &filePath) { + UA_ByteString fileContents = UA_STRING_NULL; + fileContents.length = 0; + + QFile file(filePath); + if (!file.open(QFile::ReadOnly)) + return fileContents; + + fileContents.length = file.size(); + fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte)); + if (!fileContents.data) + return fileContents; + + if (file.read(reinterpret_cast<char*>(fileContents.data), fileContents.length) != fileContents.length) { + UA_ByteString_deleteMembers(&fileContents); + fileContents.length = 0; + return fileContents; + } + return fileContents; +} + +bool TestServer::createSecureServerConfig() { - m_config = UA_ServerConfig_new_minimal(43344, nullptr); + const QString certificateFilePath = QLatin1String(":/pki/own/certs/open62541-testserver.der"); + const QString privateKeyFilePath = QLatin1String(":/pki/own/private/open62541-testserver.der"); + + UA_ByteString certificate = loadFile(certificateFilePath); + UaDeleter<UA_ByteString> certificateDeleter(&certificate, UA_ByteString_deleteMembers); + UA_ByteString privateKey = loadFile(privateKeyFilePath); + UaDeleter<UA_ByteString> privateKeyDeleter(&privateKey, UA_ByteString_deleteMembers); + + if (certificate.length == 0) { + UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Failed to load certificate %s", certificateFilePath.toLocal8Bit().constData()); + return false; + } + if (privateKey.length == 0) { + UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Failed to load private key %s", privateKeyFilePath.toLocal8Bit().constData()); + return false; + } + + // Load the trustlist + QDir trustDir(":/pki/trusted/certs"); + if (!trustDir.exists()) { + UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Trust directory does not exist"); + return false; + } + + const auto trustedCerts = trustDir.entryList(QDir::Files); + const size_t trustListSize = trustedCerts.size(); + int i = 0; + + UA_STACKARRAY(UA_ByteString, trustList, trustListSize); + UaArrayDeleter<UA_TYPES_BYTESTRING> trustListDeleter(&trustList, trustListSize); + + for (const auto &entry : trustedCerts) { + trustList[i] = loadFile(trustDir.filePath(entry)); + if (trustList[i].length == 0) { + UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Failed to load trusted certificate"); + return false; + } else { + UA_LOG_DEBUG(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Trusted certificate %s loaded", entry.toLocal8Bit().constData()); + } + ++i; + } + + // Loading of a revocation list currently unsupported + UA_ByteString *revocationList = nullptr; + size_t revocationListSize = 0; + + m_config = UA_ServerConfig_new_minimal(portNumber, nullptr); if (!m_config) return false; + UA_StatusCode retval = UA_CertificateVerification_Trustlist(&m_config->certificateVerification, + trustList, trustListSize, + revocationList, revocationListSize); + + if (retval != UA_STATUSCODE_GOOD) + return false; + + retval = UA_Nodestore_default_new(&m_config->nodestore); + if (retval != UA_STATUSCODE_GOOD) + return false; + + if (trustListSize == 0) + UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "No CA trust-list provided. Any remote certificate will be accepted."); + + /* Allocate the endpoints */ + m_config->endpointsSize = 0; + m_config->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint) * 5); + if (!m_config->endpoints) + return false; + + /* Populate the endpoints */ + retval = createSecurityPolicyNoneEndpoint(m_config, &m_config->endpoints[m_config->endpointsSize], certificate); + ++m_config->endpointsSize; + if (retval != UA_STATUSCODE_GOOD) + return false; + + retval = createSecurityPolicyBasic128Rsa15Endpoint(m_config, &m_config->endpoints[m_config->endpointsSize], + UA_MESSAGESECURITYMODE_SIGN, certificate, privateKey); + ++m_config->endpointsSize; + if (retval != UA_STATUSCODE_GOOD) + return false; + + retval = createSecurityPolicyBasic128Rsa15Endpoint(m_config, &m_config->endpoints[m_config->endpointsSize], + UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, certificate, privateKey); + ++m_config->endpointsSize; + if (retval != UA_STATUSCODE_GOOD) + return false; + + retval = createSecurityPolicyBasic256Sha256Endpoint(m_config, &m_config->endpoints[m_config->endpointsSize], + UA_MESSAGESECURITYMODE_SIGN, certificate, privateKey); + ++m_config->endpointsSize; + if (retval != UA_STATUSCODE_GOOD) + return false; + + retval = createSecurityPolicyBasic256Sha256Endpoint(m_config, &m_config->endpoints[m_config->endpointsSize], + UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, certificate, privateKey); + ++m_config->endpointsSize; + if (retval != UA_STATUSCODE_GOOD) + return false; + + // Do not delete items on success. + // They will be used by the server. + certificateDeleter.release(); + privateKeyDeleter.release(); + trustListDeleter.release(); + + return true; +} +#endif + +bool TestServer::init() +{ + bool success; + +#if defined UA_ENABLE_ENCRYPTION + success = createSecureServerConfig(); +#else + success = createInsecureServerConfig(); +#endif + // This is needed for COIN because the hostname returned by gethostname() is not resolvable. m_config->customHostname = UA_String_fromChars("localhost"); + if (!success || !m_config) + return false; + m_server = UA_Server_new(m_config); if (!m_server) diff --git a/tests/open62541-testserver/testserver.h b/tests/open62541-testserver/testserver.h index 0a0f383..c70fef6 100644 --- a/tests/open62541-testserver/testserver.h +++ b/tests/open62541-testserver/testserver.h @@ -55,6 +55,10 @@ public: explicit TestServer(QObject *parent = nullptr); ~TestServer(); bool init(); + bool createInsecureServerConfig(); +#if defined UA_ENABLE_ENCRYPTION + bool createSecureServerConfig(); +#endif int registerNamespace(const QString &ns); UA_NodeId addFolder(const QString &nodeString, const QString &displayName, const QString &description = QString()); |