Revision control
++ // Copyright (C) 2019 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ #define UMDF_USING_NTSTATUS // Avoid ntstatus redefinitions
++
++ #include "qwinregistry_p.h"
++ #include <QtCore/qvarlengtharray.h>
++ #include <QtCore/qdebug.h>
++ #include <QtCore/qendian.h>
++ #include <QtCore/qlist.h>
++ #include <QtCore/qwineventnotifier.h>
++ #include <QtCore/private/qsystemerror_p.h>
++
++ #include <ntstatus.h>
++
++ // User mode version of ZwQueryKey, as per:
++ extern "C" NTSTATUS NTSYSCALLAPI NTAPI NtQueryKey(HANDLE KeyHandle, int KeyInformationClass,
++ PVOID KeyInformation, ULONG Length, PULONG ResultLength);
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ static const wchar_t *nullTerminate(const QString &s)
++ {
++ // ### port to QString::nullTerminate() if this stays around for longer
++ return reinterpret_cast<const wchar_t*>(s.utf16());
++ }
++
++ // Open a key with the specified permissions (KEY_READ/KEY_WRITE).
++ // "access" is to explicitly use the 32- or 64-bit branch.
++ QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const wchar_t *subKey,
++ REGSAM permissions, REGSAM access)
++ {
++ if (RegOpenKeyExW(parentHandle, subKey,
++ 0, permissions | access, &m_key) != ERROR_SUCCESS) {
++ m_key = nullptr;
++ }
++ }
++
++ QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const QString &subKey,
++ REGSAM permissions, REGSAM access)
++ : QWinRegistryKey(parentHandle, nullTerminate(subKey), permissions, access)
++ {
++ }
++
++ QWinRegistryKey::~QWinRegistryKey()
++ {
++ close();
++ }
++
++ void QWinRegistryKey::close()
++ {
++ if (isValid()) {
++ RegCloseKey(m_key);
++ m_key = nullptr;
++ }
++ }
++
++ QString QWinRegistryKey::name() const
++ {
++ if (!isValid())
++ return {};
++
++ // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_key_name_information
++ constexpr int kKeyNameInformation = 3;
++ struct KeyNameInformation { ULONG length = 0; WCHAR name[1] = {}; };
++
++ // Resolve name of key iteratively by first computing the needed size,
++ // and then querying the name, accounting for the possibility that the
++ // name length changes behind our back a few times.
++ DWORD keyInformationSize = 0;
++ for (int i = 0; i < 5; ++i) {
++ QByteArray buffer(keyInformationSize, 0u);
++ auto *keyNameInformation = reinterpret_cast<KeyNameInformation *>(buffer.data());
++ switch (NtQueryKey(m_key, kKeyNameInformation, keyNameInformation,
++ keyInformationSize, &keyInformationSize)) {
++ case STATUS_BUFFER_TOO_SMALL:
++ case STATUS_BUFFER_OVERFLOW:
++ continue;
++ case STATUS_SUCCESS: {
++ return QString::fromWCharArray(keyNameInformation->name,
++ keyNameInformation->length / sizeof(wchar_t));
++ }
++ default:
++ return {};
++ }
++ }
++
++ return {};
++ }
++
++ QVariant QWinRegistryKey::value(const wchar_t *subKey) const
++ {
++ // NOTE: Empty value name is allowed in Windows registry, it means the default
++ // or unnamed value of a key, you can read/write/delete such value normally.
++
++ if (!isValid())
++ return {};
++
++ // Get the size and type of the value.
++ DWORD dataType = REG_NONE;
++ DWORD dataSize = 0;
++ LONG ret = RegQueryValueExW(m_key, subKey, nullptr, &dataType, nullptr, &dataSize);
++ if (ret != ERROR_SUCCESS)
++ return {};
++
++ // Workaround for rare cases where the trailing '\0' is missing.
++ if (dataType == REG_SZ || dataType == REG_EXPAND_SZ)
++ dataSize += 2;
++ else if (dataType == REG_MULTI_SZ)
++ dataSize += 4;
++
++ // Get the value.
++ QVarLengthArray<unsigned char> data(dataSize);
++ std::fill(data.data(), data.data() + dataSize, 0u);
++
++ ret = RegQueryValueExW(m_key, subKey, nullptr, nullptr, data.data(), &dataSize);
++ if (ret != ERROR_SUCCESS)
++ return {};
++
++ switch (dataType) {
++ case REG_SZ:
++ case REG_EXPAND_SZ: {
++ if (dataSize > 0) {
++ return QString::fromWCharArray(
++ reinterpret_cast<const wchar_t *>(data.constData()));
++ }
++ return QString();
++ }
++
++ case REG_MULTI_SZ: {
++ if (dataSize > 0) {
++ QStringList list = {};
++ int i = 0;
++ while (true) {
++ const QString str = QString::fromWCharArray(
++ reinterpret_cast<const wchar_t *>(data.constData()) + i);
++ i += str.length() + 1;
++ if (str.isEmpty())
++ break;
++ list.append(str);
++ }
++ return list;
++ }
++ return QStringList();
++ }
++
++ case REG_NONE: // No specific type, treat as binary data.
++ case REG_BINARY: {
++ if (dataSize > 0) {
++ return QString::fromWCharArray(
++ reinterpret_cast<const wchar_t *>(data.constData()), data.size() / 2);
++ }
++ return QString();
++ }
++
++ case REG_DWORD: // Same as REG_DWORD_LITTLE_ENDIAN
++ return qFromLittleEndian<quint32>(data.constData());
++
++ case REG_DWORD_BIG_ENDIAN:
++ return qFromBigEndian<quint32>(data.constData());
++
++ case REG_QWORD: // Same as REG_QWORD_LITTLE_ENDIAN
++ return qFromLittleEndian<quint64>(data.constData());
++
++ default:
++ break;
++ }
++
++ return {};
++ }
++
++ QVariant QWinRegistryKey::value(const QString &subKey) const
++ {
++ return value(nullTerminate(subKey));
++ }
++
++ // Returns the value of the specified subKey as a string, obtained using
++ // qvariant_cast from the underlying QVariant. If that value is not a string,
++ // or the subKey has no value, a string whose isNull() is true is returned.
++ // Otherwise, the resulting string (which may be empty) is returned.
++ QString QWinRegistryKey::stringValue(const wchar_t *subKey) const
++ {
++ if (auto v = value<QString>(subKey))
++ return std::move(*v);
++ return QString();
++ }
++
++ QString QWinRegistryKey::stringValue(const QString &subKey) const
++ {
++ return stringValue(nullTerminate(subKey));
++ }
++
++ QWinRegistryNotifier::QWinRegistryNotifier(HKEY parentHandle, const wchar_t *subKey,
++ QObject *parent)
++ : QWinRegistryNotifier(QWinRegistryKey(parentHandle, subKey), parent)
++ {
++ }
++
++ QWinRegistryNotifier::QWinRegistryNotifier(HKEY parentHandle, const QString &subKey,
++ QObject *parent)
++ : QWinRegistryNotifier(QWinRegistryKey(parentHandle, subKey), parent)
++ {
++ }
++
++ QWinRegistryNotifier::QWinRegistryNotifier(QWinRegistryKey &&key, QObject *parent)
++ : QObject(parent), m_key(std::move(key))
++ {
++ if (!m_key.isValid())
++ return;
++
++ m_keyChangedEvent.reset(CreateEvent(nullptr, false, false, nullptr));
++ auto *notifier = new QWinEventNotifier(m_keyChangedEvent.get(), this);
++
++ auto registerForNotification = [this] {
++ constexpr auto changeFilter =
++ REG_NOTIFY_CHANGE_NAME
++ | REG_NOTIFY_CHANGE_ATTRIBUTES
++ | REG_NOTIFY_CHANGE_LAST_SET
++ | REG_NOTIFY_CHANGE_SECURITY;
++
++ if (auto status = RegNotifyChangeKeyValue(m_key.handle(), true, changeFilter,
++ m_keyChangedEvent.get(), true); status != ERROR_SUCCESS) {
++ qWarning() << "Failed to register notification for registry key"
++ << this << "due to" << QSystemError::windowsString(status);
++ }
++ };
++
++ QObject::connect(notifier, &QWinEventNotifier::activated, this,
++ [this, registerForNotification] {
++ emit valueChanged();
++ registerForNotification();
++ });
++
++ registerForNotification();
++ }
++
++ #ifndef QT_NO_DEBUG_STREAM
++ QDebug operator<<(QDebug debug, const QWinRegistryKey &key)
++ {
++ QDebugStateSaver saver(debug);
++ debug.nospace();
++ debug << "QWinRegistryKey(";
++ if (key.isValid())
++ debug << key.name();
++ debug << ')';
++ return debug;
++ }
++ #endif
++
++ QT_END_NAMESPACE