File indexing completed on 2024-04-28 03:52:39
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 SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de> 0007 0008 SPDX-License-Identifier: LGPL-2.1-or-later 0009 */ 0010 0011 #include "Polkit1Backend.h" 0012 #include "kauthdebug.h" 0013 0014 #include <KWaylandExtras> 0015 #include <KWindowSystem> 0016 0017 #include <QCoreApplication> 0018 #include <QTimer> 0019 #include <qplugin.h> 0020 0021 #include <QGuiApplication> 0022 #include <QWindow> 0023 0024 #include <QDBusConnection> 0025 #include <QDBusConnectionInterface> 0026 #include <QDBusPendingCallWatcher> 0027 #include <QDBusPendingReply> 0028 0029 #include <PolkitQt1/Subject> 0030 #include <polkitqt1-version.h> 0031 0032 constexpr QLatin1String c_kdeAgentService{"org.kde.polkit-kde-authentication-agent-1"}; 0033 constexpr QLatin1String c_kdeAgentPath{"/org/kde/Polkit1AuthAgent"}; 0034 constexpr QLatin1String c_kdeAgentInterface{"org.kde.Polkit1AuthAgent"}; 0035 0036 namespace KAuth 0037 { 0038 0039 Polkit1Backend::Polkit1Backend() 0040 : AuthBackend() 0041 { 0042 setCapabilities(AuthorizeFromHelperCapability | PreAuthActionCapability); 0043 0044 // Setup useful signals 0045 connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::configChanged, this, &KAuth::Polkit1Backend::checkForResultChanged); 0046 connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::consoleKitDBChanged, this, &KAuth::Polkit1Backend::checkForResultChanged); 0047 } 0048 0049 Polkit1Backend::~Polkit1Backend() 0050 { 0051 } 0052 0053 void Polkit1Backend::preAuthAction(const QString &action, QWindow *parentWindow) 0054 { 0055 // If a parent was not specified, skip this 0056 if (!parentWindow) { 0057 qCDebug(KAUTH) << "Parent widget does not exist, skipping"; 0058 return; 0059 } 0060 0061 // Check if we actually are entitled to use GUI capabilities 0062 if (!qGuiApp) { 0063 qCDebug(KAUTH) << "Not streaming parent as we are on a TTY application"; 0064 return; 0065 } 0066 0067 // Are we running our KDE auth agent? 0068 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.polkit-kde-authentication-agent-1"))) { 0069 if (KWindowSystem::isPlatformWayland()) { 0070 KWaylandExtras::exportWindow(parentWindow); 0071 connect( 0072 KWaylandExtras::self(), 0073 &KWaylandExtras::windowExported, 0074 this, 0075 [this, action, parentWindow](QWindow *window, const QString &handle) { 0076 if (window == parentWindow) { 0077 sendWindowHandle(action, handle); 0078 } 0079 }, 0080 Qt::SingleShotConnection); 0081 0082 // Generate and send an XDG Activation token. 0083 sendActivationToken(action, parentWindow); 0084 } else { 0085 // Retrieve the dialog root window Id 0086 const qulonglong wId = parentWindow->winId(); 0087 0088 sendWindowHandle(action, QString::number(wId)); 0089 0090 // Call the old method for compatibility. 0091 QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWIdForAction")); 0092 methodCall << action; 0093 methodCall << wId; 0094 0095 // Legacy call has to be blocking, old agent doesn't handle it coming in delayed. 0096 const auto reply = QDBusConnection::sessionBus().call(methodCall); 0097 if (reply.type() != QDBusMessage::ReplyMessage) { 0098 qWarning() << "Failed to set window id" << wId << "for" << action << reply.errorMessage(); 0099 } 0100 } 0101 } else { 0102 qCDebug(KAUTH) << "KDE polkit agent appears too old or not registered on the bus"; 0103 } 0104 } 0105 0106 void Polkit1Backend::sendWindowHandle(const QString &action, const QString &handle) 0107 { 0108 // Send it over the bus to our agent 0109 QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWindowHandleForAction")); 0110 methodCall << action; 0111 methodCall << handle; 0112 0113 const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall); 0114 auto *watcher = new QDBusPendingCallWatcher(reply, this); 0115 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, handle, action] { 0116 watcher->deleteLater(); 0117 0118 QDBusPendingReply<> reply = *watcher; 0119 if (reply.isError()) { 0120 qCWarning(KAUTH) << "Failed to set window handle" << handle << "for" << action << reply.error().message(); 0121 } 0122 }); 0123 } 0124 0125 void Polkit1Backend::sendActivationToken(const QString &action, QWindow *window) 0126 { 0127 const auto requestedSerial = KWaylandExtras::lastInputSerial(window); 0128 connect( 0129 KWaylandExtras::self(), 0130 &KWaylandExtras::xdgActivationTokenArrived, 0131 this, 0132 [this, requestedSerial, action](quint32 serial, const QString &token) { 0133 if (serial != requestedSerial || token.isEmpty()) { 0134 return; 0135 } 0136 QDBusMessage methodCall = 0137 QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setActivationTokenForAction")); 0138 methodCall << action; 0139 methodCall << token; 0140 0141 const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall); 0142 auto *watcher = new QDBusPendingCallWatcher(reply, this); 0143 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, token, action] { 0144 watcher->deleteLater(); 0145 0146 QDBusPendingReply<> reply = *watcher; 0147 if (reply.isError()) { 0148 qCWarning(KAUTH) << "Failed to set activation token" << token << "for" << action << reply.error().message(); 0149 } 0150 }); 0151 }, 0152 Qt::SingleShotConnection); 0153 KWaylandExtras::requestXdgActivationToken(window, requestedSerial, {}); 0154 } 0155 0156 Action::AuthStatus Polkit1Backend::authorizeAction(const QString &action) 0157 { 0158 Q_UNUSED(action) 0159 // Always return Yes here, we'll authorize inside isCallerAuthorized 0160 return Action::AuthorizedStatus; 0161 } 0162 0163 void Polkit1Backend::setupAction(const QString &action) 0164 { 0165 m_cachedResults[action] = actionStatus(action); 0166 } 0167 0168 Action::AuthStatus Polkit1Backend::actionStatus(const QString &action) 0169 { 0170 PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID())); 0171 auto authority = PolkitQt1::Authority::instance(); 0172 PolkitQt1::Authority::Result r = authority->checkAuthorizationSync(action, subject, PolkitQt1::Authority::None); 0173 0174 if (authority->hasError()) { 0175 qCDebug(KAUTH) << "Encountered error while checking action status, error code:" << authority->lastError() << authority->errorDetails(); 0176 authority->clearError(); 0177 return Action::InvalidStatus; 0178 } 0179 0180 switch (r) { 0181 case PolkitQt1::Authority::Yes: 0182 return Action::AuthorizedStatus; 0183 case PolkitQt1::Authority::No: 0184 case PolkitQt1::Authority::Unknown: 0185 return Action::DeniedStatus; 0186 default: 0187 return Action::AuthRequiredStatus; 0188 } 0189 } 0190 0191 QByteArray Polkit1Backend::callerID() const 0192 { 0193 return QDBusConnection::systemBus().baseService().toUtf8(); 0194 } 0195 0196 bool Polkit1Backend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details) 0197 { 0198 PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID)); 0199 PolkitQt1::Authority *authority = PolkitQt1::Authority::instance(); 0200 QMap<QString, QString> polkit1Details; 0201 for (auto it = details.cbegin(); it != details.cend(); ++it) { 0202 polkit1Details.insert(it.key(), it.value().toString()); 0203 } 0204 0205 PolkitQt1::Authority::Result result; 0206 QEventLoop e; 0207 connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, &e, [&result, &e](PolkitQt1::Authority::Result _result) { 0208 result = _result; 0209 e.quit(); 0210 }); 0211 0212 #if POLKITQT1_IS_VERSION(0, 113, 0) 0213 authority->checkAuthorizationWithDetails(action, subject, PolkitQt1::Authority::AllowUserInteraction, polkit1Details); 0214 #else 0215 authority->checkAuthorization(action, subject, PolkitQt1::Authority::AllowUserInteraction); 0216 #endif 0217 e.exec(); 0218 0219 if (authority->hasError()) { 0220 qCDebug(KAUTH) << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails(); 0221 authority->clearError(); 0222 } 0223 0224 switch (result) { 0225 case PolkitQt1::Authority::Yes: 0226 return true; 0227 default: 0228 return false; 0229 } 0230 } 0231 0232 void Polkit1Backend::checkForResultChanged() 0233 { 0234 for (auto it = m_cachedResults.begin(); it != m_cachedResults.end(); ++it) { 0235 const QString action = it.key(); 0236 if (it.value() != actionStatus(action)) { 0237 *it = actionStatus(action); 0238 Q_EMIT actionStatusChanged(action, *it); 0239 } 0240 } 0241 } 0242 0243 QVariantMap Polkit1Backend::backendDetails(const DetailsMap &details) 0244 { 0245 QVariantMap backendDetails; 0246 for (auto it = details.cbegin(); it != details.cend(); ++it) { 0247 switch (it.key()) { 0248 case Action::AuthDetail::DetailMessage: 0249 backendDetails.insert(QStringLiteral("polkit.message"), it.value()); 0250 break; 0251 case Action::AuthDetail::DetailOther: 0252 default: 0253 backendDetails.insert(QStringLiteral("other_details"), it.value()); 0254 break; 0255 } 0256 } 0257 return backendDetails; 0258 } 0259 0260 } // namespace Auth 0261 0262 #include "Polkit1Backend.moc"