From 3ac6daa9e1c5d7dae2a3cd1c6a388174b462f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 16:47:31 +0100 Subject: [PATCH 1/4] ui: fix setting client_endian field defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a VNC client sends a "set pixel format" message, the 'client_endian' field will get initialized, however, it is valid to omit this message if the client wants to use the server's native pixel format. In the latter scenario nothing is initializing the 'client_endian' field, so it remains set to 0, matching neither G_LITTLE_ENDIAN nor G_BIG_ENDIAN. This then results in pixel format conversion routines taking the wrong code paths. This problem existed before the 'client_be' flag was changed into the 'client_endian' value, but the lack of initialization meant it semantically defaulted to little endian, so only big endian systems would potentially be exposed to incorrect pixel translation. The 'virt-viewer' / 'remote-viewer' apps always send a "set pixel format" message so aren't exposed to any problems, but the classical 'vncviewer' app will show the problem easily. Fixes: 7ed96710e82c385c6cfc3d064eec7dde20f0f3fd Reported-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/vnc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/vnc.c b/ui/vnc.c index e9c30aad62..a16be468b9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2329,6 +2329,7 @@ static void pixel_format_message (VncState *vs) { char pad[3] = { 0, 0, 0 }; vs->client_pf = qemu_default_pixelformat(32); + vs->client_endian = G_BYTE_ORDER; vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ vnc_write_u8(vs, vs->client_pf.depth); /* depth */ From 8fc3d63d685751734fb9c8c0284dc44a36a8e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 17:14:38 +0100 Subject: [PATCH 2/4] ui: add trace events for all client messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us see the full flow of RFB messages received from the client. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/trace-events | 14 +++++++++++++ ui/vnc.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/ui/trace-events b/ui/trace-events index 3da0d5e280..3eba9ca3a8 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -48,13 +48,27 @@ vnc_msg_server_ext_desktop_resize(void *state, void *ioc, int width, int height, vnc_msg_client_audio_enable(void *state, void *ioc) "VNC client msg audio enable state=%p ioc=%p" vnc_msg_client_audio_disable(void *state, void *ioc) "VNC client msg audio disable state=%p ioc=%p" vnc_msg_client_audio_format(void *state, void *ioc, int fmt, int channels, int freq) "VNC client msg audio format state=%p ioc=%p fmt=%d channels=%d freq=%d" +vnc_msg_client_cut_text(void *state, void *ioc, int len) "VNC client msg cut text state=%p ioc=%p len=%u" +vnc_msg_client_cut_text_ext(void *state, void *ioc, int len, int flags) "VNC client msg cut text state=%p ioc=%p len=%u flags=%u" +vnc_msg_client_ext_key_event(void *state, void *ioc, int down, int sym, int keycode) "VNC client msg ext key event state=%p ioc=%p down=%u sym=%u keycode=%u" +vnc_msg_client_framebuffer_update_request(void *state, void *ioc, int incremental, int x, int y, int w, int h) "VNC client msg framebuffer update request state=%p ioc=%p incremental=%u x=%u y=%u w=%u h=%u" +vnc_msg_client_key_event(void *state, void *ioc, int down, int sym) "VNC client msg key event state=%p ioc=%p down=%u sym=%u" +vnc_msg_client_pointer_event(void *state, void *ioc, int button_mask, int x, int y) "VNC client msg pointer event state=%p ioc=%p button_mask=%u x=%u y=%u" vnc_msg_client_set_desktop_size(void *state, void *ioc, int width, int height, int screens) "VNC client msg set desktop size state=%p ioc=%p size=%dx%d screens=%d" +vnc_msg_client_set_encodings(void *state, void *ioc, int limit) "VNC client msg set encodings state=%p ioc=%p limit=%u" +vnc_msg_client_set_pixel_format(void *state, void *ioc, int bpp, int big_endian, int true_color) "VNC client msg set pixel format state=%p ioc=%p bpp=%u big_endian=%u true_color=%u" +vnc_msg_client_set_pixel_format_rgb(void *state, void *ioc, int red_max, int green_max, int blue_max, int red_shift, int green_shift, int blue_shift) "VNC client msg set pixel format RGB state=%p ioc=%p red_max=%u green_max=%u blue_max=%u red_shift=%u green_shift=%u blue_shift=%u" +vnc_msg_client_xvp(void *state, void *ioc, int version, int action) "VNC client msg XVP state=%p ioc=%p version=%u action=%u" vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p" vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s" vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p" vnc_client_disconnect_start(void *state, void *ioc) "VNC client disconnect start state=%p ioc=%p" vnc_client_disconnect_finish(void *state, void *ioc) "VNC client disconnect finish state=%p ioc=%p" vnc_client_io_wrap(void *state, void *ioc, const char *type) "VNC client I/O wrap state=%p ioc=%p type=%s" +vnc_client_pixel_format(void *state, void *ioc, int bpp, int depth, int endian) "VNC client pixel format state=%p ioc=%p bpp=%u depth=%u endian=%u" +vnc_client_pixel_format_red(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format red state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_green(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format green state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_blue(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format blue state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" vnc_client_throttle_threshold(void *state, void *ioc, size_t oldoffset, size_t offset, int client_width, int client_height, int bytes_per_pixel, void *audio_cap) "VNC client throttle threshold state=%p ioc=%p oldoffset=%zu newoffset=%zu width=%d height=%d bpp=%d audio=%p" vnc_client_throttle_incremental(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle incremental state=%p ioc=%p job-update=%d offset=%zu" vnc_client_throttle_forced(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle forced state=%p ioc=%p job-update=%d offset=%zu" diff --git a/ui/vnc.c b/ui/vnc.c index a16be468b9..c309882ddb 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2314,6 +2314,25 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; + trace_vnc_client_pixel_format(vs, vs->ioc, + vs->client_pf.bits_per_pixel, + vs->client_pf.depth, + vs->client_endian); + trace_vnc_client_pixel_format_red(vs, vs->ioc, + vs->client_pf.rmax, + vs->client_pf.rbits, + vs->client_pf.rshift, + vs->client_pf.rmask); + trace_vnc_client_pixel_format_green(vs, vs->ioc, + vs->client_pf.gmax, + vs->client_pf.gbits, + vs->client_pf.gshift, + vs->client_pf.gmask); + trace_vnc_client_pixel_format_blue(vs, vs->ioc, + vs->client_pf.bmax, + vs->client_pf.bbits, + vs->client_pf.bshift, + vs->client_pf.bmask); if (!true_color_flag) { send_color_map(vs); @@ -2388,6 +2407,17 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 20; + trace_vnc_msg_client_set_pixel_format(vs, vs->ioc, + read_u8(data, 4), + read_u8(data, 6), + read_u8(data, 7)); + trace_vnc_msg_client_set_pixel_format_rgb(vs, vs->ioc, + read_u16(data, 8), + read_u16(data, 10), + read_u16(data, 12), + read_u8(data, 14), + read_u8(data, 15), + read_u8(data, 16)); set_pixel_format(vs, read_u8(data, 4), read_u8(data, 6), read_u8(data, 7), read_u16(data, 8), read_u16(data, 10), @@ -2410,12 +2440,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) memcpy(data + 4 + (i * 4), &val, sizeof(val)); } + trace_vnc_msg_client_set_encodings(vs, vs->ioc, limit); set_encodings(vs, (int32_t *)(data + 4), limit); break; case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST: if (len == 1) return 10; + trace_vnc_msg_client_framebuffer_update_request(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4), + read_u16(data, 6), + read_u16(data, 8)); framebuffer_update_request(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), read_u16(data, 6), read_u16(data, 8)); @@ -2424,12 +2461,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 8; + trace_vnc_msg_client_key_event(vs, vs->ioc, + read_u8(data, 1), + read_u32(data, 4)); key_event(vs, read_u8(data, 1), read_u32(data, 4)); break; case VNC_MSG_CLIENT_POINTER_EVENT: if (len == 1) return 6; + trace_vnc_msg_client_pointer_event(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4)); pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); break; case VNC_MSG_CLIENT_CUT_TEXT: @@ -2461,9 +2505,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vnc_client_error(vs); break; } + trace_vnc_msg_client_cut_text_ext(vs, vs->ioc, + dlen, read_u32(data, 8)); vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12); break; } + trace_vnc_msg_client_cut_text(vs, vs->ioc, read_u32(data, 4)); vnc_client_cut_text(vs, read_u32(data, 4), data + 8); break; case VNC_MSG_CLIENT_XVP: @@ -2478,6 +2525,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 4) { uint8_t version = read_u8(data, 2); uint8_t action = read_u8(data, 3); + trace_vnc_msg_client_xvp(vs, vs->ioc, version, action); if (version != 1) { error_report("vnc: xvp client message version %d != 1", @@ -2511,6 +2559,10 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 2) return 12; + trace_vnc_msg_client_ext_key_event(vs, vs->ioc, + read_u16(data, 2), + read_u32(data, 4), + read_u32(data, 8)); ext_key_event(vs, read_u16(data, 2), read_u32(data, 4), read_u32(data, 8)); break; From 2183ab62512c6253293e83cce3970b0b42e65630 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 15:44:17 +0100 Subject: [PATCH 3/4] crypto/x509-utils: Check for error from gnutls_x509_crt_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity notes that in qcrypto_get_x509_cert_fingerprint() we call gnutls_x509_crt_init() but don't check for an error return. Add the missing check. Coverity: CID 1593155 Fixes: 10a1d34fc0d ("crypto: Introduce x509 utils") Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- crypto/x509-utils.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c index 8bad00a51b..39bb6d4d8c 100644 --- a/crypto/x509-utils.c +++ b/crypto/x509-utils.c @@ -46,7 +46,11 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, return -1; } - gnutls_x509_crt_init(&crt); + if (gnutls_x509_crt_init(&crt) < 0) { + error_setg(errp, "Unable to initialize certificate: %s", + gnutls_strerror(ret)); + return -1; + } if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) { error_setg(errp, "Failed to import certificate"); From 6f7f3419cce86553dd239f10a5deb9ab872bd8c2 Mon Sep 17 00:00:00 2001 From: Henry Kleynhans Date: Wed, 22 Dec 2021 15:05:59 +0000 Subject: [PATCH 4/4] crypto: load all certificates in X509 CA file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some CA files may contain multiple intermediaries and roots of trust. These may not fit into the hard-coded limit of 16. Extend the validation code to allocate enough space to load all of the certificates present in the CA file and ensure they are cleaned up. Reviewed-by: Daniel P. Berrangé Signed-off-by: Henry Kleynhans [DB: drop MAX_CERTS constant & whitespace tweaks] Signed-off-by: Daniel P. Berrangé --- crypto/tlscredsx509.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 63a72fe47c..cd1f504471 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -426,9 +426,8 @@ qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, static int qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, const char *certFile, - gnutls_x509_crt_t *certs, - unsigned int certMax, - size_t *ncerts, + gnutls_x509_crt_t **certs, + unsigned int *ncerts, Error **errp) { gnutls_datum_t data; @@ -449,20 +448,18 @@ qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, data.data = (unsigned char *)buf; data.size = strlen(buf); - if (gnutls_x509_crt_list_import(certs, &certMax, &data, - GNUTLS_X509_FMT_PEM, 0) < 0) { + if (gnutls_x509_crt_list_import2(certs, ncerts, &data, + GNUTLS_X509_FMT_PEM, 0) < 0) { error_setg(errp, "Unable to import CA certificate list %s", certFile); return -1; } - *ncerts = certMax; return 0; } -#define MAX_CERTS 16 static int qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, bool isServer, @@ -471,12 +468,11 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, Error **errp) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacerts[MAX_CERTS]; - size_t ncacerts = 0; + gnutls_x509_crt_t *cacerts = NULL; + unsigned int ncacerts = 0; size_t i; int ret = -1; - memset(cacerts, 0, sizeof(cacerts)); if (certFile && access(certFile, R_OK) == 0) { cert = qcrypto_tls_creds_load_cert(creds, @@ -488,8 +484,9 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, } if (access(cacertFile, R_OK) == 0) { if (qcrypto_tls_creds_load_ca_cert_list(creds, - cacertFile, cacerts, - MAX_CERTS, &ncacerts, + cacertFile, + &cacerts, + &ncacerts, errp) < 0) { goto cleanup; } @@ -526,6 +523,8 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, for (i = 0; i < ncacerts; i++) { gnutls_x509_crt_deinit(cacerts[i]); } + g_free(cacerts); + return ret; }