File indexing completed on 2024-05-26 05:37:20
0001 // SPDX-License-Identifier: GPL-2.0-or-later 0002 // SPDX-FileCopyrightText: 2011 Craig Drummond <craig.p.drummond@gmail.com> 0003 // SPDX-FileCopyrightText: 2018 Alexis Lopes Zubeta <contact@azubieta.net> 0004 // SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org> 0005 /* 0006 * UFW KControl Module 0007 */ 0008 0009 #include "ufwclient.h" 0010 #include "ufwlogmodel.h" 0011 0012 #include <rule.h> 0013 #include <systemdjob.h> 0014 #include <types.h> 0015 0016 #include <QDebug> 0017 #include <QDir> 0018 #include <QNetworkInterface> 0019 #include <QProcess> 0020 #include <QRegularExpression> 0021 #include <QStandardPaths> 0022 #include <QVariantMap> 0023 #include <QXmlStreamReader> 0024 0025 #include <KAuth/ExecuteJob> 0026 0027 #include <KConfig> 0028 #include <KConfigGroup> 0029 #include <KLocalizedString> 0030 #include <KPluginFactory> 0031 0032 #include <loglistmodel.h> 0033 #include <rulelistmodel.h> 0034 0035 K_PLUGIN_CLASS_WITH_JSON(UfwClient, "ufwbackend.json") 0036 Q_LOGGING_CATEGORY(UFWClientDebug, "ufw.client") 0037 0038 namespace 0039 { 0040 void debugState(KAuth::Action::AuthStatus status) 0041 { 0042 using Status = KAuth::Action::AuthStatus; 0043 switch (status) { 0044 case Status::AuthorizedStatus: 0045 qCDebug(UFWClientDebug) << "Job Authorized"; 0046 break; 0047 case Status::AuthRequiredStatus: 0048 qCDebug(UFWClientDebug) << "Job Requires authentication"; 0049 break; 0050 case Status::UserCancelledStatus: 0051 qCDebug(UFWClientDebug) << "User cancelled!"; 0052 break; 0053 case Status::DeniedStatus: 0054 qCDebug(UFWClientDebug) << "Password denied"; 0055 break; 0056 case Status::InvalidStatus: 0057 qCDebug(UFWClientDebug) << "Invalid Status!"; 0058 break; 0059 case Status::ErrorStatus: 0060 qCDebug(UFWClientDebug) << "Job is in an error state"; 0061 break; 0062 } 0063 } 0064 } 0065 0066 UfwClient::UfwClient(QObject *parent, const QVariantList &args) 0067 : IFirewallClientBackend(parent, args) 0068 , m_rulesModel(new RuleListModel(this)) 0069 , m_logsAutoRefresh(true) 0070 { 0071 queryExecutable("ufw"); 0072 } 0073 0074 QString UfwClient::name() const 0075 { 0076 return QStringLiteral("ufw"); 0077 } 0078 0079 bool UfwClient::isTcpAndUdp(int protocolIdx) 0080 { 0081 return protocolIdx == 0; 0082 } 0083 0084 void UfwClient::refresh() 0085 { 0086 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::ListenProfiles); 0087 } 0088 void UfwClient::enableService(bool value) 0089 { 0090 SystemdJob *job = new SystemdJob(static_cast<SYSTEMD::actions>(value), QStringLiteral("ufw.service"), true); 0091 0092 connect(job, &SystemdJob::result, this, [job] { 0093 if (job->error()) { 0094 qCDebug(UFWClientDebug) << "SystemdJob Error: " << job->error() << job->errorString(); 0095 return; 0096 } 0097 }); 0098 job->start(); 0099 } 0100 bool UfwClient::enabled() const 0101 { 0102 return m_currentProfile.enabled(); 0103 } 0104 0105 KJob *UfwClient::setEnabled(bool value) 0106 { 0107 if (enabled() == value) { 0108 return nullptr; 0109 } 0110 0111 QVariantMap args{ 0112 {"cmd", "setStatus"}, 0113 {"status", value}, 0114 }; 0115 0116 KAuth::Action modifyAction = buildModifyAction(args); 0117 qCDebug(UFWClientDebug) << "Starting the set Enabled job"; 0118 KAuth::ExecuteJob *job = modifyAction.execute(); 0119 0120 connect(job, &KAuth::ExecuteJob::result, this, [this, job, value] { 0121 qCDebug(UFWClientDebug) << "Set Enabled job finished, triggering a status query."; 0122 if (job->error() == KJob::NoError) { 0123 enableService(value); 0124 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::DontListenProfiles); 0125 } else { 0126 qCDebug(UFWClientDebug) << "Job error: " << job->error(); 0127 } 0128 }); 0129 0130 return job; 0131 } 0132 0133 KJob *UfwClient::queryStatus(FirewallClient::DefaultDataBehavior defaultsBehavior, FirewallClient::ProfilesBehavior profilesBehavior) 0134 { 0135 qCDebug(UFWClientDebug) << "Status query starting"; 0136 if (m_busy) { 0137 qWarning() << "Ufw client is busy"; 0138 return nullptr; 0139 } 0140 0141 m_busy = true; 0142 0143 const bool readDefaults = defaultsBehavior == FirewallClient::DefaultDataBehavior::ReadDefaults; 0144 const bool listProfiles = profilesBehavior == FirewallClient::ProfilesBehavior::ListenProfiles; 0145 0146 QVariantMap args{ 0147 {"defaults", readDefaults}, 0148 {"profiles", listProfiles}, 0149 }; 0150 0151 if (m_queryAction.name().isEmpty()) { 0152 m_queryAction = buildQueryAction(args); 0153 } 0154 0155 KAuth::ExecuteJob *job = m_queryAction.execute(); 0156 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0157 qCDebug(UFWClientDebug) << "Status Query finished, setting the profile"; 0158 m_busy = false; 0159 0160 if (job->error() != KJob::NoError) { 0161 Q_EMIT showErrorMessage(i18n("There was an error in the backend! Please report it.\n%1 %2", job->action().name(), job->errorString())); 0162 qWarning() << job->action().name() << job->errorString(); 0163 return; 0164 } 0165 QByteArray response = job->data().value("response", "").toByteArray(); 0166 setProfile(Profile(response)); 0167 }); 0168 0169 qCDebug(UFWClientDebug) << "Starting the Status Query"; 0170 job->start(); 0171 return job; 0172 } 0173 0174 KJob *UfwClient::setDefaultIncomingPolicy(QString policy) 0175 { 0176 if (policy == defaultIncomingPolicy()) { 0177 return nullptr; 0178 } 0179 0180 const QString xmlArg = QStringLiteral("<defaults incoming=\"%1\"/>").arg(policy); 0181 0182 QVariantMap args{ 0183 {"cmd", "setDefaults"}, 0184 {"xml", xmlArg}, 0185 }; 0186 0187 KAuth::Action modifyAction = buildModifyAction(args); 0188 0189 KAuth::ExecuteJob *job = modifyAction.execute(); 0190 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0191 if (!job->error()) { 0192 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::DontListenProfiles); 0193 } 0194 }); 0195 0196 job->start(); 0197 return job; 0198 } 0199 0200 KJob *UfwClient::setDefaultOutgoingPolicy(QString policy) 0201 { 0202 if (policy == defaultOutgoingPolicy()) { 0203 return nullptr; 0204 } 0205 0206 const QString xmlArg = QStringLiteral("<defaults outgoing=\"%1\"/>").arg(policy); 0207 0208 QVariantMap args{ 0209 {"cmd", "setDefaults"}, 0210 {"xml", xmlArg}, 0211 }; 0212 0213 KAuth::Action modifyAction = buildModifyAction(args); 0214 0215 KAuth::ExecuteJob *job = modifyAction.execute(); 0216 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0217 if (!job->error()) { 0218 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::DontListenProfiles); 0219 } 0220 }); 0221 0222 job->start(); 0223 return job; 0224 } 0225 0226 void UfwClient::setLogsAutoRefresh(bool logsAutoRefresh) 0227 { 0228 if (m_logsAutoRefresh == logsAutoRefresh) 0229 return; 0230 0231 if (logsAutoRefresh) { 0232 connect(&m_logsRefreshTimer, &QTimer::timeout, this, &UfwClient::refreshLogs); 0233 m_logsRefreshTimer.setInterval(3000); 0234 m_logsRefreshTimer.start(); 0235 } else { 0236 disconnect(&m_logsRefreshTimer, &QTimer::timeout, this, &UfwClient::refreshLogs); 0237 m_logsRefreshTimer.stop(); 0238 } 0239 0240 m_logsAutoRefresh = logsAutoRefresh; 0241 Q_EMIT logsAutoRefreshChanged(m_logsAutoRefresh); 0242 } 0243 0244 void UfwClient::refreshLogs() 0245 { 0246 if (!m_logs) { 0247 logs(); 0248 qWarning() << "Trying to refresh logs without logs model, creating the object."; 0249 return; 0250 } 0251 0252 KAuth::Action action("org.kde.ufw.viewlog"); 0253 action.setHelperId("org.kde.ufw"); 0254 0255 QVariantMap args; 0256 if (m_rawLogs.size() > 0) 0257 args["lastLine"] = m_rawLogs.last(); 0258 0259 action.setArguments(args); 0260 0261 m_logs->setBusy(true); 0262 0263 KAuth::ExecuteJob *job = action.execute(); 0264 connect(job, &KAuth::ExecuteJob::finished, this, [this, job] { 0265 m_logs->setBusy(false); 0266 0267 if (job->error()) { 0268 Q_EMIT m_logs->showErrorMessage(i18n("Error fetching firewall logs: %1", job->errorString())); 0269 return; 0270 } 0271 0272 const QStringList newLogs = job->data().value("lines", "").toStringList(); 0273 // FIXME do we really need to store this raw thing here and then processed in the model? 0274 m_rawLogs.append(newLogs); 0275 m_logs->addRawLogs(newLogs); 0276 }); 0277 0278 job->start(); 0279 } 0280 0281 void UfwClient::setProfile(Profile profile) 0282 { 0283 qCDebug(UFWClientDebug) << "Profile Received, Setting the profile on the model"; 0284 auto oldProfile = m_currentProfile; 0285 m_currentProfile = profile; 0286 m_rulesModel->setProfile(m_currentProfile); 0287 if (m_currentProfile.enabled() != oldProfile.enabled()) 0288 Q_EMIT enabledChanged(m_currentProfile.enabled()); 0289 0290 if (m_currentProfile.defaultIncomingPolicy() != oldProfile.defaultIncomingPolicy()) { 0291 const QString policy = Types::toString(m_currentProfile.defaultIncomingPolicy()); 0292 Q_EMIT defaultIncomingPolicyChanged(policy); 0293 } 0294 0295 if (m_currentProfile.defaultOutgoingPolicy() != oldProfile.defaultOutgoingPolicy()) { 0296 const QString policy = Types::toString(m_currentProfile.defaultOutgoingPolicy()); 0297 Q_EMIT defaultOutgoingPolicyChanged(policy); 0298 } 0299 0300 queryKnownApplications(); 0301 } 0302 0303 KAuth::Action UfwClient::buildQueryAction(const QVariantMap &arguments) 0304 { 0305 auto action = KAuth::Action("org.kde.ufw.query"); 0306 action.setHelperId("org.kde.ufw"); 0307 action.setArguments(arguments); 0308 0309 return action; 0310 } 0311 0312 KAuth::Action UfwClient::buildModifyAction(const QVariantMap &arguments) 0313 { 0314 auto action = KAuth::Action("org.kde.ufw.modify"); 0315 action.setHelperId("org.kde.ufw"); 0316 action.setArguments(arguments); 0317 0318 return action; 0319 } 0320 0321 RuleListModel *UfwClient::rules() const 0322 { 0323 return m_rulesModel; 0324 } 0325 0326 Rule *UfwClient::ruleAt(int index) 0327 { 0328 auto cRules = m_currentProfile.rules(); 0329 0330 if (index < 0 || index >= cRules.count()) { 0331 return nullptr; 0332 } 0333 0334 Rule *rule = cRules.at(index); 0335 return rule; 0336 } 0337 0338 KJob *UfwClient::addRule(Rule *r) 0339 { 0340 if (!r) { 0341 qWarning() << "nullptr rule"; 0342 return nullptr; 0343 } 0344 0345 QVariantMap args{ 0346 {"cmd", "addRules"}, 0347 {"count", 1}, 0348 {"xml0", toXml(r)}, 0349 }; 0350 0351 KAuth::Action modifyAction = buildModifyAction(args); 0352 0353 KAuth::ExecuteJob *job = modifyAction.execute(); 0354 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0355 if (job->error() == KJob::NoError) { 0356 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::ListenProfiles); 0357 } 0358 }); 0359 0360 job->start(); 0361 return job; 0362 } 0363 0364 KJob *UfwClient::removeRule(int index) 0365 { 0366 if (index < 0 || index >= m_currentProfile.rules().count()) { 0367 qWarning() << __FUNCTION__ << "invalid rule index"; 0368 return nullptr; 0369 } 0370 0371 // Correct index 0372 index += 1; 0373 0374 QVariantMap args{ 0375 {"cmd", "removeRule"}, 0376 {"index", QString::number(index)}, 0377 }; 0378 0379 KAuth::Action modifyAction = buildModifyAction(args); 0380 KAuth::ExecuteJob *job = modifyAction.execute(); 0381 connect(job, &KAuth::ExecuteJob::statusChanged, this, [](KAuth::Action::AuthStatus status) { 0382 debugState(status); 0383 }); 0384 0385 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0386 if (!job->error()) { 0387 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::ListenProfiles); 0388 } 0389 }); 0390 0391 job->start(); 0392 return job; 0393 } 0394 0395 KJob *UfwClient::updateRule(Rule *r) 0396 { 0397 /* UFW does not support update rules. the tests showed that remove / insert is flacky. */ 0398 Q_UNUSED(r); 0399 return nullptr; 0400 } 0401 0402 KJob *UfwClient::moveRule(int from, int to) 0403 { 0404 const QList<Rule *> cRules = m_currentProfile.rules(); 0405 if (from < 0 || from >= cRules.count()) { 0406 qWarning() << "invalid from index"; 0407 return nullptr; 0408 } 0409 0410 // Correct indices 0411 from += 1; 0412 to += 1; 0413 0414 QVariantMap args{ 0415 {"cmd", "moveRule"}, 0416 {"from", from}, 0417 {"to", to}, 0418 }; 0419 0420 KAuth::Action modifyAction = buildModifyAction(args); 0421 KAuth::ExecuteJob *job = modifyAction.execute(); 0422 connect(job, &KAuth::ExecuteJob::finished, this, [this, job] { 0423 if (!job->error()) { 0424 queryStatus(FirewallClient::DefaultDataBehavior::ReadDefaults, FirewallClient::ProfilesBehavior::ListenProfiles); 0425 } 0426 }); 0427 0428 job->start(); 0429 return job; 0430 } 0431 0432 QString UfwClient::defaultIncomingPolicy() const 0433 { 0434 auto policy_t = m_currentProfile.defaultIncomingPolicy(); 0435 return Types::toString(policy_t); 0436 } 0437 0438 QString UfwClient::defaultOutgoingPolicy() const 0439 { 0440 auto policy_t = m_currentProfile.defaultOutgoingPolicy(); 0441 return Types::toString(policy_t); 0442 } 0443 0444 LogListModel *UfwClient::logs() 0445 { 0446 if (!m_logs) { 0447 m_logs = new UfwLogModel(this); 0448 refreshLogs(); 0449 } 0450 return m_logs; 0451 } 0452 0453 bool UfwClient::logsAutoRefresh() const 0454 { 0455 return m_logsAutoRefresh; 0456 } 0457 0458 namespace 0459 { 0460 bool isNumber(const QString &s) 0461 { 0462 bool error = true; 0463 int dummy = s.toInt(&error); 0464 Q_UNUSED(dummy) 0465 return error; 0466 } 0467 0468 QString portStrToInt(const QString &portStr) 0469 { 0470 QFile file("/etc/services"); 0471 if (!file.open(QIODevice::ReadOnly)) { 0472 qCDebug(UFWClientDebug) << "Could not open file, returning"; 0473 return portStr; 0474 } 0475 while (!file.atEnd()) { 0476 QString line = file.readLine(); 0477 if (!line.startsWith(portStr.toLocal8Bit())) { 0478 continue; 0479 } 0480 0481 // http 80/tcp 0482 auto list = line.split(QRegularExpression("\\s+")); 0483 if (list.size() > 1) { 0484 if (list[1].contains('/')) { 0485 return list[1].split('/')[0]; 0486 } else { 0487 return list[1]; 0488 } 0489 } 0490 } 0491 return ""; 0492 } 0493 } 0494 0495 Rule *UfwClient::createRuleFromConnection(const QString &protocol, const QString &localAddress, const QString &foreignAddres, const QString &status) 0496 { 0497 // FIXME use a regexp for that and support ipv6! 0498 auto _localAddress = localAddress; 0499 _localAddress.replace("*", ""); 0500 _localAddress.replace("0.0.0.0", ""); 0501 0502 auto _foreignAddres = foreignAddres; 0503 _foreignAddres.replace("*", ""); 0504 _foreignAddres.replace("0.0.0.0", ""); 0505 0506 auto localAddressData = _localAddress.split(":"); 0507 auto foreignAddresData = _foreignAddres.split(":"); 0508 0509 if (!isNumber(localAddressData[1])) { 0510 localAddressData[1] = portStrToInt(localAddressData[1]); 0511 } 0512 if (!isNumber(foreignAddresData[1])) { 0513 foreignAddresData[1] = portStrToInt(foreignAddresData[1]); 0514 } 0515 0516 auto rule = new Rule(); 0517 rule->setIncoming(status == QStringLiteral("LISTEN")); 0518 rule->setPolicy("deny"); 0519 0520 qCDebug(UFWClientDebug) << "-----------------------"; 0521 qCDebug(UFWClientDebug) << foreignAddresData << localAddressData; 0522 qCDebug(UFWClientDebug) << "------------------------"; 0523 0524 // Prepare rule draft 0525 if (status == QStringLiteral("LISTEN")) { 0526 rule->setSourceAddress(foreignAddresData[0]); 0527 rule->setSourcePort(foreignAddresData[1]); 0528 rule->setDestinationAddress(localAddressData[0]); 0529 rule->setDestinationPort(localAddressData[1]); 0530 } else { 0531 rule->setSourceAddress(localAddressData[0]); 0532 rule->setSourcePort(localAddressData[1]); 0533 rule->setDestinationAddress(foreignAddresData[0]); 0534 rule->setDestinationPort(foreignAddresData[1]); 0535 } 0536 0537 rule->setProtocol(knownProtocols().indexOf(protocol.toUpper())); 0538 return rule; 0539 } 0540 0541 Rule *UfwClient::createRuleFromLog(const QString &protocol, 0542 const QString &sourceAddress, 0543 const QString &sourcePort, 0544 const QString &destinationAddress, 0545 const QString &destinationPort, 0546 const QString &inn) 0547 { 0548 // Transform to the ufw notation 0549 auto rule = new Rule(); 0550 0551 auto _sourceAddress = sourceAddress; 0552 _sourceAddress.replace("*", ""); 0553 _sourceAddress.replace("0.0.0.0", ""); 0554 0555 auto _destinationAddress = destinationAddress; 0556 _destinationAddress.replace("*", ""); 0557 _destinationAddress.replace("0.0.0.0", ""); 0558 0559 // Heuristic to determine whether we should be ipv6 0560 // TODO error when one is ipv6 and the other isn't? 0561 if (sourceAddress.contains(QLatin1Char(':')) && destinationAddress.contains(QLatin1Char(':'))) { 0562 rule->setIpv6(true); 0563 } 0564 0565 // Prepare rule draft 0566 rule->setIncoming(inn.size()); 0567 rule->setPolicy("deny"); 0568 rule->setSourceAddress(_sourceAddress); 0569 rule->setSourcePort(sourcePort); 0570 0571 rule->setDestinationAddress(_destinationAddress); 0572 rule->setDestinationPort(destinationPort); 0573 0574 rule->setProtocol(knownProtocols().indexOf(protocol.toUpper())); 0575 return rule; 0576 } 0577 0578 IFirewallClientBackend *UfwClient::createMethod(FirewallClient *parent) 0579 { 0580 IFirewallClientBackend *instance = new UfwClient(parent, {} /*args*/); 0581 return instance; 0582 } 0583 0584 void UfwClient::refreshProfiles() 0585 { 0586 static const char *constProfileDir = "/etc/ufw/applications.d/"; 0587 0588 const QStringList files(QDir(constProfileDir).entryList(QDir::NoDotAndDotDot)); 0589 0590 QList<Entry> profiles; 0591 for (const auto &file : files) { 0592 KConfig cfg(constProfileDir + file, KConfig::SimpleConfig); 0593 0594 for (const auto &group : cfg.groupList()) { 0595 const QString ports(cfg.group(group).readEntry("ports", QString())); 0596 0597 if (!ports.isEmpty() && !profiles.contains(group)) { 0598 profiles.append(Entry(group, ports)); 0599 } 0600 } 0601 } 0602 0603 setProfiles(profiles); 0604 } 0605 0606 QStringList UfwClient::knownProtocols() 0607 { 0608 return {i18n("Any"), "TCP", "UDP"}; 0609 } 0610 0611 QString UfwClient::toXml(Rule *r) const 0612 { 0613 QString xmlString; 0614 0615 QXmlStreamWriter xml(&xmlString); 0616 0617 xml.writeStartElement(QStringLiteral("rule")); 0618 0619 if (r->position() != 0) { 0620 qCDebug(UFWClientDebug) << "Getting the position" << r->position(); 0621 xml.writeAttribute(QStringLiteral("position"), QString::number(r->position())); 0622 } 0623 0624 xml.writeAttribute(QStringLiteral("action"), Types::toString(r->action())); 0625 xml.writeAttribute(QStringLiteral("direction"), r->incoming() ? QStringLiteral("in") : QStringLiteral("out")); 0626 // ufw doesn't know when to treat a service (simplified rule) as in or out by itself, so we have to hint it the correct 0627 // direction. 0628 bool hintUFW = r->incoming() && r->simplified(); 0629 0630 if (!r->destinationApplication().isEmpty()) { 0631 xml.writeAttribute(QStringLiteral("dapp"), r->destinationApplication()); 0632 } else if (!r->destinationPort().isEmpty()) { 0633 xml.writeAttribute(QStringLiteral("dport"), r->destinationPort()); 0634 } 0635 if (!r->sourceApplication().isEmpty()) { 0636 hintUFW ? xml.writeAttribute(QStringLiteral("dapp"), r->sourceApplication()) : xml.writeAttribute(QStringLiteral("sapp"), r->sourceApplication()); 0637 } else if (!r->sourcePort().isEmpty()) { 0638 hintUFW ? xml.writeAttribute(QStringLiteral("dport"), r->sourcePort()) : xml.writeAttribute(QStringLiteral("sport"), r->sourcePort()); 0639 } 0640 0641 if (!FirewallClient::isTcpAndUdp(r->protocol())) { 0642 xml.writeAttribute(QStringLiteral("protocol"), FirewallClient::knownProtocols().at(r->protocol())); 0643 } 0644 0645 if (!r->destinationAddress().isEmpty()) { 0646 xml.writeAttribute(QStringLiteral("dst"), r->destinationAddress()); 0647 } 0648 if (!r->sourceAddress().isEmpty()) { 0649 xml.writeAttribute(QStringLiteral("src"), r->sourceAddress()); 0650 } 0651 0652 if (!r->interfaceIn().isEmpty()) { 0653 xml.writeAttribute(QStringLiteral("interface_in"), r->interfaceIn()); 0654 } 0655 if (!r->interfaceOut().isEmpty()) { 0656 xml.writeAttribute(QStringLiteral("interface_out"), r->interfaceOut()); 0657 } 0658 0659 xml.writeAttribute(QStringLiteral("logtype"), Types::toString(r->logging())); 0660 0661 xml.writeAttribute(QStringLiteral("v6"), r->ipv6() ? QStringLiteral("True") : QStringLiteral("False")); 0662 0663 xml.writeEndElement(); 0664 0665 return xmlString; 0666 } 0667 0668 bool UfwClient::isCurrentlyLoaded() const 0669 { 0670 QProcess process; 0671 const QString pname = "systemctl"; 0672 const QStringList args = {"status", "ufw"}; 0673 0674 process.start(pname, args); 0675 process.waitForFinished(); 0676 0677 // systemctl returns 0 for status if the app is loaded, and 3 otherwise. 0678 qCDebug(UFWClientDebug) << "Ufw is loaded?" << (process.exitCode() == EXIT_SUCCESS); 0679 0680 return process.exitCode() == EXIT_SUCCESS; 0681 } 0682 0683 bool UfwClient::supportsRuleUpdate() const 0684 { 0685 return false; 0686 } 0687 0688 QString UfwClient::version() const 0689 { 0690 QProcess process; 0691 QStringList args = {"--version"}; 0692 0693 process.start(executablePath(), args); 0694 process.waitForFinished(); 0695 0696 if (process.exitCode() != EXIT_SUCCESS) { 0697 return i18n("Error fetching information from the firewall."); 0698 } 0699 0700 return process.readAllStandardOutput(); 0701 } 0702 0703 QStringList UfwClient::knownApplications() 0704 { 0705 return m_knownApplications; 0706 } 0707 0708 void UfwClient::queryKnownApplications() 0709 { 0710 auto action = KAuth::Action("org.kde.ufw.queryapps"); 0711 action.setHelperId("org.kde.ufw"); 0712 0713 KAuth::ExecuteJob *job = action.execute(); 0714 0715 connect(job, &KAuth::ExecuteJob::result, this, [this, job] { 0716 if (job->error() == KJob::NoError) { 0717 m_knownApplications = job->data()["response"].toStringList(); 0718 qCDebug(UFWClientDebug) << "Setting the known applications to" << m_knownApplications; 0719 } else { 0720 qCDebug(UFWClientDebug) << "Job error: " << job->error(); 0721 } 0722 }); 0723 job->start(); 0724 } 0725 0726 #include "ufwclient.moc"