summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui_form.hh6
-rw-r--r--src/gui_gtk.cc125
-rw-r--r--src/gui_qt.cc124
-rw-r--r--src/monitor-gui.cc111
4 files changed, 336 insertions, 30 deletions
diff --git a/src/gui_form.hh b/src/gui_form.hh
index c91ebce..1c65ee6 100644
--- a/src/gui_form.hh
+++ b/src/gui_form.hh
@@ -40,6 +40,11 @@ public:
std::string const& description, uint8_t flags,
std::vector<Filter> const& filter) = 0;
+ virtual void add_bool(std::string const& id, std::string const& label,
+ bool value, std::string const& description) = 0;
+
+ virtual void enable(std::string const& id, bool enable) = 0;
+
virtual void add_listener(Listener* listener) = 0;
virtual void remove_listener(Listener* listener) = 0;
@@ -50,6 +55,7 @@ public:
virtual std::string get_string(std::string const& id) const = 0;
virtual uint64_t get_number(std::string const& id) const = 0;
virtual std::string get_file(std::string const& id) const = 0;
+ virtual bool get_bool(std::string const& id) const = 0;
protected:
GuiForm() {}
diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc
index c7031a8..301b92f 100644
--- a/src/gui_gtk.cc
+++ b/src/gui_gtk.cc
@@ -529,7 +529,6 @@ public:
g_settings_sync();
}
- using Config::get;
std::string const& get(std::string const& key,
std::string const& fallback) override {
auto variant = g_settings_get_user_value(settings_.get(), key.c_str());
@@ -546,6 +545,13 @@ public:
g_variant_unref(variant);
return memory_[key].c_str();
}
+ bool get(std::string const& key, bool fallback) override {
+ auto variant = g_settings_get_user_value(settings_.get(), key.c_str());
+ if (!variant) return fallback;
+ bool ret = g_variant_get_boolean(variant);
+ g_variant_unref(variant);
+ return ret;
+ }
bool is_set(std::string const& key) override {
auto variant = g_settings_get_user_value(settings_.get(), key.c_str());
if (!variant) return false;
@@ -555,6 +561,9 @@ public:
void set(std::string const& key, std::string const& value) override {
g_settings_set_string(settings_.get(), key.c_str(), value.c_str());
}
+ void set(std::string const& key, bool value) override {
+ g_settings_set_boolean(settings_.get(), key.c_str(), value);
+ }
void remove(std::string const& key) override {
g_settings_reset(settings_.get(), key.c_str());
}
@@ -931,6 +940,37 @@ public:
flags, filter));
}
+ void add_bool(std::string const& id, std::string const& label,
+ bool value, std::string const& description) override {
+ values_.emplace_back(new BoolValue(this, id, label, description, value));
+ }
+
+ void enable(std::string const& id, bool enable) override {
+ for (auto const& value : values_) {
+ if (value->id_ == id) {
+ value->enable_ = enable;
+ if (value->entry_) {
+ gtk_widget_set_sensitive(value->entry_, enable);
+ }
+ switch (value->type_) {
+ case STRING:
+ case NUMBER:
+ case BOOLEAN:
+ break;
+ case FILE: {
+ auto v = static_cast<FileValue*>(value.get());
+ if (v->button_) {
+ gtk_widget_set_sensitive(GTK_WIDGET(v->button_), enable);
+ } else if (v->chooser_) {
+ gtk_widget_set_sensitive(GTK_WIDGET(v->chooser_), enable);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
std::string get_string(std::string const& id) const override {
for (auto const& value : values_) {
if (value->id_ == id && value->type_ == STRING) {
@@ -980,6 +1020,19 @@ public:
return "";
}
+ bool get_bool(std::string const& id) const override {
+ for (auto const& value : values_) {
+ if (value->id_ == id && value->type_ == BOOLEAN) {
+ if (value->entry_) {
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(value->entry_));
+ }
+ return static_cast<BoolValue*>(value.get())->value_;
+ }
+ }
+ assert(false);
+ return false;
+ }
+
void set_error(std::string const& error) override {
if (!error_) {
assert(false);
@@ -1019,9 +1072,17 @@ public:
gtk_grid_attach(GTK_GRID(grid), label, 0, row++, 3, 1);
}
for (auto& value : values_) {
- auto label = gtk_label_new(value->label_.c_str());
- value->entry_ = gtk_entry_new();
- gtk_entry_set_activates_default(GTK_ENTRY(value->entry_), true);
+ GtkWidget* label;
+ if (value->type_ != BOOLEAN) {
+ label = gtk_label_new(value->label_.c_str());
+ } else {
+ label = nullptr;
+ }
+ if (value->type_ != FILE
+ || (static_cast<FileValue*>(value.get())->flags_ & FILE_SAVE)) {
+ value->entry_ = gtk_entry_new();
+ gtk_entry_set_activates_default(GTK_ENTRY(value->entry_), true);
+ }
GtkWidget* extra = nullptr;
switch (value->type_) {
case STRING:
@@ -1044,11 +1105,10 @@ public:
case FILE: {
auto v = static_cast<FileValue*>(value.get());
if ((v->flags_ & FILE_SAVE) == 0) {
- gtk_widget_destroy(value->entry_);
- auto button = gtk_file_chooser_button_new(
+ assert(!value->entry_);
+ value->entry_ = gtk_file_chooser_button_new(
v->label_.c_str(), GTK_FILE_CHOOSER_ACTION_OPEN);
- v->chooser_ = GTK_FILE_CHOOSER(button);
- value->entry_ = button;
+ v->chooser_ = GTK_FILE_CHOOSER(value->entry_);
g_signal_connect(G_OBJECT(v->chooser_), "file-set",
G_CALLBACK(file_set), value.get());
} else {
@@ -1061,6 +1121,7 @@ public:
g_signal_connect(G_OBJECT(extra), "clicked",
G_CALLBACK(show_filechooser), v);
v->chooser_ = GTK_FILE_CHOOSER(chooser);
+ v->button_ = extra;
gtk_editable_set_editable(GTK_EDITABLE(value->entry_), false);
gtk_file_chooser_set_do_overwrite_confirmation(v->chooser_, true);
g_signal_connect(G_OBJECT(value->entry_), "changed",
@@ -1079,13 +1140,28 @@ public:
}
break;
}
+ case BOOLEAN:
+ assert(!label);
+ gtk_widget_destroy(value->entry_);
+ value->entry_ = gtk_check_button_new_with_label(value->label_.c_str());
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(value->entry_),
+ static_cast<BoolValue*>(value.get())->value_);
+ g_signal_connect(G_OBJECT(value->entry_), "toggled",
+ G_CALLBACK(toggled), value.get());
+ break;
+ }
+ gint col = 0;
+ if (label) {
+ gtk_grid_attach(GTK_GRID(grid), label, col++, row, 1, 1);
}
- gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
+ gtk_widget_set_sensitive(value->entry_, value->enable_);
if (extra) {
- gtk_grid_attach(GTK_GRID(grid), value->entry_, 1, row, 1, 1);
- gtk_grid_attach(GTK_GRID(grid), extra, 2, row, 1, 1);
+ gtk_widget_set_sensitive(extra, value->enable_);
+ gtk_grid_attach(GTK_GRID(grid), value->entry_, col, row, 2 - col, 1);
+ gtk_grid_attach(GTK_GRID(grid), extra, ++col, row, 1, 1);
} else {
- gtk_grid_attach(GTK_GRID(grid), value->entry_, 1, row, 2, 1);
+ gtk_grid_attach(GTK_GRID(grid), value->entry_, col, row, 3 - col, 1);
}
row++;
if (!value->description_.empty()) {
@@ -1138,6 +1214,10 @@ public:
v->chooser_ = nullptr;
break;
}
+ case BOOLEAN:
+ static_cast<BoolValue*>(value.get())->value_ =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(value->entry_));
+ break;
}
value->entry_ = nullptr;
}
@@ -1153,6 +1233,7 @@ protected:
STRING,
NUMBER,
FILE,
+ BOOLEAN,
};
struct Value {
@@ -1161,12 +1242,13 @@ protected:
std::string const id_;
std::string const label_;
std::string const description_;
+ bool enable_;
GtkWidget* entry_;
Value(GtkGuiForm* me, Type type, std::string const& id,
std::string const& label, std::string const& description)
: me_(me), type_(type), id_(id), label_(label), description_(description),
- entry_(nullptr) {
+ enable_(true), entry_(nullptr) {
}
};
@@ -1193,12 +1275,22 @@ protected:
uint8_t flags_;
std::vector<Filter> filter_;
GtkFileChooser* chooser_;
+ GtkWidget* button_;
FileValue(GtkGuiForm* me, std::string const& id, std::string const& label,
std::string const& description, std::string const& value,
uint8_t flags, std::vector<Filter> const& filter)
: Value(me, FILE, id, label, description), value_(value),
- flags_(flags), filter_(filter) {
+ flags_(flags), filter_(filter), chooser_(nullptr), button_(nullptr) {
+ }
+ };
+
+ struct BoolValue : public Value {
+ bool value_;
+
+ BoolValue(GtkGuiForm* me, std::string const& id, std::string const& label,
+ std::string const& description, bool value)
+ : Value(me, BOOLEAN, id, label, description), value_(value) {
}
};
@@ -1264,6 +1356,11 @@ protected:
value->me_->notify_changed(value->id_);
}
+ static void toggled(GtkToggleButton* UNUSED(button), gpointer user_data) {
+ auto value = reinterpret_cast<Value*>(user_data);
+ value->me_->notify_changed(value->id_);
+ }
+
virtual gint add_extra_widgets(GtkGrid* UNUSED(grid), gint row,
gint UNUSED(colspan)) {
return row;
diff --git a/src/gui_qt.cc b/src/gui_qt.cc
index cad859e..77e292e 100644
--- a/src/gui_qt.cc
+++ b/src/gui_qt.cc
@@ -4,6 +4,7 @@
#include <QAction>
#include <QApplication>
+#include <QCheckBox>
#include <QClipboard>
#include <QCloseEvent>
#include <QDialogButtonBox>
@@ -863,6 +864,37 @@ public:
flags, filter));
}
+ void add_bool(std::string const& id, std::string const& label,
+ bool value, std::string const& description) override {
+ values_.emplace_back(new BoolValue(id, label, description, value));
+ }
+
+ void enable(std::string const& id, bool enable) override {
+ for (auto const& value : values_) {
+ if (value->id_ == id) {
+ value->enable_ = enable;
+ if (value->edit_) {
+ value->edit_->setEnabled(enable);
+ }
+ switch (value->type_) {
+ case STRING:
+ case NUMBER:
+ break;
+ case FILE: {
+ auto v = static_cast<FileValue*>(value.get());
+ if (v->button_) v->button_->setEnabled(enable);
+ break;
+ }
+ case BOOLEAN: {
+ auto v = static_cast<BoolValue*>(value.get());
+ if (v->check_) v->check_->setEnabled(enable);
+ break;
+ }
+ }
+ }
+ }
+ }
+
std::string get_string(std::string const& id) const override {
for (auto const& value : values_) {
if (value->id_ == id && value->type_ == STRING) {
@@ -905,6 +937,20 @@ public:
return "";
}
+ bool get_bool(std::string const& id) const override {
+ for (auto const& value : values_) {
+ if (value->id_ == id && value->type_ == BOOLEAN) {
+ auto v = static_cast<BoolValue*>(value.get());
+ if (v->check_) {
+ return v->check_->isChecked();
+ }
+ return v->value_;
+ }
+ }
+ assert(false);
+ return "";
+ }
+
void set_error(std::string const& error) override {
if (!error_) {
assert(false);
@@ -927,6 +973,7 @@ protected:
STRING,
NUMBER,
FILE,
+ BOOLEAN,
};
struct Value {
@@ -934,12 +981,13 @@ protected:
std::string const id_;
std::string const label_;
std::string const description_;
+ bool enable_;
QLineEdit* edit_;
Value(Type type, std::string const& id, std::string const& label,
std::string const& description)
: type_(type), id_(id), label_(label), description_(description),
- edit_(nullptr) {
+ enable_(true), edit_(nullptr) {
}
};
@@ -967,13 +1015,25 @@ protected:
std::string value_;
uint8_t flags_;
std::vector<Filter> filter_;
+ QPushButton* button_;
FileValue(std::string const& id, std::string const& label,
std::string const& description,
std::string const& value,
uint8_t flags, std::vector<Filter> const& filter)
: Value(FILE, id, label, description), value_(value),
- flags_(flags), filter_(filter) {
+ flags_(flags), filter_(filter), button_(nullptr) {
+ }
+ };
+
+ struct BoolValue : public Value {
+ bool value_;
+ QCheckBox* check_;
+
+ BoolValue(std::string const& id, std::string const& label,
+ std::string const& description, bool value)
+ : Value(BOOLEAN, id, label, description), value_(value),
+ check_(nullptr) {
}
};
@@ -1000,9 +1060,16 @@ protected:
layout->addWidget(text, row++, 0, 1, 3);
}
for (auto& value : values_) {
- auto label = new QLabel(QString::fromStdString(value->label_));
- auto edit = new QLineEdit();
- value->edit_ = edit;
+ QWidget* label;
+ QLineEdit* edit;
+ if (value->type_ != BOOLEAN) {
+ label = new QLabel(QString::fromStdString(value->label_));
+ edit = new QLineEdit();
+ value->edit_ = edit;
+ } else {
+ label = nullptr;
+ edit = nullptr;
+ }
switch (value->type_) {
case STRING:
edit->setText(QString::fromStdString(
@@ -1020,24 +1087,45 @@ protected:
edit->setText(QString::fromStdString(
static_cast<FileValue*>(value.get())->value_));
break;
+ case BOOLEAN: {
+ auto v = static_cast<BoolValue*>(value.get());
+ assert(!label);
+ v->check_ = new QCheckBox(QString::fromStdString(v->label_));
+ label = v->check_;
+ v->check_->setChecked(v->value_);
+ v->check_->setEnabled(v->enable_);
+ v->check_->connect(v->check_, &QCheckBox::stateChanged,
+ [=](int UNUSED(state)) {
+ notify_changed(v->id_);
+ });
+ break;
+ }
}
auto vp = value.get();
- edit->connect(edit, &QLineEdit::textChanged,
- [=](QString const& UNUSED(text)) {
- notify_changed(vp->id_);
- });
- layout->addWidget(label, row, 0);
+ if (edit) {
+ edit->connect(edit, &QLineEdit::textChanged,
+ [=](QString const& UNUSED(text)) {
+ notify_changed(vp->id_);
+ });
+ edit->setEnabled(value->enable_);
+ }
if (value->type_ == FILE) {
- auto button = new QPushButton("...");
+ auto v = static_cast<FileValue*>(vp);
+ v->button_ = new QPushButton("...");
+ v->button_->setEnabled(v->enable_);
edit->setReadOnly(true);
- dialog_->connect(button, &QPushButton::clicked,
+ dialog_->connect(v->button_, &QPushButton::clicked,
[=](bool UNUSED(checked)) {
- showFileDialog(static_cast<FileValue*>(vp));
+ showFileDialog(v);
});
+ layout->addWidget(label, row, 0);
layout->addWidget(edit, row, 1);
- layout->addWidget(button, row, 2);
- } else {
+ layout->addWidget(v->button_, row, 2);
+ } else if (edit) {
+ layout->addWidget(label, row, 0);
layout->addWidget(edit, row, 1, 1, 2);
+ } else {
+ layout->addWidget(label, row, 0, 1, 3);
}
++row;
if (!value->description_.empty()) {
@@ -1074,6 +1162,12 @@ protected:
static_cast<FileValue*>(value.get())->value_ =
value->edit_->text().toStdString();
break;
+ case BOOLEAN: {
+ auto v = static_cast<BoolValue*>(value.get());
+ v->value_ = v->check_->isChecked();
+ v->check_ = nullptr;
+ break;
+ }
}
value->edit_ = nullptr;
}
diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc
index d293935..037f2ff 100644
--- a/src/monitor-gui.cc
+++ b/src/monitor-gui.cc
@@ -67,6 +67,25 @@ bool parse_address(std::string const& addr, std::string* host, uint16_t* port) {
return true;
}
+#if HAVE_SSL
+const char* CERT_BUNDLE[] = {
+ "/etc/ssl/certs/ca-certificates.crt",
+ NULL
+};
+std::string default_cert_bundle() {
+ static std::string cache;
+ if (cache.empty()) {
+ for (auto bundle = CERT_BUNDLE; *bundle; ++bundle) {
+ if (access(*bundle, R_OK) == 0) {
+ cache.assign(*bundle);
+ break;
+ }
+ }
+ }
+ return cache;
+}
+#endif // HAVE_SSL
+
class PackageList : public GuiListModel {
public:
struct Package {
@@ -374,7 +393,13 @@ private:
class SetupFormListener : public GuiFormApply::Listener {
public:
- void changed(GuiForm* UNUSED(form), std::string const& UNUSED(id)) override {
+ void changed(GuiForm* form, std::string const& id) override {
+ if (id.compare("mitm") == 0) {
+ auto enable = form->get_bool(id);
+ form->enable("ssl-certs", enable);
+ form->enable("ssl-ca", enable);
+ form->enable("unsecure", enable);
+ }
}
bool about_to_close(GuiForm* form) override {
@@ -383,6 +408,19 @@ private:
form->set_error("Empty proxy port");
return false;
}
+ auto const& mitm = form->get_bool("mitm");
+ if (mitm) {
+ auto const& certs = form->get_file("ssl-certs");
+ if (certs.empty()) {
+ form->set_error("No SSL certificates file set");
+ return false;
+ }
+ auto const& ca = form->get_file("ssl-ca");
+ if (ca.empty()) {
+ form->set_error("No SSL CA file set");
+ return false;
+ }
+ }
return true;
}
};
@@ -411,8 +449,49 @@ private:
}
config_->set("bind", bind);
config_->set("port", port);
+#if HAVE_SSL
+ auto const& mitm = form->get_bool("mitm");
+ config_->set("mitm", mitm);
proxy_config_->set("proxy_bind", bind);
proxy_config_->set("proxy_port", port);
+ if (mitm) {
+ auto const& certs = form->get_file("ssl-certs");
+ auto const& ca = form->get_file("ssl-ca");
+ auto const& unsecure = form->get_bool("unsecure");
+ if (certs.empty()) {
+ form->set_error("No SSL certificates file set");
+ form->applied(false);
+ return;
+ }
+ if (access(certs.c_str(), R_OK)) {
+ form->set_error("SSL certificates file not readable");
+ form->applied(false);
+ return;
+ }
+ if (ca.empty()) {
+ form->set_error("No SSL CA file set");
+ form->applied(false);
+ return;
+ }
+ if (access(ca.c_str(), R_OK)) {
+ form->set_error("SSL CA file not readable");
+ form->applied(false);
+ return;
+ }
+ config_->set("ssl-certs", certs);
+ config_->set("ssl-ca", ca);
+ config_->set("unsecure", unsecure);
+ proxy_config_->set("ssl_cert_bundle", certs);
+ proxy_config_->set("ssl_ca_cert", ca);
+ proxy_config_->set("ssl_ca_key", ca);
+ proxy_config_->set("ssl_unsecure", unsecure);
+ } else {
+ proxy_config_->remove("ssl_cert_bundle");
+ proxy_config_->remove("ssl_ca_cert");
+ proxy_config_->remove("ssl_ca_key");
+ proxy_config_->remove("ssl_unsecure");
+ }
+#endif // HAVE_SSL
proxy_config_->set("__one_single_monitor", "true");
io::auto_fd proxy_fd(
Proxy::setup_accept_socket(proxy_config_, proxy_logger_));
@@ -622,6 +701,36 @@ public:
connect_->add_string("port", "Port",
main_->config()->get("port", "8080"),
"Port to listen for proxy connections on.");
+#if HAVE_SSL
+ bool mitm = main_->config()->get("mitm", false);
+ connect_->add_bool("mitm", "Intercept SSL traffic",
+ mitm,
+ "If enabled SSL connections will be intercepted"
+ " by the proxy to log unencrypted traffic.");
+ std::vector<GuiFormApply::Filter> filter;
+ filter.emplace_back();
+ filter.back().name = "PEM";
+ filter.back().masks.emplace_back("*.pem");
+ connect_->add_file("ssl-ca", "Certificate Authority",
+ main_->config()->get("ssl-ca",
+ main_->config()->get("genca-output", "")),
+ "CA and key to sign all fake server certificates with",
+ GuiForm::FILE_OPEN, filter);
+ connect_->enable("ssl-ca", mitm);
+ filter.back().name = "CRT";
+ filter.back().masks.emplace_back("*.crt");
+ connect_->add_file("ssl-certs", "Certificate bundle",
+ main_->config()->get("ssl-certs",
+ default_cert_bundle()),
+ "Certificate bundle to verify remote SSL connections",
+ GuiForm::FILE_OPEN, filter);
+ connect_->enable("ssl-certs", mitm);
+ connect_->add_bool("unsecure", "Allow unsecure remote connections",
+ main_->config()->get("unsecure", false),
+ "Allow deprecated protocols such as SSLv3 and "
+ " self-signed or missmatched certificates");
+ connect_->enable("unsecure", mitm);
+#endif // HAVE_SSL
connect_->add_listener(lst.get());
if (connect_->show(main_.get())) {
monitor_->attach();