File indexing completed on 2024-05-12 03:54:23
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org> 0003 SPDX-FileCopyrightText: 1998, 1999, 2000 Waldo Bastian <bastian@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kauthorized.h" 0009 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QList> 0013 #include <QUrl> 0014 0015 #include "kconfig_core_log_settings.h" 0016 #include <QCoreApplication> 0017 #include <ksharedconfig.h> 0018 #include <stdlib.h> // srand(), rand() 0019 #ifndef Q_OS_WIN 0020 #include <netdb.h> 0021 #include <unistd.h> 0022 #endif 0023 0024 #include <kconfiggroup.h> 0025 0026 #include <QMutexLocker> 0027 #include <QRecursiveMutex> 0028 0029 extern bool kde_kiosk_exception; 0030 0031 class URLActionRule 0032 { 0033 public: 0034 #define checkExactMatch(s, b) \ 0035 if (s.isEmpty()) \ 0036 b = true; \ 0037 else if (s[s.length() - 1] == QLatin1Char('!')) { \ 0038 b = false; \ 0039 s.chop(1); \ 0040 } else \ 0041 b = true; 0042 #define checkStartWildCard(s, b) \ 0043 if (s.isEmpty()) \ 0044 b = true; \ 0045 else if (s[0] == QLatin1Char('*')) { \ 0046 b = true; \ 0047 s.remove(0, 1); \ 0048 } else \ 0049 b = false; 0050 #define checkEqual(s, b) b = (s == QLatin1String("=")); 0051 0052 URLActionRule(const QByteArray &act, 0053 const QString &bProt, 0054 const QString &bHost, 0055 const QString &bPath, 0056 const QString &dProt, 0057 const QString &dHost, 0058 const QString &dPath, 0059 bool perm) 0060 : action(act) 0061 , baseProt(bProt) 0062 , baseHost(bHost) 0063 , basePath(bPath) 0064 , destProt(dProt) 0065 , destHost(dHost) 0066 , destPath(dPath) 0067 , permission(perm) 0068 { 0069 checkExactMatch(baseProt, baseProtWildCard); 0070 checkStartWildCard(baseHost, baseHostWildCard); 0071 checkExactMatch(basePath, basePathWildCard); 0072 checkExactMatch(destProt, destProtWildCard); 0073 checkStartWildCard(destHost, destHostWildCard); 0074 checkExactMatch(destPath, destPathWildCard); 0075 checkEqual(destProt, destProtEqual); 0076 checkEqual(destHost, destHostEqual); 0077 } 0078 0079 bool baseMatch(const QUrl &url, const QString &protClass) const 0080 { 0081 if (baseProtWildCard) { 0082 if (!baseProt.isEmpty() // 0083 && !url.scheme().startsWith(baseProt) // 0084 && (protClass.isEmpty() || (protClass != baseProt))) { 0085 return false; 0086 } 0087 } else { 0088 if (url.scheme() != baseProt // 0089 && (protClass.isEmpty() || (protClass != baseProt))) { 0090 return false; 0091 } 0092 } 0093 if (baseHostWildCard) { 0094 if (!baseHost.isEmpty() && !url.host().endsWith(baseHost)) { 0095 return false; 0096 } 0097 } else { 0098 if (url.host() != baseHost) { 0099 return false; 0100 } 0101 } 0102 if (basePathWildCard) { 0103 if (!basePath.isEmpty() && !url.path().startsWith(basePath)) { 0104 return false; 0105 } 0106 } else { 0107 if (url.path() != basePath) { 0108 return false; 0109 } 0110 } 0111 return true; 0112 } 0113 0114 bool destMatch(const QUrl &url, const QString &protClass, const QUrl &base, const QString &baseClass) const 0115 { 0116 if (destProtEqual) { 0117 if (url.scheme() != base.scheme() // 0118 && (protClass.isEmpty() || baseClass.isEmpty() || protClass != baseClass)) { 0119 return false; 0120 } 0121 } else if (destProtWildCard) { 0122 if (!destProt.isEmpty() // 0123 && !url.scheme().startsWith(destProt) // 0124 && (protClass.isEmpty() || (protClass != destProt))) { 0125 return false; 0126 } 0127 } else { 0128 if (url.scheme() != destProt // 0129 && (protClass.isEmpty() || (protClass != destProt))) { 0130 return false; 0131 } 0132 } 0133 if (destHostWildCard) { 0134 if (!destHost.isEmpty() && !url.host().endsWith(destHost)) { 0135 return false; 0136 } 0137 } else if (destHostEqual) { 0138 if (url.host() != base.host()) { 0139 return false; 0140 } 0141 } else { 0142 if (url.host() != destHost) { 0143 return false; 0144 } 0145 } 0146 if (destPathWildCard) { 0147 if (!destPath.isEmpty() && !url.path().startsWith(destPath)) { 0148 return false; 0149 } 0150 } else { 0151 if (url.path() != destPath) { 0152 return false; 0153 } 0154 } 0155 return true; 0156 } 0157 0158 QByteArray action; 0159 QString baseProt; 0160 QString baseHost; 0161 QString basePath; 0162 QString destProt; 0163 QString destHost; 0164 QString destPath; 0165 bool baseProtWildCard : 1; 0166 bool baseHostWildCard : 1; 0167 bool basePathWildCard : 1; 0168 bool destProtWildCard : 1; 0169 bool destHostWildCard : 1; 0170 bool destPathWildCard : 1; 0171 bool destProtEqual : 1; 0172 bool destHostEqual : 1; 0173 bool permission; 0174 }; 0175 0176 Q_DECLARE_TYPEINFO(URLActionRule, Q_RELOCATABLE_TYPE); 0177 0178 class KAuthorizedPrivate 0179 { 0180 public: 0181 KAuthorizedPrivate() 0182 : actionRestrictions(false) 0183 , blockEverything(false) 0184 { 0185 Q_ASSERT_X(QCoreApplication::instance(), "KAuthorizedPrivate()", "There has to be an existing QCoreApplication::instance() pointer"); 0186 0187 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0188 0189 Q_ASSERT_X(config, "KAuthorizedPrivate()", "There has to be an existing KSharedConfig::openConfig() pointer"); 0190 if (!config) { 0191 blockEverything = true; 0192 return; 0193 } 0194 actionRestrictions = config->hasGroup(QStringLiteral("KDE Action Restrictions")) && !kde_kiosk_exception; 0195 } 0196 0197 ~KAuthorizedPrivate() 0198 { 0199 } 0200 0201 bool actionRestrictions : 1; 0202 bool blockEverything : 1; 0203 QList<URLActionRule> urlActionRestrictions; 0204 QRecursiveMutex mutex; 0205 }; 0206 0207 Q_GLOBAL_STATIC(KAuthorizedPrivate, authPrivate) 0208 #define KAUTHORIZED_D KAuthorizedPrivate *d = authPrivate() 0209 0210 KAuthorized::KAuthorized() 0211 : QObject(nullptr) 0212 { 0213 } 0214 0215 bool KAuthorized::authorize(const QString &genericAction) 0216 { 0217 KAUTHORIZED_D; 0218 if (d->blockEverything) { 0219 return false; 0220 } 0221 0222 if (!d->actionRestrictions) { 0223 return true; 0224 } 0225 0226 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Action Restrictions")); 0227 return cg.readEntry(genericAction, true); 0228 } 0229 0230 bool KAuthorized::authorize(KAuthorized::GenericRestriction action) 0231 { 0232 const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericRestriction>(); 0233 0234 if (metaEnum.isValid() && action != 0) { 0235 return KAuthorized::authorize(QString::fromLatin1(metaEnum.valueToKey(action)).toLower()); 0236 } 0237 qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericRestriction requested" << action; 0238 return false; 0239 } 0240 0241 bool KAuthorized::authorizeAction(const QString &action) 0242 { 0243 KAUTHORIZED_D; 0244 if (d->blockEverything) { 0245 return false; 0246 } 0247 if (!d->actionRestrictions || action.isEmpty()) { 0248 return true; 0249 } 0250 0251 return authorize(QLatin1String("action/") + action); 0252 } 0253 0254 bool KAuthorized::authorizeAction(KAuthorized::GenericAction action) 0255 { 0256 const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericAction>(); 0257 if (metaEnum.isValid() && action != 0) { 0258 return KAuthorized::authorizeAction(QString::fromLatin1(metaEnum.valueToKey(action)).toLower()); 0259 } 0260 qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericAction requested" << action; 0261 return false; 0262 } 0263 0264 bool KAuthorized::authorizeControlModule(const QString &menuId) 0265 { 0266 if (menuId.isEmpty() || kde_kiosk_exception) { 0267 return true; 0268 } 0269 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Control Module Restrictions")); 0270 return cg.readEntry(menuId, true); 0271 } 0272 0273 // Exported for unittests (e.g. in KIO, we're missing tests for this in kconfig) 0274 KCONFIGCORE_EXPORT void loadUrlActionRestrictions(const KConfigGroup &cg) 0275 { 0276 KAUTHORIZED_D; 0277 const QString Any; 0278 0279 d->urlActionRestrictions.clear(); 0280 d->urlActionRestrictions.append(URLActionRule("open", Any, Any, Any, Any, Any, Any, true)); 0281 d->urlActionRestrictions.append(URLActionRule("list", Any, Any, Any, Any, Any, Any, true)); 0282 // TEST: 0283 // d->urlActionRestrictions.append( 0284 // URLActionRule("list", Any, Any, Any, Any, Any, Any, false)); 0285 // d->urlActionRestrictions.append( 0286 // URLActionRule("list", Any, Any, Any, "file", Any, QDir::homePath(), true)); 0287 d->urlActionRestrictions.append(URLActionRule("link", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true)); 0288 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true)); 0289 0290 // We allow redirections to file: but not from internet protocols, redirecting to file: 0291 // is very popular among KIO workers and we don't want to break them 0292 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("file"), Any, Any, true)); 0293 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":internet"), Any, Any, QStringLiteral("file"), Any, Any, false)); 0294 0295 // local protocols may redirect everywhere 0296 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":local"), Any, Any, Any, Any, Any, true)); 0297 0298 // Anyone may redirect to about: 0299 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("about"), Any, Any, true)); 0300 0301 // Anyone may redirect to mailto: 0302 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("mailto"), Any, Any, true)); 0303 0304 // Anyone may redirect to itself, cq. within it's own group 0305 d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("="), Any, Any, true)); 0306 0307 d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral("about"), Any, Any, Any, Any, Any, true)); 0308 0309 int count = cg.readEntry("rule_count", 0); 0310 QString keyFormat = QStringLiteral("rule_%1"); 0311 for (int i = 1; i <= count; i++) { 0312 QString key = keyFormat.arg(i); 0313 const QStringList rule = cg.readEntry(key, QStringList()); 0314 if (rule.count() != 8) { 0315 continue; 0316 } 0317 const QByteArray action = rule[0].toLatin1(); 0318 const QString refProt = rule[1]; 0319 const QString refHost = rule[2]; 0320 QString refPath = rule[3]; 0321 const QString urlProt = rule[4]; 0322 const QString urlHost = rule[5]; 0323 QString urlPath = rule[6]; 0324 const bool bEnabled = (rule[7].compare(QLatin1String("true"), Qt::CaseInsensitive) == 0); 0325 0326 if (refPath.startsWith(QLatin1String("$HOME"))) { 0327 refPath.replace(0, 5, QDir::homePath()); 0328 } else if (refPath.startsWith(QLatin1Char('~'))) { 0329 refPath.replace(0, 1, QDir::homePath()); 0330 } 0331 if (urlPath.startsWith(QLatin1String("$HOME"))) { 0332 urlPath.replace(0, 5, QDir::homePath()); 0333 } else if (urlPath.startsWith(QLatin1Char('~'))) { 0334 urlPath.replace(0, 1, QDir::homePath()); 0335 } 0336 0337 if (refPath.startsWith(QLatin1String("$TMP"))) { 0338 refPath.replace(0, 4, QDir::tempPath()); 0339 } 0340 if (urlPath.startsWith(QLatin1String("$TMP"))) { 0341 urlPath.replace(0, 4, QDir::tempPath()); 0342 } 0343 0344 d->urlActionRestrictions.append(URLActionRule(action, refProt, refHost, refPath, urlProt, urlHost, urlPath, bEnabled)); 0345 } 0346 } 0347 0348 namespace KAuthorizedInternal 0349 { 0350 /** 0351 * Helper for KAuthorized::allowUrlAction in KIO 0352 * @private 0353 */ 0354 KCONFIGCORE_EXPORT void allowUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL) 0355 { 0356 KAUTHORIZED_D; 0357 QMutexLocker locker((&d->mutex)); 0358 0359 const QString basePath = _baseURL.adjusted(QUrl::StripTrailingSlash).path(); 0360 const QString destPath = _destURL.adjusted(QUrl::StripTrailingSlash).path(); 0361 0362 d->urlActionRestrictions.append( 0363 URLActionRule(action.toLatin1(), _baseURL.scheme(), _baseURL.host(), basePath, _destURL.scheme(), _destURL.host(), destPath, true)); 0364 } 0365 0366 /** 0367 * Helper for KAuthorized::authorizeUrlAction in KIO 0368 * @private 0369 */ 0370 KCONFIGCORE_EXPORT bool 0371 authorizeUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL, const QString &baseClass, const QString &destClass) 0372 { 0373 KAUTHORIZED_D; 0374 QMutexLocker locker(&(d->mutex)); 0375 if (d->blockEverything) { 0376 return false; 0377 } 0378 0379 if (_destURL.isEmpty()) { 0380 return true; 0381 } 0382 0383 bool result = false; 0384 if (d->urlActionRestrictions.isEmpty()) { 0385 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE URL Restrictions")); 0386 loadUrlActionRestrictions(cg); 0387 } 0388 0389 QUrl baseURL(_baseURL); 0390 baseURL.setPath(QDir::cleanPath(baseURL.path())); 0391 0392 QUrl destURL(_destURL); 0393 destURL.setPath(QDir::cleanPath(destURL.path())); 0394 0395 for (const URLActionRule &rule : std::as_const(d->urlActionRestrictions)) { 0396 if ((result != rule.permission) && // No need to check if it doesn't make a difference 0397 (action == QLatin1String(rule.action.constData())) && rule.baseMatch(baseURL, baseClass) 0398 && rule.destMatch(destURL, destClass, baseURL, baseClass)) { 0399 result = rule.permission; 0400 } 0401 } 0402 return result; 0403 } 0404 } // namespace 0405 0406 #include "moc_kauthorized.cpp"