spice: verify against the entry's PEM CA, not a file path

The SPICE CA certificate field and the Proxmox spiceproxy response
both carry PEM text, which rustguac sends as guacd's ca-cert arg. The
SPICE plugin passed that value to spice-gtk's ca-file property, which
is a path. OpenSSL could not open a file named after the certificate,
spice-gtk logged "loading ca certs from -----BEGIN CERTIFICATE-----
... failed", fell back to the system trust store, and rejected any
server signed by a private CA, so verification only worked with
"ignore certificate" turned on.

Add guacd patch 011: when ca-cert contains a PEM header, hand it to
spice-gtk's in-memory ca property (a GByteArray of PEM data, available
since spice-gtk 0.15), which adds each certificate to the TLS trust
store. The certificate never touches disk. Any other value is still
passed as ca-file, so a path keeps working. The settings field is
renamed ca_file to ca_cert to match what it holds.

Fixes #308

(cherry picked from commit 6bfc5b38f2)
This commit is contained in:
Jad Seifeddine
2026-10-19 14:50:51 +07:00
committed by Dave Kempe
parent 30913f586d
commit 20a1da28ca
2 changed files with 89 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
diff --git a/src/protocols/spice/auth.c b/src/protocols/spice/auth.c
index ac89c90a..f381f875 100644
--- a/src/protocols/spice/auth.c
+++ b/src/protocols/spice/auth.c
@@ -51,9 +51,20 @@ void guac_spice_session_configure(guac_client* client, SpiceSession* session) {
g_object_set(session, "tls-port", settings->tls_port, NULL);
- /* Provide CA certificate for verification, if given */
- if (settings->ca_file != NULL)
- g_object_set(session, "ca-file", settings->ca_file, NULL);
+ /* Provide CA certificate for verification, if given. PEM data is
+ * passed in memory through the "ca" property; anything else is
+ * treated as the path to a CA file. */
+ if (settings->ca_cert != NULL) {
+ if (strstr(settings->ca_cert, "-----BEGIN ") != NULL) {
+ GByteArray* ca = g_byte_array_new();
+ g_byte_array_append(ca, (const guint8*) settings->ca_cert,
+ strlen(settings->ca_cert));
+ g_object_set(session, "ca", ca, NULL);
+ g_byte_array_unref(ca);
+ }
+ else
+ g_object_set(session, "ca-file", settings->ca_cert, NULL);
+ }
/* Provide expected certificate subject, if given */
if (settings->cert_subject != NULL)
diff --git a/src/protocols/spice/settings.c b/src/protocols/spice/settings.c
index c2600f4f..2ea1914c 100644
--- a/src/protocols/spice/settings.c
+++ b/src/protocols/spice/settings.c
@@ -140,8 +140,9 @@ enum SPICE_ARGS_IDX {
IDX_TLS,
/**
- * The path to a PEM-encoded certificate authority file used to verify the
- * SPICE server's TLS certificate.
+ * The certificate authority used to verify the SPICE server's TLS
+ * certificate: either PEM-encoded certificate data, or the path to a file
+ * containing it.
*/
IDX_CA_CERT,
@@ -515,7 +516,7 @@ guac_spice_settings* guac_spice_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_TLS, false);
- settings->ca_file =
+ settings->ca_cert =
guac_user_parse_args_string(user, GUAC_SPICE_CLIENT_ARGS, argv,
IDX_CA_CERT, NULL);
@@ -801,7 +802,7 @@ void guac_spice_settings_free(guac_spice_settings* settings) {
guac_mem_free(settings->tls_port);
guac_mem_free(settings->username);
guac_mem_free(settings->password);
- guac_mem_free(settings->ca_file);
+ guac_mem_free(settings->ca_cert);
guac_mem_free(settings->cert_subject);
guac_mem_free(settings->pubkey);
guac_mem_free(settings->proxy);
diff --git a/src/protocols/spice/settings.h b/src/protocols/spice/settings.h
index 8c3018d4..c2425472 100644
--- a/src/protocols/spice/settings.h
+++ b/src/protocols/spice/settings.h
@@ -129,10 +129,11 @@ typedef struct guac_spice_settings {
bool tls;
/**
- * The path to a PEM-encoded certificate authority file used to verify the
- * SPICE server's TLS certificate, or NULL if no CA file was provided.
+ * The certificate authority used to verify the SPICE server's TLS
+ * certificate, or NULL if none was provided. This is either PEM-encoded
+ * certificate data or the path to a file containing it.
*/
- char* ca_file;
+ char* ca_cert;
/**
* The expected subject of the SPICE server's TLS certificate, or NULL if
+8
View File
@@ -199,6 +199,14 @@ Vendored as the diff of the PR branch against its merge-base with our pinned gua
**Files patched:** `src/protocols/rdp/settings.{c,h}`, `src/protocols/rdp/channels/disp.{c,h}`, `src/protocols/rdp/input.c`, `src/protocols/rdp/user.c`.
## 011-spice-ca-pem.patch
**Bug:** rustguac sends the SPICE CA certificate (the connection entry's PEM, or the Proxmox cluster CA from `spiceproxy`) as the `ca-cert` connect arg, and the plugin from `008` passed it to spice-gtk's `ca-file` property. `ca-file` is a path, so OpenSSL failed to open a "file" named after the PEM text, spice-gtk logged `loading ca certs from ... failed`, fell back to the system trust store, and rejected servers signed by a private CA. See [sol1/rustguac#308](https://github.com/sol1/rustguac/issues/308).
**Fix:** When `ca-cert` contains a PEM header (`-----BEGIN `), pass it in memory through spice-gtk's `ca` property (a `GByteArray` of PEM data, available since spice-gtk 0.15), which adds each certificate to the TLS trust store. Any other value is still treated as a path and passed as `ca-file`. The settings field is renamed `ca_file` -> `ca_cert` to match.
**Files patched:** `src/protocols/spice/auth.c`, `src/protocols/spice/settings.{c,h}`.
## Applying patches
Patches are applied automatically by all build scripts (`build-deb.sh`, `build-rpm.sh`, `install.sh`, `dev.sh`, `Dockerfile`). To apply manually: