client: add command line support for ciphers, ca file, and host certificate subject

This commit is contained in:
Yonit Halperin 2010-03-18 10:21:47 +01:00 committed by Alexander Larsson
parent f16e16393e
commit 457693fcfa
7 changed files with 116 additions and 10 deletions

View File

@ -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<HotKeysParser> 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;

View File

@ -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<int>& 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<bool> _enabled_channels;
std::vector<RedScreen*> _screens;
RedScreen* _main_screen;

View File

@ -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());

View File

@ -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();
}

View File

@ -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<ChannelFactory*> Factorys;

View File

@ -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");

View File

@ -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);