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 
29 extern bool kde_kiosk_exception;
30 
31 class URLActionRule
32 {
33 public:
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 
176 Q_DECLARE_TYPEINFO(URLActionRule, Q_RELOCATABLE_TYPE);
177 
178 class KAuthorizedPrivate
179 {
180 public:
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 
207 Q_GLOBAL_STATIC(KAuthorizedPrivate, authPrivate)
208 #define KAUTHORIZED_D KAuthorizedPrivate *d = authPrivate()
209 
210 KAuthorized::KAuthorized()
211  : QObject(nullptr)
212 {
213 }
214 
215 bool 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 {
232  const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericRestriction>();
233 
234  if (metaEnum.isValid() && action != 0) {
235  return KAuthorized::authorize(QString::fromLatin1(metaEnum.valueToKey(action)).toLower());
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 {
256  const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericAction>();
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)
274 KCONFIGCORE_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 
348 namespace KAuthorizedInternal
349 {
350 /**
351  * Helper for KAuthorized::allowUrlAction in KIO
352  * @private
353  */
354 KCONFIGCORE_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  */
370 KCONFIGCORE_EXPORT bool
371 authorizeUrlAction(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"
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
CaseInsensitive
QString scheme() const const
int count(const T &value) const const
void clear()
QByteArray toLatin1() const const
QString homePath()
bool authorizeUrlAction(const QString &action, const QUrl &baseUrl, const QUrl &destUrl)
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.
static Q_INVOKABLE bool authorize(const QString &action)
Returns whether the user is permitted to perform a certain action.
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
QString tempPath()
bool isEmpty() const const
const char * valueToKey(int value) const const
StripTrailingSlash
bool isEmpty() const const
QCoreApplication * instance()
bool isValid() const const
static Q_INVOKABLE bool authorizeAction(const QString &action)
Returns whether the user is permitted to perform a certain action.
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
Definition: kconfiggroup.h:216
QString & replace(int position, int n, QChar after)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString cleanPath(const QString &path)
QString toLower() const const
GenericRestriction
The enum values lower cased represent the action that is authorized For example the SHELL_ACCESS valu...
Definition: kauthorized.h:41
QString host(QUrl::ComponentFormattingOptions options) const const
QString path(QUrl::ComponentFormattingOptions options) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
QUrl adjusted(QUrl::FormattingOptions options) const const
void allowUrlAction(const QString &action, const QUrl &baseUrl, const QUrl &destUrl)
static Q_INVOKABLE bool authorizeControlModule(const QString &pluginId)
Returns whether the user is permitted to use a certain control module.
QString & append(QChar ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 04:07:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.