In the case of TLS-PSK-enabled migration, Libvirt manages the lifecycle of the pre-shared key. Add a new test scenario to the migration cookie test suite to validate the generation of pre-shared key when the QEMU_MIGRATION_COOKIE_TLS_PSK flag is set. In addition, the test also checks the length and the hexadecimal encoding of the pre-shared key. Signed-off-by: Abhisek Panda <abhisek.panda1@nutanix.com> --- tests/qemumigrationcookiexmltest.c | 123 +++++++++++++++++++++++++++++ tests/testutilsqemu.c | 12 +++ 2 files changed, 135 insertions(+) diff --git a/tests/qemumigrationcookiexmltest.c b/tests/qemumigrationcookiexmltest.c index adf2eb4d08..0914793974 100644 --- a/tests/qemumigrationcookiexmltest.c +++ b/tests/qemumigrationcookiexmltest.c @@ -16,6 +16,7 @@ #include <config.h> +#include <stddef.h> #include <unistd.h> #include <sys/types.h> @@ -92,6 +93,99 @@ testQemuMigrationCookieDataFree(struct testQemuMigrationCookieData *data) } +static int +testQemuMigrationCookiePSKValidate(const char *key) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(&driver); + size_t expected_key_len = 2 * cfg->migrateTLSPSKLength; /* The pre-shared key's length is 2 * PSK length */ + size_t i; + + if (!key) { + VIR_TEST_DEBUG("\nThe pre-shared key is not present in the migration cookie."); + return -1; + } + + if (strlen(key) != expected_key_len) { + VIR_TEST_DEBUG("\nThe length of the pre-shared key is incorrect."); + return -1; + } + + /* Check the characters of the key */ + for (i = 0; i < expected_key_len; i++) { + if (!g_ascii_isxdigit(key[i])) { + VIR_TEST_DEBUG("\nThe key contains non-hex characters"); + return -1; + } + } + + return 0; +} + + +static int +testQemuMigrationCookiePSKAttribute(const void *opaque) +{ + struct testQemuMigrationCookieData *data = (struct testQemuMigrationCookieData *) opaque; + g_autoptr(qemuMigrationCookie) cookie = NULL; + qemuDomainObjPrivate *priv; + + /* if the VM object parsing step failed there's nothing this test can do */ + if (!data->vm) { + VIR_TEST_DEBUG("\nmissing VM object\n"); + return -1; + } + priv = data->vm->privateData; + + if (!(cookie = qemuMigrationCookieNew(data->vm->def, NULL))) + return -1; + + g_free(cookie->localHostname); + cookie->localHostname = g_strdup("hostname2"); + + if (virUUIDParse("8b3f4dc4-6a8e-5f9b-94a5-4c35babd8d95", cookie->localHostuuid) < 0) { + VIR_TEST_DEBUG("\nfailed to parse fake UUID"); + return -1; + } + + /* Format the migration cookie at the source */ + if (qemuMigrationCookieFormat(cookie, + &driver, + data->vm, + data->cookiePopulateParty, + &data->xmlstr, + &data->xmlstrlen, + data->cookiePopulateFlags) < 0) { + VIR_TEST_DEBUG("\nfailed to populate and format qemu migration cookie"); + return -1; + } + + /* Check for the <migration-key> element in the generated migration cookie XML */ + if (!strstr(data->xmlstr, "<migration-key>")) { + VIR_TEST_DEBUG("\nthe migration cookie is missing the migration-key element:\n%s\n", + data->xmlstr); + return -1; + } + + /* Parse the migration cookie at the destination */ + if (!(data->cookie = qemuMigrationCookieParse(&driver, + data->vm, + data->vm->def, + NULL, + priv->qemuCaps, + data->xmlstr, + data->xmlstrlen, + data->cookieParseFlags))) { + VIR_TEST_DEBUG("\nfailed to parse qemu migration cookie:\n%s\n", data->xmlstr); + return -1; + } + + if (testQemuMigrationCookiePSKValidate(data->cookie->tlsPSK) < 0) + return -1; + + return 0; +} + + static int testQemuMigrationCookiePopulate(const void *opaque) { @@ -418,6 +512,32 @@ testQemuMigrationCookieXML2XMLBitmaps(const char *name, return ret; } +/* tests the generation and persistence of the pre-shared keys */ +static int +testQemuMigrationCookiePSK(const char *domxml) +{ + struct testQemuMigrationCookieData *data = g_new0(struct testQemuMigrationCookieData, 1); + int ret = 0; + + data->cookiePopulateFlags = QEMU_MIGRATION_COOKIE_TLS_PSK; + data->cookiePopulateParty = QEMU_MIGRATION_SOURCE; + data->cookieParseFlags = QEMU_MIGRATION_COOKIE_TLS_PSK; + data->inStatus = g_strconcat(abs_srcdir, "/", domxml, NULL); + + /* load status XML as domain object */ + if (testQemuMigrationCookieDomInit(data) < 0) + ret = -1; + + /* test PSK generation and persistence */ + if (virTestRun("qemumigrationcookie-psk-lifecycle", + testQemuMigrationCookiePSKAttribute, data) < 0) + ret = -1; + + testQemuMigrationCookieDataFree(data); + + return ret; +} + static int mymain(void) @@ -448,6 +568,9 @@ mymain(void) if (testQemuMigrationCookieXML2XMLBitmaps("nbd-bitmaps", "qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml", 0) < 0) ret = -1; + if (testQemuMigrationCookiePSK("qemustatusxml2xmldata/modern-in.xml") < 0) + ret = -1; + virBufferFreeAndReset(&testnamebuf); cleanup: diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index e5e3342b53..b0e0d41895 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -297,6 +297,7 @@ qemuTestCapsCacheInsert(virFileCache *cache, #define STATEDIRTEMPLATE abs_builddir "/qemustatedir-XXXXXX" #define CONFIGDIRTEMPLATE abs_builddir "/qemuconfigdir-XXXXXX" +#define TLSPSKDIRTEMPLATE abs_builddir "/pskstatedir-XXXXXX" int qemuTestDriverInit(virQEMUDriver *driver) { @@ -304,6 +305,7 @@ int qemuTestDriverInit(virQEMUDriver *driver) virSecurityManager *mgr = NULL; char statedir[] = STATEDIRTEMPLATE; char configdir[] = CONFIGDIRTEMPLATE; + char tlspskdir[] = TLSPSKDIRTEMPLATE; memset(driver, 0, sizeof(*driver)); @@ -328,6 +330,7 @@ int qemuTestDriverInit(virQEMUDriver *driver) * dirs. */ VIR_FREE(cfg->stateDir); VIR_FREE(cfg->configDir); + VIR_FREE(cfg->tlsPSKStateDir); /* Override paths to ensure predictable output * @@ -367,6 +370,13 @@ int qemuTestDriverInit(virQEMUDriver *driver) cfg->configDir = g_strdup(configdir); + if (!g_mkdtemp(tlspskdir)) { + fprintf(stderr, "Cannot create fake tlsPSKStateDir"); + goto error; + } + + cfg->tlsPSKStateDir = g_strdup(tlspskdir); + driver->caps = testQemuCapsInit(); if (!driver->caps) goto error; @@ -433,6 +443,8 @@ int qemuTestDriverInit(virQEMUDriver *driver) cfg->dumpGuestCore = false; + cfg->migrateTLSPSKLength = 32; + driver->privileged = true; return 0; -- 2.43.7