KConfig

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

KDE's Doxygen guidelines are available online.