1 /* firewall_rules_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include <config.h>
11 
12 #include "firewall_rules_dialog.h"
13 #include <ui_firewall_rules_dialog.h>
14 
15 #include "epan/packet_info.h"
16 #include "epan/to_str.h"
17 
18 #include "ui/all_files_wildcard.h"
19 #include "ui/firewall_rules.h"
20 #include "ui/help_url.h"
21 
22 #include "wsutil/file_util.h"
23 #include "wsutil/utf8_entities.h"
24 
25 #include "wireshark_application.h"
26 #include "ui/qt/widgets/wireshark_file_dialog.h"
27 
28 #include <QClipboard>
29 #include <QMessageBox>
30 #include <QPushButton>
31 #include <QTextCursor>
32 
33 // XXX As described in bug 2482, some of the generated rules don't
34 // make sense. We could generate rules for every conceivable use case,
35 // but that would add complexity. We could also add controls to let
36 // users fine-tune rule output, but that would also add complexity.
37 
FirewallRulesDialog(QWidget & parent,CaptureFile & cf)38 FirewallRulesDialog::FirewallRulesDialog(QWidget &parent, CaptureFile &cf) :
39     WiresharkDialog(parent, cf),
40     ui(new Ui::FirewallRulesDialog),
41     prod_(0)
42 {
43     ui->setupUi(this);
44 
45     setWindowSubtitle(tr("Firewall ACL Rules"));
46 
47     ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Copy"));
48 
49     file_name_ = cf.fileName(); // XXX Add extension?
50     packet_num_ = cf.packetInfo()->num;
51 
52     packet_info *pinfo = cf.packetInfo();
53     copy_address(&dl_src_, &(pinfo->dl_src));
54     copy_address(&dl_dst_, &(pinfo->dl_dst));
55     copy_address(&net_src_, &(pinfo->net_src));
56     copy_address(&net_dst_, &(pinfo->net_dst));
57     ptype_ = pinfo->ptype;
58     src_port_ = pinfo->srcport;
59     dst_port_ = pinfo->destport;
60     int nf_item = 0;
61 
62     for (size_t prod = 0; prod < firewall_product_count(); prod++) {
63         QString prod_name = firewall_product_name(prod);
64 
65         // Default to Netfilter since it's likely the most popular.
66         if (prod_name.contains("Netfilter")) nf_item = ui->productComboBox->count();
67         ui->productComboBox->addItem(prod_name);
68     }
69     ui->productComboBox->setCurrentIndex(nf_item);
70 
71     ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
72 }
73 
~FirewallRulesDialog()74 FirewallRulesDialog::~FirewallRulesDialog()
75 {
76     delete ui;
77 }
78 
updateWidgets()79 void FirewallRulesDialog::updateWidgets()
80 {
81     WiresharkDialog::updateWidgets();
82 
83     QString comment_pfx = firewall_product_comment_prefix(prod_);
84     QString rule_hint = firewall_product_rule_hint(prod_);
85     QString rule_line;
86 
87     rule_line = QString("%1 %2 rules for %3, packet %4.")
88             .arg(comment_pfx)
89             .arg(firewall_product_name(prod_))
90             .arg(file_name_)
91             .arg(packet_num_);
92 
93     if (!rule_hint.isEmpty()) rule_line += " " + rule_hint;
94 
95     ui->textBrowser->clear();
96     ui->textBrowser->append(rule_line);
97 
98     syntax_func v4_func = firewall_product_ipv4_func(prod_);
99     syntax_func port_func = firewall_product_port_func(prod_);
100     syntax_func v4_port_func = firewall_product_ipv4_port_func(prod_);
101     syntax_func mac_func = firewall_product_mac_func(prod_);
102 
103     if (v4_func && net_src_.type == AT_IPv4) {
104         addRule(tr("IPv4 source address."), v4_func, &net_src_, src_port_);
105         addRule(tr("IPv4 destination address."), v4_func, &net_dst_, dst_port_);
106     }
107 
108     if (port_func && (ptype_ == PT_TCP || ptype_ == PT_UDP)) {
109         addRule(tr("Source port."), port_func, &net_src_, src_port_);
110         addRule(tr("Destination port."), port_func, &net_dst_, dst_port_);
111     }
112 
113     if (v4_port_func && net_src_.type == AT_IPv4 &&
114             (ptype_ == PT_TCP || ptype_ == PT_UDP)) {
115         addRule(tr("IPv4 source address and port."), v4_port_func, &net_src_, src_port_);
116         addRule(tr("IPv4 destination address and port."), v4_port_func, &net_dst_, dst_port_);
117     }
118 
119     if (mac_func && dl_src_.type == AT_ETHER) {
120         addRule(tr("MAC source address."), mac_func, &dl_src_, src_port_);
121         addRule(tr("MAC destination address."), mac_func, &dl_dst_, dst_port_);
122     }
123 
124     ui->textBrowser->moveCursor(QTextCursor::Start);
125 
126     ui->inboundCheckBox->setEnabled(firewall_product_does_inbound(prod_));
127 }
128 
129 #define ADDR_BUF_LEN 200
addRule(QString description,syntax_func rule_func,address * addr,guint32 port)130 void FirewallRulesDialog::addRule(QString description, syntax_func rule_func, address *addr, guint32 port)
131 {
132     if (!rule_func) return;
133 
134     char addr_buf[ADDR_BUF_LEN];
135     QString comment_pfx = firewall_product_comment_prefix(prod_);
136     GString *rule_str = g_string_new("");
137     gboolean inbound = ui->inboundCheckBox->isChecked();
138     gboolean deny = ui->denyCheckBox->isChecked();
139 
140     address_to_str_buf(addr, addr_buf, ADDR_BUF_LEN);
141     rule_func(rule_str, addr_buf, port, ptype_, inbound, deny);
142     ui->textBrowser->append(QString());
143 
144     QString comment_line = comment_pfx + " " + description;
145     ui->textBrowser->append(comment_line);
146     ui->textBrowser->append(rule_str->str);
147 
148     g_string_free(rule_str, TRUE);
149 }
150 
151 
on_productComboBox_currentIndexChanged(int new_idx)152 void FirewallRulesDialog::on_productComboBox_currentIndexChanged(int new_idx)
153 {
154     prod_ = (size_t) new_idx;
155     updateWidgets();
156 }
157 
on_inboundCheckBox_toggled(bool)158 void FirewallRulesDialog::on_inboundCheckBox_toggled(bool)
159 {
160     updateWidgets();
161 }
162 
on_denyCheckBox_toggled(bool)163 void FirewallRulesDialog::on_denyCheckBox_toggled(bool)
164 {
165     updateWidgets();
166 }
167 
on_buttonBox_clicked(QAbstractButton * button)168 void FirewallRulesDialog::on_buttonBox_clicked(QAbstractButton *button)
169 {
170     if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
171         QString save_title = QString("Save %1 rules as" UTF8_HORIZONTAL_ELLIPSIS)
172                 .arg(firewall_product_name(prod_));
173         QByteArray file_name = WiresharkFileDialog::getSaveFileName(this,
174                                                  save_title,
175                                                  wsApp->lastOpenDir().canonicalPath(),
176                                                  tr("Text file (*.txt);;All Files (" ALL_FILES_WILDCARD ")")
177                                                  ).toUtf8();
178         if (file_name.length() > 0) {
179             QFile save_file(file_name);
180             QByteArray rule_text = ui->textBrowser->toPlainText().toUtf8();
181 
182             save_file.open(QIODevice::WriteOnly);
183             save_file.write(rule_text);
184             save_file.close();
185 
186             if (save_file.error() != QFile::NoError) {
187                 QMessageBox::warning(this, tr("Warning"), tr("Unable to save %1").arg(save_file.fileName()));
188                 return;
189             }
190 
191             /* Save the directory name for future file dialogs. */
192             wsApp->setLastOpenDirFromFilename(file_name);
193         }
194     } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
195         if (ui->textBrowser->textCursor().hasSelection()) {
196             ui->textBrowser->copy();
197         } else {
198             wsApp->clipboard()->setText(ui->textBrowser->toPlainText());
199         }
200     }
201 }
202 
on_buttonBox_helpRequested()203 void FirewallRulesDialog::on_buttonBox_helpRequested()
204 {
205     wsApp->helpTopicAction(HELP_FIREWALL_DIALOG);
206 }
207