File indexing completed on 2024-05-05 16:05:15

0001 /*
0002     SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
0003     SPDX-FileCopyrightText: 2009 Radek Novacek <rnovacek@redhat.com>
0004     SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
0005     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #include "Polkit1Backend.h"
0011 #include "kauthdebug.h"
0012 
0013 #include <QCoreApplication>
0014 #include <QTimer>
0015 #include <qplugin.h>
0016 
0017 #include <QApplication>
0018 #include <QWidget>
0019 
0020 #include <QDBusConnection>
0021 #include <QDBusConnectionInterface>
0022 
0023 #include <PolkitQt1/Subject>
0024 #include <polkitqt1-version.h>
0025 
0026 namespace KAuth
0027 {
0028 Polkit1Backend::Polkit1Backend()
0029     : AuthBackend()
0030 {
0031     setCapabilities(AuthorizeFromHelperCapability | CheckActionExistenceCapability | PreAuthActionCapability);
0032 
0033     // Setup useful signals
0034     connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::configChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
0035     connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::consoleKitDBChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
0036 }
0037 
0038 Polkit1Backend::~Polkit1Backend()
0039 {
0040 }
0041 
0042 void Polkit1Backend::preAuthAction(const QString &action, QWidget *parent)
0043 {
0044     // If a parent was not specified, skip this
0045     if (!parent) {
0046         qCDebug(KAUTH) << "Parent widget does not exist, skipping";
0047         return;
0048     }
0049 
0050     // Are we running our KDE auth agent?
0051     if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.polkit-kde-authentication-agent-1"))) {
0052         // Check if we actually are entitled to use GUI capabilities
0053         if (qApp == nullptr || !qobject_cast<QApplication *>(qApp)) {
0054             qCDebug(KAUTH) << "Not streaming parent as we are on a TTY application";
0055         }
0056 
0057         // Retrieve the dialog root window Id
0058         qulonglong wId = parent->effectiveWinId();
0059 
0060         // Send it over the bus to our agent
0061         QDBusMessage methodCall = QDBusMessage::createMethodCall(QLatin1String("org.kde.polkit-kde-authentication-agent-1"),
0062                                                                  QLatin1String("/org/kde/Polkit1AuthAgent"),
0063                                                                  QLatin1String("org.kde.Polkit1AuthAgent"),
0064                                                                  QLatin1String("setWIdForAction"));
0065 
0066         methodCall << action;
0067         methodCall << wId;
0068 
0069         QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(methodCall);
0070         call.waitForFinished();
0071 
0072         if (call.isError()) {
0073             qCWarning(KAUTH) << "ERROR while streaming the parent!!" << call.error();
0074         }
0075     } else {
0076         qCDebug(KAUTH) << "KDE polkit agent appears too old or not registered on the bus";
0077     }
0078 }
0079 
0080 Action::AuthStatus Polkit1Backend::authorizeAction(const QString &action)
0081 {
0082     Q_UNUSED(action)
0083     // Always return Yes here, we'll authorize inside isCallerAuthorized
0084     return Action::AuthorizedStatus;
0085 }
0086 
0087 void Polkit1Backend::setupAction(const QString &action)
0088 {
0089     m_cachedResults[action] = actionStatus(action);
0090 }
0091 
0092 Action::AuthStatus Polkit1Backend::actionStatus(const QString &action)
0093 {
0094     PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID()));
0095     auto authority = PolkitQt1::Authority::instance();
0096     PolkitQt1::Authority::Result r = authority->checkAuthorizationSync(action, subject, PolkitQt1::Authority::None);
0097 
0098     if (authority->hasError()) {
0099         qCDebug(KAUTH) << "Encountered error while checking action status, error code:" << authority->lastError() << authority->errorDetails();
0100         authority->clearError();
0101         return Action::InvalidStatus;
0102     }
0103 
0104     switch (r) {
0105     case PolkitQt1::Authority::Yes:
0106         return Action::AuthorizedStatus;
0107     case PolkitQt1::Authority::No:
0108     case PolkitQt1::Authority::Unknown:
0109         return Action::DeniedStatus;
0110     default:
0111         return Action::AuthRequiredStatus;
0112     }
0113 }
0114 
0115 QByteArray Polkit1Backend::callerID() const
0116 {
0117     return QDBusConnection::systemBus().baseService().toUtf8();
0118 }
0119 
0120 AuthBackend::ExtraCallerIDVerificationMethod Polkit1Backend::extraCallerIDVerificationMethod() const
0121 {
0122     return VerifyAgainstDBusServiceName;
0123 }
0124 
0125 bool Polkit1Backend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
0126 {
0127     PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID));
0128     PolkitQt1::Authority *authority = PolkitQt1::Authority::instance();
0129     QMap<QString, QString> polkit1Details;
0130     for (auto it = details.cbegin(); it != details.cend(); ++it) {
0131         polkit1Details.insert(it.key(), it.value().toString());
0132     }
0133 
0134     PolkitQt1::Authority::Result result;
0135     QEventLoop e;
0136     connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, &e, [&result, &e](PolkitQt1::Authority::Result _result) {
0137         result = _result;
0138         e.quit();
0139     });
0140 
0141 #if POLKITQT1_IS_VERSION(0, 113, 0)
0142     authority->checkAuthorizationWithDetails(action, subject, PolkitQt1::Authority::AllowUserInteraction, polkit1Details);
0143 #else
0144     authority->checkAuthorization(action, subject, PolkitQt1::Authority::AllowUserInteraction);
0145 #endif
0146     e.exec();
0147 
0148     if (authority->hasError()) {
0149         qCDebug(KAUTH) << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails();
0150         authority->clearError();
0151     }
0152 
0153     switch (result) {
0154     case PolkitQt1::Authority::Yes:
0155         return true;
0156     default:
0157         return false;
0158     }
0159 }
0160 
0161 void Polkit1Backend::checkForResultChanged()
0162 {
0163     for (auto it = m_cachedResults.begin(); it != m_cachedResults.end(); ++it) {
0164         const QString action = it.key();
0165         if (it.value() != actionStatus(action)) {
0166             *it = actionStatus(action);
0167             Q_EMIT actionStatusChanged(action, *it);
0168         }
0169     }
0170 }
0171 
0172 bool Polkit1Backend::actionExists(const QString &action)
0173 {
0174     return m_cachedResults.value(action) != Action::InvalidStatus;
0175 }
0176 
0177 QVariantMap Polkit1Backend::backendDetails(const DetailsMap &details)
0178 {
0179     QVariantMap backendDetails;
0180     for (auto it = details.cbegin(); it != details.cend(); ++it) {
0181         switch (it.key()) {
0182         case Action::AuthDetail::DetailMessage:
0183             backendDetails.insert(QStringLiteral("polkit.message"), it.value());
0184             break;
0185         case Action::AuthDetail::DetailOther:
0186         default:
0187             backendDetails.insert(QStringLiteral("other_details"), it.value());
0188             break;
0189         }
0190     }
0191     return backendDetails;
0192 }
0193 
0194 } // namespace Auth
0195 
0196 #include "moc_Polkit1Backend.cpp"