]> xenbits.xensource.com Git - libvirt.git/commitdiff
storage: Add support to create a luks volume
authorJohn Ferlan <jferlan@redhat.com>
Thu, 2 Jun 2016 15:33:47 +0000 (11:33 -0400)
committerJohn Ferlan <jferlan@redhat.com>
Tue, 19 Jul 2016 13:40:01 +0000 (09:40 -0400)
Partially resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1301021

If the volume xml was looking to create a luks volume take the necessary
steps in order to make that happen.

The processing will be:
 1. create a temporary file (virStorageBackendCreateQemuImgSecretPath)
   1a. use the storage driver state dir path that uses the pool and
       volume name as a base.

 2. create a secret object (virStorageBackendCreateQemuImgSecretObject)
   2a. use an alias combinding the volume name and "_luks0"
   2b. add the file to the object

 3. create/add luks options to the commandline (virQEMUBuildLuksOpts)
   3a. at the very least a "key-secret=%s" using the secret object alias
   3b. if found in the XML the various "cipher" and "ivgen" options

Signed-off-by: John Ferlan <jferlan@redhat.com>
src/libvirt_private.syms
src/storage/storage_backend.c
src/storage/storage_backend.h
src/util/virqemu.c
src/util/virqemu.h
tests/storagevolxml2argvtest.c

index 973efd34c5d94abb2ac55a6ec91955ee3c798199..cd3cc64e42270f56ade8ee6bdeeeb3acde00b228 100644 (file)
@@ -2197,6 +2197,7 @@ virProcessWait;
 
 # util/virqemu.h
 virQEMUBuildBufferEscapeComma;
+virQEMUBuildLuksOpts;
 virQEMUBuildObjectCommandlineFromJSON;
 
 
index 4b0b19c45ca5326a4688a052bc5cc2712e38b33b..430dccfeee80fc3d4c817395bd3d70f2f7f9430f 100644 (file)
 #include "viralloc.h"
 #include "internal.h"
 #include "secret_conf.h"
+#include "secret_util.h"
 #include "viruuid.h"
 #include "virstoragefile.h"
 #include "storage_backend.h"
 #include "virlog.h"
 #include "virfile.h"
+#include "virjson.h"
+#include "virqemu.h"
 #include "stat-time.h"
 #include "virstring.h"
 #include "virxml.h"
@@ -907,6 +910,7 @@ virStorageBackendQemuImgSupportsCompat(const char *qemuimg)
     return ret;
 }
 
+
 static int
 virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
 {
@@ -925,6 +929,10 @@ virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
     return ret;
 }
 
+/* The _virStorageBackendQemuImgInfo separates the command line building from
+ * the volume definition so that qemuDomainSnapshotCreateInactiveExternal can
+ * use it without needing to deal with a volume.
+ */
 struct _virStorageBackendQemuImgInfo {
     int format;
     const char *path;
@@ -941,21 +949,36 @@ struct _virStorageBackendQemuImgInfo {
     const char *inputPath;
     const char *inputFormatStr;
     int inputFormat;
+
+    char *secretAlias;
+    const char *secretPath;
 };
 
+
 static int
-virStorageBackendCreateQemuImgOpts(char **opts,
+virStorageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr enc,
+                                   char **opts,
                                    struct _virStorageBackendQemuImgInfo info)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
 
-    if (info.backingPath)
-        virBufferAsprintf(&buf, "backing_fmt=%s,",
-                          virStorageFileFormatTypeToString(info.backingFormat));
-    if (info.encryption)
-        virBufferAddLit(&buf, "encryption=on,");
-    if (info.preallocate)
-        virBufferAddLit(&buf, "preallocation=metadata,");
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
+        if (!enc) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("missing luks encryption information"));
+            goto error;
+        }
+        virQEMUBuildLuksOpts(&buf, enc, info.secretAlias);
+    } else {
+        if (info.backingPath)
+            virBufferAsprintf(&buf, "backing_fmt=%s,",
+                              virStorageFileFormatTypeToString(info.backingFormat));
+        if (info.encryption)
+            virBufferAddLit(&buf, "encryption=on,");
+        if (info.preallocate)
+            virBufferAddLit(&buf, "preallocation=metadata,");
+    }
+
     if (info.nocow)
         virBufferAddLit(&buf, "nocow=on,");
 
@@ -1025,6 +1048,22 @@ virStorageBackendCreateQemuImgCheckEncryption(int format,
             if (virStorageGenerateQcowEncryption(conn, vol) < 0)
                 return -1;
         }
+    } else if (format == VIR_STORAGE_FILE_LUKS) {
+        if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unsupported volume encryption format %d"),
+                           vol->target.encryption->format);
+            return -1;
+        }
+        if (enc->nsecrets > 1) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("too many secrets for luks encryption"));
+            return -1;
+        }
+        if (enc->nsecrets == 0) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("no secret provided for luks encryption"));
+        }
     } else {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                        _("volume encryption unsupported with format %s"), type);
@@ -1069,6 +1108,12 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
     int accessRetCode = -1;
     char *absolutePath = NULL;
 
+    if (info->format == VIR_STORAGE_FILE_LUKS) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("cannot set backing store for luks volume"));
+        return -1;
+    }
+
     info->backingFormat = vol->target.backingStore->format;
     info->backingPath = vol->target.backingStore->path;
 
@@ -1122,6 +1167,7 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
 static int
 virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
                                          int imgformat,
+                                         virStorageEncryptionInfoDefPtr enc,
                                          struct _virStorageBackendQemuImgInfo info)
 {
     char *opts = NULL;
@@ -1130,7 +1176,7 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
         imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT)
         info.compat = "0.10";
 
-    if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0)
+    if (virStorageBackendCreateQemuImgOpts(enc, &opts, info) < 0)
         return -1;
     if (opts)
         virCommandAddArgList(cmd, "-o", opts, NULL);
@@ -1140,6 +1186,39 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
 }
 
 
+/* Add a secret object to the command line:
+ *    --object secret,id=$secretAlias,file=$secretPath
+ *
+ *    NB: format=raw is assumed
+ */
+static int
+virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd,
+                                           virStorageVolDefPtr vol,
+                                           struct _virStorageBackendQemuImgInfo *info)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *commandStr = NULL;
+
+    if (virAsprintf(&info->secretAlias, "%s_luks0", vol->name) < 0)
+        return -1;
+
+    virBufferAsprintf(&buf, "secret,id=%s,file=", info->secretAlias);
+    virQEMUBuildBufferEscapeComma(&buf, info->secretPath);
+
+    if (virBufferCheckError(&buf) < 0) {
+        virBufferFreeAndReset(&buf);
+        return -1;
+    }
+
+    commandStr = virBufferContentAndReset(&buf);
+
+    virCommandAddArgList(cmd, "--object", commandStr, NULL);
+
+    VIR_FREE(commandStr);
+    return 0;
+}
+
+
 /* Create a qemu-img virCommand from the supplied binary path,
  * volume definitions and imgformat
  */
@@ -1150,7 +1229,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
                                          virStorageVolDefPtr inputvol,
                                          unsigned int flags,
                                          const char *create_tool,
-                                         int imgformat)
+                                         int imgformat,
+                                         const char *secretPath)
 {
     virCommandPtr cmd = NULL;
     const char *type;
@@ -1162,7 +1242,10 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
         .compat = vol->target.compat,
         .features = vol->target.features,
         .nocow = vol->target.nocow,
+        .secretPath = secretPath,
+        .secretAlias = NULL,
     };
+    virStorageEncryptionInfoDefPtr enc = NULL;
 
     virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL);
 
@@ -1192,6 +1275,18 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
                        _("format features only available with qcow2"));
         return NULL;
     }
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
+        if (inputvol) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("cannot use inputvol with luks volume"));
+            return NULL;
+        }
+        if (!info.encryption) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("missing encryption description"));
+            return NULL;
+        }
+    }
 
     if (inputvol &&
         virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0)
@@ -1226,10 +1321,22 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
     if (info.backingPath)
         virCommandAddArgList(cmd, "-b", info.backingPath, NULL);
 
-    if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, info) < 0) {
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
+        if (virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) {
+            VIR_FREE(info.secretAlias);
+            virCommandFree(cmd);
+            return NULL;
+        }
+        enc = &vol->target.encryption->encinfo;
+    }
+
+    if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat,
+                                                 enc, info) < 0) {
+        VIR_FREE(info.secretAlias);
         virCommandFree(cmd);
         return NULL;
     }
+    VIR_FREE(info.secretAlias);
 
     if (info.inputPath)
         virCommandAddArg(cmd, info.inputPath);
@@ -1240,6 +1347,77 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
     return cmd;
 }
 
+
+static char *
+virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn,
+                                         virStoragePoolObjPtr pool,
+                                         virStorageVolDefPtr vol)
+{
+    virStorageEncryptionPtr enc = vol->target.encryption;
+    char *secretPath = NULL;
+    int fd = -1;
+    uint8_t *secret = NULL;
+    size_t secretlen = 0;
+
+    if (!enc) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing encryption description"));
+        return NULL;
+    }
+
+    if (!conn || !conn->secretDriver ||
+        !conn->secretDriver->secretLookupByUUID ||
+        !conn->secretDriver->secretLookupByUsage ||
+        !conn->secretDriver->secretGetValue) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("unable to look up encryption secret"));
+        return NULL;
+    }
+
+    if (!(secretPath = virStoragePoolObjBuildTempFilePath(pool, vol)))
+        goto cleanup;
+
+    if ((fd = mkostemp(secretPath, O_CLOEXEC)) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("failed to open luks secret file for write"));
+        goto error;
+    }
+
+    if (virSecretGetSecretString(conn, &enc->secrets[0]->seclookupdef,
+                                 VIR_SECRET_USAGE_TYPE_VOLUME,
+                                 &secret, &secretlen) < 0)
+        goto error;
+
+    if (safewrite(fd, secret, secretlen) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("failed to write luks secret file"));
+        goto error;
+    }
+    VIR_FORCE_CLOSE(fd);
+
+    if ((vol->target.perms->uid != (uid_t) -1) &&
+        (vol->target.perms->gid != (gid_t) -1)) {
+        if (chown(secretPath, vol->target.perms->uid,
+                  vol->target.perms->gid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to chown luks secret file"));
+            goto error;
+        }
+    }
+
+ cleanup:
+    VIR_DISPOSE_N(secret, secretlen);
+    VIR_FORCE_CLOSE(fd);
+
+    return secretPath;
+
+ error:
+    unlink(secretPath);
+    VIR_FREE(secretPath);
+    goto cleanup;
+}
+
+
 int
 virStorageBackendCreateQemuImg(virConnectPtr conn,
                                virStoragePoolObjPtr pool,
@@ -1251,6 +1429,7 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
     char *create_tool;
     int imgformat;
     virCommandPtr cmd;
+    char *secretPath = NULL;
 
     virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
 
@@ -1266,8 +1445,15 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
     if (imgformat < 0)
         goto cleanup;
 
+    if (vol->target.format == VIR_STORAGE_FILE_LUKS) {
+        if (!(secretPath =
+              virStorageBackendCreateQemuImgSecretPath(conn, pool, vol)))
+            goto cleanup;
+    }
+
     cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol,
-                                                   flags, create_tool, imgformat);
+                                                   flags, create_tool,
+                                                   imgformat, secretPath);
     if (!cmd)
         goto cleanup;
 
@@ -1275,6 +1461,10 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
 
     virCommandFree(cmd);
  cleanup:
+    if (secretPath) {
+        unlink(secretPath);
+        VIR_FREE(secretPath);
+    }
     VIR_FREE(create_tool);
     return ret;
 }
index 5bc622cc5f6d87a7cc59f12b99268611e16582e5..28e1a651705be14f981fedc0d4e5d21418311005 100644 (file)
@@ -241,7 +241,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
                                          virStorageVolDefPtr inputvol,
                                          unsigned int flags,
                                          const char *create_tool,
-                                         int imgformat);
+                                         int imgformat,
+                                         const char *secretPath);
 
 /* ------- virStorageFile backends ------------ */
 typedef struct _virStorageFileBackend virStorageFileBackend;
index 79a4292e7593ff5855f3737074b5c0b6de3d06db..7d181e19d3fdffc343cddf19abbbb5eae9d14156 100644 (file)
@@ -155,3 +155,61 @@ virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str)
 {
     virBufferEscape(buf, ',', ",", "%s", str);
 }
+
+
+/**
+ * virQEMUBuildLuksOpts:
+ * @buf: buffer to build the string into
+ * @enc: pointer to encryption info
+ * @alias: alias to use
+ *
+ * Generate the string for id=$alias and any encryption options for
+ * into the buffer.
+ *
+ * Important note, a trailing comma (",") is built into the return since
+ * it's expected other arguments are appended after the id=$alias string.
+ * So either turn something like:
+ *
+ *     "key-secret=$alias,"
+ *
+ * or
+ *     "key-secret=$alias,cipher-alg=twofish-256,cipher-mode=cbc,
+ *     hash-alg=sha256,ivgen-alg=plain64,igven-hash-alg=sha256,"
+ *
+ */
+void
+virQEMUBuildLuksOpts(virBufferPtr buf,
+                     virStorageEncryptionInfoDefPtr enc,
+                     const char *alias)
+{
+    virBufferAsprintf(buf, "key-secret=%s,", alias);
+
+    if (!enc->cipher_name)
+        return;
+
+    virBufferAddLit(buf, "cipher-alg=");
+    virQEMUBuildBufferEscapeComma(buf, enc->cipher_name);
+    virBufferAsprintf(buf, "-%u,", enc->cipher_size);
+    if (enc->cipher_mode) {
+        virBufferAddLit(buf, "cipher-mode=");
+        virQEMUBuildBufferEscapeComma(buf, enc->cipher_mode);
+        virBufferAddLit(buf, ",");
+    }
+    if (enc->cipher_hash) {
+        virBufferAddLit(buf, "hash-alg=");
+        virQEMUBuildBufferEscapeComma(buf, enc->cipher_hash);
+        virBufferAddLit(buf, ",");
+    }
+    if (!enc->ivgen_name)
+        return;
+
+    virBufferAddLit(buf, "ivgen-alg=");
+    virQEMUBuildBufferEscapeComma(buf, enc->ivgen_name);
+    virBufferAddLit(buf, ",");
+
+    if (enc->ivgen_hash) {
+        virBufferAddLit(buf, "ivgen-hash-alg=");
+        virQEMUBuildBufferEscapeComma(buf, enc->ivgen_hash);
+        virBufferAddLit(buf, ",");
+    }
+}
index 103341201b00acde25a8e0e562b6004f4b7ff790..eb75d1b287a0abbe543e4b17e019cc6990506cc1 100644 (file)
 # include "internal.h"
 # include "virbuffer.h"
 # include "virjson.h"
+# include "virstorageencryption.h"
 
 char *virQEMUBuildObjectCommandlineFromJSON(const char *type,
                                             const char *alias,
                                             virJSONValuePtr props);
 
 void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str);
+void virQEMUBuildLuksOpts(virBufferPtr buf,
+                          virStorageEncryptionInfoDefPtr enc,
+                          const char *alias)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
 #endif /* __VIR_QEMU_H_ */
index ccfe9abc1d8988805ae8c68423ffb1642c929439..e300821f89093b5d19de1f68f583c407d3b2275d 100644 (file)
@@ -83,7 +83,8 @@ testCompareXMLToArgvFiles(bool shouldFail,
 
     cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, &poolobj, vol,
                                                    inputvol, flags,
-                                                   create_tool, imgformat);
+                                                   create_tool, imgformat,
+                                                   NULL);
     if (!cmd) {
         if (shouldFail) {
             virResetLastError();