File indexing completed on 2024-04-28 16:52:16

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 "profile.h"
0010 #include "firewallclient.h"
0011 
0012 #include <QBuffer>
0013 #include <QFile>
0014 #include <QIODevice>
0015 #include <QStringList>
0016 #include <QTextStream>
0017 #include <QXmlStreamReader>
0018 
0019 Profile::Profile(QByteArray &xml, bool isSys)
0020     : m_fields(0)
0021     , m_enabled(false)
0022     , m_ipv6Enabled(false)
0023     , m_logLevel(Types::LOG_OFF)
0024     , m_defaultIncomingPolicy(Types::POLICY_ALLOW)
0025     , m_defaultOutgoingPolicy(Types::POLICY_ALLOW)
0026     , m_isSystem(isSys)
0027 {
0028     QBuffer buffer;
0029     buffer.setBuffer(&xml);
0030     load(&buffer);
0031 }
0032 
0033 Profile::Profile(QFile &file, bool isSys)
0034     : m_fields(0)
0035     , m_enabled(false)
0036     , m_ipv6Enabled(false)
0037     , m_logLevel(Types::LOG_OFF)
0038     , m_defaultIncomingPolicy(Types::POLICY_ALLOW)
0039     , m_defaultOutgoingPolicy(Types::POLICY_ALLOW)
0040     , m_fileName(file.fileName())
0041     , m_isSystem(isSys)
0042 {
0043     load(&file);
0044 }
0045 
0046 Profile::Profile(const QVector<Rule *> &rules, const QVariantMap &args, bool isSys)
0047     : m_fields(0)
0048     , m_enabled(false)
0049     , m_ipv6Enabled(false)
0050     , m_logLevel(Types::LOG_OFF)
0051     , m_defaultIncomingPolicy(Types::POLICY_ALLOW)
0052     , m_defaultOutgoingPolicy(Types::POLICY_ALLOW)
0053     , m_isSystem(isSys)
0054 {
0055     setRules(rules);
0056     setArgs(args);
0057 }
0058 
0059 void Profile::setRules(const QVector<Rule *> &newrules)
0060 {
0061     m_rules = newrules;
0062 }
0063 
0064 void Profile::setArgs(const QVariantMap &args)
0065 {
0066     const QString new_defaultIncomingPolicy = args.value(QStringLiteral("defaultIncomingPolicy")).toString();
0067     const QString new_defaultOutgoingPolicy = args.value(QStringLiteral("defaultIncomingPolicy")).toString();
0068     const QString new_loglevel = args.value(QStringLiteral("logLevel")).toString();
0069     const QStringList new_modules = args.value(QStringLiteral("modules")).toStringList();
0070 
0071     m_defaultIncomingPolicy = new_defaultIncomingPolicy.isEmpty() ? Types::POLICY_ALLOW : Types::toPolicy(new_defaultIncomingPolicy);
0072     m_defaultOutgoingPolicy = new_defaultOutgoingPolicy.isEmpty() ? Types::POLICY_ALLOW : Types::toPolicy(new_defaultOutgoingPolicy);
0073     m_logLevel = new_loglevel.isEmpty() ? Types::LOG_OFF : Types::toLogLevel(new_loglevel);
0074     m_enabled = args.value(QStringLiteral("status")).toBool();
0075     m_ipv6Enabled = args.value("ipv6Enabled").toBool();
0076 
0077     if (!new_modules.isEmpty()) {
0078         m_modules = QSet<QString>(std::begin(new_modules), std::end(new_modules));
0079     }
0080 }
0081 
0082 void Profile::setDefaultIncomingPolicy(const QString &policy)
0083 {
0084     m_defaultIncomingPolicy = Types::toPolicy(policy);
0085 }
0086 
0087 void Profile::setDefaultOutgoingPolicy(const QString &policy)
0088 {
0089     m_defaultOutgoingPolicy = Types::toPolicy(policy);
0090 }
0091 
0092 QString Profile::toXml() const
0093 {
0094     QString str;
0095     QTextStream stream(&str);
0096 
0097     stream << "<ufw full=\"true\" >" << Qt::endl << ' ' << defaultsXml() << Qt::endl << " <rules>" << Qt::endl;
0098 
0099     /* for (const auto &rule : m_rules) { */
0100     /* stream << "  " << rule->toXml(); */
0101     /* } */
0102 
0103     stream << " </rules>" << Qt::endl << ' ' << modulesXml() << Qt::endl << "</ufw>" << Qt::endl;
0104 
0105     return str;
0106 }
0107 
0108 QString Profile::defaultsXml() const
0109 {
0110     static const auto defaultString = QStringLiteral(R"(<defaults ipv6="%1" loglevel="%2" incoming="%3" outgoing="%4"/>)");
0111 
0112     return defaultString.arg(m_ipv6Enabled ? "yes" : "no")
0113         .arg(Types::toString(m_logLevel))
0114         .arg(Types::toString(m_defaultIncomingPolicy))
0115         .arg(Types::toString(m_defaultOutgoingPolicy));
0116 }
0117 
0118 QString Profile::modulesXml() const
0119 {
0120     return QString("<modules enabled=\"") + QStringList(m_modules.values()).join(" ") + QString("\" />");
0121 }
0122 
0123 void Profile::load(QIODevice *device)
0124 {
0125     device->open(QIODevice::ReadOnly);
0126     QXmlStreamReader reader(device);
0127 
0128     bool isFullProfile = false;
0129     while (!reader.atEnd()) {
0130         auto token = reader.readNext();
0131         if (token == QXmlStreamReader::Invalid) {
0132             break;
0133         }
0134         if (token != QXmlStreamReader::StartElement) {
0135             continue;
0136         }
0137         if (reader.name() == QStringLiteral("ufw")) {
0138             isFullProfile = reader.attributes().value(QStringLiteral("full")) == QStringLiteral("true");
0139             continue;
0140         } else if (reader.name() == QStringLiteral("status")) {
0141             m_enabled = reader.attributes().value(QStringLiteral("enabled")) == QStringLiteral("true");
0142             m_fields |= FIELD_STATUS;
0143         } else if (reader.name() == QStringLiteral("rules")) {
0144             m_fields |= FIELD_RULES;
0145             continue;
0146         } else if (reader.name() == QLatin1String("rule")) {
0147             static QString ANY_ADDR = QStringLiteral("0.0.0.0/0");
0148             static QString ANY_ADDR_V6 = QStringLiteral("::/0");
0149             static QString ANY_PORT = QStringLiteral("any");
0150 
0151             const auto attr = reader.attributes();
0152 
0153             // Handle Enums.
0154             const auto action = Types::toPolicy(attr.value(QLatin1String("action")).toString());
0155 
0156             // TODO: Check that this is ok.
0157             const auto protocol = FirewallClient::indexOfProtocol(attr.value(QLatin1String("protocol")).toString());
0158             /* qDebug() << "Reading protocol from profile:" << protocol << attr.value(QLatin1String("protocol")); */
0159 
0160             const auto logType = Types::toLogging(attr.value(QLatin1String("logtype")).toString());
0161 
0162             // Handle values that have weird defaults.
0163             const auto anyAddrs = QList<QString>({ANY_ADDR, ANY_ADDR_V6});
0164             const auto dst = attr.value("dst").toString();
0165             const auto src = attr.value("src").toString();
0166             const auto sport = attr.value("sport").toString();
0167             const auto dport = attr.value("dport").toString();
0168 
0169             const QString destAddress = anyAddrs.contains(dst) ? QString() : dst;
0170             const QString sourceAddress = anyAddrs.contains(src) ? QString() : src;
0171             const QString sourcePort = sport == ANY_PORT ? QString() : sport;
0172             const QString destPort = dport == ANY_PORT ? QString() : dport;
0173 
0174             m_rules.append(new Rule(action,
0175                                     attr.value("direction") == QStringLiteral("in"),
0176                                     logType,
0177                                     protocol,
0178                                     sourceAddress,
0179                                     sourcePort,
0180                                     destAddress,
0181                                     destPort,
0182                                     attr.value("interface_in").toString(),
0183                                     attr.value("interface_out").toString(),
0184                                     attr.value("sapp").toString(),
0185                                     attr.value("dapp").toString(),
0186                                     attr.value("position").toInt(),
0187                                     attr.value("v6") == QStringLiteral("True")));
0188         } else if (reader.name() == QLatin1String("defaults")) {
0189             m_fields |= FIELD_DEFAULTS;
0190 
0191             const auto attr = reader.attributes();
0192 
0193             m_logLevel = Types::toLogLevel(attr.value(QLatin1String("loglevel")).toString());
0194 
0195             m_defaultIncomingPolicy = Types::toPolicy(attr.value(QLatin1String("incoming")).toString());
0196             m_defaultOutgoingPolicy = Types::toPolicy(attr.value(QLatin1String("outgoing")).toString());
0197 
0198             m_ipv6Enabled = (attr.value("ipv6") == QLatin1String("yes"));
0199         } else if (reader.name() == QLatin1String("modules")) {
0200             m_fields |= FIELD_MODULES;
0201             const auto attr = reader.attributes();
0202             const auto moduleList = attr.value("enabled").toString().split(" ", Qt::SkipEmptyParts);
0203             m_modules = QSet<QString>(std::begin(moduleList), std::end(moduleList));
0204         }
0205     }
0206     if (isFullProfile && (!(m_fields & FIELD_RULES) || !(m_fields & FIELD_DEFAULTS) || !(m_fields & FIELD_MODULES))) {
0207         m_fields = 0;
0208     }
0209 }
0210 
0211 void Profile::setEnabled(bool value)
0212 {
0213     m_enabled = value;
0214 }