diff mbox series

[RFC,v1,12/26] kvm: vmi: add 'key' property

Message ID 20200415005938.23895-13-alazar@bitdefender.com
State New
Headers show
Series VM introspection | expand

Commit Message

Adalbert Lazăr April 15, 2020, 12:59 a.m. UTC
The introspection tool can be authenticated if the 'key' parameter is
set with the ID of a secret object holding a shared secret between the
introspection tool and QEMU of the introspected VM.

Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com>
---
 accel/kvm/vmi.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
diff mbox series

Patch

diff --git a/accel/kvm/vmi.c b/accel/kvm/vmi.c
index 5659663caa..f456ca56ef 100644
--- a/accel/kvm/vmi.c
+++ b/accel/kvm/vmi.c
@@ -14,6 +14,8 @@ 
 #include "qom/object_interfaces.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/kvm.h"
+#include "crypto/secret.h"
+#include "crypto/hash.h"
 #include "chardev/char.h"
 #include "chardev/char-fe.h"
 
@@ -31,6 +33,11 @@  typedef struct VMIntrospection {
     CharBackend sock;
     int sock_fd;
 
+    char *keyid;
+    Object *key;
+    uint8_t cookie_hash[QEMU_VMI_COOKIE_HASH_SIZE];
+    bool key_with_cookie;
+
     qemu_vmi_from_introspector hsk_in;
     uint64_t hsk_in_read_pos;
     uint64_t hsk_in_read_size;
@@ -109,6 +116,14 @@  static void prop_set_chardev(Object *obj, const char *value, Error **errp)
     i->chardevid = g_strdup(value);
 }
 
+static void prop_set_key(Object *obj, const char *value, Error **errp)
+{
+    VMIntrospection *i = VM_INTROSPECTION(obj);
+
+    g_free(i->keyid);
+    i->keyid = g_strdup(value);
+}
+
 static void prop_get_uint32(Object *obj, Visitor *v, const char *name,
                             void *opaque, Error **errp)
 {
@@ -153,6 +168,7 @@  static void instance_init(Object *obj)
     update_vm_start_time(i);
 
     object_property_add_str(obj, "chardev", NULL, prop_set_chardev, NULL);
+    object_property_add_str(obj, "key", NULL, prop_set_key, NULL);
 
     i->handshake_timeout = HANDSHAKE_TIMEOUT_SEC;
     object_property_add(obj, "handshake_timeout", "uint32",
@@ -213,6 +229,7 @@  static void instance_finalize(Object *obj)
     VMIntrospection *i = VM_INTROSPECTION(obj);
 
     g_free(i->chardevid);
+    g_free(i->keyid);
 
     cancel_handshake_timer(i);
 
@@ -276,6 +293,16 @@  static bool send_handshake_info(VMIntrospection *i, Error **errp)
     return true;
 }
 
+static bool validate_handshake_cookie(VMIntrospection *i)
+{
+    if (!i->key_with_cookie) {
+        return true;
+    }
+
+    return 0 == memcmp(&i->cookie_hash, &i->hsk_in.cookie_hash,
+                       sizeof(i->cookie_hash));
+}
+
 static bool validate_handshake(VMIntrospection *i, Error **errp)
 {
     uint32_t min_accepted_size;
@@ -288,6 +315,11 @@  static bool validate_handshake(VMIntrospection *i, Error **errp)
         return false;
     }
 
+    if (!validate_handshake_cookie(i)) {
+        error_setg(errp, "VMI: received cookie doesn't match");
+        return false;
+    }
+
     /*
      * Check hsk_in.struct_size and sizeof(hsk_in) before accessing any
      * other fields. We might get fewer bytes from applications using
@@ -468,6 +500,31 @@  static void chr_event(void *opaque, QEMUChrEvent event)
     }
 }
 
+static bool make_cookie_hash(const char *key_id, uint8_t *cookie_hash,
+                             Error **errp)
+{
+    uint8_t *cookie = NULL, *hash = NULL;
+    size_t cookie_size, hash_size = 0;
+    bool done = false;
+
+    if (qcrypto_secret_lookup(key_id, &cookie, &cookie_size, errp) == 0
+            && qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA1,
+                                  (const char *)cookie, cookie_size,
+                                  &hash, &hash_size, errp) == 0) {
+        if (hash_size == QEMU_VMI_COOKIE_HASH_SIZE) {
+            memcpy(cookie_hash, hash, QEMU_VMI_COOKIE_HASH_SIZE);
+            done = true;
+        } else {
+            error_setg(errp, "VMI: hash algorithm size mismatch");
+        }
+    }
+
+    g_free(cookie);
+    g_free(hash);
+
+    return done;
+}
+
 static Error *vm_introspection_init(VMIntrospection *i)
 {
     Error *err = NULL;
@@ -486,6 +543,15 @@  static Error *vm_introspection_init(VMIntrospection *i)
         return err;
     }
 
+    if (i->keyid) {
+        if (!make_cookie_hash(i->keyid, i->cookie_hash, &err)) {
+            return err;
+        }
+        i->key_with_cookie = true;
+    } else {
+        warn_report("VMI: the introspection tool won't be 'authenticated'");
+    }
+
     chr = qemu_chr_find(i->chardevid);
     if (!chr) {
         error_setg(&err, "VMI: device '%s' not found", i->chardevid);