Read-only connections currently cannot use qemu-monitor-command even for
HMP "info" commands, which only query QEMU state.

Allow HMP commands whose first token is exactly "info" on read-only
connections. Keep QMP commands, file descriptor passing, and all other
HMP commands restricted to read-write connections.

Signed-off-by: Siddhi Katage <[email protected]>
---
 src/libvirt-qemu.c       | 13 +++++++++++--
 src/libvirt_private.syms |  1 +
 src/qemu/qemu_driver.c   | 21 +++++++++++++++++++--
 src/util/virqemu.c       | 21 +++++++++++++++++++++
 src/util/virqemu.h       |  2 ++
 5 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c
index 2898b71983..d38b747b44 100644
--- a/src/libvirt-qemu.c
+++ b/src/libvirt-qemu.c
@@ -25,6 +25,7 @@
 #include "virlog.h"
 #include "viruuid.h"
 #include "datatypes.h"
+#include "virqemu.h"
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
@@ -77,8 +78,12 @@ virDomainQemuMonitorCommand(virDomainPtr domain, const char 
*cmd,
     virCheckDomainReturn(domain, -1);
     conn = domain->conn;
 
+    virCheckNonNullArgGoto(cmd, error);
     virCheckNonNullArgGoto(result, error);
-    virCheckReadOnlyGoto(conn->flags, error);
+
+    if (!(flags & VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP) ||
+        !virQEMUHMPCommandIsInfo(cmd))
+        virCheckReadOnlyGoto(conn->flags, error);
 
     if (conn->driver->domainQemuMonitorCommand) {
         int ret;
@@ -170,7 +175,11 @@ virDomainQemuMonitorCommandWithFiles(virDomainPtr domain,
     }
 
     virCheckNonNullArgGoto(result, error);
-    virCheckReadOnlyGoto(conn->flags, error);
+
+    if (ninfiles > 0 || outfiles ||
+        !(flags & VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP) ||
+        !virQEMUHMPCommandIsInfo(cmd))
+        virCheckReadOnlyGoto(conn->flags, error);
 
     if (conn->driver->domainQemuMonitorCommandWithFiles) {
         int ret;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 30c4564456..6f7505ecde 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3351,6 +3351,7 @@ virQEMUBuildBufferEscapeComma;
 virQEMUBuildCommandLineJSON;
 virQEMUBuildCommandLineJSONArrayNumbered;
 virQEMUBuildCommandLineJSONArrayObjectsStr;
+virQEMUHMPCommandIsInfo;
 
 
 # util/virrandom.h
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 544955ecf9..2f542e0b8a 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -100,6 +100,7 @@
 #include "vircgroup.h"
 #include "virperf.h"
 #include "virnuma.h"
+#include "virqemu.h"
 #include "netdev_bandwidth_conf.h"
 #include "virdomainsnapshotobjlist.h"
 #include "virenum.h"
@@ -13752,6 +13753,14 @@ qemuDomainBackupGetXMLDesc(virDomainPtr domain,
 #define QEMU_DOMAIN_MONITOR_COMMAND_FLAGS \
     VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP
 
+static int
+qemuMonitorDomainGetInfo(virConnectPtr conn,
+                         virDomainDef *def)
+{
+    return virDomainGetInfoEnsureACL(conn, def);
+}
+
+
 static int
 qemuDomainQemuMonitorCommandWithFiles(virDomainPtr domain,
                                       const char *cmd,
@@ -13789,8 +13798,16 @@ qemuDomainQemuMonitorCommandWithFiles(virDomainPtr 
domain,
     if (!(vm = qemuDomainObjFromDomain(domain)))
         goto cleanup;
 
-    if (virDomainQemuMonitorCommandWithFilesEnsureACL(domain->conn, vm->def) < 
0)
-        goto cleanup;
+    /* HMP "info" commands only query QEMU state, so read access is enough. */
+    if (ninfds == 0 && !outfds &&
+        (flags & VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP) &&
+        virQEMUHMPCommandIsInfo(cmd)) {
+        if (qemuMonitorDomainGetInfo(domain->conn, vm->def) < 0)
+            goto cleanup;
+    } else {
+        if (virDomainQemuMonitorCommandWithFilesEnsureACL(domain->conn, 
vm->def) < 0)
+            goto cleanup;
+    }
 
     if (virDomainObjBeginJob(vm, VIR_JOB_QUERY) < 0)
         goto cleanup;
diff --git a/src/util/virqemu.c b/src/util/virqemu.c
index d2a69026c8..74f3729b67 100644
--- a/src/util/virqemu.c
+++ b/src/util/virqemu.c
@@ -32,6 +32,27 @@
 
 VIR_LOG_INIT("util.qemu");
 
+
+bool
+virQEMUHMPCommandIsInfo(const char *cmd)
+{
+    g_autofree char *trimmed = g_strdup(cmd);
+    const char *args;
+
+    if (!trimmed)
+        return false;
+
+    g_strstrip(trimmed);
+
+    if (!STRPREFIX(trimmed, "info"))
+        return false;
+
+    args = trimmed + strlen("info");
+
+    return *args == '\0' || g_ascii_isspace(*args);
+}
+
+
 struct virQEMUCommandLineJSONIteratorData {
     const char *prefix;
     virBuffer *buf;
diff --git a/src/util/virqemu.h b/src/util/virqemu.h
index e5d36b95c4..b9e79fb9b6 100644
--- a/src/util/virqemu.h
+++ b/src/util/virqemu.h
@@ -42,3 +42,5 @@ int virQEMUBuildCommandLineJSON(virJSONValue *value,
                                 virQEMUBuildCommandLineJSONArrayFormatFunc 
array);
 
 void virQEMUBuildBufferEscapeComma(virBuffer *buf, const char *str);
+
+bool virQEMUHMPCommandIsInfo(const char *cmd);
-- 
2.47.3

Reply via email to