mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-27 23:49:04 +00:00
client: add command line support for ciphers, ca file, and host certificate subject
This commit is contained in:
parent
f16e16393e
commit
457693fcfa
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user