@@ -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);
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(+)