summaryrefslogtreecommitdiff
path: root/gtk/spice-channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/spice-channel.c')
-rw-r--r--gtk/spice-channel.c140
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;