From 457693fcfac7c178bdc9ca23295aa52d815ea0d6 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Thu, 18 Mar 2010 10:21:47 +0100 Subject: client: add command line support for ciphers, ca file, and host certificate subject --- 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(-) (limited to 'client') diff --git a/client/application.cpp b/client/application.cpp index 73a3461f..b9033997 100644 --- a/client/application.cpp +++ b/client/application.cpp @@ -321,6 +321,7 @@ enum AppCommands { Application::Application() : ProcessLoop (this) , _client (*this) + , _con_ciphers ("DEFAULT") , _enabled_channels (SPICE_END_CHANNEL, true) , _main_screen (NULL) , _active (false) @@ -363,6 +364,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 @@ -1683,6 +1689,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; @@ -1798,6 +1863,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, @@ -1826,6 +1894,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'); @@ -1843,11 +1919,6 @@ bool Application::process_cmd_line(int argc, char** argv) _peer_con_opt[i] = 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; @@ -1894,6 +1965,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 b2a18d37..adf25175 100644 --- a/client/application.h +++ b/client/application.h @@ -188,6 +188,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;} @@ -217,6 +218,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); @@ -283,6 +287,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 e360d5f4..511c1cff 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().c_str(), _client.get_password().c_str()); diff --git a/client/red_client.cpp b/client/red_client.cpp index 40c77677..6a83a051 100644 --- a/client/red_client.cpp +++ b/client/red_client.cpp @@ -170,14 +170,15 @@ void Migrate::run() DBG(0, ""); try { conn_type = _client.get_connection_options(SPICE_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; @@ -227,6 +228,7 @@ void Migrate::start(const SpiceMsgMainMigrationBegin* migrate) migrate->pub_key_size); } + _con_ciphers = _client.get_connection_ciphers(); _password = _client._password; Lock lock(_lock); _running = true; @@ -460,6 +462,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 e62ed77b..5e783a5f 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; @@ -155,6 +156,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, SpiceWaitForChannel* wait_list); PixmapCache& get_pixmap_cache() {return _pixmap_cache;} @@ -225,6 +227,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 5d939a4d..2f1ac194 100644 --- a/client/red_peer.cpp +++ b/client/red_peer.cpp @@ -466,7 +466,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; @@ -515,6 +514,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 091f51aa..d31ba972 100644 --- a/client/red_peer.h +++ b/client/red_peer.h @@ -72,11 +72,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) { } @@ -97,6 +99,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