From 015674649d08a09d1422726efeca01f9ddf9929b Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Sat, 7 May 2022 13:56:34 +0300 Subject: [PATCH] kfirewall: move from kde-workspace Signed-off-by: Ivailo Monev --- kfirewall/CMakeLists.txt | 10 + kfirewall/kcm/CMakeLists.txt | 37 +++ kfirewall/kcm/kcm_kfirewallconfig.desktop | 16 + kfirewall/kcm/kfirewallconfig.cpp | 349 ++++++++++++++++++++++ kfirewall/kcm/kfirewallconfig.h | 61 ++++ kfirewall/kcm/kfirewallconfig.ui | 121 ++++++++ kfirewall/kcm/kfirewallhelper.cpp | 180 +++++++++++ kfirewall/kcm/kfirewallhelper.h | 35 +++ kfirewall/kded/CMakeLists.txt | 30 ++ kfirewall/kded/kded_kfirewall.cpp | 115 +++++++ kfirewall/kded/kded_kfirewall.h | 47 +++ kfirewall/kded/kfirewall.desktop | 11 + 12 files changed, 1012 insertions(+) create mode 100644 kfirewall/CMakeLists.txt create mode 100644 kfirewall/kcm/CMakeLists.txt create mode 100644 kfirewall/kcm/kcm_kfirewallconfig.desktop create mode 100644 kfirewall/kcm/kfirewallconfig.cpp create mode 100644 kfirewall/kcm/kfirewallconfig.h create mode 100644 kfirewall/kcm/kfirewallconfig.ui create mode 100644 kfirewall/kcm/kfirewallhelper.cpp create mode 100644 kfirewall/kcm/kfirewallhelper.h create mode 100644 kfirewall/kded/CMakeLists.txt create mode 100644 kfirewall/kded/kded_kfirewall.cpp create mode 100644 kfirewall/kded/kded_kfirewall.h create mode 100644 kfirewall/kded/kfirewall.desktop diff --git a/kfirewall/CMakeLists.txt b/kfirewall/CMakeLists.txt new file mode 100644 index 00000000..4aceefc2 --- /dev/null +++ b/kfirewall/CMakeLists.txt @@ -0,0 +1,10 @@ +project(kfirewall) + +find_program(IPTABLES_EXECUTABLE iptables-restore) +add_feature_info(iptables + IPTABLES_EXECUTABLE + "Firewall support" +) + +add_subdirectory(kcm) +add_subdirectory(kded) diff --git a/kfirewall/kcm/CMakeLists.txt b/kfirewall/kcm/CMakeLists.txt new file mode 100644 index 00000000..11cf7558 --- /dev/null +++ b/kfirewall/kcm/CMakeLists.txt @@ -0,0 +1,37 @@ +########### next target ############### + +set(kfirewallconfig_SRCS + kfirewallconfig.cpp + kfirewallconfig.ui +) + +kde4_add_plugin(kcm_kfirewallconfig ${kfirewallconfig_SRCS}) + +target_link_libraries(kcm_kfirewallconfig + ${KDE4_KDEUI_LIBS} + ${KDE4_KCMUTILS_LIBS} +) + +install( + TARGETS kcm_kfirewallconfig + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + FILES kcm_kfirewallconfig.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +########### next target ############### + +add_executable(kcmkfirewallhelper kfirewallhelper.cpp) +target_link_libraries(kcmkfirewallhelper + ${KDE4_KDECORE_LIBS} +) + +install( + TARGETS kcmkfirewallhelper + DESTINATION ${KDE4_LIBEXEC_INSTALL_DIR} +) + +kde4_install_auth_helper_files(kcmkfirewallhelper org.kde.kcontrol.kcmkfirewall root) diff --git a/kfirewall/kcm/kcm_kfirewallconfig.desktop b/kfirewall/kcm/kcm_kfirewallconfig.desktop new file mode 100644 index 00000000..06e8cace --- /dev/null +++ b/kfirewall/kcm/kcm_kfirewallconfig.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Exec=kcmshell4 kfirewallconfig +Icon=security-high +Type=Service + +X-KDE-ServiceTypes=KCModule +X-KDE-Library=kcm_kfirewallconfig +X-KDE-ParentApp=kcontrol +X-KDE-System-Settings-Parent-Category=network-settings +X-KDE-System-Settings-Parent-Category-V2=network-settings +X-DocPath=kcontrol/kfirewallconfig/index.html +Categories=Qt;KDE;X-KDE-settings-network-settings; + +Name=Firewall +Comment=Change filrewall options +X-KDE-Keywords=Filrewall diff --git a/kfirewall/kcm/kfirewallconfig.cpp b/kfirewall/kcm/kfirewallconfig.cpp new file mode 100644 index 00000000..7eb79271 --- /dev/null +++ b/kfirewall/kcm/kfirewallconfig.cpp @@ -0,0 +1,349 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfirewallconfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +K_PLUGIN_FACTORY(KCMFirewallFactory, registerPlugin();) +K_EXPORT_PLUGIN(KCMFirewallFactory("kcmfirewallconfig", "kcm_firewallconfig")) + +KCMFirewall::KCMFirewall(QWidget* parent, const QVariantList& args) + : KCModule(KCMFirewallFactory::componentData(), parent), + m_kfirewallconfigpath(KStandardDirs::locateLocal("data", "kfirewall.json")) +{ + Q_UNUSED(args); + + setQuickHelp(i18n("

KFirewall

This module allows you to change KDE firewall options.")); + + setupUi(this); + + const QString iptablesexe = KStandardDirs::findRootExe("iptables-restore"); + messagewidget->setMessageType(KMessageWidget::Error); + messagewidget->setCloseButtonVisible(false); + if (iptablesexe.isEmpty()) { + rulestable->setEnabled(false); + addbutton->setEnabled(false); + removebutton->setEnabled(false); + messagewidget->show(); + messagewidget->setText(i18n("iptables is not available")); + } else { + messagewidget->hide(); + } + + KAboutData *about = new KAboutData( + I18N_NOOP("kcmkfirewall"), 0, + ki18n("KDE Firewall Module"), + 0, KLocalizedString(), KAboutData::License_GPL, + ki18n("Copyright 2022, Ivailo Monev xakepa10@gmail.com") + ); + about->addAuthor(ki18n("Ivailo Monev"), KLocalizedString(), "xakepa10@gmail.com"); + setAboutData(about); + + setNeedsAuthorization(true); + + load(); + + connect( + rulestable, SIGNAL(itemChanged(QTableWidgetItem*)), + this, SLOT(slotItemChanged(QTableWidgetItem*)) + ); + connect( + rulestable->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)) + ); + + rulestable->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch); + + connect( + addbutton, SIGNAL(pressed()), + this, SLOT(slotAddRule()) + ); + connect( + removebutton, SIGNAL(pressed()), + this, SLOT(slotRemoveRule()) + ); +} + +KCMFirewall::~KCMFirewall() +{ +} + +void KCMFirewall::load() +{ + rulestable->clearContents(); + rulestable->setRowCount(0); + + QFile kfirewallfile(m_kfirewallconfigpath); + if (!kfirewallfile.open(QFile::ReadOnly)) { + // may not exist yet + kDebug() << "Could not open config for reading" << kfirewallfile.errorString(); + emit changed(false); + return; + } + + const QByteArray kfirewalljsondata = kfirewallfile.readAll(); + QJsonDocument kfirewalljsondocument = QJsonDocument::fromJson(kfirewalljsondata); + if (!kfirewalljsondata.isEmpty() && kfirewalljsondocument.isNull()) { + KMessageBox::error(this, i18n("Could create JSON document: %1", kfirewalljsondocument.errorString())); + return; + } + const QVariantMap kfirewallsettingsmap = kfirewalljsondocument.toVariant().toMap(); + // qDebug() << Q_FUNC_INFO << kfirewallsettingsmap; + + int counter = 0; + foreach(const QString &key, kfirewallsettingsmap.keys()) { + addRuleRow(); + const QVariantMap rowsettingsmap = kfirewallsettingsmap.value(key).toMap(); + const QString trafficvalue = rowsettingsmap.value(QString::fromLatin1("traffic")).toString(); + const QString addressvalue = rowsettingsmap.value(QString::fromLatin1("address")).toString(); + const uint portvalue = rowsettingsmap.value(QString::fromLatin1("port")).toUInt(); + const QString actionvalue = rowsettingsmap.value(QString::fromLatin1("action")).toString(); + + int trafficindex = 0; + if (trafficvalue == QLatin1String("inbound")) { + trafficindex = 0; + } else if (trafficvalue == QLatin1String("outbound")) { + trafficindex = 1; + } else { + kWarning() << "Invalid traffic value"; + } + QComboBox* tabletrafficwidget = qobject_cast(rulestable->cellWidget(counter, 0)); + tabletrafficwidget->setCurrentIndex(trafficindex); + + QTableWidgetItem* tableaddresswidget = rulestable->item(counter, 1); + tableaddresswidget->setText(addressvalue); + + if (portvalue > INT_MAX) { + kWarning() << "Port value outside range" << portvalue; + } + QSpinBox* tableportwidget = qobject_cast(rulestable->cellWidget(counter, 2)); + tableportwidget->setValue(portvalue); + + int actionindex = 0; + if (actionvalue == QLatin1String("accept")) { + actionindex = 0; + } else if (actionvalue == QLatin1String("reject")) { + actionindex = 1; + } else { + kWarning() << "Invalid action value" << actionvalue; + } + QComboBox* tableactionwidget = qobject_cast(rulestable->cellWidget(counter, 3)); + tableactionwidget->setCurrentIndex(actionindex); + + counter++; + } + + emit changed(false); +} + +void KCMFirewall::save() +{ + QVariantMap kfirewallsettingsmap; + const QVariant uservalue = QVariant(KUser().loginName()); + for (int i = 0; i < rulestable->rowCount(); i++) { + QVariant trafficvalue; + const QComboBox* tabletrafficwidget = qobject_cast(rulestable->cellWidget(i, 0)); + switch (tabletrafficwidget->currentIndex()) { + case 0: { + trafficvalue = QVariant(QByteArray("inbound")); + break; + } + case 1: { + trafficvalue = QVariant(QByteArray("outbound")); + break; + } + default: { + Q_ASSERT(false); + break; + } + } + // qDebug() << Q_FUNC_INFO << trafficvalue; + + const QTableWidgetItem* tableaddresswidget = rulestable->item(i, 1); + const QVariant addressvalue = QVariant(tableaddresswidget->text()); + // qDebug() << Q_FUNC_INFO << addressvalue; + + const QSpinBox* tableportwidget = qobject_cast(rulestable->cellWidget(i, 2)); + const QVariant portvalue = QVariant(tableportwidget->value()); + // qDebug() << Q_FUNC_INFO << portvalue; + + if (addressvalue.toString().isEmpty() && tableportwidget->value() <= 0) { + KMessageBox::error(this, i18n("Either address or port must be specified")); + return; + } + + QVariant actionvalue; + const QComboBox* tableactionwidget = qobject_cast(rulestable->cellWidget(i, 3)); + switch (tableactionwidget->currentIndex()) { + case 0: { + actionvalue = QVariant(QByteArray("accept")); + break; + } + case 1: { + actionvalue = QVariant(QByteArray("reject")); + break; + } + default: { + Q_ASSERT(false); + break; + } + } + // qDebug() << Q_FUNC_INFO << actionvalue; + + QVariantMap rowsettingsmap; + rowsettingsmap.insert(QString::fromLatin1("user"), uservalue); + rowsettingsmap.insert(QString::fromLatin1("traffic"), trafficvalue); + rowsettingsmap.insert(QString::fromLatin1("address"), addressvalue); + rowsettingsmap.insert(QString::fromLatin1("port"), portvalue); + rowsettingsmap.insert(QString::fromLatin1("action"), actionvalue); + kfirewallsettingsmap.insert(QString::number(i), rowsettingsmap); + } + // qDebug() << Q_FUNC_INFO << kfirewallsettingsmap; + + QJsonDocument kfirewalljsondocument = QJsonDocument::fromVariant(kfirewallsettingsmap); + if (!kfirewallsettingsmap.isEmpty() && kfirewalljsondocument.isNull()) { + KMessageBox::error(this, i18n("Could create JSON document: %1", kfirewalljsondocument.errorString())); + return; + } + const QByteArray kfirewalljsondata = kfirewalljsondocument.toJson(); + + QFile kfirewallfile(m_kfirewallconfigpath); + if (!kfirewallfile.open(QFile::WriteOnly)) { + KMessageBox::error(this, i18n("Could not open config for writing: %1", kfirewallfile.errorString())); + return; + } + if (kfirewallfile.write(kfirewalljsondata) != kfirewalljsondata.size()) { + KMessageBox::error(this, i18n("Could not write config: %1", kfirewallfile.errorString())); + return; + } + + emit changed(false); +} + +void KCMFirewall::defaults() +{ + rulestable->clearContents(); + rulestable->setRowCount(0); + emit changed(true); +} + +void KCMFirewall::addRuleRow() +{ + const int rulesrowcount = rulestable->rowCount(); + rulestable->setRowCount(rulesrowcount + 1); + + QComboBox* tabletrafficwidget = new QComboBox(); + tabletrafficwidget->addItem(i18n("Inbound")); + tabletrafficwidget->addItem(i18n("Outbound")); + connect( + tabletrafficwidget, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotTrafficChanged(int)) + ); + rulestable->setCellWidget(rulesrowcount, 0, tabletrafficwidget); + + QTableWidgetItem* tableaddresswidget = new QTableWidgetItem(); + tableaddresswidget->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + rulestable->setItem(rulesrowcount, 1, tableaddresswidget); + + QSpinBox* tableportwidget = new QSpinBox(); + tableportwidget->setRange(0, INT_MAX); + tableportwidget->setValue(0); + connect( + tableportwidget, SIGNAL(valueChanged(int)), + this, SLOT(slotPortChanged(int)) + ); + rulestable->setCellWidget(rulesrowcount, 2, tableportwidget); + + QComboBox* tabletactionwidget = new QComboBox(); + tabletactionwidget->addItem(i18n("Accept")); + tabletactionwidget->addItem(i18n("Reject")); + connect( + tabletactionwidget, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotActionChanged(int)) + ); + rulestable->setCellWidget(rulesrowcount, 3, tabletactionwidget); + + rulestable->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch); +} + +void KCMFirewall::slotAddRule() +{ + addRuleRow(); + emit changed(true); +} + +void KCMFirewall::slotRemoveRule() +{ + QList removedrows; + foreach (const QModelIndex &modelindex, rulestable->selectionModel()->selectedIndexes()) { + const int modelindexrow = modelindex.row(); + // qDebug() << Q_FUNC_INFO << modelindexrow; + if (removedrows.contains(modelindexrow)) { + continue; + } + rulestable->removeRow(modelindexrow); + removedrows.append(modelindexrow); + } + emit changed(true); +} + +void KCMFirewall::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + removebutton->setEnabled(!selected.isEmpty()); +} + +void KCMFirewall::slotItemChanged(QTableWidgetItem* tablewidget) +{ + Q_UNUSED(tablewidget); + emit changed(true); +} + +void KCMFirewall::slotTrafficChanged(const int value) +{ + Q_UNUSED(value); + emit changed(true); +} + +void KCMFirewall::slotPortChanged(const int value) +{ + Q_UNUSED(value); + emit changed(true); +} + +void KCMFirewall::slotActionChanged(const int value) +{ + Q_UNUSED(value); + emit changed(true); +} + +#include "moc_kfirewallconfig.cpp" diff --git a/kfirewall/kcm/kfirewallconfig.h b/kfirewall/kcm/kfirewallconfig.h new file mode 100644 index 00000000..1d796614 --- /dev/null +++ b/kfirewall/kcm/kfirewallconfig.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFIREWALLCONFIG_H +#define KFIREWALLCONFIG_H + +#include + +#include "ui_kfirewallconfig.h" + +/** + * Control look of KDE firewall + * + * @author Ivailo Monev (xakepa10@gmail.com) + */ +class KCMFirewall : public KCModule, public Ui_KFirewallDialog +{ + Q_OBJECT +public: + // KCModule reimplementations + KCMFirewall(QWidget* parent, const QVariantList&); + ~KCMFirewall(); + +public Q_SLOTS: + void load() final; + void save() final; + void defaults() final; + +private: + void addRuleRow(); + +private Q_SLOTS: + void slotAddRule(); + void slotRemoveRule(); + + void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void slotItemChanged(QTableWidgetItem* tablewidget); + void slotTrafficChanged(const int value); + void slotPortChanged(const int value); + void slotActionChanged(const int value); + +private: + QString m_kfirewallconfigpath; +}; + +#endif // KFIREWALLCONFIG_H diff --git a/kfirewall/kcm/kfirewallconfig.ui b/kfirewall/kcm/kfirewallconfig.ui new file mode 100644 index 00000000..89a8b6fc --- /dev/null +++ b/kfirewall/kcm/kfirewallconfig.ui @@ -0,0 +1,121 @@ + + + KFirewallDialog + + + + 0 + 0 + 501 + 467 + + + + + + + Rules + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QAbstractItemView::SelectRows + + + true + + + + Traffic + + + + + Address + + + + + Port + + + + + Action + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add + + + + + + + + + + + + false + + + Remove + + + + + + + + + + + + + + + + + + + KMessageWidget + QFrame +
kmessagewidget.h
+ 1 +
+
+ + +
diff --git a/kfirewall/kcm/kfirewallhelper.cpp b/kfirewall/kcm/kfirewallhelper.cpp new file mode 100644 index 00000000..bd8f1bb7 --- /dev/null +++ b/kfirewall/kcm/kfirewallhelper.cpp @@ -0,0 +1,180 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfirewallhelper.h" + +#include +#include +#include +#include + +static QByteArray ruleForSettings(const QByteArray &uservalue, const QByteArray &trafficvalue, + const QByteArray &addressvalue, const uint portvalue, + const QByteArray &actionvalue, const bool appendrules, const bool tcprule) +{ + if (!tcprule && portvalue <= 0) { + // no port specified + return QByteArray(); + } + + QByteArray iptablesruledata; + bool isinbound = false; + QByteArray iptablestraffic = trafficvalue.toUpper(); + if (iptablestraffic == "INBOUND") { + iptablestraffic = "INPUT"; + isinbound = true; + } else { + iptablestraffic = "OUTPUT"; + } + + if (appendrules) { + iptablesruledata.append("--append "); + } else { + iptablesruledata.append("--delete "); + } + iptablesruledata.append(iptablestraffic); + if (!addressvalue.isEmpty()) { + iptablesruledata.append(" --destination "); + iptablesruledata.append(addressvalue); + } + if (portvalue > 0) { + if (tcprule) { + iptablesruledata.append(" --proto tcp --dport "); + iptablesruledata.append(QByteArray::number(portvalue)); + } else { + iptablesruledata.append(" --proto udp --dport "); + iptablesruledata.append(QByteArray::number(portvalue)); + } + } + if (!isinbound) { + // NOTE: only output can be user-bound + iptablesruledata.append(" --match owner --uid-owner "); + iptablesruledata.append(uservalue); + } + iptablesruledata.append(" --jump "); + iptablesruledata.append(actionvalue.toUpper()); + iptablesruledata.append("\n"); + return iptablesruledata; +} + +static QByteArray rulesForParameters(const QVariantMap ¶meters, const bool appendrules) +{ + QByteArray iptablesruledata("*filter\n"); + foreach (const QString &key, parameters.keys()) { + const QVariantMap rulesettingsmap = parameters.value(key).toMap(); + const QByteArray uservalue = rulesettingsmap.value(QString::fromLatin1("user")).toByteArray(); + const QByteArray trafficvalue = rulesettingsmap.value(QString::fromLatin1("traffic")).toByteArray(); + const QByteArray addressvalue = rulesettingsmap.value(QString::fromLatin1("address")).toByteArray(); + const uint portvalue = rulesettingsmap.value(QString::fromLatin1("port")).toUInt(); + const QByteArray actionvalue = rulesettingsmap.value(QString::fromLatin1("action")).toByteArray(); + // qDebug() << Q_FUNC_INFO << trafficvalue << addressvalue << portvalue << actionvalue; + + iptablesruledata.append( + ruleForSettings( + uservalue, trafficvalue, addressvalue, portvalue, actionvalue, + appendrules, true + ) + ); + iptablesruledata.append( + ruleForSettings( + uservalue, trafficvalue, addressvalue, portvalue, actionvalue, + appendrules, false + ) + ); + } + iptablesruledata.append("COMMIT\n"); + // qDebug() << Q_FUNC_INFO << iptablesruledata; + return iptablesruledata; +} + +static ActionReply applyRules(KFirewallHelper *helper, const QString &iptablesexe, + const QByteArray &iptablesruledata) +{ + QProcess iptablesproc(helper); + iptablesproc.start(iptablesexe); + if (!iptablesproc.waitForStarted()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Could not start iptables-restore"); + errorreply.setErrorCode(3); + return errorreply; + } + if (iptablesproc.write(iptablesruledata) != iptablesruledata.size()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Could not write rules"); + errorreply.setErrorCode(4); + return errorreply; + } + iptablesproc.closeWriteChannel(); + iptablesproc.waitForFinished(); + if (iptablesproc.exitCode() != 0) { + QString errorstring = iptablesproc.readAllStandardError().trimmed(); + if (errorstring.isEmpty()) { + errorstring = QString::fromLatin1("Could not apply rules"); + } + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription(errorstring); + errorreply.setErrorCode(5); + return errorreply; + } + + return KAuth::ActionReply::SuccessReply; +} + +ActionReply KFirewallHelper::apply(const QVariantMap ¶meters) +{ + if (parameters.isEmpty()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Empty rules"); + errorreply.setErrorCode(1); + return errorreply; + } + + const QString iptablesexe = KStandardDirs::findRootExe("iptables-restore"); + if (iptablesexe.isEmpty()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Could not find iptables-restore"); + errorreply.setErrorCode(2); + return errorreply; + } + + return applyRules(this, iptablesexe, rulesForParameters(parameters, true)); +} + +ActionReply KFirewallHelper::revert(const QVariantMap ¶meters) +{ + //qDebug() << Q_FUNC_INFO << parameters; + + if (parameters.isEmpty()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Empty rules"); + errorreply.setErrorCode(1); + return errorreply; + } + + const QString iptablesexe = KStandardDirs::findRootExe("iptables-restore"); + if (iptablesexe.isEmpty()) { + KAuth::ActionReply errorreply(KAuth::ActionReply::HelperError); + errorreply.setErrorDescription("Could not find iptables-restore"); + errorreply.setErrorCode(2); + return errorreply; + } + + return applyRules(this, iptablesexe, rulesForParameters(parameters, false)); +} + +KDE4_AUTH_HELPER_MAIN("org.kde.kcontrol.kcmkfirewall", KFirewallHelper) diff --git a/kfirewall/kcm/kfirewallhelper.h b/kfirewall/kcm/kfirewallhelper.h new file mode 100644 index 00000000..72fa710b --- /dev/null +++ b/kfirewall/kcm/kfirewallhelper.h @@ -0,0 +1,35 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFIREWALLHELPER_H +#define KFIREWALLHELPER_H + +#include + +// methods return type must be ActionReply otherwise QMetaObject::invokeMethod() fails +using namespace KAuth; + +class KFirewallHelper : public QObject +{ + Q_OBJECT +public slots: + ActionReply apply(const QVariantMap ¶meters); + ActionReply revert(const QVariantMap ¶meters); +}; + +#endif // KFIREWALLHELPER_H diff --git a/kfirewall/kded/CMakeLists.txt b/kfirewall/kded/CMakeLists.txt new file mode 100644 index 00000000..81516548 --- /dev/null +++ b/kfirewall/kded/CMakeLists.txt @@ -0,0 +1,30 @@ +########### next target ############### + +set(kded_kfirewall_SRCS + kded_kfirewall.cpp + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kfirewall.xml +) + +qt4_generate_dbus_interface(kded_kfirewall.h org.kde.kfirewall.xml ) + +kde4_add_plugin(kded_kfirewall ${kded_kfirewall_SRCS}) +target_link_libraries(kded_kfirewall PRIVATE + ${KDE4_KDECORE_LIBS} +) + +install( + TARGETS kded_kfirewall + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + FILES kfirewall.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/kded +) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kfirewall.xml + DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR} +) + + diff --git a/kfirewall/kded/kded_kfirewall.cpp b/kfirewall/kded/kded_kfirewall.cpp new file mode 100644 index 00000000..e476b212 --- /dev/null +++ b/kfirewall/kded/kded_kfirewall.cpp @@ -0,0 +1,115 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kded_kfirewall.h" + +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KFirewallModuleFactory, registerPlugin();) +K_EXPORT_PLUGIN(KFirewallModuleFactory("kfirewall")) + +KFirewallModule::KFirewallModule(QObject *parent, const QList&) + : KDEDModule(parent), + m_kfirewallconfigpath(KStandardDirs::locateLocal("data", "kfirewall.json")), + m_watcher(this) +{ + enable(); + + connect( + &m_watcher, SIGNAL(fileChanged(QString)), + this, SLOT(slotFileChanged(QString)) + ); + m_watcher.addPath(m_kfirewallconfigpath); +} + +KFirewallModule::~KFirewallModule() +{ + disable(); +} + +bool KFirewallModule::enable() +{ + m_kfirewallsettingsmap.clear(); + QFile kfirewallfile(m_kfirewallconfigpath); + if (!kfirewallfile.open(QFile::ReadOnly)) { + // may not exist yet but if it is created eventually it will be applied + kDebug() << "Could not open config for reading" << kfirewallfile.errorString(); + return true; + } + + const QByteArray kfirewalljsondata = kfirewallfile.readAll(); + QJsonDocument kfirewalljsondocument = QJsonDocument::fromJson(kfirewalljsondata); + if (!kfirewalljsondata.isEmpty() && kfirewalljsondocument.isNull()) { + kWarning() << "Could not create JSON document" << kfirewalljsondocument.errorString(); + return false; + } + m_kfirewallsettingsmap = kfirewalljsondocument.toVariant().toMap(); + if (m_kfirewallsettingsmap.isEmpty()) { + kDebug() << "No firewall rules"; + return true; + } + + KAuth::Action kfirewallaction("org.kde.kcontrol.kcmkfirewall.apply"); + kfirewallaction.setHelperID("org.kde.kcontrol.kcmkfirewall"); + kfirewallaction.setArguments(m_kfirewallsettingsmap); + KAuth::ActionReply kfirewallreply = kfirewallaction.execute(); + // qDebug() << Q_FUNC_INFO << kfirewallreply.errorCode() << kfirewallreply.errorDescription(); + + if (kfirewallreply != KAuth::ActionReply::SuccessReply) { + kWarning() << kfirewallreply.errorCode() << kfirewallreply.errorDescription(); + return false; + } + + return true; +} + +bool KFirewallModule::disable() +{ + if (m_kfirewallsettingsmap.isEmpty()) { + kDebug() << "No firewall rules"; + return true; + } + + KAuth::Action kfirewallaction("org.kde.kcontrol.kcmkfirewall.revert"); + kfirewallaction.setHelperID("org.kde.kcontrol.kcmkfirewall"); + kfirewallaction.setArguments(m_kfirewallsettingsmap); + KAuth::ActionReply kfirewallreply = kfirewallaction.execute(); + // qDebug() << Q_FUNC_INFO << kfirewallreply.errorCode() << kfirewallreply.errorDescription(); + + if (kfirewallreply != KAuth::ActionReply::SuccessReply) { + kWarning() << kfirewallreply.errorCode() << kfirewallreply.errorDescription(); + return false; + } + + m_kfirewallsettingsmap.clear(); + return true; +} + +void KFirewallModule::slotFileChanged(const QString &path) +{ + Q_UNUSED(path); + disable(); + enable(); +} + +#include "moc_kded_kfirewall.cpp" diff --git a/kfirewall/kded/kded_kfirewall.h b/kfirewall/kded/kded_kfirewall.h new file mode 100644 index 00000000..82f83bb4 --- /dev/null +++ b/kfirewall/kded/kded_kfirewall.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE project + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFIREWALL_KDED_H +#define KFIREWALL_KDED_H + +#include +#include + +class KFirewallModule: public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kfirewall") + +public: + KFirewallModule(QObject *parent, const QList&); + ~KFirewallModule(); + +public Q_SLOTS: + Q_SCRIPTABLE bool enable(); + Q_SCRIPTABLE bool disable(); + +private Q_SLOTS: + void slotFileChanged(const QString &path); + +private: + QString m_kfirewallconfigpath; + QVariantMap m_kfirewallsettingsmap; + QFileSystemWatcher m_watcher; +}; + +#endif // KFIREWALL_KDED_H diff --git a/kfirewall/kded/kfirewall.desktop b/kfirewall/kded/kfirewall.desktop new file mode 100644 index 00000000..496b3d6e --- /dev/null +++ b/kfirewall/kded/kfirewall.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Icon=security-high +Name=Firewall +Comment=Firewall service +Type=Service +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=kfirewall +X-KDE-DBus-ModuleName=kfirewall +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=false +OnlyShowIn=KDE;