Use pledge(2) and unveil(2) between QApplication[0] initialisation (uses shmget(2) not covered by pledge) and execution, covering both CLI and GUI mode. web-eid/Qt creates/reads/writes configuration/log directories/files. Application specific directories are created up front so they can be unveiled. Filesystem access is thus limited to - dynamic Qt/XDG paths required by web-eid, e.g. rw-c ~/.config/RIA/web-eid/ rw-c ~/.local/share/RIA/web-eid/ - fixed paths required by web-eid rw-- /var/run/pcscd/pcscd.comm r--- /usr/local/ - dynamic XDG paths, optional for web-eid, potentially used by Qt, e.g. rw-- ~/.cache/at-spi/ r--- ~/.config/qt6ct/ 0: https://doc.qt.io/qt-6/qapplication.html 1: https://doc.qt.io/qt-6/qsettings.html 2: https://doc.qt.io/qt-6/qstandardpaths.html Index: src/app/main.cpp --- src/app/main.cpp.orig +++ src/app/main.cpp @@ -24,6 +24,17 @@ #include "controller.hpp" #include "logging.hpp" +#ifdef Q_OS_OPENBSD +#include + +#include +#include +#include +#include +#include +#include +#endif // Q_OS_OPENBSD + #include int main(int argc, char* argv[]) @@ -32,6 +43,90 @@ int main(int argc, char* argv[]) Q_INIT_RESOURCE(translations); Application app(argc, argv, QStringLiteral("web-eid")); + +#ifdef Q_OS_OPENBSD + // src/ui/webeiddialog.cpp WebEidDialog::WebEidDialog() writes "lang" + // src/controller/logging.cpp messageHandler() reads "logging" + const QString configFilePath = QSettings().fileName(); + // src/controller/logging.cpp openLogFile() creates/writes dir/file + const QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + + // ensure config dir exists + QFileInfo configFileInfo { configFilePath }; + QDir configFileDir { configFileInfo.dir() }; + bool configDirExists = configFileDir.mkpath(configFileDir.absolutePath()); + + // ensure log dir exists + QDir logFileDir { logFilePath }; + bool logFileDirExists = logFileDir.mkpath(logFileDir.absolutePath()); + + // permit any files under config and log dirs + if (configDirExists) { + const std::string configFileDirStr = configFileDir.absolutePath().toStdString(); + if (unveil(configFileDirStr.c_str(), "rwc") == -1) { + std::cerr << "unveil: " << configFileDirStr << ": " << strerror(errno) << std::endl; + return -1; + } + } + if (logFileDirExists) { + const std::string logFileDirStr = logFileDir.absolutePath().toStdString(); + if (unveil(logFileDirStr.c_str(), "rwc") == -1) { + std::cerr << "unveil: " << logFileDirStr << ": " << strerror(errno) << std::endl; + return -1; + } + } + + // permit pcscd(8) communication + if (unveil("/var/run/pcscd/pcscd.comm", "rw") == -1) { + std::cerr << "unveil: /var/run/pcscd/pcscd.comm: " << strerror(errno) << std::endl; + return -1; + } + + // permit ports Qt6 and libraries + if (unveil("/usr/local", "r") == -1) { + std::cerr << "unveil: /usr/local: " << strerror(errno) << std::endl; + return -1; + } + +// Qt uses https://www.freedesktop.org/wiki/Accessibility/AT-SPI2/, socket path from X11 root window's AT_SPI_BUS property +// fix following on startup to keep accessibility features working: +// WARNING: Error in contacting registry: "org.freedesktop.DBus.Error.Disconnected" "Not connected to D-Bus server" + const QString genericCacheDirPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); + if (!genericCacheDirPath.isEmpty()) { + QDir AT_SPI2Dir { genericCacheDirPath + "/at-spi" }; + const std::string AT_SPI2DirStr = AT_SPI2Dir.absolutePath().toStdString(); + if (unveil(AT_SPI2DirStr.c_str(), "rw") == -1) { + std::cerr << "unveil: " << AT_SPI2DirStr << ": " << strerror(errno) << std::endl; + return -1; + } + } + +// Qt watches its config, iff environment contains QT_QPA_PLATFORMTHEME=qt6ct +// fix following on startup: +// QKqueueFileSystemWatcherEngine::addPaths: open: No such file or directory + const QString genericConfigDirPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + if (!genericConfigDirPath.isEmpty()) { + QDir QPADir { genericConfigDirPath + "/qt6ct" }; + const std::string QPADirStr = QPADir.absolutePath().toStdString(); + if (unveil(QPADirStr.c_str(), "r") == -1) { + std::cerr << "unveil: " << QPADirStr << ": " << strerror(errno) << std::endl; + return -1; + } + } + + // "rpath cpath wpath" Qt owns web-eid's config and log directories + // "inet dns" web-eid talks to the internet + // "fattr flock ps" Qt locks file access, sysctl(2) KERN_PROC_PID from QLockFile + // "unix" web-eid and Qt communicate with PCSC and D-Bus + // "prot_exec" Qt dlopen(3) libqsvg.so + // + // src/controller/application.cpp Application::isDarkTheme() has code under + // '#elif 0' (the only QProcess occurence in web-eid) that would need "proc exec". + if (pledge("stdio rpath cpath wpath inet fattr flock unix dns prot_exec ps", NULL) == -1) { + std::cerr << "pledge: " << strerror(errno) << std::endl; + return -1; + } +#endif // Q_OS_OPENBSD try { Controller controller(app.parseArgs());