KConfig

kauthorized.cpp
1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
3 SPDX-FileCopyrightText: 1998, 1999, 2000 Waldo Bastian <bastian@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kauthorized.h"
9
10#include <QDebug>
11#include <QDir>
12#include <QList>
13#include <QUrl>
14
15#include "kconfig_core_log_settings.h"
16#include <QCoreApplication>
17#include <ksharedconfig.h>
18#include <stdlib.h> // srand(), rand()
19#ifndef Q_OS_WIN
20#include <netdb.h>
21#include <unistd.h>
22#endif
23
24#include <kconfiggroup.h>
25
26#include <QMutexLocker>
27#include <QRecursiveMutex>
28
29extern bool kde_kiosk_exception;
30
31class URLActionRule
32{
33public:
34#define checkExactMatch(s, b) \
35 if (s.isEmpty()) \
36 b = true; \
37 else if (s[s.length() - 1] == QLatin1Char('!')) { \
38 b = false; \
39 s.chop(1); \
40 } else \
41 b = true;
42#define checkStartWildCard(s, b) \
43 if (s.isEmpty()) \
44 b = true; \
45 else if (s[0] == QLatin1Char('*')) { \
46 b = true; \
47 s.remove(0, 1); \
48 } else \
49 b = false;
50#define checkEqual(s, b) b = (s == QLatin1String("="));
51
52 URLActionRule(const QByteArray &act,
53 const QString &bProt,
54 const QString &bHost,
55 const QString &bPath,
56 const QString &dProt,
57 const QString &dHost,
58 const QString &dPath,
59 bool perm)
60 : action(act)
61 , baseProt(bProt)
62 , baseHost(bHost)
63 , basePath(bPath)
64 , destProt(dProt)
65 , destHost(dHost)
66 , destPath(dPath)
67 , permission(perm)
68 {
69 checkExactMatch(baseProt, baseProtWildCard);
70 checkStartWildCard(baseHost, baseHostWildCard);
71 checkExactMatch(basePath, basePathWildCard);
72 checkExactMatch(destProt, destProtWildCard);
73 checkStartWildCard(destHost, destHostWildCard);
74 checkExactMatch(destPath, destPathWildCard);
75 checkEqual(destProt, destProtEqual);
76 checkEqual(destHost, destHostEqual);
77 }
78
79 bool baseMatch(const QUrl &url, const QString &protClass) const
80 {
81 if (baseProtWildCard) {
82 if (!baseProt.isEmpty() //
83 && !url.scheme().startsWith(baseProt) //
84 && (protClass.isEmpty() || (protClass != baseProt))) {
85 return false;
86 }
87 } else {
88 if (url.scheme() != baseProt //
89 && (protClass.isEmpty() || (protClass != baseProt))) {
90 return false;
91 }
92 }
93 if (baseHostWildCard) {
94 if (!baseHost.isEmpty() && !url.host().endsWith(baseHost)) {
95 return false;
96 }
97 } else {
98 if (url.host() != baseHost) {
99 return false;
100 }
101 }
102 if (basePathWildCard) {
103 if (!basePath.isEmpty() && !url.path().startsWith(basePath)) {
104 return false;
105 }
106 } else {
107 if (url.path() != basePath) {
108 return false;
109 }
110 }
111 return true;
112 }
113
114 bool destMatch(const QUrl &url, const QString &protClass, const QUrl &base, const QString &baseClass) const
115 {
116 if (destProtEqual) {
117 if (url.scheme() != base.scheme() //
118 && (protClass.isEmpty() || baseClass.isEmpty() || protClass != baseClass)) {
119 return false;
120 }
121 } else if (destProtWildCard) {
122 if (!destProt.isEmpty() //
123 && !url.scheme().startsWith(destProt) //
124 && (protClass.isEmpty() || (protClass != destProt))) {
125 return false;
126 }
127 } else {
128 if (url.scheme() != destProt //
129 && (protClass.isEmpty() || (protClass != destProt))) {
130 return false;
131 }
132 }
133 if (destHostWildCard) {
134 if (!destHost.isEmpty() && !url.host().endsWith(destHost)) {
135 return false;
136 }
137 } else if (destHostEqual) {
138 if (url.host() != base.host()) {
139 return false;
140 }
141 } else {
142 if (url.host() != destHost) {
143 return false;
144 }
145 }
146 if (destPathWildCard) {
147 if (!destPath.isEmpty() && !url.path().startsWith(destPath)) {
148 return false;
149 }
150 } else {
151 if (url.path() != destPath) {
152 return false;
153 }
154 }
155 return true;
156 }
157
158 QByteArray action;
159 QString baseProt;
160 QString baseHost;
161 QString basePath;
162 QString destProt;
163 QString destHost;
164 QString destPath;
165 bool baseProtWildCard : 1;
166 bool baseHostWildCard : 1;
167 bool basePathWildCard : 1;
168 bool destProtWildCard : 1;
169 bool destHostWildCard : 1;
170 bool destPathWildCard : 1;
171 bool destProtEqual : 1;
172 bool destHostEqual : 1;
173 bool permission;
174};
175
176Q_DECLARE_TYPEINFO(URLActionRule, Q_RELOCATABLE_TYPE);
177
178class KAuthorizedPrivate
179{
180public:
181 KAuthorizedPrivate()
182 : actionRestrictions(false)
183 , blockEverything(false)
184 {
185 Q_ASSERT_X(QCoreApplication::instance(), "KAuthorizedPrivate()", "There has to be an existing QCoreApplication::instance() pointer");
186
188
189 Q_ASSERT_X(config, "KAuthorizedPrivate()", "There has to be an existing KSharedConfig::openConfig() pointer");
190 if (!config) {
191 blockEverything = true;
192 return;
193 }
194 actionRestrictions = config->hasGroup(QStringLiteral("KDE Action Restrictions")) && !kde_kiosk_exception;
195 }
196
197 ~KAuthorizedPrivate()
198 {
199 }
200
201 bool actionRestrictions : 1;
202 bool blockEverything : 1;
203 QList<URLActionRule> urlActionRestrictions;
204 QRecursiveMutex mutex;
205};
206
207Q_GLOBAL_STATIC(KAuthorizedPrivate, authPrivate)
208#define KAUTHORIZED_D KAuthorizedPrivate *d = authPrivate()
209
210KAuthorized::KAuthorized()
211 : QObject(nullptr)
212{
213}
214
215bool KAuthorized::authorize(const QString &genericAction)
216{
217 KAUTHORIZED_D;
218 if (d->blockEverything) {
219 return false;
220 }
221
222 if (!d->actionRestrictions) {
223 return true;
224 }
225
226 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Action Restrictions"));
227 return cg.readEntry(genericAction, true);
228}
229
231{
233
234 if (metaEnum.isValid() && action != 0) {
236 }
237 qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericRestriction requested" << action;
238 return false;
239}
240
242{
243 KAUTHORIZED_D;
244 if (d->blockEverything) {
245 return false;
246 }
247 if (!d->actionRestrictions || action.isEmpty()) {
248 return true;
249 }
250
251 return authorize(QLatin1String("action/") + action);
252}
253
255{
257 if (metaEnum.isValid() && action != 0) {
259 }
260 qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericAction requested" << action;
261 return false;
262}
263
265{
266 if (menuId.isEmpty() || kde_kiosk_exception) {
267 return true;
268 }
269 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Control Module Restrictions"));
270 return cg.readEntry(menuId, true);
271}
272
273// Exported for unittests (e.g. in KIO, we're missing tests for this in kconfig)
274KCONFIGCORE_EXPORT void loadUrlActionRestrictions(const KConfigGroup &cg)
275{
276 KAUTHORIZED_D;
277 const QString Any;
278
279 d->urlActionRestrictions.clear();
280 d->urlActionRestrictions.append(URLActionRule("open", Any, Any, Any, Any, Any, Any, true));
281 d->urlActionRestrictions.append(URLActionRule("list", Any, Any, Any, Any, Any, Any, true));
282 // TEST:
283 // d->urlActionRestrictions.append(
284 // URLActionRule("list", Any, Any, Any, Any, Any, Any, false));
285 // d->urlActionRestrictions.append(
286 // URLActionRule("list", Any, Any, Any, "file", Any, QDir::homePath(), true));
287 d->urlActionRestrictions.append(URLActionRule("link", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true));
288 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true));
289
290 // We allow redirections to file: but not from internet protocols, redirecting to file:
291 // is very popular among KIO workers and we don't want to break them
292 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("file"), Any, Any, true));
293 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":internet"), Any, Any, QStringLiteral("file"), Any, Any, false));
294
295 // local protocols may redirect everywhere
296 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":local"), Any, Any, Any, Any, Any, true));
297
298 // Anyone may redirect to about:
299 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("about"), Any, Any, true));
300
301 // Anyone may redirect to mailto:
302 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("mailto"), Any, Any, true));
303
304 // Anyone may redirect to itself, cq. within it's own group
305 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("="), Any, Any, true));
306
307 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral("about"), Any, Any, Any, Any, Any, true));
308
309 int count = cg.readEntry("rule_count", 0);
310 QString keyFormat = QStringLiteral("rule_%1");
311 for (int i = 1; i <= count; i++) {
312 QString key = keyFormat.arg(i);
313 const QStringList rule = cg.readEntry(key, QStringList());
314 if (rule.count() != 8) {
315 continue;
316 }
317 const QByteArray action = rule[0].toLatin1();
318 const QString refProt = rule[1];
319 const QString refHost = rule[2];
320 QString refPath = rule[3];
321 const QString urlProt = rule[4];
322 const QString urlHost = rule[5];
323 QString urlPath = rule[6];
324 const bool bEnabled = (rule[7].compare(QLatin1String("true"), Qt::CaseInsensitive) == 0);
325
326 if (refPath.startsWith(QLatin1String("$HOME"))) {
327 refPath.replace(0, 5, QDir::homePath());
328 } else if (refPath.startsWith(QLatin1Char('~'))) {
329 refPath.replace(0, 1, QDir::homePath());
330 }
331 if (urlPath.startsWith(QLatin1String("$HOME"))) {
332 urlPath.replace(0, 5, QDir::homePath());
333 } else if (urlPath.startsWith(QLatin1Char('~'))) {
334 urlPath.replace(0, 1, QDir::homePath());
335 }
336
337 if (refPath.startsWith(QLatin1String("$TMP"))) {
338 refPath.replace(0, 4, QDir::tempPath());
339 }
340 if (urlPath.startsWith(QLatin1String("$TMP"))) {
341 urlPath.replace(0, 4, QDir::tempPath());
342 }
343
344 d->urlActionRestrictions.append(URLActionRule(action, refProt, refHost, refPath, urlProt, urlHost, urlPath, bEnabled));
345 }
346}
347
348namespace KAuthorizedInternal
349{
350/**
351 * Helper for KAuthorized::allowUrlAction in KIO
352 * @private
353 */
354KCONFIGCORE_EXPORT void allowUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL)
355{
356 KAUTHORIZED_D;
357 QMutexLocker locker((&d->mutex));
358
359 const QString basePath = _baseURL.adjusted(QUrl::StripTrailingSlash).path();
360 const QString destPath = _destURL.adjusted(QUrl::StripTrailingSlash).path();
361
362 d->urlActionRestrictions.append(
363 URLActionRule(action.toLatin1(), _baseURL.scheme(), _baseURL.host(), basePath, _destURL.scheme(), _destURL.host(), destPath, true));
364}
365
366/**
367 * Helper for KAuthorized::authorizeUrlAction in KIO
368 * @private
369 */
370KCONFIGCORE_EXPORT bool
371authorizeUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL, const QString &baseClass, const QString &destClass)
372{
373 KAUTHORIZED_D;
374 QMutexLocker locker(&(d->mutex));
375 if (d->blockEverything) {
376 return false;
377 }
378
379 if (_destURL.isEmpty()) {
380 return true;
381 }
382
383 bool result = false;
384 if (d->urlActionRestrictions.isEmpty()) {
385 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE URL Restrictions"));
386 loadUrlActionRestrictions(cg);
387 }
388
389 QUrl baseURL(_baseURL);
390 baseURL.setPath(QDir::cleanPath(baseURL.path()));
391
392 QUrl destURL(_destURL);
393 destURL.setPath(QDir::cleanPath(destURL.path()));
394
395 for (const URLActionRule &rule : std::as_const(d->urlActionRestrictions)) {
396 if ((result != rule.permission) && // No need to check if it doesn't make a difference
397 (action == QLatin1String(rule.action.constData())) && rule.baseMatch(baseURL, baseClass)
398 && rule.destMatch(destURL, destClass, baseURL, baseClass)) {
399 result = rule.permission;
400 }
401 }
402 return result;
403}
404} // namespace
405
406#include "moc_kauthorized.cpp"
static Q_INVOKABLE bool authorize(const QString &action)
Returns whether the user is permitted to perform a certain action.
static Q_INVOKABLE bool authorizeControlModule(const QString &pluginId)
Returns whether the user is permitted to use a certain control module.
static Q_INVOKABLE bool authorizeAction(const QString &action)
Returns whether the user is permitted to perform a certain action.
GenericRestriction
The enum values lower cased represent the action that is authorized For example the SHELL_ACCESS valu...
Definition kauthorized.h:41
A class for one specific group in a KConfig object.
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Creates a KSharedConfig object to manipulate a configuration file.
void allowUrlAction(const QString &action, const QUrl &baseUrl, const QUrl &destUrl)
bool authorizeUrlAction(const QString &action, const QUrl &baseUrl, const QUrl &destUrl)
QCoreApplication * instance()
QString cleanPath(const QString &path)
QString homePath()
QString tempPath()
const_pointer constData() const const
qsizetype count() const const
QMetaEnum fromType()
bool isValid() const const
const char * valueToKey(int value) const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
void clear()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QString toLower() const const
CaseInsensitive
StripTrailingSlash
QUrl adjusted(FormattingOptions options) const const
QString host(ComponentFormattingOptions options) const const
bool isEmpty() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:57:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.