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

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 "rule.h"
0010 #include "appprofiles.h"
0011 #include "firewallclient.h"
0012 #include <KLocalizedString>
0013 #include <QByteArray>
0014 #include <QMap>
0015 #include <QTextStream>
0016 #include <arpa/inet.h>
0017 #include <netdb.h>
0018 
0019 #include "firewallclient.h"
0020 
0021 // Keep in sync with kcm_ufw_helper.py
0022 static const char *ANY_ADDR = "0.0.0.0/0";
0023 static const char *ANY_ADDR_V6 = "::/0";
0024 static const char *ANY_PORT = "any";
0025 
0026 // Shorten an IPv6 address (if applicable)
0027 static QString shortenAddress(const QString &addr)
0028 {
0029     if (!addr.contains(":")) {
0030         return addr;
0031     }
0032     QByteArray bytes(addr.toLatin1());
0033     unsigned char num[16];
0034 
0035     if (inet_pton(AF_INET6, bytes.constData(), num) > 0) {
0036         char conv[41];
0037         if (NULL != inet_ntop(AF_INET6, num, conv, 41)) {
0038             return QLatin1String(conv);
0039         }
0040     }
0041     return addr;
0042 }
0043 
0044 static QString addIface(const QString &orig, const QString &iface)
0045 {
0046     return iface.isEmpty() ? orig : i18nc("address on interface", "%1 on %2", orig, iface);
0047 }
0048 
0049 static QString serviceName(short port)
0050 {
0051     static QMap<int, QString> serviceMap;
0052 
0053     if (serviceMap.contains(port)) {
0054         return serviceMap[port];
0055     }
0056 
0057     struct servent *ent = getservbyport(htons(port), 0L);
0058 
0059     if (ent && ent->s_name) {
0060         serviceMap[port] = ent->s_name;
0061         return serviceMap[port];
0062     }
0063 
0064     return {};
0065 }
0066 
0067 static QString formatPort(const QString &port, int prot)
0068 {
0069     return port.isEmpty() ? Rule::protocolSuffix(prot, QString()) : port + Rule::protocolSuffix(prot);
0070 }
0071 
0072 static QString modifyAddress(const QString &addr, const QString &port)
0073 {
0074     if (addr.isEmpty() || ANY_ADDR == addr || ANY_ADDR_V6 == addr) {
0075         return port.isEmpty() ? i18n("Anywhere") : QString();
0076     }
0077 
0078     return shortenAddress(addr);
0079 }
0080 
0081 static QString modifyPort(const QString &port, int prot, bool matchPortNoProto = false)
0082 {
0083     if (port.isEmpty()) {
0084         return port;
0085     }
0086     // Does it match a pre-configured application?
0087     Types::PredefinedPort pp = Types::toPredefinedPort(port + Rule::protocolSuffix(prot));
0088 
0089     // When matching glog lines, the protocol is *always* specified - but don't always want this when
0090     // matching names...
0091     if (matchPortNoProto && Types::PP_COUNT == pp) {
0092         pp = Types::toPredefinedPort(port);
0093     }
0094 
0095     if (Types::PP_COUNT != pp) {
0096         return i18nc("service/application name (port numbers)", "%1 (%2)", Types::toString(pp, true), port + Rule::protocolSuffix(prot));
0097     }
0098 
0099     // Is it a service known to /etc/services ???
0100     bool ok(false);
0101     QString service;
0102     short portNum = port.toShort(&ok);
0103 
0104     if (ok) {
0105         service = serviceName(portNum);
0106     }
0107 
0108     if (!service.isEmpty()) {
0109         return i18nc("service/application name (port numbers)", "%1 (%2)", service, formatPort(port, prot));
0110     }
0111 
0112     // Just return port/servicename and protocol
0113     return formatPort(port, prot);
0114 }
0115 
0116 static QString modifyApp(const QString &app, const QString &port, int prot)
0117 {
0118     if (app.isEmpty()) {
0119         return port;
0120     }
0121 
0122     // TODO: Send the profile, not the app name.
0123     Entry profile({});
0124     //     Entry profile(get(app));
0125 
0126     return i18nc("service/application name (port numbers)", "%1 (%2)", app, profile.name.isEmpty() ? formatPort(port, prot) : profile.ports);
0127 }
0128 
0129 int Rule::servicePort(const QString &name)
0130 {
0131     static QMap<QString, int> serviceMap;
0132 
0133     if (serviceMap.contains(name)) {
0134         return serviceMap[name];
0135     }
0136 
0137     QByteArray l1 = name.toLatin1();
0138     struct servent *ent = getservbyname(l1.constData(), 0L);
0139 
0140     if (ent && ent->s_name) {
0141         serviceMap[name] = ntohs(ent->s_port);
0142         return serviceMap[name];
0143     }
0144 
0145     return 0;
0146 }
0147 
0148 QString Rule::protocolSuffix(int prot, const QString &sep)
0149 {
0150     if (FirewallClient::isTcpAndUdp(prot)) {
0151         return {};
0152     }
0153 
0154     if (prot == -1) {
0155         prot = 0;
0156         qWarning() << "Invalid protocol -1, defaulting to" << FirewallClient::knownProtocols().at(prot);
0157     }
0158 
0159     return sep + FirewallClient::knownProtocols().at(prot);
0160 }
0161 
0162 QString Rule::modify(const QString &address, const QString &port, const QString &application, const QString &iface, int protocol, bool matchPortNoProto)
0163 {
0164     if ((port == ANY_PORT || port.isEmpty()) && (address.isEmpty() || ANY_ADDR == address || ANY_ADDR_V6 == address))
0165         return addIface(i18n("Anywhere"), iface);
0166 
0167     bool isAnyAddress = address.isEmpty() || ANY_ADDR == address || ANY_ADDR_V6 == address, isAnyPort = port.isEmpty() || ANY_PORT == port;
0168     QString bPort = application.isEmpty() ? modifyPort(port, protocol, matchPortNoProto) : modifyApp(application, port, protocol),
0169             bAddr = modifyAddress(address, port);
0170 
0171     return addIface(isAnyAddress ? isAnyPort ? i18n("Anywhere") : bPort : bAddr.isEmpty() ? bPort : bAddr + QChar(' ') + bPort, iface);
0172 }
0173 
0174 Rule::Rule()
0175     : m_position(0)
0176     , m_action(Types::POLICY_REJECT)
0177     , m_incoming(true)
0178     , m_ipv6(false)
0179     , m_simplified(true)
0180     , m_protocol(0)
0181     , m_logtype(Types::LOGGING_OFF)
0182     , m_interface(0)
0183 {
0184 }
0185 
0186 QString Rule::fromStr() const
0187 {
0188     /* qDebug() << "Before Crashing" << m_protocol; */
0189     return modify(m_sourceAddress, m_sourcePort, m_sourceApplication, m_interfaceIn, m_protocol);
0190 }
0191 
0192 QString Rule::toStr() const
0193 {
0194     return modify(m_destAddress, m_destPort, m_destApplication, m_interfaceOut, m_protocol);
0195 }
0196 
0197 QString Rule::actionStr() const
0198 {
0199     return m_incoming ? i18nc("firewallAction incoming", "%1 incoming", Types::toString(m_action, true))
0200                       : i18nc("firewallAction outgoing", "%1 outgoing", Types::toString(m_action, true));
0201 }
0202 
0203 QString Rule::ipV6Str() const
0204 {
0205     return m_ipv6 ? i18n("Yes") : QString();
0206 }
0207 
0208 QString Rule::loggingStr() const
0209 {
0210     return Types::toString(m_logtype, false);
0211 }
0212 
0213 void Rule::setPolicy(const QString &policy)
0214 {
0215     auto policy_t = Types::toPolicy(policy);
0216 
0217     if (policy_t == action()) {
0218         return;
0219     }
0220 
0221     m_action = policy_t;
0222     Q_EMIT policyChanged(policy);
0223 }
0224 
0225 void Rule::setIncoming(bool incoming)
0226 {
0227     if (m_incoming == incoming) {
0228         return;
0229     }
0230 
0231     m_incoming = incoming;
0232     Q_EMIT incomingChanged(incoming);
0233 }
0234 
0235 void Rule::setSourceAddress(const QString &sourceAddress)
0236 {
0237     if (m_sourceAddress == sourceAddress) {
0238         return;
0239     }
0240     m_sourceAddress = sourceAddress;
0241     Q_EMIT sourceAddressChanged(sourceAddress);
0242 }
0243 
0244 void Rule::setSourcePort(const QString &sourcePort)
0245 {
0246     if (m_sourcePort == sourcePort) {
0247         return;
0248     }
0249 
0250     m_sourcePort = sourcePort;
0251     Q_EMIT sourcePortChanged(sourcePort);
0252 }
0253 
0254 void Rule::setDestinationAddress(const QString &destinationAddress)
0255 {
0256     if (m_destAddress == destinationAddress) {
0257         return;
0258     }
0259     m_destAddress = destinationAddress;
0260     Q_EMIT destinationAddressChanged(destinationAddress);
0261 }
0262 
0263 void Rule::setDestinationPort(const QString &destinationPort)
0264 {
0265     if (m_destPort == destinationPort) {
0266         return;
0267     }
0268 
0269     m_destPort = destinationPort;
0270     Q_EMIT destinationPortChanged(destinationPort);
0271 }
0272 
0273 void Rule::setIpv6(bool ipv6)
0274 {
0275     if (m_ipv6 == ipv6) {
0276         return;
0277     }
0278 
0279     m_ipv6 = ipv6;
0280     Q_EMIT ipv6Changed(ipv6);
0281 }
0282 
0283 void Rule::setProtocol(int v)
0284 {
0285     Q_ASSERT(v != -1);
0286     m_protocol = v;
0287 }
0288 
0289 void Rule::setInterface(int interface)
0290 {
0291     if (m_interface == interface) {
0292         return;
0293     }
0294 
0295     m_interfaceStr = interface != 0 ? FirewallClient::knownInterfaces().at(interface) : QString();
0296     m_interface = interface;
0297 
0298     Q_EMIT interfaceChanged(interface);
0299 }
0300 
0301 void Rule::setLogging(const QString &logging)
0302 {
0303     auto logging_t = Types::toLogging(logging);
0304     if (m_logtype == logging_t) {
0305         return;
0306     }
0307 
0308     m_logtype = logging_t;
0309     Q_EMIT loggingChanged(logging);
0310 }
0311 
0312 void Rule::setPosition(int position)
0313 {
0314     if (m_position == position) {
0315         return;
0316     }
0317 
0318     m_position = position;
0319     Q_EMIT positionChanged(position);
0320 }
0321 
0322 QString Rule::policy() const
0323 {
0324     return Types::toString(action());
0325 }
0326 
0327 QString Rule::destinationAddress() const
0328 {
0329     return m_destAddress;
0330 }
0331 
0332 QString Rule::interfaceStr() const
0333 {
0334     return m_interfaceStr;
0335 }
0336 
0337 int Rule::interface() const
0338 {
0339     return m_interface;
0340 }
0341 
0342 QString Rule::destinationPort() const
0343 {
0344     return m_destPort;
0345 }
0346 
0347 int Rule::position() const
0348 {
0349     return m_position;
0350 }
0351 Types::Policy Rule::action() const
0352 {
0353     return m_action;
0354 }
0355 bool Rule::incoming() const
0356 {
0357     return m_incoming;
0358 }
0359 bool Rule::ipv6() const
0360 {
0361     return m_ipv6;
0362 }
0363 
0364 int Rule::protocol() const
0365 {
0366     return m_protocol;
0367 }
0368 
0369 QString Rule::sourceApplication() const
0370 {
0371     return m_sourceApplication;
0372 }
0373 
0374 QString Rule::sourceAddress() const
0375 {
0376     return m_sourceAddress;
0377 }
0378 QString Rule::sourcePort() const
0379 {
0380     return m_sourcePort;
0381 }
0382 QString Rule::interfaceIn() const
0383 {
0384     return m_interfaceIn;
0385 }
0386 QString Rule::interfaceOut() const
0387 {
0388     return m_interfaceOut;
0389 }
0390 
0391 Types::Logging Rule::logging() const
0392 {
0393     return m_logtype;
0394 }
0395 
0396 void Rule::setV6(const bool v)
0397 {
0398     m_ipv6 = v;
0399 }
0400 
0401 QString Rule::destinationApplication() const
0402 {
0403     return m_destApplication;
0404 }
0405 
0406 void Rule::setSimplified(bool value)
0407 {
0408     if (m_simplified == value) {
0409         return;
0410     }
0411     m_simplified = value;
0412     Q_EMIT simplifiedChanged(value);
0413 }
0414 
0415 void Rule::setSourceApplication(const QString &app)
0416 {
0417     if (m_sourceApplication == app) {
0418         return;
0419     }
0420     m_sourceApplication = app;
0421     Q_EMIT sourceApplicationChanged(app);
0422 }
0423 bool Rule::simplified() const
0424 {
0425     return m_simplified;
0426 }