From ddfe3639386c235d50a54c46071983525ed3a645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20K=C3=B6hne?= Date: Tue, 13 Dec 2022 13:48:20 +0100 Subject: OpcuaViewer: Don't locate files relative to binary The logic to locate files relative to the binary is fragile. It is currently broken for CMake on Windows, and not actually implemented in qmake. Furthermore, it requires the binary directory to be writable at runtime, because additional folders will be created there. Instead, embed the files via qrc, and extract them at runtime into AppLocalDataLocation, which we know we can read/write to. Pick-to: 6.4 6.5 Fixes: QTBUG-109097 Change-Id: Ia6994339f4eba94ee6ef0cd82eadcc72563b7d47 Reviewed-by: Ivan Solovev --- examples/opcua/opcuaviewer/CMakeLists.txt | 14 ++++- examples/opcua/opcuaviewer/mainwindow.cpp | 92 ++++++++++++++---------------- examples/opcua/opcuaviewer/mainwindow.h | 2 - examples/opcua/opcuaviewer/opcuaviewer.pro | 12 ++++ src/opcua/doc/src/security.qdoc | 7 +++ 5 files changed, 73 insertions(+), 54 deletions(-) diff --git a/examples/opcua/opcuaviewer/CMakeLists.txt b/examples/opcua/opcuaviewer/CMakeLists.txt index 08776be..1445a85 100644 --- a/examples/opcua/opcuaviewer/CMakeLists.txt +++ b/examples/opcua/opcuaviewer/CMakeLists.txt @@ -14,9 +14,6 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui OpcUa Widgets) qt_standard_project_setup() -file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/pki" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") - qt_add_executable(opcuaviewer certificatedialog.cpp certificatedialog.h certificatedialog.ui main.cpp @@ -25,6 +22,17 @@ qt_add_executable(opcuaviewer treeitem.cpp treeitem.h ) +qt_add_resources(opcuaviewer "pki" + PREFIX / + FILES + pki/own/certs/opcuaviewer.der + pki/own/private/opcuaviewer.pem + pki/trusted/certs/3d8ec65c47524d6ad67bed912c19a895.der + pki/trusted/certs/ca.der + pki/trusted/certs/open62541-testserver.der + pki/trusted/crl/ca.crl.pem +) + set_target_properties(opcuaviewer PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE TRUE diff --git a/examples/opcua/opcuaviewer/mainwindow.cpp b/examples/opcua/opcuaviewer/mainwindow.cpp index b05605f..00d6e55 100644 --- a/examples/opcua/opcuaviewer/mainwindow.cpp +++ b/examples/opcua/opcuaviewer/mainwindow.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -117,23 +118,51 @@ MainWindow::~MainWindow() delete ui; } +static bool copyDirRecursively(const QString &from, const QString &to) +{ + const QDir srcDir(from); + const QDir targetDir(to); + if (!QDir().mkpath(to)) + return false; + + const QFileInfoList infos = + srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + for (const QFileInfo &info : infos) { + const QString srcItemPath = info.absoluteFilePath(); + const QString dstItemPath = targetDir.absoluteFilePath(info.fileName()); + if (info.isDir()) { + if (!copyDirRecursively(srcItemPath, dstItemPath)) + return false; + } else if (info.isFile()) { + if (!QFile::copy(srcItemPath, dstItemPath)) + return false; + } + } + return true; +} + //! [PKI Configuration] void MainWindow::setupPkiConfiguration() { - QString pkidir = QCoreApplication::applicationDirPath(); -#ifdef Q_OS_WIN - pkidir += "../"; -#endif - pkidir += "/pki"; - m_pkiConfig.setClientCertificateFile(pkidir + "/own/certs/opcuaviewer.der"); - m_pkiConfig.setPrivateKeyFile(pkidir + "/own/private/opcuaviewer.pem"); - m_pkiConfig.setTrustListDirectory(pkidir + "/trusted/certs"); - m_pkiConfig.setRevocationListDirectory(pkidir + "/trusted/crl"); - m_pkiConfig.setIssuerListDirectory(pkidir + "/issuers/certs"); - m_pkiConfig.setIssuerRevocationListDirectory(pkidir + "/issuers/crl"); - - // create the folders if they don't exist yet - createPkiFolders(); + const QDir pkidir = + QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/pki"); + + if (!pkidir.exists() && !copyDirRecursively(":/pki", pkidir.path())) + qFatal("Could not set up directory %s!", qUtf8Printable(pkidir.path())); + + m_pkiConfig.setClientCertificateFile(pkidir.absoluteFilePath("own/certs/opcuaviewer.der")); + m_pkiConfig.setPrivateKeyFile(pkidir.absoluteFilePath("own/private/opcuaviewer.pem")); + m_pkiConfig.setTrustListDirectory(pkidir.absoluteFilePath("trusted/certs")); + m_pkiConfig.setRevocationListDirectory(pkidir.absoluteFilePath("trusted/crl")); + m_pkiConfig.setIssuerListDirectory(pkidir.absoluteFilePath("issuers/certs")); + m_pkiConfig.setIssuerRevocationListDirectory(pkidir.absoluteFilePath("issuers/crl")); + + const QStringList toCreate = { m_pkiConfig.issuerListDirectory(), + m_pkiConfig.issuerRevocationListDirectory() }; + for (const QString &dir : toCreate) { + if (!QDir().mkpath(dir)) + qFatal("Could not create directory %s!", qUtf8Printable(dir)); + } } //! [PKI Configuration] @@ -343,41 +372,6 @@ void MainWindow::log(const QString &text, const QColor &color) log(text, QString(), color); } -bool MainWindow::createPkiPath(const QString &path) -{ - const QString msg = tr("Creating PKI path '%1': %2"); - - QDir dir; - const bool ret = dir.mkpath(path); - if (ret) - qDebug() << msg.arg(path, "SUCCESS."); - else - qCritical("%s", qPrintable(msg.arg(path, "FAILED."))); - - return ret; -} - -bool MainWindow::createPkiFolders() -{ - bool result = createPkiPath(m_pkiConfig.trustListDirectory()); - if (!result) - return result; - - result = createPkiPath(m_pkiConfig.revocationListDirectory()); - if (!result) - return result; - - result = createPkiPath(m_pkiConfig.issuerListDirectory()); - if (!result) - return result; - - result = createPkiPath(m_pkiConfig.issuerRevocationListDirectory()); - if (!result) - return result; - - return result; -} - void MainWindow::showErrorDialog(QOpcUaErrorState *errorState) { int result = 0; diff --git a/examples/opcua/opcuaviewer/mainwindow.h b/examples/opcua/opcuaviewer/mainwindow.h index 1f59048..5f08f50 100644 --- a/examples/opcua/opcuaviewer/mainwindow.h +++ b/examples/opcua/opcuaviewer/mainwindow.h @@ -48,8 +48,6 @@ private: void createClient(); void updateUiState(); void setupPkiConfiguration(); - bool createPkiFolders(); - bool createPkiPath(const QString &path); private: Ui::MainWindow *ui; diff --git a/examples/opcua/opcuaviewer/opcuaviewer.pro b/examples/opcua/opcuaviewer/opcuaviewer.pro index cbe8057..ca0a31a 100644 --- a/examples/opcua/opcuaviewer/opcuaviewer.pro +++ b/examples/opcua/opcuaviewer/opcuaviewer.pro @@ -21,3 +21,15 @@ HEADERS += \ FORMS += \ certificatedialog.ui \ mainwindow.ui \ + +pki.files = \ + pki/own/certs/opcuaviewer.der \ + pki/own/private/opcuaviewer.pem \ + pki/trusted/certs/3d8ec65c47524d6ad67bed912c19a895.der \ + pki/trusted/certs/ca.der \ + pki/trusted/certs/open62541-testserver.der \ + pki/trusted/crl/ca.crl.pem + +pki.prefix = / + +RESOURCES += pki diff --git a/src/opcua/doc/src/security.qdoc b/src/opcua/doc/src/security.qdoc index 62656c7..cdcff38 100644 --- a/src/opcua/doc/src/security.qdoc +++ b/src/opcua/doc/src/security.qdoc @@ -129,7 +129,14 @@ Certificate: \li Configure the correct Application Identity \snippet ../../examples/opcua/opcuaviewer/mainwindow.cpp Application Identity \li Configure PKI locations so that the SDK can find the certificate, private key, trust list etc. + + See for instance the code from the \l{Qt OPC UA Viewer Example}: \snippet ../../examples/opcua/opcuaviewer/mainwindow.cpp PKI Configuration + + In the example, we extract pre-configured own and trusted certificates + from the Qt resource system to a writable location in the file system. + The remaining directories for issuer (revocation) lists are created + manually. \endlist \section2 PKI Folder Layout -- cgit v1.2.3