提交 2552fec2 编写于 作者: J John Ferlan

encryption: Add <cipher> and <ivgen> to encryption

For a luks device, allow the configuration of a specific cipher to be
used for encrypting the volume.
Signed-off-by: NJohn Ferlan <jferlan@redhat.com>
上级 9bbf0d7e
......@@ -68,6 +68,60 @@
be used as the passphrase to decrypt the volume.
<span class="since">Since 2.1.0</span>.
</p>
<p>
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.
</p>
<dl>
<dt><code>cipher</code></dt>
<dd>This element describes the cipher algorithm to be used to either
encrypt or decrypt the luks volume. This element has the following
attributes:
<dl>
<dt><code>name</code></dt>
<dd>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.</dd>
<dt><code>size</code></dt>
<dd>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.</dd>
<dt><code>mode</code></dt>
<dd>An optional cipher algorithm mode such as 'cbc', 'xts',
'ecb', etc. Support of the specific cipher mode is
hypervisor dependent.</dd>
<dt><code>hash</code></dt>
<dd>An optional master key hash algorithm such as 'md5', 'sha1',
'sha256', etc. Support of the specific hash algorithm is
hypervisor dependent.</dd>
</dl>
</dd>
<dt><code>ivgen</code></dt>
<dd>This optional element describes the initialization vector
generation algorithm used in conjunction with the
<code>cipher</code>. If the <code>cipher</code> is not provided,
then an error will be generated by the parser.
<dl>
<dt><code>name</code></dt>
<dd>The name of the algorithm, such as 'plain', 'plain64',
'essiv', etc. Support of the specific algorithm is hypervisor
dependent.</dd>
<dt><code>hash</code></dt>
<dd>An optional hash algorithm such as 'md5', 'sha1', 'sha256',
etc. Support of the specific ivgen hash algorithm is hypervisor
dependent.</dd>
</dl>
</dd>
</dl>
<h2><a name="example">Examples</a></h2>
......@@ -81,9 +135,12 @@
&lt;/encryption&gt;</pre>
<p>
Here is a simple example, specifying use of the <code>luks</code> format
where it's assumed that a <code>secret</code> has been defined using a
<code>usage</code> element with a <code>id</code> of "luks_example":
Assuming a <a href="formatsecret.html#luksUsageType">
<code>luks secret</code></a> is already defined using a
<code>usage</code> element with an <code>name</code> of "luks_example",
a simple example specifying use of the <code>luks</code> format
for either volume creation without a specific cipher being defined or
as part of a domain volume definition:
</p>
<pre>
&lt;encryption format='luks'&gt;
......@@ -91,5 +148,25 @@
&lt;/encryption&gt;
</pre>
<p>
Here is an example, specifying use of the <code>luks</code> format for
a specific cipher algorihm for volume creation:
</p>
<pre>
&lt;volume&gt;
&lt;name&gt;twofish.luks&lt;/name&gt;
&lt;capacity unit='G'&gt;5&lt;/capacity&gt;
&lt;target&gt;
&lt;path&gt;/var/lib/libvirt/images/demo.luks&lt;/path&gt;
&lt;format type='luks'/&gt;
&lt;encryption format='luks'&gt;
&lt;secret type='passphrase' usage='luks_example'/&gt;
&lt;cipher name='twofish' size='256' mode='cbc' hash='sha256'/&gt;
&lt;ivgen name='plain64' hash='sha256'/&gt;
&lt;/encryption&gt;
&lt;/target&gt;
&lt;/volume&gt;
</pre>
</body>
</html>
......@@ -15,9 +15,19 @@
<value>luks</value>
</choice>
</attribute>
<zeroOrMore>
<ref name='secret'/>
</zeroOrMore>
<interleave>
<zeroOrMore>
<ref name='secret'/>
</zeroOrMore>
<optional>
<element name='cipher'>
<ref name='keycipher'/>
</element>
<element name='ivgen'>
<ref name='keyivgen'/>
</element>
</optional>
</interleave>
</element>
</define>
......@@ -136,4 +146,32 @@
</optional>
</define>
<define name='keycipher'>
<attribute name='name'>
<text/>
</attribute>
<attribute name='size'>
<ref name="unsignedInt"/>
</attribute>
<optional>
<attribute name='mode'>
<text/>
</attribute>
<attribute name='hash'>
<text/>
</attribute>
</optional>
</define>
<define name='keyivgen'>
<attribute name='name'>
<text/>
</attribute>
<optional>
<attribute name='hash'>
<text/>
</attribute>
</optional>
</define>
</grammar>
......@@ -7802,6 +7802,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 <cipher> for a domain is "
"unnecessary"));
goto error;
}
}
def->dst = target;
target = NULL;
def->src->auth = authdef;
......
......@@ -35,6 +35,7 @@
#include "viruuid.h"
#include "virfile.h"
#include "virsecret.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
......@@ -45,6 +46,17 @@ VIR_ENUM_IMPL(virStorageEncryptionFormat,
VIR_STORAGE_ENCRYPTION_FORMAT_LAST,
"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)
{
......@@ -63,6 +75,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);
}
......@@ -80,6 +93,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)
{
......@@ -100,6 +130,9 @@ virStorageEncryptionCopy(const virStorageEncryption *src)
goto error;
}
if (virStorageEncryptionInfoDefCopy(&src->encinfo, &ret->encinfo) < 0)
goto error;
return ret;
error:
......@@ -153,6 +186,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)
{
......@@ -196,6 +284,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:
......@@ -250,6 +360,28 @@ virStorageEncryptionSecretFormat(virBufferPtr buf,
return 0;
}
static void
virStorageEncryptionInfoDefFormat(virBufferPtr buf,
const virStorageEncryptionInfoDef *enc)
{
virBufferEscapeString(buf, "<cipher name='%s'", enc->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='%s'", enc->ivgen_name);
if (enc->ivgen_hash)
virBufferEscapeString(buf, " hash='%s'", enc->ivgen_hash);
virBufferAddLit(buf, "/>\n");
}
}
int
virStorageEncryptionFormat(virBufferPtr buf,
virStorageEncryptionPtr enc)
......@@ -270,6 +402,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, "</encryption>\n");
......
......@@ -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)
......
<domain type='qemu'>
<name>encryptdisk</name>
<uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>524288</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='luks'/>
<source file='/storage/guest_disks/encryptdisk'/>
<target dev='vda' bus='virtio'/>
<encryption format='luks'>
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
</encryption>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>
<disk type='file' device='disk'>
<driver name='qemu' type='luks'/>
<source file='/storage/guest_disks/encryptdisk2'/>
<target dev='vdb' bus='virtio'/>
<encryption format='luks'>
<secret type='passphrase' usage='mycluster_myname'/>
</encryption>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</disk>
<controller type='usb' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</memballoon>
</devices>
</domain>
../qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml
\ No newline at end of file
......@@ -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");
......
<volume>
<name>LuksDemo.img</name>
<key>/var/lib/libvirt/images/LuksDemo.img</key>
<source>
</source>
<capacity unit="G">5</capacity>
<allocation>294912</allocation>
<target>
<path>/var/lib/libvirt/images/LuksDemo.img</path>
<format type='luks'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
<encryption format='luks'>
<secret type='passphrase' usage='mumblyfratz'/>
<cipher name='serpent' size='256' mode='cbc' hash='sha256'/>
<ivgen name='plain64' hash='sha256'/>
</encryption>
</target>
</volume>
<volume type='file'>
<name>LuksDemo.img</name>
<key>/var/lib/libvirt/images/LuksDemo.img</key>
<source>
</source>
<capacity unit='bytes'>5368709120</capacity>
<allocation unit='bytes'>294912</allocation>
<target>
<path>/var/lib/libvirt/images/LuksDemo.img</path>
<format type='luks'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
<encryption format='luks'>
<secret type='passphrase' usage='mumblyfratz'/>
<cipher name='serpent' size='256' mode='cbc' hash='sha256'/>
<ivgen name='plain64' hash='sha256'/>
</encryption>
</target>
</volume>
......@@ -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");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册