File indexing completed on 2025-01-05 05:07:02

0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 // SPDX-FileCopyrightText: 2020 Lucas Biaggi <lbjanuario@gmail.com>
0003 /*
0004  * Firewalld backend for plasma firewall
0005  */
0006 
0007 #include <QDBusConnection>
0008 #include <QDBusMessage>
0009 #include <QDBusPendingCall>
0010 #include <QDBusPendingReply>
0011 #include <QDebug>
0012 #include <QRegularExpression>
0013 
0014 #include <KLocalizedString>
0015 
0016 #include "firewalldjob.h"
0017 
0018 Q_LOGGING_CATEGORY(FirewallDJobDebug, "firewalld.job")
0019 
0020 namespace FIREWALLD
0021 {
0022 const QString BUS = QStringLiteral("org.fedoraproject.FirewallD1");
0023 const QString PATH = QStringLiteral("/org/fedoraproject/FirewallD1");
0024 }
0025 
0026 namespace DIRECT
0027 {
0028 const QString KCM_FIREWALLD_DIR = QStringLiteral("/etc/kcm/firewalld");
0029 const QString LOG_FILE = QStringLiteral("/var/log/firewalld.log");
0030 const QString INTERFACE = QStringLiteral("org.fedoraproject.FirewallD1.direct");
0031 }
0032 
0033 namespace SAVE
0034 {
0035 const QString METHOD = QStringLiteral("runtimeToPermanent");
0036 }
0037 
0038 namespace SERVICES
0039 {
0040 const QString INTERFACE = QStringLiteral("org.fedoraproject.FirewallD1");
0041 const QString METHOD = QStringLiteral("listServices");
0042 }
0043 
0044 namespace SIMPLE
0045 {
0046 const QString INTERFACE = QStringLiteral("org.fedoraproject.FirewallD1.zone");
0047 }
0048 
0049 namespace AUTH
0050 {
0051 const QString METHOD = QStringLiteral("authorizeAll");
0052 }
0053 
0054 enum {
0055     DBUSFIREWALLDDERROR = KJob::UserDefinedError,
0056 };
0057 
0058 FirewalldJob::FirewalldJob(){};
0059 
0060 FirewalldJob::FirewalldJob(const QByteArray &call, const QVariantList &args, const FirewalldJob::JobType &type)
0061     : KJob()
0062     , m_type(type)
0063     , m_call(call)
0064     , m_args(args){};
0065 
0066 FirewalldJob::FirewalldJob(const FirewalldJob::JobType &type)
0067     : KJob()
0068     , m_type(type){};
0069 
0070 template<typename T>
0071 T FirewalldJob::connectCall(QDBusPendingCallWatcher *watcher)
0072 {
0073     QDBusPendingReply<T> reply = *watcher;
0074     if (reply.isError()) {
0075         setErrorText(reply.error().message());
0076         setError(DBUSFIREWALLDDERROR);
0077         qCDebug(FirewallDJobDebug) << "job error message: " << errorString();
0078         emitResult();
0079     }
0080     return reply.value();
0081 }
0082 
0083 void FirewalldJob::connectCall(QDBusPendingCallWatcher *watcher)
0084 {
0085     QDBusPendingReply<> reply = *watcher;
0086     if (reply.isError()) {
0087         setErrorText(reply.error().message());
0088         setError(DBUSFIREWALLDDERROR);
0089         qCDebug(FirewallDJobDebug) << "job error message: " << errorString();
0090         emitResult();
0091     }
0092 }
0093 
0094 void FirewalldJob::firewalldAction(const QString &bus, const QString &path, const QString &interface, const QString &method, const QVariantList &args)
0095 {
0096     QDBusMessage call = QDBusMessage::createMethodCall(bus, path, interface, method);
0097     call.setArguments(args);
0098     QDBusPendingCall message = QDBusConnection::systemBus().asyncCall(call);
0099     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(message, this);
0100     if (args.isEmpty()) {
0101         connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, interface, method](QDBusPendingCallWatcher *watcher) {
0102             watcher->deleteLater();
0103             if (interface == DIRECT::INTERFACE) { // iptables rules like
0104                 QList<firewalld_reply> reply = connectCall<QList<firewalld_reply>>(watcher);
0105                 if (!reply.isEmpty()) {
0106                     m_firewalldreply = reply;
0107                 }
0108             } else if (interface == SERVICES::INTERFACE && method != SAVE::METHOD
0109                        && method != AUTH::METHOD) { // list services available or enabled services AND don't execute runtimeToPermanent HERE
0110                 QStringList reply = connectCall<QStringList>(watcher);
0111                 if (!reply.isEmpty()) {
0112                     m_services = reply;
0113                 }
0114 
0115             } else {
0116                 connectCall(watcher); // save executed here
0117             }
0118             emitResult();
0119             return;
0120         });
0121 
0122     } else {
0123         connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, interface, method](QDBusPendingCallWatcher *watcher) {
0124             watcher->deleteLater();
0125             if (interface == SIMPLE::INTERFACE) { // believe or not to get the data from active zone you need to send ""
0126                 if (method.contains(QRegularExpression("^(add|remove)"))) {
0127                     QString reply = connectCall<QString>(watcher);
0128                     if (!reply.isEmpty())
0129                         qCDebug(FirewallDJobDebug) << "manipulated zone: " << reply;
0130 
0131                 } else if (method == "getZoneSettings2") {
0132                     QMap<QString, QVariant> settings;
0133                     settings = connectCall<QMap<QString, QVariant>>(watcher);
0134                     m_target = settings["target"].toString();
0135                 } else {
0136                     QStringList reply = connectCall<QStringList>(watcher);
0137                     if (!reply.isEmpty()) {
0138                         m_services = reply;
0139                     }
0140                 }
0141             } else {
0142                 connectCall(watcher);
0143             }
0144             emitResult();
0145             return;
0146         });
0147     }
0148 }
0149 
0150 QList<firewalld_reply> FirewalldJob::getFirewalldreply() const
0151 {
0152     return m_firewalldreply;
0153 }
0154 
0155 FirewalldJob::~FirewalldJob() = default;
0156 
0157 void FirewalldJob::start()
0158 {
0159     switch (m_type) {
0160     case FirewalldJob::SIMPLIFIEDRULE:
0161     case FirewalldJob::SIMPLELIST: {
0162         qCDebug(FirewallDJobDebug) << "firewalld zone interface: " << m_call << m_args;
0163         firewalldAction(FIREWALLD::BUS, FIREWALLD::PATH, SIMPLE::INTERFACE, m_call, m_args);
0164         break;
0165     }
0166     case FirewalldJob::FIREWALLD: {
0167         qCDebug(FirewallDJobDebug) << "firewalld direct: " << m_call << m_args;
0168         /* firewalldAction(m_call, m_args); */
0169         firewalldAction(FIREWALLD::BUS, FIREWALLD::PATH, DIRECT::INTERFACE, m_call, m_args);
0170         break;
0171     }
0172     case FirewalldJob::SAVEFIREWALLD: {
0173         qCDebug(FirewallDJobDebug) << i18n("firewalld saving (runtime to permanent)");
0174         firewalldAction(FIREWALLD::BUS, FIREWALLD::PATH, SERVICES::INTERFACE, SAVE::METHOD);
0175         /* saveFirewalld(); */
0176         break;
0177     }
0178     case FirewalldJob::LISTSERVICES: {
0179         /* listServices(); */
0180         firewalldAction(FIREWALLD::BUS, FIREWALLD::PATH, SERVICES::INTERFACE, SERVICES::METHOD);
0181         break;
0182     }
0183     case FirewalldJob::ALL: {
0184         firewalldAction(FIREWALLD::BUS, FIREWALLD::PATH, SERVICES::INTERFACE, AUTH::METHOD);
0185         break;
0186     }
0187 
0188     default:
0189         emitResult();
0190         return;
0191     }
0192     return;
0193 };
0194 
0195 QString FirewalldJob::name() const
0196 {
0197     return m_type == FirewalldJob::SAVEFIREWALLD ? i18n("firewalld saving") : i18n("firewalld %1").arg(QString(m_call));
0198 }
0199 
0200 QStringList FirewalldJob::getServices() const
0201 {
0202     return m_services;
0203 }
0204 
0205 QString FirewalldJob::getDefaultIncomingPolicy() const
0206 {
0207     return m_target;
0208 }