From 629e64743474a040863ef1267eb48568c6e3e80c Mon Sep 17 00:00:00 2001 Message-Id: <629e64743474a040863ef1267eb48568c6e3e80c@dist-git> From: John Ferlan Date: Mon, 25 Jul 2016 12:42:49 -0400 Subject: [PATCH] encryption: Add and to encryption https://bugzilla.redhat.com/show_bug.cgi?id=1301021 For a luks device, allow the configuration of a specific cipher to be used for encrypting the volume. Signed-off-by: John Ferlan (cherry picked from commit 2552fec248c665f3417a5d36840555da6bbe96a4) --- docs/formatstorageencryption.html.in | 83 ++++++++++++- docs/schemas/storagecommon.rng | 44 ++++++- src/conf/domain_conf.c | 11 ++ src/util/virstorageencryption.c | 136 +++++++++++++++++++++ src/util/virstorageencryption.h | 14 +++ .../qemuxml2argv-luks-disk-cipher.xml | 45 +++++++ .../qemuxml2xmlout-luks-disk-cipher.xml | 1 + tests/qemuxml2xmltest.c | 1 + tests/storagevolxml2xmlin/vol-luks-cipher.xml | 23 ++++ tests/storagevolxml2xmlout/vol-luks-cipher.xml | 23 ++++ tests/storagevolxml2xmltest.c | 1 + 11 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml create mode 100644 tests/storagevolxml2xmlin/vol-luks-cipher.xml create mode 100644 tests/storagevolxml2xmlout/vol-luks-cipher.xml diff --git a/docs/formatstorageencryption.html.in b/docs/formatstorageencryption.html.in index 380763e..f2b0ffd 100644 --- a/docs/formatstorageencryption.html.in +++ b/docs/formatstorageencryption.html.in @@ -68,6 +68,60 @@ be used as the passphrase to decrypt the volume. Since 2.1.0.

+

+ For volume creation, it is possible to specify the encryption + algorithm used to encrypt the luks volume. The following two + optional elements may be provided for that purpose. It is hypervisor + dependent as to which algorithms are supported. The default algorithm + used by the storage driver backend when using qemu-img to create + the volume is 'aes-256-cbc' using 'essiv' for initialization vector + generation and 'sha256' hash algorithm for both the cipher and the + initialization vector generation. +

+ +
+
cipher
+
This element describes the cipher algorithm to be used to either + encrypt or decrypt the luks volume. This element has the following + attributes: +
+
name
+
The name of the cipher algorithm used for data encryption, + such as 'aes', 'des', 'cast5', 'serpent', 'twofish', etc. + Support of the specific algorithm is storage driver + implementation dependent.
+
size
+
The size of the cipher in bits, such as '256', '192', '128', + etc. Support of the specific size for a specific cipher is + hypervisor dependent.
+
mode
+
An optional cipher algorithm mode such as 'cbc', 'xts', + 'ecb', etc. Support of the specific cipher mode is + hypervisor dependent.
+
hash
+
An optional master key hash algorithm such as 'md5', 'sha1', + 'sha256', etc. Support of the specific hash algorithm is + hypervisor dependent.
+
+
+
ivgen
+
This optional element describes the initialization vector + generation algorithm used in conjunction with the + cipher. If the cipher is not provided, + then an error will be generated by the parser. +
+
name
+
The name of the algorithm, such as 'plain', 'plain64', + 'essiv', etc. Support of the specific algorithm is hypervisor + dependent.
+
hash
+
An optional hash algorithm such as 'md5', 'sha1', 'sha256', + etc. Support of the specific ivgen hash algorithm is hypervisor + dependent.
+
+
+
+

Examples

@@ -81,9 +135,12 @@ </encryption>

- Here is a simple example, specifying use of the luks format - where it's assumed that a secret has been defined using a - usage element with a id of "luks_example": + Assuming a + luks secret is already defined using a + usage element with an name of "luks_example", + a simple example specifying use of the luks format + for either volume creation without a specific cipher being defined or + as part of a domain volume definition:

       <encryption format='luks'>
@@ -91,5 +148,25 @@
       </encryption>
     
+

+ Here is an example, specifying use of the luks format for + a specific cipher algorihm for volume creation: +

+
+      <volume>
+        <name>twofish.luks</name>
+        <capacity unit='G'>5</capacity>
+        <target>
+          <path>/var/lib/libvirt/images/demo.luks</path>
+          <format type='luks'/>
+          <encryption format='luks'>
+             <secret type='passphrase' usage='luks_example'/>
+             <cipher name='twofish' size='256' mode='cbc' hash='sha256'/>
+             <ivgen name='plain64' hash='sha256'/>
+          </encryption>
+        </target>
+      </volume>
+    
+ diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index 63b55b4..316fbae 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -15,9 +15,19 @@ luks - - - + + + + + + + + + + + + + @@ -136,4 +146,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8b9b623..abf889a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7869,6 +7869,17 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt, def->startupPolicy = val; } + if (encryption) { + if (encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && + encryption->encinfo.cipher_name) { + + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("supplying the for a domain is " + "unnecessary")); + goto error; + } + } + def->dst = target; target = NULL; def->src->auth = authdef; diff --git a/src/util/virstorageencryption.c b/src/util/virstorageencryption.c index 2b59420..116a235 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -35,6 +35,7 @@ #include "viruuid.h" #include "virfile.h" #include "virsecret.h" +#include "virstring.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -46,6 +47,17 @@ VIR_ENUM_IMPL(virStorageEncryptionFormat, "default", "qcow", "luks") static void +virStorageEncryptionInfoDefFree(virStorageEncryptionInfoDefPtr def) +{ + VIR_FREE(def->cipher_name); + VIR_FREE(def->cipher_mode); + VIR_FREE(def->cipher_hash); + VIR_FREE(def->ivgen_name); + VIR_FREE(def->ivgen_hash); +} + + +static void virStorageEncryptionSecretFree(virStorageEncryptionSecretPtr secret) { if (!secret) @@ -64,6 +76,7 @@ virStorageEncryptionFree(virStorageEncryptionPtr enc) for (i = 0; i < enc->nsecrets; i++) virStorageEncryptionSecretFree(enc->secrets[i]); + virStorageEncryptionInfoDefFree(&enc->encinfo); VIR_FREE(enc->secrets); VIR_FREE(enc); } @@ -81,6 +94,23 @@ virStorageEncryptionSecretCopy(const virStorageEncryptionSecret *src) return ret; } + +static int +virStorageEncryptionInfoDefCopy(const virStorageEncryptionInfoDef *src, + virStorageEncryptionInfoDefPtr dst) +{ + dst->cipher_size = src->cipher_size; + if (VIR_STRDUP(dst->cipher_name, src->cipher_name) < 0 || + VIR_STRDUP(dst->cipher_mode, src->cipher_mode) < 0 || + VIR_STRDUP(dst->cipher_hash, src->cipher_hash) < 0 || + VIR_STRDUP(dst->ivgen_name, src->ivgen_name) < 0 || + VIR_STRDUP(dst->ivgen_hash, src->ivgen_hash) < 0) + return -1; + + return 0; +} + + virStorageEncryptionPtr virStorageEncryptionCopy(const virStorageEncryption *src) { @@ -101,6 +131,9 @@ virStorageEncryptionCopy(const virStorageEncryption *src) goto error; } + if (virStorageEncryptionInfoDefCopy(&src->encinfo, &ret->encinfo) < 0) + goto error; + return ret; error: @@ -154,6 +187,61 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, return NULL; } + +static int +virStorageEncryptionInfoParseCipher(xmlNodePtr info_node, + virStorageEncryptionInfoDefPtr info) +{ + int ret = -1; + char *size_str = NULL; + + if (!(info->cipher_name = virXMLPropString(info_node, "name"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cipher info missing 'name' attribute")); + goto cleanup; + } + + if ((size_str = virXMLPropString(info_node, "size")) && + virStrToLong_uip(size_str, NULL, 10, &info->cipher_size) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse cipher size: '%s'"), + size_str); + goto cleanup; + } + + if (!size_str) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cipher info missing 'size' attribute")); + goto cleanup; + } + + info->cipher_mode = virXMLPropString(info_node, "mode"); + info->cipher_hash = virXMLPropString(info_node, "hash"); + + ret = 0; + + cleanup: + VIR_FREE(size_str); + return ret; +} + + +static int +virStorageEncryptionInfoParseIvgen(xmlNodePtr info_node, + virStorageEncryptionInfoDefPtr info) +{ + if (!(info->ivgen_name = virXMLPropString(info_node, "name"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing ivgen info name string")); + return -1; + } + + info->ivgen_hash = virXMLPropString(info_node, "hash"); + + return 0; +} + + static virStorageEncryptionPtr virStorageEncryptionParseXML(xmlXPathContextPtr ctxt) { @@ -197,6 +285,28 @@ virStorageEncryptionParseXML(xmlXPathContextPtr ctxt) VIR_FREE(nodes); } + if (ret->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + xmlNodePtr tmpnode; + + if ((tmpnode = virXPathNode("./cipher[1]", ctxt))) { + if (virStorageEncryptionInfoParseCipher(tmpnode, &ret->encinfo) < 0) + goto cleanup; + } + + if ((tmpnode = virXPathNode("./ivgen[1]", ctxt))) { + /* If no cipher node, then fail */ + if (!ret->encinfo.cipher_name) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("ivgen element found, but cipher is missing")); + goto cleanup; + } + + if (virStorageEncryptionInfoParseIvgen(tmpnode, &ret->encinfo) < 0) + goto cleanup; + } + } + + return ret; cleanup: @@ -251,6 +361,28 @@ virStorageEncryptionSecretFormat(virBufferPtr buf, return 0; } + +static void +virStorageEncryptionInfoDefFormat(virBufferPtr buf, + const virStorageEncryptionInfoDef *enc) +{ + virBufferEscapeString(buf, "cipher_name); + virBufferAsprintf(buf, " size='%u'", enc->cipher_size); + if (enc->cipher_mode) + virBufferEscapeString(buf, " mode='%s'", enc->cipher_mode); + if (enc->cipher_hash) + virBufferEscapeString(buf, " hash='%s'", enc->cipher_hash); + virBufferAddLit(buf, "/>\n"); + + if (enc->ivgen_name) { + virBufferEscapeString(buf, "ivgen_name); + if (enc->ivgen_hash) + virBufferEscapeString(buf, " hash='%s'", enc->ivgen_hash); + virBufferAddLit(buf, "/>\n"); + } +} + + int virStorageEncryptionFormat(virBufferPtr buf, virStorageEncryptionPtr enc) @@ -271,6 +403,10 @@ virStorageEncryptionFormat(virBufferPtr buf, return -1; } + if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && + enc->encinfo.cipher_name) + virStorageEncryptionInfoDefFormat(buf, &enc->encinfo); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index 5e1be3b..fa439fb 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -44,6 +44,18 @@ struct _virStorageEncryptionSecret { virSecretLookupTypeDef seclookupdef; }; +/* It's possible to dictate the cipher and if necessary iv */ +typedef struct _virStorageEncryptionInfoDef virStorageEncryptionInfoDef; +typedef virStorageEncryptionInfoDef *virStorageEncryptionInfoDefPtr; +struct _virStorageEncryptionInfoDef { + unsigned int cipher_size; + char *cipher_name; + char *cipher_mode; + char *cipher_hash; + char *ivgen_name; + char *ivgen_hash; +}; + typedef enum { /* "default" is only valid for volume creation */ VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT = 0, @@ -61,6 +73,8 @@ struct _virStorageEncryption { size_t nsecrets; virStorageEncryptionSecretPtr *secrets; + + virStorageEncryptionInfoDef encinfo; }; virStorageEncryptionPtr virStorageEncryptionCopy(const virStorageEncryption *src) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml new file mode 100644 index 0000000..9ce15c0 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml @@ -0,0 +1,45 @@ + + encryptdisk + 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 + 1048576 + 524288 + 1 + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+ + + diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml new file mode 120000 index 0000000..fa55233 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index a757fdb..d00d209 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -503,6 +503,7 @@ mymain(void) DO_TEST("encrypted-disk"); DO_TEST("encrypted-disk-usage"); DO_TEST("luks-disks"); + DO_TEST("luks-disk-cipher"); DO_TEST("memtune"); DO_TEST("memtune-unlimited"); DO_TEST("blkiotune"); diff --git a/tests/storagevolxml2xmlin/vol-luks-cipher.xml b/tests/storagevolxml2xmlin/vol-luks-cipher.xml new file mode 100644 index 0000000..009246f --- /dev/null +++ b/tests/storagevolxml2xmlin/vol-luks-cipher.xml @@ -0,0 +1,23 @@ + + LuksDemo.img + /var/lib/libvirt/images/LuksDemo.img + + + 5 + 294912 + + /var/lib/libvirt/images/LuksDemo.img + + + 0644 + 0 + 0 + + + + + + + + + diff --git a/tests/storagevolxml2xmlout/vol-luks-cipher.xml b/tests/storagevolxml2xmlout/vol-luks-cipher.xml new file mode 100644 index 0000000..9014849 --- /dev/null +++ b/tests/storagevolxml2xmlout/vol-luks-cipher.xml @@ -0,0 +1,23 @@ + + LuksDemo.img + /var/lib/libvirt/images/LuksDemo.img + + + 5368709120 + 294912 + + /var/lib/libvirt/images/LuksDemo.img + + + 0644 + 0 + 0 + + + + + + + + + diff --git a/tests/storagevolxml2xmltest.c b/tests/storagevolxml2xmltest.c index a36a706..db82bea 100644 --- a/tests/storagevolxml2xmltest.c +++ b/tests/storagevolxml2xmltest.c @@ -106,6 +106,7 @@ mymain(void) DO_TEST("pool-dir", "vol-qcow2-0.10-lazy"); DO_TEST("pool-dir", "vol-qcow2-nobacking"); DO_TEST("pool-dir", "vol-luks"); + DO_TEST("pool-dir", "vol-luks-cipher"); DO_TEST("pool-disk", "vol-partition"); DO_TEST("pool-logical", "vol-logical"); DO_TEST("pool-logical", "vol-logical-backing"); -- 2.9.2