mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-29 17:07:15 +00:00
server,client: server authentication for secured channels.
3 available mechanisms: by public key, by host name, and by certificate subject name. In the former method, chain of trust verification is not performed. The CA certificate files are looked for under <spice-config-dir>/spice_truststore.pem windows <spice-config-dir>=%APPDATA%\spicec\ linux <spice-config-dir>=$HOME/.spicec/
This commit is contained in:
parent
dcf326cfd5
commit
3eae1c80d9
@ -51,6 +51,8 @@
|
||||
#define STICKY_KEY_PIXMAP ALT_IMAGE_RES_ID
|
||||
#define STICKY_KEY_TIMEOUT 750
|
||||
|
||||
#define CA_FILE_NAME "spice_truststore.pem"
|
||||
|
||||
#ifdef CAIRO_CANVAS_CACH_IS_SHARED
|
||||
mutex_t cairo_surface_user_data_mutex;
|
||||
#endif
|
||||
@ -1818,6 +1820,11 @@ 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_spice_config_dir(_host_auth_opt.CA_file);
|
||||
_host_auth_opt.CA_file += CA_FILE_NAME;
|
||||
|
||||
parser.begin(argc, argv);
|
||||
|
||||
char* val;
|
||||
@ -1836,12 +1843,11 @@ bool Application::process_cmd_line(int argc, char** argv)
|
||||
break;
|
||||
}
|
||||
case SPICE_OPT_SPORT: {
|
||||
if ((port = str_to_port(val)) == -1) {
|
||||
if ((sport = str_to_port(val)) == -1) {
|
||||
std::cout << "invalid secure port " << val << "\n";
|
||||
_exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
|
||||
return false;
|
||||
}
|
||||
sport = port;
|
||||
break;
|
||||
}
|
||||
case SPICE_OPT_FULL_SCREEN:
|
||||
|
||||
@ -187,6 +187,7 @@ public:
|
||||
void external_show();
|
||||
void connect();
|
||||
const PeerConnectionOptMap& get_con_opt_map() {return _peer_con_opt;}
|
||||
const RedPeer::HostAuthOptions& get_host_auth_opt() { return _host_auth_opt;}
|
||||
uint32_t get_mouse_mode();
|
||||
const std::vector<int>& get_canvas_types() { return _canvas_types;}
|
||||
|
||||
@ -280,6 +281,7 @@ private:
|
||||
private:
|
||||
RedClient _client;
|
||||
PeerConnectionOptMap _peer_con_opt;
|
||||
RedPeer::HostAuthOptions _host_auth_opt;
|
||||
std::vector<bool> _enabled_channels;
|
||||
std::vector<RedScreen*> _screens;
|
||||
RedScreen* _main_screen;
|
||||
|
||||
@ -48,6 +48,8 @@ public:
|
||||
|
||||
static void send_quit_request();
|
||||
|
||||
static void get_spice_config_dir(std::string& path);
|
||||
|
||||
enum ThreadPriority {
|
||||
PRIORITY_INVALID,
|
||||
PRIORITY_TIME_CRITICAL,
|
||||
|
||||
@ -89,6 +89,8 @@ void RedChannelBase::link(uint32_t connection_id, const std::string& password)
|
||||
header.major_version);
|
||||
}
|
||||
|
||||
_remote_minor = header.minor_version;
|
||||
|
||||
AutoArray<uint8_t> reply_buf(new uint8_t[header.size]);
|
||||
recive(reply_buf.get(), header.size);
|
||||
|
||||
@ -155,11 +157,11 @@ void RedChannelBase::link(uint32_t connection_id, const std::string& password)
|
||||
}
|
||||
|
||||
void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connection_id,
|
||||
uint32_t ip, std::string password)
|
||||
const char* host, std::string password)
|
||||
{
|
||||
if (options.allow_unsecure()) {
|
||||
try {
|
||||
RedPeer::connect_unsecure(ip, options.unsecure_port);
|
||||
RedPeer::connect_unsecure(host, options.unsecure_port);
|
||||
link(connection_id, password);
|
||||
return;
|
||||
} catch (...) {
|
||||
@ -170,16 +172,10 @@ void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connecti
|
||||
}
|
||||
}
|
||||
ASSERT(options.allow_secure());
|
||||
RedPeer::connect_secure(options, ip);
|
||||
RedPeer::connect_secure(options, host);
|
||||
link(connection_id, password);
|
||||
}
|
||||
|
||||
void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connection_id,
|
||||
const char* host, std::string password)
|
||||
{
|
||||
connect(options, connection_id, host_by_name(host), password);
|
||||
}
|
||||
|
||||
void RedChannelBase::set_capability(ChannelCaps& caps, uint32_t cap)
|
||||
{
|
||||
uint32_t word_index = cap / 32;
|
||||
@ -399,7 +395,8 @@ void RedChannel::run()
|
||||
set_state(CONNECTING_STATE);
|
||||
ConnectionOptions con_options(_client.get_connection_options(get_type()),
|
||||
_client.get_port(),
|
||||
_client.get_sport());
|
||||
_client.get_sport(),
|
||||
_client.get_host_auth_options());
|
||||
RedChannelBase::connect(con_options, _client.get_connection_id(),
|
||||
_client.get_host().c_str(),
|
||||
_client.get_password().c_str());
|
||||
|
||||
@ -55,14 +55,14 @@ public:
|
||||
uint8_t get_type() { return _type;}
|
||||
uint8_t get_id() { return _id;}
|
||||
|
||||
void connect(const ConnectionOptions& options, uint32_t connection_id, uint32_t ip,
|
||||
std::string password);
|
||||
void connect(const ConnectionOptions& options, uint32_t connection_id, const char *host,
|
||||
std::string password);
|
||||
|
||||
const ChannelCaps& get_common_caps() { return _common_caps;}
|
||||
const ChannelCaps& get_caps() {return _caps;}
|
||||
|
||||
uint32_t get_peer_minor() { return _remote_minor;}
|
||||
|
||||
protected:
|
||||
void set_common_capability(uint32_t cap);
|
||||
void set_capability(uint32_t cap);
|
||||
@ -83,6 +83,8 @@ private:
|
||||
|
||||
ChannelCaps _remote_common_caps;
|
||||
ChannelCaps _remote_caps;
|
||||
|
||||
uint32_t _remote_minor;
|
||||
};
|
||||
|
||||
class SendTrigger: public EventSources::Trigger {
|
||||
|
||||
@ -23,6 +23,15 @@
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
typedef struct __attribute__ ((__packed__)) OldRedMigrationBegin {
|
||||
#else
|
||||
typedef struct __declspec(align(1)) OldRedMigrationBegin {
|
||||
#endif
|
||||
uint16_t port;
|
||||
uint16_t sport;
|
||||
char host[0];
|
||||
} OldRedMigrationBegin;
|
||||
|
||||
class MouseModeEvent: public Event {
|
||||
public:
|
||||
@ -156,18 +165,19 @@ void Migrate::connect_one(MigChannel& channel, const RedPeer::ConnectionOptions&
|
||||
void Migrate::run()
|
||||
{
|
||||
uint32_t connection_id;
|
||||
RedPeer::ConnectionOptions::Type conn_type;
|
||||
|
||||
DBG(0, "");
|
||||
try {
|
||||
RedPeer::ConnectionOptions con_opt(_client.get_connection_options(RED_CHANNEL_MAIN),
|
||||
_port, _port);
|
||||
conn_type = _client.get_connection_options(RED_CHANNEL_MAIN);
|
||||
RedPeer::ConnectionOptions con_opt(conn_type, _port, _sport, _auth_options);
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
connection_id = _client.get_connection_id();
|
||||
connect_one(**iter, con_opt, connection_id);
|
||||
|
||||
for (++iter; iter != _channels.end(); ++iter) {
|
||||
con_opt = RedPeer::ConnectionOptions(
|
||||
_client.get_connection_options((*iter)->get_type()),
|
||||
_port, _sport);
|
||||
conn_type = _client.get_connection_options((*iter)->get_type());
|
||||
con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport, _auth_options);
|
||||
connect_one(**iter, con_opt, connection_id);
|
||||
}
|
||||
_connected = true;
|
||||
@ -199,9 +209,24 @@ void Migrate::start(const RedMigrationBegin* migrate)
|
||||
{
|
||||
DBG(0, "");
|
||||
abort();
|
||||
_host.assign(migrate->host);
|
||||
_port = migrate->port ? migrate->port : -1;
|
||||
_sport = migrate->sport ? migrate->sport : -1;
|
||||
if ((RED_VERSION_MAJOR == 1) && (_client.get_peer_minor() < 2)) {
|
||||
LOG_INFO("server minor version incompatible for destination authentication"
|
||||
"(missing dest pubkey in RedMigrationBegin)");
|
||||
OldRedMigrationBegin* old_migrate = (OldRedMigrationBegin*)migrate;
|
||||
_host.assign(old_migrate->host);
|
||||
_port = old_migrate->port ? old_migrate->port : -1;
|
||||
_sport = old_migrate->sport ? old_migrate->sport : -1;;
|
||||
_auth_options = _client.get_host_auth_options();
|
||||
} else {
|
||||
_host.assign(((char*)migrate) + migrate->host_offset);
|
||||
_port = migrate->port ? migrate->port : -1;
|
||||
_sport = migrate->sport ? migrate->sport : -1;
|
||||
_auth_options.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY;
|
||||
_auth_options.host_pubkey.assign(((uint8_t*)migrate)+ migrate->pub_key_offset,
|
||||
((uint8_t*)migrate)+ migrate->pub_key_offset +
|
||||
migrate->pub_key_size);
|
||||
}
|
||||
|
||||
_password = _client._password;
|
||||
Lock lock(_lock);
|
||||
_running = true;
|
||||
@ -433,6 +458,8 @@ void RedClient::connect()
|
||||
for (; iter != end; iter++) {
|
||||
_con_opt_map[(*iter).first] = (*iter).second;
|
||||
}
|
||||
|
||||
_host_auth_opt = _application.get_host_auth_opt();
|
||||
RedChannel::connect();
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,7 @@ private:
|
||||
std::string _host;
|
||||
int _port;
|
||||
int _sport;
|
||||
RedPeer::HostAuthOptions _auth_options;
|
||||
Thread* _thread;
|
||||
Mutex _lock;
|
||||
Condition _cond;
|
||||
@ -153,6 +154,7 @@ public:
|
||||
Application& get_application() { return _application;}
|
||||
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;}
|
||||
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;}
|
||||
@ -222,6 +224,7 @@ private:
|
||||
AutoRef<AgentTimer> _agent_timer;
|
||||
|
||||
PeerConnectionOptMap _con_opt_map;
|
||||
RedPeer::HostAuthOptions _host_auth_opt;
|
||||
Migrate _migrate;
|
||||
Mutex _channels_lock;
|
||||
typedef std::list<ChannelFactory*> Factorys;
|
||||
|
||||
@ -16,16 +16,26 @@
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include "red.h"
|
||||
#include "red_peer.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "platform_utils.h"
|
||||
|
||||
typedef struct SslVerifyCbData {
|
||||
RedPeer::HostAuthOptions info;
|
||||
const char* host_name;
|
||||
bool all_preverify_ok;
|
||||
} SslVerifyCbData;
|
||||
|
||||
static void ssl_error()
|
||||
{
|
||||
unsigned long last_error = ERR_peek_last_error();
|
||||
|
||||
ERR_print_errors_fp(stderr);
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SSL_ERROR, "SSL Error");
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SSL_ERROR, "SSL Error:", ERR_error_string(last_error, NULL));
|
||||
}
|
||||
|
||||
RedPeer::RedPeer()
|
||||
@ -80,13 +90,15 @@ uint32_t RedPeer::host_by_name(const char* host)
|
||||
return ntohl(return_value);
|
||||
}
|
||||
|
||||
void RedPeer::connect_unsecure(uint32_t ip, int port)
|
||||
void RedPeer::connect_unsecure(const char* host, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int no_delay;
|
||||
|
||||
uint32_t ip;
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer == INVALID_SOCKET);
|
||||
try {
|
||||
ip = host_by_name(host);
|
||||
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(ip);
|
||||
@ -120,15 +132,349 @@ void RedPeer::connect_unsecure(uint32_t ip, int port)
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::connect_unsecure(const char* host, int port)
|
||||
bool RedPeer::verify_pubkey(X509* cert, const HostAuthOptions::PublicKey& key)
|
||||
{
|
||||
connect_unsecure(host_by_name(host), port);
|
||||
EVP_PKEY* cert_pubkey = NULL;
|
||||
EVP_PKEY* orig_pubkey = NULL;
|
||||
BIO* bio = NULL;
|
||||
uint8_t* c_key = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (key.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(cert);
|
||||
|
||||
try {
|
||||
cert_pubkey = X509_get_pubkey(cert);
|
||||
if (!cert_pubkey) {
|
||||
THROW("reading public key from certificate failed");
|
||||
}
|
||||
|
||||
c_key = new uint8_t[key.size()];
|
||||
memcpy(c_key, &key[0], key.size());
|
||||
|
||||
bio = BIO_new_mem_buf((void*)c_key, key.size());
|
||||
if (!bio) {
|
||||
THROW("creating BIO failed");
|
||||
}
|
||||
|
||||
orig_pubkey = d2i_PUBKEY_bio(bio, NULL);
|
||||
if (!orig_pubkey) {
|
||||
THROW("reading pubkey from bio failed");
|
||||
}
|
||||
|
||||
ret = EVP_PKEY_cmp(orig_pubkey, cert_pubkey);
|
||||
|
||||
BIO_free(bio);
|
||||
EVP_PKEY_free(orig_pubkey);
|
||||
EVP_PKEY_free(cert_pubkey);
|
||||
delete []c_key;
|
||||
if (ret == 1) {
|
||||
DBG(0, "public keys match");
|
||||
return true;
|
||||
} else if (ret == 0) {
|
||||
DBG(0, "public keys mismatch");
|
||||
return false;
|
||||
} else {
|
||||
DBG(0, "public keys types mismatch");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception& e) {
|
||||
LOG_WARN("%s", e.what());
|
||||
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
if (orig_pubkey) {
|
||||
EVP_PKEY_free(orig_pubkey);
|
||||
}
|
||||
|
||||
if (cert_pubkey) {
|
||||
EVP_PKEY_free(cert_pubkey);
|
||||
}
|
||||
delete []c_key;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: use SSL_CTX_set_cipher_list, SSL_CTX_load_verify_location etc.
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, uint32_t ip)
|
||||
/* From gnutls: compare host_name against certificate, taking account of wildcards.
|
||||
* return true on success or false on error.
|
||||
*
|
||||
* note: cert_name_size is required as X509 certs can contain embedded NULs in
|
||||
* the strings such as CN or subjectAltName
|
||||
*/
|
||||
bool RedPeer::x509_cert_host_name_compare(const char *cert_name, int cert_name_size,
|
||||
const char *host_name)
|
||||
{
|
||||
connect_unsecure(ip, options.secure_port);
|
||||
/* find the first different character */
|
||||
for (; *cert_name && *host_name && (toupper(*cert_name) == toupper(*host_name));
|
||||
cert_name++, host_name++, cert_name_size--);
|
||||
|
||||
/* the strings are the same */
|
||||
if (cert_name_size == 0 && *host_name == '\0')
|
||||
return true;
|
||||
|
||||
if (*cert_name == '*')
|
||||
{
|
||||
/* a wildcard certificate */
|
||||
cert_name++;
|
||||
cert_name_size--;
|
||||
|
||||
while (true)
|
||||
{
|
||||
/* Use a recursive call to allow multiple wildcards */
|
||||
if (RedPeer::x509_cert_host_name_compare(cert_name, cert_name_size, host_name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* wildcards are only allowed to match a single domain
|
||||
component or component fragment */
|
||||
if (*host_name == '\0' || *host_name == '.')
|
||||
break;
|
||||
host_name++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* From gnutls_x509_crt_check_hostname - compares the hostname with certificate's hostname
|
||||
*
|
||||
* This function will check if the given certificate's subject matches
|
||||
* the hostname. This is a basic implementation of the matching
|
||||
* described in RFC2818 (HTTPS), which takes into account wildcards,
|
||||
* and the DNSName/IPAddress subject alternative name PKIX extension.
|
||||
*
|
||||
*/
|
||||
bool RedPeer::verify_host_name(X509* cert, const char* host_name)
|
||||
{
|
||||
GENERAL_NAMES* subject_alt_names;
|
||||
bool found_dns_name = false;
|
||||
struct in_addr addr;
|
||||
int addr_len = 0;
|
||||
bool cn_match = false;
|
||||
|
||||
ASSERT(cert);
|
||||
|
||||
// only IpV4 supported
|
||||
if (inet_aton(host_name, &addr)) {
|
||||
addr_len = sizeof(struct in_addr);
|
||||
}
|
||||
|
||||
/* try matching against:
|
||||
* 1) a DNS name or IP address as an alternative name (subjectAltName) extension
|
||||
* in the certificate
|
||||
* 2) the common name (CN) in the certificate
|
||||
*
|
||||
* either of these may be of the form: *.domain.tld
|
||||
*
|
||||
* only try (2) if there is no subjectAltName extension of
|
||||
* type dNSName
|
||||
*/
|
||||
|
||||
|
||||
subject_alt_names = (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
||||
|
||||
if (subject_alt_names) {
|
||||
int num_alts = sk_GENERAL_NAME_num(subject_alt_names);
|
||||
for (int i = 0; i < num_alts; i++) {
|
||||
const GENERAL_NAME* name = sk_GENERAL_NAME_value(subject_alt_names, i);
|
||||
if (name->type == GEN_DNS) {
|
||||
found_dns_name = true;
|
||||
if (RedPeer::x509_cert_host_name_compare((char *)ASN1_STRING_data(name->d.dNSName),
|
||||
ASN1_STRING_length(name->d.dNSName),
|
||||
host_name)) {
|
||||
DBG(0, "alt name match=%s", ASN1_STRING_data(name->d.dNSName));
|
||||
GENERAL_NAMES_free(subject_alt_names);
|
||||
return true;
|
||||
}
|
||||
} else if (name->type == GEN_IPADD) {
|
||||
int alt_ip_len = ASN1_STRING_length(name->d.iPAddress);
|
||||
found_dns_name = true;
|
||||
if ((addr_len == alt_ip_len)&&
|
||||
!memcmp(ASN1_STRING_data(name->d.iPAddress), &addr, addr_len)) {
|
||||
DBG(0, "alt name IP match=%s",
|
||||
inet_ntoa(*((struct in_addr*)ASN1_STRING_data(name->d.dNSName))));
|
||||
GENERAL_NAMES_free(subject_alt_names);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GENERAL_NAMES_free(subject_alt_names);
|
||||
}
|
||||
|
||||
if (found_dns_name)
|
||||
{
|
||||
DBG(0, "SubjectAltName mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* extracting commonNames */
|
||||
X509_NAME* subject = X509_get_subject_name(cert);
|
||||
if (subject) {
|
||||
int pos = -1;
|
||||
X509_NAME_ENTRY* cn_entry;
|
||||
ASN1_STRING* cn_asn1;
|
||||
|
||||
while ((pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos)) != -1) {
|
||||
cn_entry = X509_NAME_get_entry(subject, pos);
|
||||
if (!cn_entry) {
|
||||
continue;
|
||||
}
|
||||
cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
|
||||
if (!cn_asn1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RedPeer::x509_cert_host_name_compare((char*)ASN1_STRING_data(cn_asn1),
|
||||
ASN1_STRING_length(cn_asn1),
|
||||
host_name)) {
|
||||
DBG(0, "common name match=%s", (char*)ASN1_STRING_data(cn_asn1));
|
||||
cn_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cn_match) {
|
||||
DBG(0, "common name mismatch");
|
||||
}
|
||||
return cn_match;
|
||||
|
||||
}
|
||||
|
||||
bool RedPeer::verify_subject(X509* cert, const HostAuthOptions::CertFieldValueList& subject)
|
||||
{
|
||||
X509_NAME* cert_subject = NULL;
|
||||
HostAuthOptions::CertFieldValueList::const_iterator subject_iter;
|
||||
X509_NAME* in_subject;
|
||||
int ret;
|
||||
|
||||
ASSERT(cert);
|
||||
|
||||
cert_subject = X509_get_subject_name(cert);
|
||||
if (!cert_subject) {
|
||||
LOG_WARN("reading certificate subject failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (X509_NAME_entry_count(cert_subject) != subject.size()) {
|
||||
DBG(0, "subject mismatch: #entries cert=%d, input=%d",
|
||||
X509_NAME_entry_count(cert_subject), subject.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
in_subject = X509_NAME_new();
|
||||
if (!in_subject) {
|
||||
LOG_WARN("failed to allocate X509_NAME");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (subject_iter = subject.begin(); subject_iter != subject.end(); subject_iter++) {
|
||||
if (!X509_NAME_add_entry_by_txt(in_subject,
|
||||
subject_iter->first.c_str(),
|
||||
MBSTRING_UTF8,
|
||||
(const unsigned char*)subject_iter->second.c_str(),
|
||||
subject_iter->second.length(), -1, 0)) {
|
||||
LOG_WARN("failed to add entry %s=%s to X509_NAME",
|
||||
subject_iter->first.c_str(), subject_iter->second.c_str());
|
||||
X509_NAME_free(in_subject);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = X509_NAME_cmp(cert_subject, in_subject);
|
||||
X509_NAME_free(in_subject);
|
||||
|
||||
if (ret == 0) {
|
||||
DBG(0, "subjects match");
|
||||
return true;
|
||||
} else {
|
||||
DBG(0, "subjects mismatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int RedPeer::ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
int depth;
|
||||
SSL *ssl;
|
||||
X509* cert;
|
||||
SslVerifyCbData* verify_data;
|
||||
int auth_flags;
|
||||
|
||||
depth = X509_STORE_CTX_get_error_depth(ctx);
|
||||
|
||||
ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
if (!ssl) {
|
||||
LOG_WARN("failed to get ssl connection");
|
||||
return 0;
|
||||
}
|
||||
|
||||
verify_data = (SslVerifyCbData*)SSL_get_app_data(ssl);
|
||||
auth_flags = verify_data->info.type_flags;
|
||||
|
||||
if (depth > 0) {
|
||||
// if certificate verification failed, we can still authorize the server
|
||||
// if its public key matches the one we hold in the peer_connect_options.
|
||||
if (!preverify_ok) {
|
||||
DBG(0, "openssl verify failed at depth=%d", depth);
|
||||
verify_data->all_preverify_ok = false;
|
||||
if (auth_flags & HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return preverify_ok;
|
||||
}
|
||||
}
|
||||
|
||||
/* depth == 0 */
|
||||
cert = X509_STORE_CTX_get_current_cert(ctx);
|
||||
if (!cert) {
|
||||
LOG_WARN("failed to get server certificate");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auth_flags & HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
|
||||
if (verify_pubkey(cert, verify_data->info.host_pubkey)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!verify_data->all_preverify_ok || !preverify_ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auth_flags & HostAuthOptions::HOST_AUTH_OP_NAME) {
|
||||
if (verify_host_name(cert, verify_data->host_name)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (auth_flags & HostAuthOptions::HOST_AUTH_OP_SUBJECT) {
|
||||
if (verify_subject(cert, verify_data->info.host_subject)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: use SSL_CTX_set_cipher_list, etc.
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
|
||||
{
|
||||
int return_code;
|
||||
int auth_flags;
|
||||
SslVerifyCbData auth_data;
|
||||
|
||||
connect_unsecure(host, options.secure_port);
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer != INVALID_SOCKET);
|
||||
|
||||
try {
|
||||
@ -137,12 +483,39 @@ void RedPeer::connect_secure(const ConnectionOptions& options, uint32_t ip)
|
||||
#else
|
||||
SSL_METHOD *ssl_method = TLSv1_method();
|
||||
#endif
|
||||
auth_data.info = options.host_auth;
|
||||
auth_data.host_name = host;
|
||||
auth_data.all_preverify_ok = true;
|
||||
|
||||
_ctx = SSL_CTX_new(ssl_method);
|
||||
if (_ctx == NULL) {
|
||||
ssl_error();
|
||||
}
|
||||
|
||||
auth_flags = auth_data.info.type_flags;
|
||||
if ((auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_NAME) ||
|
||||
(auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT)) {
|
||||
std::string CA_file = auth_data.info.CA_file;
|
||||
ASSERT(!CA_file.empty());
|
||||
|
||||
return_code = SSL_CTX_load_verify_locations(_ctx, CA_file.c_str(), NULL);
|
||||
if (return_code != 1) {
|
||||
if (auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
|
||||
LOG_WARN("SSL_CTX_load_verify_locations failed, CA_file=%s. "
|
||||
"only pubkey authentication is active", CA_file.c_str());
|
||||
auth_data.info.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY;
|
||||
}
|
||||
else {
|
||||
LOG_WARN("SSL_CTX_load_verify_locations failed CA_file=%s", CA_file.c_str());
|
||||
ssl_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auth_flags) {
|
||||
SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, ssl_verify_callback);
|
||||
}
|
||||
|
||||
_ssl = SSL_new(_ctx);
|
||||
if (!_ssl) {
|
||||
THROW("create ssl failed");
|
||||
@ -154,10 +527,13 @@ void RedPeer::connect_secure(const ConnectionOptions& options, uint32_t ip)
|
||||
}
|
||||
|
||||
SSL_set_bio(_ssl, sbio, sbio);
|
||||
SSL_set_app_data(_ssl, &auth_data);
|
||||
|
||||
int return_code = SSL_connect(_ssl);
|
||||
return_code = SSL_connect(_ssl);
|
||||
if (return_code <= 0) {
|
||||
SSL_get_error(_ssl, return_code);
|
||||
int ssl_error_code = SSL_get_error(_ssl, return_code);
|
||||
LOG_WARN("failed to connect w/SSL, ssl_error %s",
|
||||
ERR_error_string(ssl_error_code, NULL));
|
||||
ssl_error();
|
||||
}
|
||||
} catch (...) {
|
||||
@ -167,11 +543,6 @@ void RedPeer::connect_secure(const ConnectionOptions& options, uint32_t ip)
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
|
||||
{
|
||||
connect_secure(options, host_by_name(host));
|
||||
}
|
||||
|
||||
void RedPeer::shutdown()
|
||||
{
|
||||
if (_peer != INVALID_SOCKET) {
|
||||
|
||||
@ -37,6 +37,30 @@ public:
|
||||
class OutMessage;
|
||||
class DisconnectedException {};
|
||||
|
||||
class HostAuthOptions {
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
HOST_AUTH_OP_PUBKEY = 1,
|
||||
HOST_AUTH_OP_NAME = (1 << 1),
|
||||
HOST_AUTH_OP_SUBJECT = (1 << 2),
|
||||
};
|
||||
|
||||
typedef std::vector<uint8_t> PublicKey;
|
||||
typedef std::pair<std::string, std::string> CertFieldValuePair;
|
||||
typedef std::list<CertFieldValuePair> CertFieldValueList;
|
||||
|
||||
HostAuthOptions() : type_flags(0) {}
|
||||
|
||||
public:
|
||||
|
||||
int type_flags;
|
||||
|
||||
PublicKey host_pubkey;
|
||||
CertFieldValueList host_subject;
|
||||
std::string CA_file;
|
||||
};
|
||||
|
||||
class ConnectionOptions {
|
||||
public:
|
||||
|
||||
@ -47,10 +71,12 @@ public:
|
||||
CON_OP_BOTH,
|
||||
};
|
||||
|
||||
ConnectionOptions(Type in_type, int in_port, int in_sport)
|
||||
ConnectionOptions(Type in_type, int in_port, int in_sport,
|
||||
const HostAuthOptions& in_host_auth)
|
||||
: type (in_type)
|
||||
, unsecure_port (in_port)
|
||||
, secure_port (in_sport)
|
||||
, host_auth (in_host_auth)
|
||||
{
|
||||
}
|
||||
|
||||
@ -70,12 +96,10 @@ public:
|
||||
Type type;
|
||||
int unsecure_port;
|
||||
int secure_port;
|
||||
HostAuthOptions host_auth; // for secure connection
|
||||
};
|
||||
|
||||
void connect_unsecure(uint32_t ip, int port);
|
||||
void connect_unsecure(const char* host, int port);
|
||||
|
||||
void connect_secure(const ConnectionOptions& options, uint32_t ip);
|
||||
void connect_secure(const ConnectionOptions& options, const char* host);
|
||||
|
||||
void disconnect();
|
||||
@ -95,6 +119,15 @@ protected:
|
||||
virtual void on_event() {}
|
||||
virtual int get_socket() { return _peer;}
|
||||
|
||||
static bool x509_cert_host_name_compare(const char *cert_name, int cert_name_size,
|
||||
const char *host_name);
|
||||
|
||||
static bool verify_pubkey(X509* cert, const HostAuthOptions::PublicKey& key);
|
||||
static bool verify_host_name(X509* cert, const char* host_name);
|
||||
static bool verify_subject(X509* cert, const HostAuthOptions::CertFieldValueList& subject);
|
||||
|
||||
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
|
||||
|
||||
private:
|
||||
void shutdown();
|
||||
void cleanup();
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "win_platform.h"
|
||||
#include "utils.h"
|
||||
@ -28,6 +30,8 @@
|
||||
#include "cursor.h"
|
||||
#include "named_pipe.h"
|
||||
|
||||
#define SPICE_CONFIG_DIR "spicec\\"
|
||||
|
||||
int gdi_handlers = 0;
|
||||
extern HINSTANCE instance;
|
||||
|
||||
@ -433,6 +437,21 @@ bool Platform::is_monitors_pos_valid()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform::get_spice_config_dir(std::string& path)
|
||||
{
|
||||
char app_data_path[MAX_PATH];
|
||||
HRESULT res = SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, app_data_path);
|
||||
if (res != S_OK) {
|
||||
throw Exception("get user app data dir failed");
|
||||
}
|
||||
|
||||
path = app_data_path;
|
||||
if (strcmp((app_data_path + strlen(app_data_path) - 2), "\\") != 0) {
|
||||
path += "\\";
|
||||
}
|
||||
path += SPICE_CONFIG_DIR;
|
||||
}
|
||||
|
||||
void Platform::init()
|
||||
{
|
||||
create_message_wind();
|
||||
|
||||
@ -64,6 +64,8 @@
|
||||
#define USE_XRANDR_1_2
|
||||
#endif
|
||||
|
||||
#define SPICE_CONFIG_DIR ".spicec/"
|
||||
|
||||
static Display* x_display = NULL;
|
||||
static bool x_shm_avail = false;
|
||||
static XVisualInfo **vinfo = NULL;
|
||||
@ -1911,6 +1913,18 @@ bool Platform::is_monitors_pos_valid()
|
||||
return (ScreenCount(x_display) == 1);
|
||||
}
|
||||
|
||||
void Platform::get_spice_config_dir(std::string& path)
|
||||
{
|
||||
char* home_dir = getenv("HOME");
|
||||
if (!home_dir) {
|
||||
throw Exception("get home dir failed");
|
||||
}
|
||||
|
||||
path = home_dir;
|
||||
path += "/";
|
||||
path += SPICE_CONFIG_DIR;
|
||||
}
|
||||
|
||||
static void root_win_proc(XEvent& event)
|
||||
{
|
||||
#ifdef USE_XRANDR_1_2
|
||||
|
||||
21
common/red.h
21
common/red.h
@ -46,7 +46,7 @@
|
||||
|
||||
#define RED_MAGIC (*(uint32_t*)"REDQ")
|
||||
#define RED_VERSION_MAJOR (~(uint32_t)0 - 1)
|
||||
#define RED_VERSION_MINOR 1
|
||||
#define RED_VERSION_MINOR 2
|
||||
|
||||
// Encryption & Ticketing Parameters
|
||||
#define RED_MAX_PASSWORD_LENGTH 60
|
||||
@ -209,10 +209,27 @@ typedef struct ATTR_PACKED RedMultiMediaTime {
|
||||
uint32_t time;
|
||||
} RedMultiMediaTime;
|
||||
|
||||
enum {
|
||||
RED_PUBKEY_TYPE_INVALID,
|
||||
RED_PUBKEY_TYPE_RSA,
|
||||
RED_PUBKEY_TYPE_RSA2,
|
||||
RED_PUBKEY_TYPE_DSA,
|
||||
RED_PUBKEY_TYPE_DSA1,
|
||||
RED_PUBKEY_TYPE_DSA2,
|
||||
RED_PUBKEY_TYPE_DSA3,
|
||||
RED_PUBKEY_TYPE_DSA4,
|
||||
RED_PUBKEY_TYPE_DH,
|
||||
RED_PUBKEY_TYPE_EC,
|
||||
};
|
||||
|
||||
typedef struct ATTR_PACKED RedMigrationBegin {
|
||||
uint16_t port;
|
||||
uint16_t sport;
|
||||
char host[0];
|
||||
uint32_t host_offset;
|
||||
uint32_t host_size;
|
||||
uint16_t pub_key_type;
|
||||
uint32_t pub_key_offset;
|
||||
uint32_t pub_key_size;
|
||||
} RedMigrationBegin;
|
||||
|
||||
enum {
|
||||
|
||||
214
server/reds.c
214
server/reds.c
@ -61,9 +61,10 @@ static VDIPortInterface *vdagent = NULL;
|
||||
|
||||
#define MIGRATION_NOTIFY_SPICE_KEY "spice_mig_ext"
|
||||
|
||||
#define REDS_MIG_VERSION 1
|
||||
#define REDS_MIG_VERSION 3
|
||||
#define REDS_MIG_CONTINUE 1
|
||||
#define REDS_MIG_ABORT 2
|
||||
#define REDS_MIG_DIFF_VERSION 3
|
||||
|
||||
#define REDS_AGENT_WINDOW_SIZE 10
|
||||
#define REDS_TOKENS_TO_SEND 5
|
||||
@ -278,6 +279,7 @@ typedef struct RedsState {
|
||||
uint32_t ping_id;
|
||||
uint32_t net_test_id;
|
||||
int net_test_stage;
|
||||
int peer_minor_version;
|
||||
} RedsState;
|
||||
|
||||
uint64_t bitrate_per_sec = ~0;
|
||||
@ -2908,6 +2910,8 @@ static void reds_handle_read_header_done(void *opaque)
|
||||
return;
|
||||
}
|
||||
|
||||
reds->peer_minor_version = header->minor_version;
|
||||
|
||||
if (header->size < sizeof(RedLinkMess)) {
|
||||
reds_send_link_error(link, RED_ERR_INVALID_DATA);
|
||||
red_printf("bad size %u", header->size);
|
||||
@ -4175,12 +4179,20 @@ typedef struct RedsMigSpice {
|
||||
char *host;
|
||||
int port;
|
||||
int sport;
|
||||
uint16_t cert_pub_key_type;
|
||||
uint32_t cert_pub_key_len;
|
||||
uint8_t* cert_pub_key;
|
||||
} RedsMigSpice;
|
||||
|
||||
typedef struct RedsMigSpiceMessage {
|
||||
uint32_t link_id;
|
||||
} RedsMigSpiceMessage;
|
||||
|
||||
typedef struct RedsMigCertPubKeyInfo {
|
||||
uint16_t type;
|
||||
uint32_t len;
|
||||
} RedsMigCertPubKeyInfo;
|
||||
|
||||
static int reds_mig_actual_read(RedsMigSpice *s)
|
||||
{
|
||||
for (;;) {
|
||||
@ -4289,7 +4301,9 @@ static void reds_mig_continue(RedsMigSpice *s)
|
||||
red_printf("");
|
||||
core->set_file_handlers(core, s->fd, NULL, NULL, NULL);
|
||||
host_len = strlen(s->host) + 1;
|
||||
if (!(item = new_simple_out_item(RED_MIGRATE_BEGIN, sizeof(RedMigrationBegin) + host_len))) {
|
||||
item = new_simple_out_item(RED_MIGRATE_BEGIN,
|
||||
sizeof(RedMigrationBegin) + host_len + s->cert_pub_key_len);
|
||||
if (!(item)) {
|
||||
red_printf("alloc item failed");
|
||||
reds_disconnect();
|
||||
return;
|
||||
@ -4297,7 +4311,13 @@ static void reds_mig_continue(RedsMigSpice *s)
|
||||
migrate = (RedMigrationBegin *)item->data;
|
||||
migrate->port = s->port;
|
||||
migrate->sport = s->sport;
|
||||
memcpy(migrate->host, s->host, host_len);
|
||||
migrate->host_offset = sizeof(RedMigrationBegin);
|
||||
migrate->host_size = host_len;
|
||||
migrate->pub_key_type = s->cert_pub_key_type;
|
||||
migrate->pub_key_offset = sizeof(RedMigrationBegin) + host_len;
|
||||
migrate->pub_key_size = s->cert_pub_key_len;
|
||||
memcpy((uint8_t*)(migrate) + migrate->host_offset , s->host, host_len);
|
||||
memcpy((uint8_t*)(migrate) + migrate->pub_key_offset, s->cert_pub_key, s->cert_pub_key_len);
|
||||
reds_push_pipe_item(&item->base);
|
||||
|
||||
free(s->local_args);
|
||||
@ -4362,6 +4382,68 @@ static void reds_mig_send_ticket(RedsMigSpice *s)
|
||||
BIO_free(bio_key);
|
||||
}
|
||||
|
||||
static void reds_mig_receive_cert_public_key(RedsMigSpice *s)
|
||||
{
|
||||
s->cert_pub_key = malloc(s->cert_pub_key_len);
|
||||
if (!s->cert_pub_key) {
|
||||
red_printf("alloc failed");
|
||||
reds_mig_failed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(s->cert_pub_key, s->read.buf, s->cert_pub_key_len);
|
||||
|
||||
s->read.size = RED_TICKET_PUBKEY_BYTES;
|
||||
s->read.end_pos = 0;
|
||||
s->read.handle_data = reds_mig_send_ticket;
|
||||
|
||||
core->set_file_handlers(core, s->fd, reds_mig_read, NULL, s);
|
||||
}
|
||||
|
||||
static void reds_mig_receive_cert_public_key_info(RedsMigSpice *s)
|
||||
{
|
||||
RedsMigCertPubKeyInfo* pubkey_info = (RedsMigCertPubKeyInfo*)s->read.buf;
|
||||
s->cert_pub_key_type = pubkey_info->type;
|
||||
s->cert_pub_key_len = pubkey_info->len;
|
||||
|
||||
if (s->cert_pub_key_len > RECIVE_BUF_SIZE) {
|
||||
red_printf("certificate public key length exceeds buffer size");
|
||||
reds_mig_failed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->cert_pub_key_len) {
|
||||
s->read.size = s->cert_pub_key_len;
|
||||
s->read.end_pos = 0;
|
||||
s->read.handle_data = reds_mig_receive_cert_public_key;
|
||||
} else {
|
||||
s->cert_pub_key = NULL;
|
||||
s->read.size = RED_TICKET_PUBKEY_BYTES;
|
||||
s->read.end_pos = 0;
|
||||
s->read.handle_data = reds_mig_send_ticket;
|
||||
}
|
||||
|
||||
core->set_file_handlers(core, s->fd, reds_mig_read, NULL, s);
|
||||
}
|
||||
|
||||
static void reds_mig_handle_send_abort_done(RedsMigSpice *s)
|
||||
{
|
||||
reds_mig_failed(s);
|
||||
}
|
||||
|
||||
static void reds_mig_receive_version(RedsMigSpice *s)
|
||||
{
|
||||
uint32_t* dest_version;
|
||||
uint32_t resault;
|
||||
dest_version = (uint32_t*)s->read.buf;
|
||||
resault = REDS_MIG_ABORT;
|
||||
memcpy(s->write.buf, &resault, sizeof(resault));
|
||||
s->write.length = sizeof(resault);
|
||||
s->write.now = s->write.buf;
|
||||
s->write.handle_done = reds_mig_handle_send_abort_done;
|
||||
core->set_file_handlers(core, s->fd, reds_mig_write, reds_mig_write, s);
|
||||
}
|
||||
|
||||
static void reds_mig_control(RedsMigSpice *spice_migration)
|
||||
{
|
||||
uint32_t *control;
|
||||
@ -4371,9 +4453,9 @@ static void reds_mig_control(RedsMigSpice *spice_migration)
|
||||
|
||||
switch (*control) {
|
||||
case REDS_MIG_CONTINUE:
|
||||
spice_migration->read.size = RED_TICKET_PUBKEY_BYTES;
|
||||
spice_migration->read.size = sizeof(RedsMigCertPubKeyInfo);
|
||||
spice_migration->read.end_pos = 0;
|
||||
spice_migration->read.handle_data = reds_mig_send_ticket;
|
||||
spice_migration->read.handle_data = reds_mig_receive_cert_public_key_info;
|
||||
|
||||
core->set_file_handlers(core, spice_migration->fd, reds_mig_read,
|
||||
NULL, spice_migration);
|
||||
@ -4382,6 +4464,15 @@ static void reds_mig_control(RedsMigSpice *spice_migration)
|
||||
red_printf("abort");
|
||||
reds_mig_failed(spice_migration);
|
||||
break;
|
||||
case REDS_MIG_DIFF_VERSION:
|
||||
red_printf("different versions");
|
||||
spice_migration->read.size = sizeof(uint32_t);
|
||||
spice_migration->read.end_pos = 0;
|
||||
spice_migration->read.handle_data = reds_mig_receive_version;
|
||||
|
||||
core->set_file_handlers(core, spice_migration->fd, reds_mig_read,
|
||||
NULL, spice_migration);
|
||||
break;
|
||||
default:
|
||||
red_printf("invalid control");
|
||||
reds_mig_failed(spice_migration);
|
||||
@ -4423,6 +4514,12 @@ static void reds_mig_started(void *opaque, const char *in_args)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((RED_VERSION_MAJOR == 1) && (reds->peer_minor_version < 2)) {
|
||||
red_printf("minor version mismatch client %u server %u",
|
||||
reds->peer_minor_version, RED_VERSION_MINOR);
|
||||
goto error;
|
||||
}
|
||||
|
||||
spice_migration = (RedsMigSpice *)malloc(sizeof(RedsMigSpice));
|
||||
if (!spice_migration) {
|
||||
red_printf("Could not allocate memory for spice migration structure");
|
||||
@ -4625,6 +4722,85 @@ static void reds_mig_write_all(int fd, void *buf, int len, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_mig_send_cert_public_key(int fd)
|
||||
{
|
||||
FILE* cert_file;
|
||||
X509* x509;
|
||||
EVP_PKEY* pub_key;
|
||||
unsigned char* pp = NULL;
|
||||
int length;
|
||||
BIO* mem_bio;
|
||||
RedsMigCertPubKeyInfo pub_key_info_msg;
|
||||
|
||||
if (spice_secure_port == -1) {
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_INVALID;
|
||||
pub_key_info_msg.len = 0;
|
||||
reds_mig_write_all(fd, &pub_key_info_msg, sizeof(pub_key_info_msg), "cert public key info");
|
||||
return;
|
||||
}
|
||||
|
||||
cert_file = fopen(ssl_parameters.certs_file, "r");
|
||||
if (!cert_file) {
|
||||
red_error("opening certificate failed");
|
||||
}
|
||||
|
||||
x509 = PEM_read_X509_AUX(cert_file, NULL, NULL, NULL);
|
||||
if (!x509) {
|
||||
red_error("reading x509 cert failed");
|
||||
}
|
||||
pub_key = X509_get_pubkey(x509);
|
||||
if (!pub_key) {
|
||||
red_error("reading public key failed");
|
||||
}
|
||||
|
||||
mem_bio = BIO_new(BIO_s_mem());
|
||||
i2d_PUBKEY_bio(mem_bio, pub_key);
|
||||
if (BIO_flush(mem_bio) != 1) {
|
||||
red_error("bio flush failed");
|
||||
}
|
||||
length = BIO_get_mem_data(mem_bio, &pp);
|
||||
|
||||
switch(pub_key->type) {
|
||||
case EVP_PKEY_RSA:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_RSA;
|
||||
break;
|
||||
case EVP_PKEY_RSA2:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_RSA2;
|
||||
break;
|
||||
case EVP_PKEY_DSA:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DSA;
|
||||
break;
|
||||
case EVP_PKEY_DSA1:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DSA1;
|
||||
break;
|
||||
case EVP_PKEY_DSA2:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DSA2;
|
||||
break;
|
||||
case EVP_PKEY_DSA3:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DSA3;
|
||||
break;
|
||||
case EVP_PKEY_DSA4:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DSA4;
|
||||
break;
|
||||
case EVP_PKEY_DH:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_DH;
|
||||
break;
|
||||
case EVP_PKEY_EC:
|
||||
pub_key_info_msg.type = RED_PUBKEY_TYPE_EC;
|
||||
break;
|
||||
default:
|
||||
red_error("invalid public key type");
|
||||
}
|
||||
pub_key_info_msg.len = length;
|
||||
reds_mig_write_all(fd, &pub_key_info_msg, sizeof(pub_key_info_msg), "cert public key info");
|
||||
reds_mig_write_all(fd, pp, length, "cert public key");
|
||||
|
||||
BIO_free(mem_bio);
|
||||
fclose(cert_file);
|
||||
EVP_PKEY_free(pub_key);
|
||||
X509_free(x509);
|
||||
}
|
||||
|
||||
static void reds_mig_recv(void *opaque, int fd)
|
||||
{
|
||||
uint32_t ack_message = *(uint32_t *)"ack_";
|
||||
@ -4639,16 +4815,36 @@ static void reds_mig_recv(void *opaque, int fd)
|
||||
BUF_MEM *buff;
|
||||
|
||||
reds_mig_read_all(fd, &version, sizeof(version), "version");
|
||||
|
||||
if (version != REDS_MIG_VERSION) {
|
||||
// starting from version 3, if the version of the src is bigger
|
||||
// than ours, we send our version to the src.
|
||||
if (version < REDS_MIG_VERSION) {
|
||||
resault = REDS_MIG_ABORT;
|
||||
reds_mig_write_all(fd, &resault, sizeof(resault), "resault");
|
||||
mig->notifier_done(mig, reds->mig_notifier);
|
||||
return;
|
||||
} else if (version > REDS_MIG_VERSION) {
|
||||
uint32_t src_resault;
|
||||
uint32_t self_version = REDS_MIG_VERSION;
|
||||
resault = REDS_MIG_DIFF_VERSION;
|
||||
reds_mig_write_all(fd, &resault, sizeof(resault), "resault");
|
||||
reds_mig_write_all(fd, &self_version, sizeof(self_version), "dest-version");
|
||||
reds_mig_read_all(fd, &src_resault, sizeof(src_resault), "src resault");
|
||||
|
||||
if (src_resault == REDS_MIG_ABORT) {
|
||||
red_printf("abort (response to REDS_MIG_DIFF_VERSION)");
|
||||
mig->notifier_done(mig, reds->mig_notifier);
|
||||
return;
|
||||
} else if (src_resault != REDS_MIG_CONTINUE) {
|
||||
red_printf("invalid response to REDS_MIG_DIFF_VERSION");
|
||||
mig->notifier_done(mig, reds->mig_notifier);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
resault = REDS_MIG_CONTINUE;
|
||||
reds_mig_write_all(fd, &resault, sizeof(resault), "resault");
|
||||
}
|
||||
|
||||
resault = REDS_MIG_CONTINUE;
|
||||
reds_mig_write_all(fd, &resault, sizeof(resault), "resault");
|
||||
reds_mig_send_cert_public_key(fd);
|
||||
|
||||
ticketing_info.bn = BN_new();
|
||||
if (!ticketing_info.bn) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user