diff options
Diffstat (limited to 'gtk/spice-channel.c')
-rw-r--r-- | gtk/spice-channel.c | 140 |
1 files changed, 120 insertions, 20 deletions
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index b78f744..6c980a2 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -7,10 +7,12 @@ #include <openssl/evp.h> #include <openssl/x509.h> #include <openssl/ssl.h> +#include <openssl/err.h> #include <sys/socket.h> static void spice_channel_send_msg(SpiceChannel *channel, spice_msg_out *out); +static void spice_channel_send_link(SpiceChannel *channel); /* ------------------------------------------------------------------ */ /* gobject glue */ @@ -29,8 +31,7 @@ static void spice_channel_init(SpiceChannel *channel) c->socket = -1; } -static void -spice_channel_finalize(GObject *gobject) +static void spice_channel_finalize(GObject *gobject) { SpiceChannel *channel = SPICE_CHANNEL(gobject); @@ -250,32 +251,75 @@ static int spice_channel_send(SpiceChannel *channel, void *buf, int len) { spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); - return send(c->socket, buf, len, 0); + if (c->tls) { + return SSL_write(c->ssl, buf, len); + } else { + return send(c->socket, buf, len, 0); + } } static int spice_channel_recv(SpiceChannel *channel, void *buf, int len) { spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); - int rc; + int rc, err; - rc = recv(c->socket, buf, len, 0); - switch (rc) { - case -1: - if (errno == EAGAIN) + if (c->tls) { + rc = SSL_read(c->ssl, buf, len); + if (rc > 0) { + return rc; + } + if (rc == 0) { + fprintf(stderr, "channel/tls eof: %d:%d (%s)\n", + c->info->type, c->channel_id, c->info->name); + spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED); return 0; - fprintf(stderr, "channel error: %d:%d (%s) %s\n", + } + err = SSL_get_error(c->ssl, rc); + if (err == SSL_ERROR_WANT_READ) { + return 0; + } + fprintf(stderr, "channel/tls error: %d:%d (%s): %s\n", c->info->type, c->channel_id, c->info->name, - strerror(errno)); + ERR_error_string(err, NULL)); spice_channel_disconnect(channel, SPICE_CHANNEL_ERROR_IO); return 0; - case 0: - fprintf(stderr, "channel eof: %d:%d (%s)\n", - c->info->type, c->channel_id, c->info->name); - spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED); - return 0; - default: - return rc; + } else { + rc = recv(c->socket, buf, len, 0); + switch (rc) { + case -1: + if (errno == EAGAIN) + return 0; + fprintf(stderr, "channel error: %d:%d (%s): %s\n", + c->info->type, c->channel_id, c->info->name, + strerror(errno)); + spice_channel_disconnect(channel, SPICE_CHANNEL_ERROR_IO); + return 0; + case 0: + fprintf(stderr, "channel eof: %d:%d (%s)\n", + c->info->type, c->channel_id, c->info->name); + spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED); + return 0; + default: + return rc; + } + } +} + +static void spice_channel_tls_connect(SpiceChannel *channel) +{ + spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); + int rc, err; + + rc = SSL_connect(c->ssl); + if (rc <= 0) { + err = SSL_get_error(c->ssl, rc); + if (err == SSL_ERROR_WANT_READ) { + return; + } + PANIC("SSL_connect failed: %s", ERR_error_string(err, NULL)); } + c->state = SPICE_CHANNEL_STATE_LINK_HDR; + spice_channel_send_link(channel); } static void spice_channel_send_auth(SpiceChannel *channel) @@ -285,10 +329,13 @@ static void spice_channel_send_auth(SpiceChannel *channel) int nRSASize; BIO *bioKey; RSA *rsa; - const uint8_t *password = spice_session_get_password(c->session); + const uint8_t *password; uint8_t *encrypted; int rc; + g_object_get(c->session, "password", &password, NULL); + fprintf(stderr, "%s: password \"%s\"\n", __FUNCTION__, password); + bioKey = BIO_new(BIO_s_mem()); if (bioKey == NULL) PANIC("Could not initiate BIO"); @@ -303,7 +350,8 @@ static void spice_channel_send_auth(SpiceChannel *channel) The use of RSA encryption limit the potential maximum password length. for RSA_PKCS1_OAEP_PADDING it is RSA_size(rsa) - 41. */ - rc = RSA_public_encrypt(strlen(password) + 1, password, encrypted, + rc = RSA_public_encrypt(strlen((char*)password) + 1, + password, encrypted, rsa, RSA_PKCS1_OAEP_PADDING); if (rc <= 0) PANIC("could not encrypt password"); @@ -564,6 +612,9 @@ static void spice_channel_data(int event, void *opaque) spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); switch (c->state) { + case SPICE_CHANNEL_STATE_TLS: + spice_channel_tls_connect(channel); + break; case SPICE_CHANNEL_STATE_LINK_HDR: spice_channel_recv_link_hdr(channel); break; @@ -616,9 +667,25 @@ int spice_channel_id(SpiceChannel *channel) return c->channel_id; } +static int tls_verify(int preverify_ok, X509_STORE_CTX *ctx) +{ + spice_channel *c; + char *hostname; + SSL *ssl; + + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + c = SSL_get_app_data(ssl); + + g_object_get(c->session, "host", &hostname, NULL); + /* TODO: check hostname */ + + return preverify_ok; +} + gboolean spice_channel_connect(SpiceChannel *channel) { spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel); + int rc, err; if (c->state != SPICE_CHANNEL_STATE_UNCONNECTED) { return true; @@ -638,7 +705,40 @@ reconnect: spice_channel_data, channel); if (c->tls) { - PANIC("TODO: tls"); + char *ca_file; + + c->ctx = SSL_CTX_new(TLSv1_method()); + if (c->ctx == NULL) { + PANIC("SSL_CTX_new failed"); + } + + g_object_get(c->session, "ca-file", &ca_file, NULL); + if (ca_file) { + rc = SSL_CTX_load_verify_locations(c->ctx, ca_file, NULL); + if (rc <= 0) { + fprintf(stderr, "loading ca certs from %s failed\n", ca_file); + } + } + SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, tls_verify); + + c->ssl = SSL_new(c->ctx); + if (c->ssl == NULL) { + PANIC("SSL_new failed"); + } + rc = SSL_set_fd(c->ssl, c->socket); + if (rc <= 0) { + PANIC("SSL_set_fd failed"); + } + SSL_set_app_data(c->ssl, c); + rc = SSL_connect(c->ssl); + if (rc <= 0) { + err = SSL_get_error(c->ssl, rc); + if (err == SSL_ERROR_WANT_READ) { + c->state = SPICE_CHANNEL_STATE_TLS; + return 0; + } + PANIC("SSL_connect failed: %s", ERR_error_string(err, NULL)); + } } c->state = SPICE_CHANNEL_STATE_LINK_HDR; |