File indexing completed on 2024-04-28 16:52:17
0001 // SPDX-License-Identifier: GPL-2.0-or-later 0002 // SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org> 0003 0004 #include <QCoreApplication> 0005 #include <QDebug> 0006 0007 #include "firewallclient.h" 0008 #include "rule.h" 0009 #include "rulelistmodel.h" 0010 0011 #include <KJob> 0012 0013 // Start 0014 void testDisableClient(FirewallClient *client); 0015 void testDisableClientResult(FirewallClient *client); 0016 0017 // Second Method. 0018 void testEnableClient(FirewallClient *client); 0019 void testEnableClientResult(FirewallClient *client); 0020 0021 // Third Method 0022 void testCurrentRulesEmpty(FirewallClient *client); 0023 void testCurrentRulesEmptyResult(FirewallClient *client); 0024 0025 // Fourth Method 0026 void testAddRules(FirewallClient *client); 0027 void testAddRulesResult(FirewallClient *client); 0028 0029 // Fifth Method 0030 void testRemoveRules(FirewallClient *client); 0031 void testRemoveRulesResult(FirewallClient *client); 0032 0033 void printHelp() 0034 { 0035 qDebug() << "Usage Information:"; 0036 qDebug() << "$" 0037 << "firewall_test backend_name"; 0038 qDebug() << "Where backend name is ether ufw or firewalld."; 0039 qDebug() << "Make sure you have no firewall running"; 0040 qDebug() << "And make sure you have no profile configured on either firewall."; 0041 } 0042 0043 int main(int argc, char *argv[]) 0044 { 0045 QCoreApplication app(argc, argv); 0046 0047 qDebug() << "argc" << argc; 0048 if (argc != 2) { 0049 qDebug() << "Invalid number of arguments, please use:"; 0050 printHelp(); 0051 exit(1); 0052 } 0053 0054 QString firewallBackendName = argv[1]; 0055 if (firewallBackendName != "ufw" && firewallBackendName != "firewalld") { 0056 printHelp(); 0057 exit(1); 0058 } 0059 qDebug() << firewallBackendName << "test starting"; 0060 0061 auto *client = new FirewallClient(); 0062 client->setBackend({firewallBackendName}); 0063 Q_ASSERT(client->backend() == firewallBackendName); 0064 0065 // Start trying to disable, the order of calls is the same as the definition order. 0066 // Please don't change as this is really annoying to test. 0067 testDisableClient(client); 0068 0069 return app.exec(); 0070 } 0071 0072 void testDisableClient(FirewallClient *client) 0073 { 0074 qDebug() << "Test Disable the Firewall"; 0075 KJob *enableJob = client->setEnabled(false); 0076 0077 // Ignore, we are already disabled. 0078 if (enableJob == nullptr) { 0079 testEnableClient(client); 0080 return; 0081 } 0082 0083 // Nice hack: One time connections. 0084 QMetaObject::Connection *const connection = new QMetaObject::Connection; 0085 *connection = QObject::connect(client, &FirewallClient::enabledChanged, [client, connection] { 0086 QObject::disconnect(*connection); 0087 delete connection; 0088 testDisableClientResult(client); 0089 }); 0090 0091 enableJob->start(); 0092 } 0093 0094 void testDisableClientResult(FirewallClient *client) 0095 { 0096 Q_ASSERT(client->enabled() == false); 0097 testEnableClient(client); 0098 } 0099 0100 void testEnableClient(FirewallClient *client) 0101 { 0102 qDebug() << "Test Enable the Firewall"; 0103 0104 // From here on, We will jump thru the usage via connects. 0105 KJob *enableJob = client->setEnabled(true); 0106 0107 // Ignore, we are already enabled. 0108 if (enableJob == nullptr) { 0109 testCurrentRulesEmpty(client); 0110 return; 0111 } 0112 0113 // Nice hack: One time connections. 0114 QMetaObject::Connection *const connection = new QMetaObject::Connection; 0115 *connection = QObject::connect(client, &FirewallClient::enabledChanged, [client, connection] { 0116 qDebug() << "Enabled Changed"; 0117 QObject::disconnect(*connection); 0118 delete connection; 0119 testEnableClientResult(client); 0120 }); 0121 0122 enableJob->start(); 0123 } 0124 0125 void testEnableClientResult(FirewallClient *client) 0126 { 0127 qDebug() << "Client Enabled" << client->enabled(); 0128 testCurrentRulesEmpty(client); 0129 } 0130 0131 void testCurrentRulesEmpty(FirewallClient *client) 0132 { 0133 if (client->rulesModel()->rowCount() != 0) { 0134 qDebug() << "We need a clean firewall rules to do the testing, please backup your rules and try again."; 0135 exit(1); 0136 } 0137 0138 Q_ASSERT(client->rulesModel()->rowCount() == 0); 0139 testCurrentRulesEmptyResult(client); 0140 } 0141 0142 void testCurrentRulesEmptyResult(FirewallClient *client) 0143 { 0144 testAddRules(client); 0145 } 0146 0147 // Fourth Method 0148 void testAddRules(FirewallClient *client) 0149 { 0150 QString interface = client->knownInterfaces()[0]; 0151 0152 // Expected output for firewalld 0153 // firewalld.client: ("-j", "DROP", "-p", "tcp", "-d", "127.0.0.1", "--dport=21", "-s", "127.0.0.1", "--sport=12") 0154 // firewalld.job: firewalld "addRule" ( 0155 // QVariant(QString, "ipv4"), 0156 // QVariant(QString, "filter"), 0157 // QVariant(QString, "INPUT"), 0158 // QVariant(int, 0), 0159 // QVariant(QStringList, ("-j", "DROP", "-p", "tcp", "-d", "127.0.0.1", "--dport=21", "-s", "127.0.0.1", "--sport=12")) 0160 // ) 0161 0162 // Current Output for firewalld: 0163 // firewalld.client: ("-j", "DROP", "-p", "tcp", "-d", "127.0.0.1", "--dport=21", "-s", "127.0.0.1", "--sport=12") 0164 // 0165 // firewalld.job: firewalld "addRule" ( 0166 // QVariant(QString, "ipv4"), 0167 // QVariant(QString, "filter"), 0168 // QVariant(QString, "INPUT"), 0169 // QVariant(int, 0), 0170 // QVariant(QStringList, ("-j", "DROP", "-p", "tcp", "-d", "127.0.0.1", "--dport=21", "-s", "127.0.0.1", "--sport=12"))) 0171 0172 // Expected output for ufw 0173 // Debug message from helper: Cmd args passed to ufw: ( 0174 // "--add=<rule action=\"deny\" 0175 // direction=\"in\" 0176 // dport=\"21\" 0177 // sport=\"12\" 0178 // protocol=\"TCP\" 0179 // dst=\"127.0.0.1\" 0180 // src=\"127.0.0.1\" 0181 // logtype=\"\" 0182 // v6=\"False\"/>") 0183 0184 // Current Output for ufw 0185 // Debug message from helper: Cmd args passed to ufw: ( 0186 // "--add=<rule action=\"deny\" 0187 // direction=\"in\" 0188 // dport=\"21\" 0189 // sport=\"12\" 0190 // dst=\"127.0.0.1\" 0191 // src=\"127.0.0.1\" 0192 // logtype=\"\" 0193 // v6=\"False\"/>") 0194 0195 auto *rule = new Rule(Types::Policy::POLICY_DENY, // Policy 0196 true, // Incomming? 0197 Types::Logging::LOGGING_OFF, // Logging 0198 0, // Protocol Id on knownProtocols 0199 "127.0.0.1", // Source Host 0200 "12", // Source Port 0201 "127.0.0.1", // Destination Port 0202 "21", // Destination Port 0203 QString(), // Interface In 0204 QString(), // Interface Out 0205 QString(), // Source App // Only used in UFW - Remove? 0206 QString(), // Destination App // Only used in UFW - Remove? 0207 0, // Index (TODO: Remove This) 0208 false); // IPV6? 0209 0210 // This call should perhaps have an client::addRuleFinished(), but currently we need 0211 // to rely on the model refresh 0212 client->addRule(rule); 0213 0214 // Nice hack: One time connections. 0215 QMetaObject::Connection *const connection = new QMetaObject::Connection; 0216 *connection = QObject::connect(client->rulesModel(), &RuleListModel::modelReset, [client, connection] { 0217 qDebug() << "Add rule finished."; 0218 QObject::disconnect(*connection); 0219 delete connection; 0220 testAddRulesResult(client); 0221 }); 0222 0223 // TODO: 0224 // This job is started inside of the addRule, currently we have an inconsistency on what's 0225 // started by default and what's started by Qml. We need to fix this. 0226 // job->start(); 0227 } 0228 0229 void testAddRulesResult(FirewallClient *client) 0230 { 0231 Q_ASSERT(client->rulesModel()->rowCount() == 1); 0232 client->removeRule(0); 0233 0234 // Nice hack: One time connections. 0235 QMetaObject::Connection *const connection = new QMetaObject::Connection; 0236 *connection = QObject::connect(client->rulesModel(), &RuleListModel::modelReset, [client, connection] { 0237 qDebug() << "Remove Rule Finished."; 0238 QObject::disconnect(*connection); 0239 delete connection; 0240 testRemoveRulesResult(client); 0241 }); 0242 } 0243 0244 void testRemoveRulesResult(FirewallClient *client) 0245 { 0246 Q_ASSERT(client->rulesModel()->rowCount() == 0); 0247 qDebug() << "Test Finished"; 0248 }