diff options
-rw-r--r-- | client-qt/udm-client-qt/client_session.cpp | 106 | ||||
-rw-r--r-- | client-qt/udm-client-qt/client_session.h | 10 | ||||
-rw-r--r-- | client-qt/udm-client-qt/connect_widget.cpp | 129 | ||||
-rw-r--r-- | client-qt/udm-client-qt/connect_widget.h | 14 | ||||
-rw-r--r-- | client-qt/udm-client-qt/udm-client-qt.pro | 2 | ||||
-rw-r--r-- | client-qt/udm-client-qt/udm_main.cpp | 29 | ||||
-rw-r--r-- | client-qt/udm-client-qt/udm_main.h | 5 | ||||
-rw-r--r-- | protocol/udm.proto | 2 |
8 files changed, 257 insertions, 40 deletions
diff --git a/client-qt/udm-client-qt/client_session.cpp b/client-qt/udm-client-qt/client_session.cpp index c034aa1..d01d0f5 100644 --- a/client-qt/udm-client-qt/client_session.cpp +++ b/client-qt/udm-client-qt/client_session.cpp @@ -22,6 +22,7 @@ #include "client_session.h" #include <boost/bind.hpp> #include <boost/log/trivial.hpp> +#include <openssl/sha.h> std::shared_ptr<char*> pack_data(const std::string &buf, int *size_) { @@ -48,28 +49,86 @@ std::shared_ptr<char*> pack_msg(client_msg *msg, int *size_) } -client_session::client_session(QObject *parent, boost::asio::io_service *io_service) : QObject(parent), io_service_(*io_service), socket_(*io_service) +client_session::client_session(QObject *parent, boost::asio::io_service *io_service) : QObject(parent), io_service_(*io_service) { - + socket_ssl_ = nullptr; + ssl_enabled = false; } tcp::socket& client_session::socket() { - return socket_; + return *socket_; } void client_session::client_connect(QString host, QString password, int port) { //TODO: settings for connection timeout this->pasword = password; + if(!password.isEmpty()) + { + unsigned char hash[64]; + SHA512((unsigned char*)password.toUtf8().data(), password.length(), hash); + password_sha512 = (char*)hash; + } + + + char port_buf[6]; + snprintf(port_buf, 5, "%d", port); boost::asio::ip::tcp::resolver r(io_service_); - boost::asio::ip::tcp::endpoint ep; - ep.port(port); - ep.address(boost::asio::ip::address::from_string(host.toStdString())); - socket_.async_connect(ep, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error)); + boost::asio::ip::tcp::resolver::query query(host.toUtf8().data(), port_buf); + boost::asio::ip::tcp::resolver::iterator it = r.resolve(query); + + socket_ = new tcp::socket(io_service_); + + if(!ssl_enabled) + boost::asio::async_connect(*socket_, it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error)); + else + boost::asio::async_connect(socket_ssl_->lowest_layer(), it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error)); +// socket_.async_connect(ep, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error)); + io_service_.run(); } +void client_session::client_connect_ssl(QString host, QString password, int port, QString ssl_ca, QString ssl_crt, QString ssl_key) +{ + ssl_enabled = true; + this->pasword = password; + boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12); + ctx.load_verify_file(ssl_ca.toUtf8().data()); + ctx.use_certificate_file(ssl_crt.toUtf8().data(), boost::asio::ssl::context::pem); + ctx.use_private_key_file(ssl_key.toUtf8().data(), boost::asio::ssl::context::pem); + socket_ssl_ = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(io_service_, ctx); + socket_ssl_->set_verify_mode(boost::asio::ssl::verify_peer); + socket_ssl_->set_verify_callback(boost::bind(&client_session::verify_certificate, this, _1, _2 )); + + char port_buf[6]; + snprintf(port_buf, 5, "%d", port); + boost::asio::ip::tcp::resolver r(io_service_); + boost::asio::ip::tcp::resolver::query query(host.toUtf8().data(), port_buf); + boost::asio::ip::tcp::resolver::iterator it = r.resolve(query); + + boost::asio::async_connect(socket_ssl_->lowest_layer(), it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error)); +} + +bool client_session::verify_certificate(bool preverified, boost::asio::ssl::verify_context& /*&ctx*/) +{ + // The verify callback can be used to check whether the certificate that is + // being presented is valid for the peer. For example, RFC 2818 describes + // the steps involved in doing this for HTTPS. Consult the OpenSSL + // documentation for more details. Note that the callback is called once + // for each certificate in the certificate chain, starting from the root + // certificate authority. + + // In this example we will simply print the certificate's subject name. +/* char subject_name[256]; + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + std::cout << "Verifying " << subject_name << "\n"; */ + + return preverified; +} + + void client_session::handle_connect(const boost::system::error_code &e) { BOOST_LOG_TRIVIAL(debug)<<__FILE__<<":"<<__LINE__<<"\t"<<typeid(this).name()<<"::"<<__func__; @@ -85,7 +144,10 @@ void client_session::handle_connect(const boost::system::error_code &e) emit client_connected(true, QString::fromStdString(e.message())); //listen for messages from server recv_data_ = new char[4]; - boost::asio::async_read(socket_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + if(!ssl_enabled) + boost::asio::async_read(*socket_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + else + boost::asio::async_read(*socket_ssl_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } } @@ -99,7 +161,10 @@ void client_session::handle_read(const boost::system::error_code& error, size_t delete [] recv_data_; char *buf = new char[size]; boost::system::error_code ec; - socket_.read_some(boost::asio::buffer(buf, size), ec); + if(!ssl_enabled) + socket_->read_some(boost::asio::buffer(buf, size), ec); + else + socket_ssl_->read_some(boost::asio::buffer(buf, size), ec); if(ec) { @@ -124,7 +189,10 @@ void client_session::handle_read(const boost::system::error_code& error, size_t //check if someone connected to signal and delete message if noone //this will be limited to only one client connection because client will need to delete message manually recv_data_ = new char[4]; - boost::asio::async_read(socket_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + if(!ssl_enabled) + boost::asio::async_read(*socket_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + else + boost::asio::async_read(*socket_ssl_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } // if(!parsed) else @@ -144,7 +212,10 @@ void client_session::send_message(client_msg &msg) { int size = 0; std::shared_ptr<char*> ptr = pack_msg(&msg, &size); - boost::asio::async_write(socket_, boost::asio::buffer(*ptr, size), boost::bind(&client_session::handle_write, this, boost::asio::placeholders::error)); + if(!ssl_enabled) + boost::asio::async_write(*socket_, boost::asio::buffer(*ptr, size), boost::bind(&client_session::handle_write, this, boost::asio::placeholders::error)); + else + boost::asio::async_write(*socket_ssl_, boost::asio::buffer(*ptr, size), boost::bind(&client_session::handle_write, this, boost::asio::placeholders::error)); } void client_session::handle_write(const boost::system::error_code& error) @@ -156,18 +227,17 @@ void client_session::handle_write(const boost::system::error_code& error) } -void client_session::run_io_service() -{ - io_service_.run(); -} - client_session::~client_session() { - //TODO: check this, reimplement + //TODO: correct thread termination, reimplement io_service_.stop(); + if(ssl_enabled) + delete socket_ssl_; + else + delete socket_; boost::asio::io_service *s = &io_service_; delete s; - emit terminate_thread(); + //emit terminate_thread(); } /*boost::asio::io_service &client_session::io_service() diff --git a/client-qt/udm-client-qt/client_session.h b/client-qt/udm-client-qt/client_session.h index df75640..d50c972 100644 --- a/client-qt/udm-client-qt/client_session.h +++ b/client-qt/udm-client-qt/client_session.h @@ -26,6 +26,7 @@ //boost.asio here, yes i know about qtnetwork, but i have more exp with boost asio #include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> #include <QObject> #include "../../protocol/udm.pb.h" @@ -52,17 +53,22 @@ signals: public slots: void client_connect(QString host, QString password, int port); - void run_io_service(); + void client_connect_ssl(QString host, QString password, int port, QString ssl_ca, QString ssl_crt, QString ssl_key); void send_message(client_msg &msg); private: void handle_connect(const boost::system::error_code &error); void handle_read(const boost::system::error_code& error, size_t bytes_transferred); void handle_write(const boost::system::error_code& error); + bool verify_certificate(bool preverified,boost::asio::ssl::verify_context& ctx); + + bool ssl_enabled; char *recv_data_; boost::asio::io_service &io_service_; - tcp::socket socket_; + tcp::socket *socket_; + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> *socket_ssl_; QString pasword; + QByteArray password_sha512; }; #endif // CLIENT_SESSION_H diff --git a/client-qt/udm-client-qt/connect_widget.cpp b/client-qt/udm-client-qt/connect_widget.cpp index a33c49b..2c35ea2 100644 --- a/client-qt/udm-client-qt/connect_widget.cpp +++ b/client-qt/udm-client-qt/connect_widget.cpp @@ -26,15 +26,51 @@ #include <QLabel> #include <QPushButton> #include <QSpacerItem> +#include <QMessageBox> +#include <QCheckBox> +#include <QFileDialog> connect_widget::connect_widget(QWidget *parent) : QWidget(parent) { edit_host = new QLineEdit(this), edit_port = new QLineEdit(this), edit_passwd = new QLineEdit(this); + edit_port->setInputMask("D0000"); + edit_port->setText("1"); + edit_passwd->setEchoMode(QLineEdit::Password); lbl_host = new QLabel(tr("host") + ":", this); lbl_port = new QLabel(tr("port") + ":", this); lbl_passwd = new QLabel(tr("password") + ":", this); + + chk_ssl = new QCheckBox(tr("Enable encryption"), this); + + lbl_ssl_ca = new QLabel(tr("ca cert"), this); + lbl_ssl_ca->setEnabled(false); + edit_ssl_ca = new QLineEdit(this); + edit_ssl_ca->setEnabled(false); + btn_set_ssl_ca = new QPushButton(tr("Browse"), this); + btn_set_ssl_ca->setEnabled(false); + connect(btn_set_ssl_ca, SIGNAL(clicked(bool)), this, SLOT(select_ssl_ca())); + + lbl_ssl_crt = new QLabel(tr("client cert"), this); + lbl_ssl_crt->setEnabled(false); + edit_ssl_crt = new QLineEdit(this); + edit_ssl_crt->setEnabled(false); + btn_set_ssl_crt = new QPushButton(tr("Browse"), this); + btn_set_ssl_crt->setEnabled(false); + connect(btn_set_ssl_crt, SIGNAL(clicked(bool)), this, SLOT(select_ssl_crt())); + + lbl_ssl_key = new QLabel(tr("client key"), this); + lbl_ssl_key->setEnabled(false); + edit_ssl_key = new QLineEdit(this); + edit_ssl_key->setEnabled(false); + btn_set_ssl_key = new QPushButton(tr("Browse"), this); + btn_set_ssl_key->setEnabled(false); + connect(btn_set_ssl_key, SIGNAL(clicked(bool)), this, SLOT(select_ssl_key())); + + btn_connect = new QPushButton(tr("Connect"), this); + btn_connect->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); btn_close = new QPushButton(tr("Close"), this); + btn_close->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); edit_host->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); edit_port->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); edit_passwd->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); @@ -45,24 +81,103 @@ connect_widget::connect_widget(QWidget *parent) : QWidget(parent) elements_grid->addWidget(edit_port, 0, 3); elements_grid->addWidget(lbl_passwd, 1, 0); elements_grid->addWidget(edit_passwd, 1, 1, 1, 2); + elements_grid->addWidget(chk_ssl, 2, 0, 1, 4); + elements_grid->addWidget(lbl_ssl_ca, 3, 0); + elements_grid->addWidget(edit_ssl_ca, 3, 1, 1, 2); + elements_grid->addWidget(btn_set_ssl_ca, 3, 3); + elements_grid->addWidget(lbl_ssl_crt, 4, 0); + elements_grid->addWidget(edit_ssl_crt, 4, 1, 1, 2); + elements_grid->addWidget(btn_set_ssl_crt, 4, 3); + elements_grid->addWidget(lbl_ssl_key, 5, 0); + elements_grid->addWidget(edit_ssl_key, 5, 1, 1, 2); + elements_grid->addWidget(btn_set_ssl_key, 5, 3); + + spacer_h = new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - elements_grid->addItem(spacer_h, 2, 0, 1, 4); - elements_grid->addWidget(btn_connect, 3, 0, 1, 2); - elements_grid->addWidget(btn_close, 3, 2, 1, 2); + elements_grid->addItem(spacer_h, 6, 0, 1, 4); + elements_grid->addWidget(btn_connect, 7, 0); + elements_grid->addWidget(btn_close, 7, 3); + elements_grid->setColumnStretch(1, 1); connect(btn_connect, SIGNAL(clicked(bool)), this, SLOT(connect_clicked())); + connect(btn_close, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(chk_ssl, SIGNAL(stateChanged(int)), this, SLOT(chk_ssl_state_changed(int))); } connect_widget::~connect_widget() { - edit_host->deleteLater(); - edit_port->deleteLater(); - elements_grid->deleteLater(); + //TODO: delete all widgets } void connect_widget::connect_clicked() { //TODO: check entered values + if(edit_host->text().isEmpty()) + { + QMessageBox msg; + msg.setText(tr("host must be entered")); + msg.exec(); + return; + } QString host = edit_host->text(), pass = edit_passwd->text(); - emit client_connect(host, pass, edit_port->text().toInt()); + if(chk_ssl->checkState()) + { + QString ssl_ca = edit_ssl_ca->text(), ssl_crt = edit_ssl_crt->text(), ssl_key = edit_ssl_key->text(); + //TODO: validate file path's (existense, access rights, e.t.c.) + emit client_connect_ssl(host, pass, edit_port->text().toInt(), ssl_ca, ssl_crt, ssl_key); + } + else + emit client_connect(host, pass, edit_port->text().toInt()); + close(); +} + +void connect_widget::chk_ssl_state_changed(int state) +{ + switch(state) + { + case 0: //unchecked + { + lbl_ssl_ca->setEnabled(false); + lbl_ssl_crt->setEnabled(false); + lbl_ssl_key->setEnabled(false); + edit_ssl_ca->setEnabled(false); + edit_ssl_crt->setEnabled(false); + edit_ssl_key->setEnabled(false); + btn_set_ssl_ca->setEnabled(false); + btn_set_ssl_key->setEnabled(false); + btn_set_ssl_crt->setEnabled(false); + } + break; + default: //checked + { + lbl_ssl_ca->setEnabled(true); + lbl_ssl_crt->setEnabled(true); + lbl_ssl_key->setEnabled(true); + edit_ssl_ca->setEnabled(true); + edit_ssl_crt->setEnabled(true); + edit_ssl_key->setEnabled(true); + btn_set_ssl_ca->setEnabled(true); + btn_set_ssl_key->setEnabled(true); + btn_set_ssl_crt->setEnabled(true); + } + break; + } +} +void connect_widget::select_ssl_ca() +{ + QString tmp = QFileDialog::getOpenFileName(this, tr("select ca cert")); + edit_ssl_ca->setText(tmp); +} + +void connect_widget::select_ssl_crt() +{ + QString tmp = QFileDialog::getOpenFileName(this, tr("select client cert")); + edit_ssl_crt->setText(tmp); } + +void connect_widget::select_ssl_key() +{ + QString tmp = QFileDialog::getOpenFileName(this, tr("select client key")); + edit_ssl_key->setText(tmp); +} + diff --git a/client-qt/udm-client-qt/connect_widget.h b/client-qt/udm-client-qt/connect_widget.h index 9b2470b..6d6a57f 100644 --- a/client-qt/udm-client-qt/connect_widget.h +++ b/client-qt/udm-client-qt/connect_widget.h @@ -29,6 +29,7 @@ class QGridLayout; class QLabel; class QPushButton; class QSpacerItem; +class QCheckBox; class connect_widget : public QWidget { @@ -39,16 +40,21 @@ public: signals: void client_connect(QString &host, QString &password, int port); + void client_connect_ssl(QString &host, QString &password, int port, QString &ssl_ca, QString &ssl_cert, QString &ssl_key); public slots: void connect_clicked(); + void chk_ssl_state_changed(int state); + void select_ssl_ca(); + void select_ssl_crt(); + void select_ssl_key(); private: - QLineEdit *edit_host, *edit_port, *edit_passwd; + QLineEdit *edit_host, *edit_port, *edit_passwd, *edit_ssl_ca, *edit_ssl_crt, *edit_ssl_key; QGridLayout *elements_grid; - QLabel *lbl_host, *lbl_port, *lbl_passwd; - QPushButton *btn_connect, *btn_close; + QLabel *lbl_host, *lbl_port, *lbl_passwd, *lbl_ssl_ca, *lbl_ssl_crt, *lbl_ssl_key; + QPushButton *btn_connect, *btn_close, *btn_set_ssl_ca, *btn_set_ssl_crt, *btn_set_ssl_key; QSpacerItem *spacer_h; - + QCheckBox *chk_ssl; }; #endif // CONNECT_WIDGET_H diff --git a/client-qt/udm-client-qt/udm-client-qt.pro b/client-qt/udm-client-qt/udm-client-qt.pro index 7b6d02d..3a7be9a 100644 --- a/client-qt/udm-client-qt/udm-client-qt.pro +++ b/client-qt/udm-client-qt/udm-client-qt.pro @@ -33,7 +33,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = udm-client-qt TEMPLATE = app -unix:LIBS += -lboost_thread -lboost_log -lpthread -lprotobuf -lboost_system -ldl +unix:LIBS += -lboost_thread -lboost_log -lpthread -lprotobuf -lboost_system -ldl -lcrypto -lssl SOURCES += main.cpp\ udm_main.cpp \ diff --git a/client-qt/udm-client-qt/udm_main.cpp b/client-qt/udm-client-qt/udm_main.cpp index 02c0f9a..f85dc5d 100644 --- a/client-qt/udm-client-qt/udm_main.cpp +++ b/client-qt/udm-client-qt/udm_main.cpp @@ -32,11 +32,13 @@ udm_main::udm_main(QWidget *parent) auto menu_main = this->menuBar()->addMenu(tr("Main")); auto action_connect = menu_main->addAction(tr("Connect")); connect(action_connect, SIGNAL(triggered()), this, SLOT(show_connect_widget())); + auto action_exit = menu_main->addAction(tr("Exit")); + connect(action_exit, SIGNAL(triggered(bool)), this, SLOT(close())); //TODO: something better } udm_main::~udm_main() { - + //TODO: clear all remaining data } void udm_main::show_connect_widget() @@ -48,16 +50,31 @@ void udm_main::show_connect_widget() w->show(); } -void udm_main::client_connect(QString &host, QString &password, int port) +void udm_main::client_pre_connect_init() { thread_client_session = new QThread(this); io_service_ = new boost::asio::io_service; session = new client_session(0, io_service_); //parent must be 0 here - connect(this, SIGNAL(connect_signal(QString,QString,int)), session, SLOT(client_connect(QString,QString,int))); - connect(this, SIGNAL(run_client_io_loop()), session, SLOT(run_io_service())); - connect(session, SIGNAL(terminate_thread()), thread_client_session, SLOT(terminate())); +} +void udm_main::client_connect_finalize() +{ + //connect(session, SIGNAL(terminate_thread()), thread_client_session, SLOT(terminate())); session->moveToThread(thread_client_session); thread_client_session->start(); +} + +void udm_main::client_connect(QString &host, QString &password, int port) +{ + client_pre_connect_init(); + connect(this, SIGNAL(connect_signal(QString,QString,int)), session, SLOT(client_connect(QString,QString,int))); + client_connect_finalize(); emit connect_signal(host, password, port); - emit run_client_io_loop(); +} + +void udm_main::client_connect_ssl(QString &host, QString &password, int port, QString &ssl_ca, QString &ssl_crt, QString &ssl_key) +{ + client_pre_connect_init(); + connect(this, SIGNAL(connect_signal(QString,QString,int,QString,QString)), session, SLOT(client_connect(QString,QString,int,QString,QString))); + client_connect_finalize(); + emit connect_signal_ssl(host, password, port, ssl_ca, ssl_crt, ssl_key); } diff --git a/client-qt/udm-client-qt/udm_main.h b/client-qt/udm-client-qt/udm_main.h index af8c7a7..2964e55 100644 --- a/client-qt/udm-client-qt/udm_main.h +++ b/client-qt/udm-client-qt/udm_main.h @@ -46,10 +46,13 @@ public: public slots: void show_connect_widget(); void client_connect(QString &host, QString &password, int port); + void client_connect_ssl(QString &host, QString &password, int port, QString &ssl_ca, QString &ssl_crt, QString &ssl_key); signals: void connect_signal(QString host, QString password, int port); - void run_client_io_loop(); + void connect_signal_ssl(QString host, QString password, int port, QString ssl_ca, QString ssl_crt, QString ssl_key); private: + void client_pre_connect_init(); + void client_connect_finalize(); QThread *thread_client_session; client_session *session; boost::asio::io_service *io_service_; diff --git a/protocol/udm.proto b/protocol/udm.proto index adbae08..739059c 100644 --- a/protocol/udm.proto +++ b/protocol/udm.proto @@ -25,7 +25,7 @@ enum MODULE_UI_ELEMENT_TYPE { UI_EMPTY = 0; //helper type to set empty element - UI_STRING = 1; //ui elements containing strings + UI_STR = 1; //ui elements containing strings UI_INTEGER = 2; //numeric only ui elements (use strings instead ?) UI_PROGRESS_BAR = 3; //generic progress bar UI_WINDOW = 4; //ui window ... |