From eea29361db0fcf399b9843c789a7eefae758159e Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Wed, 10 Mar 2010 10:12:15 +0200 Subject: client: add command line support for ciphers, ca file, and host certificate subject #573371 --- client/application.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++--- client/application.h | 5 +++ client/red_channel.cpp | 3 +- client/red_client.cpp | 7 ++-- client/red_client.h | 3 ++ client/red_peer.cpp | 7 +++- client/red_peer.h | 5 ++- 7 files changed, 116 insertions(+), 10 deletions(-) diff --git a/client/application.cpp b/client/application.cpp index 33cc9477..8a010e4f 100644 --- a/client/application.cpp +++ b/client/application.cpp @@ -265,6 +265,7 @@ enum AppCommands { Application::Application() : ProcessLoop (this) , _client (*this) + , _con_ciphers ("DEFAULT") , _enabled_channels (RED_CHANNEL_END, true) , _main_screen (NULL) , _active (false) @@ -306,6 +307,11 @@ Application::Application() _canvas_types[0] = CANVAS_OPTION_CAIRO; #endif + _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_NAME; + + Platform::get_app_data_dir(_host_auth_opt.CA_file, app_name); + Platform::path_append(_host_auth_opt.CA_file, CA_FILE_NAME); + std::auto_ptr parser(new HotKeysParser("toggle-fullscreen=shift+f11" ",release-cursor=shift+f12" #ifdef RED_DEBUG @@ -1489,6 +1495,65 @@ bool Application::set_channels_security(CmdLineParser& parser, bool on, char *va return true; } +bool Application::set_connection_ciphers(const char* ciphers, const char* arg0) +{ + _con_ciphers = ciphers; + return true; +} + +bool Application::set_ca_file(const char* ca_file, const char* arg0) +{ + _host_auth_opt.CA_file = ca_file; + return true; +} + +bool Application::set_host_cert_subject(const char* subject, const char* arg0) +{ + std::string subject_str(subject); + std::string::const_iterator iter = subject_str.begin(); + std::string entry; + _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT; + _host_auth_opt.host_subject.clear(); + + while (true) { + if ((iter == subject_str.end()) || (*iter == ',')) { + RedPeer::HostAuthOptions::CertFieldValuePair entry_pair; + int value_pos = entry.find_first_of('='); + if ((value_pos == std::string::npos) || (value_pos == (entry.length() - 1))) { + Platform::term_printf("%s: host_subject bad format: assignment for %s is missing\n", + arg0, entry.c_str()); + _exit_code = SPICEC_ERROR_CODE_INVALID_ARG; + return false; + } + entry_pair.first = entry.substr(0, value_pos); + entry_pair.second = entry.substr(value_pos + 1); + _host_auth_opt.host_subject.push_back(entry_pair); + DBG(0, "subject entry: %s=%s", entry_pair.first.c_str(), entry_pair.second.c_str()); + if (iter == subject_str.end()) { + break; + } + entry.clear(); + } else if (*iter == '\\') { + iter++; + if (iter == subject_str.end()) { + LOG_WARN("single \\ in host subject"); + entry.append(1, '\\'); + continue; + } else if ((*iter == '\\') || (*iter == ',')) { + entry.append(1, *iter); + } else { + LOG_WARN("single \\ in host subject"); + entry.append(1, '\\'); + continue; + } + } else { + entry.append(1, *iter); + } + iter++; + } + return true; +} + bool Application::set_canvas_option(CmdLineParser& parser, char *val, const char* arg0) { typedef std::map< std::string, CanvasOption> CanvasNamesMap; @@ -1577,6 +1642,9 @@ bool Application::process_cmd_line(int argc, char** argv) SPICE_OPT_FULL_SCREEN, SPICE_OPT_SECURE_CHANNELS, SPICE_OPT_UNSECURE_CHANNELS, + SPICE_OPT_TLS_CIPHERS, + SPICE_OPT_CA_FILE, + SPICE_OPT_HOST_SUBJECT, SPICE_OPT_ENABLE_CHANNELS, SPICE_OPT_DISABLE_CHANNELS, SPICE_OPT_CANVAS_TYPE, @@ -1596,6 +1664,14 @@ bool Application::process_cmd_line(int argc, char** argv) "force unsecure connection on the specified channels", "channel", true); parser.set_multi(SPICE_OPT_UNSECURE_CHANNELS, ','); + parser.add(SPICE_OPT_TLS_CIPHERS, "tls-ciphers", "ciphers for secure connections", + "ciphers", true); + parser.add(SPICE_OPT_CA_FILE, "ca-file", "truststore file for secure connections", + "ca-file", true); + parser.add(SPICE_OPT_HOST_SUBJECT, "host-subject", + "subject of the host certifcate. Format: field=value pairs separated" + " by commmas. Commas and backslashes within values must be preceded by" + " a backslash", "host-subject", true); parser.add(SPICE_OPT_PASSWORD, "password", "server password", "password", true, 'w'); parser.add(SPICE_OPT_FULL_SCREEN, "full-screen", "open in full screen mode", "auto-conf", false, 'f'); @@ -1616,11 +1692,6 @@ bool Application::process_cmd_line(int argc, char** argv) _peer_con_opt[RED_CHANNEL_PLAYBACK] = RedPeer::ConnectionOptions::CON_OP_INVALID; _peer_con_opt[RED_CHANNEL_RECORD] = RedPeer::ConnectionOptions::CON_OP_INVALID; - _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_NAME; - - Platform::get_app_data_dir(_host_auth_opt.CA_file, app_name); - Platform::path_append(_host_auth_opt.CA_file, CA_FILE_NAME); - parser.begin(argc, argv); char* val; @@ -1667,6 +1738,21 @@ bool Application::process_cmd_line(int argc, char** argv) return false; } break; + case SPICE_OPT_TLS_CIPHERS: + if (!set_connection_ciphers(val, argv[0])) { + return false; + } + break; + case SPICE_OPT_CA_FILE: + if (!set_ca_file(val, argv[0])) { + return false; + } + break; + case SPICE_OPT_HOST_SUBJECT: + if (!set_host_cert_subject(val, argv[0])) { + return false; + } + break; case SPICE_OPT_ENABLE_CHANNELS: if (!set_enable_channels(parser, true, val, argv[0])) { return false; diff --git a/client/application.h b/client/application.h index 27701eb0..e924ce14 100644 --- a/client/application.h +++ b/client/application.h @@ -156,6 +156,7 @@ public: void connect(); const PeerConnectionOptMap& get_con_opt_map() {return _peer_con_opt;} const RedPeer::HostAuthOptions& get_host_auth_opt() { return _host_auth_opt;} + const std::string& get_connection_ciphers() { return _con_ciphers;} uint32_t get_mouse_mode(); const std::vector& get_canvas_types() { return _canvas_types;} @@ -166,6 +167,9 @@ public: private: bool set_channels_security(CmdLineParser& parser, bool on, char *val, const char* arg0); + bool set_connection_ciphers(const char* ciphers, const char* arg0); + bool set_ca_file(const char* ca_file, const char* arg0); + bool set_host_cert_subject(const char* subject, const char* arg0); bool set_enable_channels(CmdLineParser& parser, bool enable, char *val, const char* arg0); bool set_canvas_option(CmdLineParser& parser, char *val, const char* arg0); void on_cmd_line_invalid_arg(const char* arg0, const char* what, const char* val); @@ -226,6 +230,7 @@ private: RedClient _client; PeerConnectionOptMap _peer_con_opt; RedPeer::HostAuthOptions _host_auth_opt; + std::string _con_ciphers; std::vector _enabled_channels; std::vector _screens; RedScreen* _main_screen; diff --git a/client/red_channel.cpp b/client/red_channel.cpp index 153055d9..048cef24 100644 --- a/client/red_channel.cpp +++ b/client/red_channel.cpp @@ -396,7 +396,8 @@ void RedChannel::run() ConnectionOptions con_options(_client.get_connection_options(get_type()), _client.get_port(), _client.get_sport(), - _client.get_host_auth_options()); + _client.get_host_auth_options(), + _client.get_connection_ciphers()); RedChannelBase::connect(con_options, _client.get_connection_id(), _client.get_host(), _client.get_password()); on_connect(); diff --git a/client/red_client.cpp b/client/red_client.cpp index df88e7a8..90793266 100644 --- a/client/red_client.cpp +++ b/client/red_client.cpp @@ -129,14 +129,15 @@ void Migrate::run() DBG(0, ""); try { conn_type = _client.get_connection_options(RED_CHANNEL_MAIN); - RedPeer::ConnectionOptions con_opt(conn_type, _port, _sport, _auth_options); + RedPeer::ConnectionOptions con_opt(conn_type, _port, _sport, _auth_options, _con_ciphers); MigChannels::iterator iter = _channels.begin(); connection_id = _client.get_connection_id(); connect_one(**iter, con_opt, connection_id); for (++iter; iter != _channels.end(); ++iter) { conn_type = _client.get_connection_options((*iter)->get_type()); - con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport, _auth_options); + con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport, + _auth_options, _con_ciphers); connect_one(**iter, con_opt, connection_id); } _connected = true; @@ -186,6 +187,7 @@ void Migrate::start(const RedMigrationBegin* migrate) migrate->pub_key_size); } + _con_ciphers = _client.get_connection_ciphers(); _password = _client._password; Lock lock(_lock); _running = true; @@ -410,6 +412,7 @@ void RedClient::connect() } _host_auth_opt = _application.get_host_auth_opt(); + _con_ciphers = _application.get_connection_ciphers(); RedChannel::connect(); } diff --git a/client/red_client.h b/client/red_client.h index d97128e5..b7edadbd 100644 --- a/client/red_client.h +++ b/client/red_client.h @@ -77,6 +77,7 @@ private: int _port; int _sport; RedPeer::HostAuthOptions _auth_options; + std::string _con_ciphers; Thread* _thread; Mutex _lock; Condition _cond; @@ -150,6 +151,7 @@ public: bool is_auto_display_res() { return _auto_display_res;} RedPeer::ConnectionOptions::Type get_connection_options(uint32_t channel_type); RedPeer::HostAuthOptions& get_host_auth_options() { return _host_auth_opt;} + const std::string& get_connection_ciphers() { return _con_ciphers;} void get_sync_info(uint8_t channel_type, uint8_t channel_id, SyncInfo& info); void wait_for_channels(int wait_list_size, RedWaitForChannel* wait_list); PixmapCache& get_pixmap_cache() {return _pixmap_cache;} @@ -218,6 +220,7 @@ private: PeerConnectionOptMap _con_opt_map; RedPeer::HostAuthOptions _host_auth_opt; + std::string _con_ciphers; Migrate _migrate; Mutex _channels_lock; typedef std::list Factorys; diff --git a/client/red_peer.cpp b/client/red_peer.cpp index a1dca53c..069fdae5 100644 --- a/client/red_peer.cpp +++ b/client/red_peer.cpp @@ -509,7 +509,6 @@ int RedPeer::ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) return 0; } -// todo: use SSL_CTX_set_cipher_list, etc. void RedPeer::connect_secure(const ConnectionOptions& options, const char* host) { int return_code; @@ -558,6 +557,12 @@ void RedPeer::connect_secure(const ConnectionOptions& options, const char* host) SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, ssl_verify_callback); } + return_code = SSL_CTX_set_cipher_list(_ctx, options.ciphers.c_str()); + if (return_code != 1) { + LOG_WARN("SSL_CTX_set_cipher_list failed, ciphers=%s", options.ciphers.c_str()); + ssl_error(); + } + _ssl = SSL_new(_ctx); if (!_ssl) { THROW("create ssl failed"); diff --git a/client/red_peer.h b/client/red_peer.h index 761aed1d..6a80f8cb 100644 --- a/client/red_peer.h +++ b/client/red_peer.h @@ -77,11 +77,13 @@ public: }; ConnectionOptions(Type in_type, int in_port, int in_sport, - const HostAuthOptions& in_host_auth) + const HostAuthOptions& in_host_auth, + const std::string& in_ciphers) : type (in_type) , unsecure_port (in_port) , secure_port (in_sport) , host_auth (in_host_auth) + , ciphers (in_ciphers) { } @@ -102,6 +104,7 @@ public: int unsecure_port; int secure_port; HostAuthOptions host_auth; // for secure connection + std::string ciphers; }; void connect_unsecure(const char* host, int port); -- cgit