Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qhstsstore.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qhstsstore_p.h"
6#include "qhstspolicy.h"
7
9#include "qdatastream.h"
10#include "qbytearray.h"
11#include "qdatetime.h"
12#include "qvariant.h"
13#include "qstring.h"
14#include "qdir.h"
15
16#include <utility>
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22static QString host_name_to_settings_key(const QString &hostName)
23{
24 const QByteArray hostNameAsHex(hostName.toUtf8().toHex());
25 return QString::fromLatin1(hostNameAsHex);
26}
27
28static QString settings_key_to_host_name(const QString &key)
29{
30 const QByteArray hostNameAsUtf8(QByteArray::fromHex(key.toLatin1()));
31 return QString::fromUtf8(hostNameAsUtf8);
32}
33
34QHstsStore::QHstsStore(const QString &dirName)
35 : store(absoluteFilePath(dirName), QSettings::IniFormat)
36{
37 // Disable fallbacks, we do not want to use anything but our own ini file.
38 store.setFallbacksEnabled(false);
39}
40
41QHstsStore::~QHstsStore()
42{
43 synchronize();
44}
45
46QList<QHstsPolicy> QHstsStore::readPolicies()
47{
48 // This function only attempts to read policies, making no decision about
49 // expired policies. It's up to a user (QHstsCache) to mark these policies
50 // for deletion and sync the store later. But we immediately remove keys/values
51 // (if the store isWritable) for the policies that we fail to read.
52 QList<QHstsPolicy> policies;
53
54 beginHstsGroups();
55
56 const QStringList keys = store.childKeys();
57 for (const auto &key : keys) {
58 QHstsPolicy restoredPolicy;
59 if (deserializePolicy(key, restoredPolicy)) {
60 restoredPolicy.setHost(settings_key_to_host_name(key));
61 policies.push_back(std::move(restoredPolicy));
62 } else if (isWritable()) {
63 evictPolicy(key);
64 }
65 }
66
67 endHstsGroups();
68
69 return policies;
70}
71
72void QHstsStore::addToObserved(const QHstsPolicy &policy)
73{
74 observedPolicies.push_back(policy);
75}
76
77void QHstsStore::synchronize()
78{
79 if (!isWritable())
80 return;
81
82 if (observedPolicies.size()) {
83 beginHstsGroups();
84 for (const QHstsPolicy &policy : std::as_const(observedPolicies)) {
85 const QString key(host_name_to_settings_key(policy.host()));
86 // If we fail to write a new, updated policy, we also remove the old one.
87 if (policy.isExpired() || !serializePolicy(key, policy))
88 evictPolicy(key);
89 }
90 observedPolicies.clear();
91 endHstsGroups();
92 }
93
94 store.sync();
95}
96
97bool QHstsStore::isWritable() const
98{
99 return store.isWritable();
100}
101
102QString QHstsStore::absoluteFilePath(const QString &dirName)
103{
104 const QDir dir(dirName.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
105 : dirName);
106 return dir.absoluteFilePath("hstsstore"_L1);
107}
108
109void QHstsStore::beginHstsGroups()
110{
111 store.beginGroup("StrictTransportSecurity"_L1);
112 store.beginGroup("Policies"_L1);
113}
114
115void QHstsStore::endHstsGroups()
116{
117 store.endGroup();
118 store.endGroup();
119}
120
121bool QHstsStore::deserializePolicy(const QString &key, QHstsPolicy &policy)
122{
123 Q_ASSERT(store.contains(key));
124
125 const QVariant data(store.value(key));
126 if (data.isNull() || !data.canConvert<QByteArray>())
127 return false;
128
129 const QByteArray serializedData(data.toByteArray());
130 QDataStream streamer(serializedData);
131 qint64 expiryInMS = 0;
132 if (!(streamer >> expiryInMS))
133 return false;
134 bool includesSubDomains = false;
135 if (!(streamer >> includesSubDomains))
136 return false;
137
138 policy.setExpiry(QDateTime::fromMSecsSinceEpoch(expiryInMS));
139 policy.setIncludesSubDomains(includesSubDomains);
140
141 return true;
142}
143
144bool QHstsStore::serializePolicy(const QString &key, const QHstsPolicy &policy)
145{
146 Q_ASSERT(store.isWritable());
147
148 QByteArray serializedData;
149 QDataStream streamer(&serializedData, QIODevice::WriteOnly);
150 streamer << policy.expiry().toMSecsSinceEpoch();
151 streamer << policy.includesSubDomains();
152
153 if (streamer.status() != QDataStream::Ok)
154 return false;
155
156 store.setValue(key, serializedData);
157 return true;
158}
159
160void QHstsStore::evictPolicy(const QString &key)
161{
162 Q_ASSERT(store.isWritable());
163 if (store.contains(key))
164 store.remove(key);
165}
166
167QT_END_NAMESPACE
static QString settings_key_to_host_name(const QString &key)
static QString host_name_to_settings_key(const QString &hostName)