diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2011-01-25 13:03:12 +0100 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@redhat.com> | 2011-01-25 16:53:20 +0100 |
commit | d01fa70c9421160ba2480bb6a6ab23db2bf2fe3e (patch) | |
tree | ce1d14106dc0d2e44ef3494585b376a5b04c62c8 | |
parent | 8a5a51acca9dcbe4c17b48491fe65ea70e38e48c (diff) |
gtk: make use of common/ssl_verify.c
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/spice-channel-priv.h | 2 | ||||
-rw-r--r-- | gtk/spice-channel.c | 272 |
3 files changed, 61 insertions, 215 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 57da968..0d117ec 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -205,6 +205,8 @@ libspice_client_glib_2_0_la_SOURCES = \ $(COMMON_DIR)/lz.h \ $(COMMON_DIR)/region.c \ $(COMMON_DIR)/region.h \ + $(COMMON_DIR)/ssl_verify.c \ + $(COMMON_DIR)/ssl_verify.h \ $(NULL) if WITH_PULSE diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h index aeefc93..1bd6774 100644 --- a/gtk/spice-channel-priv.h +++ b/gtk/spice-channel-priv.h @@ -27,6 +27,7 @@ /* common/ */ #include "marshallers.h" #include "demarshallers.h" +#include "ssl_verify.h" G_BEGIN_DECLS @@ -65,6 +66,7 @@ struct spice_channel { /* swapped on migration */ SSL_CTX *ctx; SSL *ssl; + SpiceOpenSSLVerify *sslverify; GSocket *sock; /* not swapped */ diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index 95d82db..f0b1a91 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -1168,200 +1168,6 @@ void spice_channel_destroy(SpiceChannel *channel) g_object_unref(channel); } -/* from gnutls - * compare hostname against certificate, taking account of wildcards - * return 1 on success or 0 on error - * - * note: certnamesize is required as X509 certs can contain embedded NULs in - * the strings such as CN or subjectAltName - */ -static int _gnutls_hostname_compare(const char *certname, - size_t certnamesize, const char *hostname) -{ - /* find the first different character */ - for (; *certname && *hostname && toupper (*certname) == toupper (*hostname); - certname++, hostname++, certnamesize--) - ; - - /* the strings are the same */ - if (certnamesize == 0 && *hostname == '\0') - return 1; - - if (*certname == '*') - { - /* a wildcard certificate */ - - certname++; - certnamesize--; - - while (1) - { - /* Use a recursive call to allow multiple wildcards */ - if (_gnutls_hostname_compare (certname, certnamesize, hostname)) - return 1; - - /* wildcards are only allowed to match a single domain - component or component fragment */ - if (*hostname == '\0' || *hostname == '.') - break; - hostname++; - } - - return 0; - } - - return 0; -} - -/** - * From gnutls and spice red_peer.c - * TODO: switch to gnutls and get rid of this - * - * This function will check if the given certificate's subject matches - * the given 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. - * - * Returns: TRUE for a successful match, and FALSE on failure. - **/ -static gboolean _x509_crt_check_hostname(X509* cert, const char *hostname) -{ - GENERAL_NAMES* subject_alt_names; - gboolean found_dns_name = FALSE; - struct in_addr addr; - int addr_len = 0; - gboolean cn_match = FALSE; - - g_return_val_if_fail(cert != NULL, FALSE); - // only IpV4 supported - if (inet_aton(hostname, &addr)) { - addr_len = sizeof(struct in_addr); - } - - /* try matching against: - * 1) a DNS name 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 - */ - - /* Check through all included subjectAltName extensions, comparing - * against all those 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 (_gnutls_hostname_compare((char *)ASN1_STRING_data(name->d.dNSName), - ASN1_STRING_length(name->d.dNSName), - hostname)) { - SPICE_DEBUG("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)) { - SPICE_DEBUG("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) { - g_warning("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 (_gnutls_hostname_compare((char*)ASN1_STRING_data(cn_asn1), - ASN1_STRING_length(cn_asn1), - hostname)) { - SPICE_DEBUG("common name match=%s", (char*)ASN1_STRING_data(cn_asn1)); - cn_match = TRUE; - break; - } - } - } - - if (!cn_match) - g_warning("common name mismatch"); - - return cn_match; -} - -/* coroutine context */ -static int tls_verify(int preverify_ok, X509_STORE_CTX *ctx) -{ - int depth; - spice_channel *c; - char *hostname; - SSL *ssl; - X509* cert; - - ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - c = SSL_get_app_data(ssl); - - depth = X509_STORE_CTX_get_error_depth(ctx); - if (depth > 0) { - if (!preverify_ok) { - SPICE_DEBUG("openssl verify failed at depth=%d", depth); - c->all_preverify_ok = FALSE; - return 0; - } else - return 1; - } - - /* depth == 0 */ - cert = X509_STORE_CTX_get_current_cert(ctx); - if (!cert) { - g_critical("failed to get server certificate"); - return 0; - } - - if (!c->all_preverify_ok || !preverify_ok) { - return 0; - } - - if (c->verify & SPICE_CHANNEL_VERIFY_HOSTNAME) { - g_object_get(c->session, "host", &hostname, NULL); - if (_x509_crt_check_hostname(cert, hostname)) - return 1; - } - - return 0; -} - /* coroutine context */ static void spice_channel_iterate_write(SpiceChannel *channel) { @@ -1483,16 +1289,16 @@ reconnect: SPICE_DEBUG("connection failed, trying with TLS port"); c->tls = true; /* FIXME: does that really work with provided fd */ goto reconnect; + } else { + SPICE_DEBUG("Connect error"); + emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_CONNECT); + goto cleanup; } - SPICE_DEBUG("Connect error"); - emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_CONNECT); - goto cleanup; } c->has_error = FALSE; if (c->tls) { - gchar *ca_file; int rc; c->ctx = SSL_CTX_new(TLSv1_method()); @@ -1501,15 +1307,27 @@ reconnect: goto cleanup; } - 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) { - g_warning("loading ca certs from %s failed", ca_file); + verify = spice_session_get_verify(c->session); + if (verify & + (SPICE_SESSION_VERIFY_PUBKEY | SPICE_SESSION_VERIFY_HOSTNAME)) { + gchar *ca_file; + + 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 != 1) + g_warning("loading ca certs from %s failed", ca_file); + g_free(ca_file); + + if (rc != 1) { + if (c->verify & SPICE_CHANNEL_VERIFY_PUBKEY) { + g_warning("only pubkey active"); + c->verify = SPICE_CHANNEL_VERIFY_PUBKEY; + } else + goto cleanup; + } } } - c->all_preverify_ok = TRUE; - SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, tls_verify); c->ssl = SSL_new(c->ctx); if (c->ssl == NULL) { @@ -1521,7 +1339,25 @@ reconnect: g_critical("SSL_set_fd failed"); goto cleanup; } - SSL_set_app_data(c->ssl, c); + + + { + gchar *hostname, *subject; + guint8 *pubkey; + guint pubkey_len; + + g_object_get(c->session, + "host", &hostname, + "cert-subject", &subject, NULL); + spice_session_get_pubkey(c->session, &pubkey, &pubkey_len); + c->sslverify = spice_openssl_verify_new(c->ssl, c->verify, + hostname, + (char*)pubkey, pubkey_len, + subject); + g_free(hostname); + g_free(subject); + } + ssl_reconnect: rc = SSL_connect(c->ssl); if (rc <= 0) { @@ -1555,6 +1391,7 @@ connected: cleanup: SPICE_DEBUG("Coroutine exit"); + SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel); g_idle_add(spice_channel_delayed_unref, data); @@ -1671,15 +1508,17 @@ static void channel_disconnect(SpiceChannel *channel) c->connect_delayed_id = 0; } - if (c->tls) { - if (c->ssl) { - SSL_free(c->ssl); - c->ssl = NULL; - } - if (c->ctx) { - SSL_CTX_free(c->ctx); - c->ctx = NULL; - } + spice_openssl_verify_free(c->sslverify); + c->sslverify = NULL; + + if (c->ssl) { + SSL_free(c->ssl); + c->ssl = NULL; + } + + if (c->ctx) { + SSL_CTX_free(c->ctx); + c->ctx = NULL; } if (c->sock) { @@ -1830,13 +1669,16 @@ void spice_channel_swap(SpiceChannel *channel, SpiceChannel *swap) GSocket *sock = c->sock; SSL_CTX *ctx = c->ctx; SSL *ssl = c->ssl; + SpiceOpenSSLVerify *sslverify = c->sslverify; c->sock = s->sock; c->ctx = s->ctx; c->ssl = s->ssl; + c->sslverify = s->sslverify; s->sock = sock; s->ctx = ctx; s->ssl = ssl; + s->sslverify = sslverify; } } |