[libvirt] [PATCH 1/9] Add volume encryption information handling.

Miloslav Trmač mitr at redhat.com
Tue Jul 21 11:11:57 UTC 2009


Define an <encryption> tag specifying volume encryption format and
format-depenedent parameters (e.g. passphrase, cipher name, key
length, key).

In most cases, the "secrets" (passphrases/keys) should only be
transferred from libvirt users to libvirt, not the other way around.
(Volume creation, when libvirt generates secrets for the user,
is the only planned exception).

Permanent storage of the secrets should be implemented outside of
libvirt, although virDomainDefineXML() will cause libvirtd to store
the secret locally with a domain.

Only the qcow/qcow2 encryption format is currently supported,
with the key/passphrase represented using base64.

This patch does not add any users; the <encryption> tag is added in
the following patches to both volumes (to support encrypted volume
creation) and domains.
---
 bootstrap                |    1 +
 po/POTFILES.in           |    1 +
 src/Makefile.am          |    1 +
 src/libvirt_private.syms |    5 +
 src/storage_encryption.c |  252 ++++++++++++++++++++++++++++++++++++++++++++++
 src/storage_encryption.h |   62 +++++++++++
 6 files changed, 322 insertions(+), 0 deletions(-)
 create mode 100644 src/storage_encryption.c
 create mode 100644 src/storage_encryption.h

diff --git a/bootstrap b/bootstrap
index 8b81e0e..885b299 100755
--- a/bootstrap
+++ b/bootstrap
@@ -65,6 +65,7 @@ gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
 <$gnulib_tool || exit
 
 modules='
+base64
 c-ctype
 close
 connect
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0ea21fd..cc99b48 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -37,6 +37,7 @@ src/storage_backend_logical.c
 src/storage_backend_scsi.c
 src/storage_conf.c
 src/storage_driver.c
+src/storage_encryption.c
 src/test.c
 src/uml_conf.c
 src/uml_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b662ae..6c628bd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@ UTIL_SOURCES =							\
 		memory.c memory.h				\
 		pci.c pci.h					\
 		qparams.c qparams.h				\
+		storage_encryption.h storage_encryption.c	\
 		threads.c threads.h				\
 		threads-pthread.h				\
 		threads-win32.h					\
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 59c78d5..9850daa 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -324,6 +324,11 @@ virStoragePartedFsTypeTypeToString;
 virStoragePoolObjLock;
 virStoragePoolObjUnlock;
 
+virStorageEncryptionFree;
+virStorageEncryptionDropSecrets;
+virStorageEncryptionParseNode;
+virStorageEncryptionFormat;
+
 
 # threads.h
 virMutexInit;
diff --git a/src/storage_encryption.c b/src/storage_encryption.c
new file mode 100644
index 0000000..c3e3219
--- /dev/null
+++ b/src/storage_encryption.c
@@ -0,0 +1,252 @@
+/*
+ * storage_encryption.h: volume encryption information
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Red Hat Author: Miloslav Trmač <mitr at redhat.com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+
+#include "base64.h"
+#include "buf.h"
+#include "memory.h"
+#include "storage_conf.h"
+#include "storage_encryption.h"
+#include "util.h"
+#include "xml.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+VIR_ENUM_IMPL(virStorageEncryptionFormat,
+              VIR_STORAGE_ENCRYPTION_FORMAT_LAST, "unencrypted", "qcow")
+
+void
+virStorageEncryptionFree(virStorageEncryptionPtr enc)
+{
+    if (!enc)
+        return;
+
+    switch (enc->format) {
+        case VIR_STORAGE_ENCRYPTION_FORMAT_UNENCRYPTED:
+            break;
+
+        case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
+            if (enc->v.qcow.passphrase != NULL) {
+                memset(enc->v.qcow.passphrase, 0,
+                       strlen(enc->v.qcow.passphrase));
+                VIR_FREE(enc->v.qcow.passphrase);
+            }
+            break;
+
+        default:
+            virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                                  _("unhandled volume encryption format %d"),
+                                  enc->format);
+            break;
+    }
+    VIR_FREE(enc);
+}
+
+void
+virStorageEncryptionDropSecrets(virStorageEncryptionPtr enc)
+{
+    if (!enc)
+        return;
+
+    switch (enc->format) {
+        case VIR_STORAGE_ENCRYPTION_FORMAT_UNENCRYPTED:
+            break;
+
+        case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
+            if (enc->v.qcow.passphrase != NULL) {
+                memset(enc->v.qcow.passphrase, 0,
+                       strlen(enc->v.qcow.passphrase));
+                VIR_FREE(enc->v.qcow.passphrase);
+            }
+            break;
+
+        default:
+            virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                                  _("unhandled volume encryption format %d"),
+                                  enc->format);
+            break;
+    }
+}
+
+static virStorageEncryptionPtr
+virStorageEncryptionParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
+{
+    virStorageEncryptionPtr ret;
+    char *format_str;
+    int format;
+
+    if (VIR_ALLOC(ret) < 0) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+    format_str = virXPathString(conn, "string(./@format)", ctxt);
+    if (format_str == NULL) {
+        virStorageReportError(conn, VIR_ERR_XML_ERROR,
+                              _("unknown volume encryption format"));
+        goto cleanup;
+    }
+    format = virStorageEncryptionFormatTypeFromString(format_str);
+    if (format < 0) {
+        virStorageReportError(conn, VIR_ERR_XML_ERROR,
+                              _("unknown volume encryption format type %s"),
+                              format_str);
+        VIR_FREE(format_str);
+        goto cleanup;
+    }
+    VIR_FREE(format_str);
+    ret->format = format;
+
+    switch (ret->format) {
+        case VIR_STORAGE_ENCRYPTION_FORMAT_UNENCRYPTED:
+            break;
+
+        case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:{
+            char *base64;
+
+            base64 = virXPathString(conn, "string(./passphrase)", ctxt);
+            if (base64 != NULL) {
+                size_t base64_len, raw_len;
+                char *raw;
+                bool base64_ok;
+
+                base64_len = strlen(base64);
+                base64_ok = base64_decode_alloc(base64, base64_len, &raw,
+                                                &raw_len);
+                memset(base64, 0, base64_len);
+                VIR_FREE(base64);
+
+                if (!base64_ok) {
+                    virStorageReportError(conn, VIR_ERR_XML_ERROR,
+                                          _("invalid base64 in passphrase"));
+                    goto cleanup;
+                }
+                if (raw == NULL) {
+                    virReportOOMError(conn);
+                    goto cleanup;
+                }
+                if (VIR_ALLOC_N(ret->v.qcow.passphrase, raw_len + 1) < 0) {
+                    virReportOOMError(conn);
+                    memset(raw, 0, raw_len);
+                    free(raw);
+                    goto cleanup;
+                }
+                memcpy(ret->v.qcow.passphrase, raw, raw_len);
+                memset(raw, 0, raw_len);
+                free(raw);
+            }
+            break;
+        }
+
+        default:
+            virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                                  _("unhandled volume encryption format %d"),
+                                  ret->format);
+            break;
+    }
+
+    return ret;
+
+  cleanup:
+    virStorageEncryptionFree(ret);
+    return NULL;
+}
+
+virStorageEncryptionPtr
+virStorageEncryptionParseNode(virConnectPtr conn,
+                              xmlDocPtr xml, xmlNodePtr root)
+{
+    xmlXPathContextPtr ctxt = NULL;
+    virStorageEncryptionPtr enc = NULL;
+
+    if (STRNEQ((const char *) root->name, "encryption")) {
+        virStorageReportError(conn, VIR_ERR_XML_ERROR,
+                              "%s", _("unknown root element for volume "
+                                      "encryption information"));
+        goto cleanup;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    ctxt->node = root;
+    enc = virStorageEncryptionParseXML(conn, ctxt);
+
+  cleanup:
+    xmlXPathFreeContext(ctxt);
+    return enc;
+}
+
+int
+virStorageEncryptionFormat(virConnectPtr conn,
+                           virBufferPtr buf,
+                           virStorageEncryptionPtr enc, bool with_secrets)
+{
+    const char *format;
+
+    format = virStorageEncryptionFormatTypeToString(enc->format);
+    if (!format) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("unexpected encryption format"));
+        return -1;
+    }
+    virBufferVSprintf(buf, "  <encryption format='%s'>\n", format);
+
+    switch (enc->format) {
+        case VIR_STORAGE_ENCRYPTION_FORMAT_UNENCRYPTED:
+            break;
+
+        case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
+            if (with_secrets && enc->v.qcow.passphrase != NULL) {
+                char *base64;
+
+                base64_encode_alloc(enc->v.qcow.passphrase,
+                                    strlen(enc->v.qcow.passphrase),
+                                    &base64);
+                if (base64 == NULL) {
+                    virReportOOMError(conn);
+                    return -1;
+                }
+                virBufferVSprintf(buf, "    <passphrase>%s</passphrase>\n",
+                                  base64);
+                free(base64);
+            }
+            break;
+
+        default:
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("unhandled volume encryption format %d"),
+                                  enc->format);
+            return -1;
+    }
+
+    virBufferAddLit(buf, "  </encryption>\n");
+
+    return 0;
+}
diff --git a/src/storage_encryption.h b/src/storage_encryption.h
new file mode 100644
index 0000000..bd0b417
--- /dev/null
+++ b/src/storage_encryption.h
@@ -0,0 +1,62 @@
+/*
+ * storage_encryption.h: volume encryption information
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Red Hat Author: Miloslav Trmač <mitr at redhat.com>
+ */
+
+#ifndef __VIR_STORAGE_ENCRYPTION_H__
+#define __VIR_STORAGE_ENCRYPTION_H__
+
+#include "internal.h"
+#include "buf.h"
+#include "util.h"
+
+#include <stdbool.h>
+#include <libxml/tree.h>
+
+enum virStorageEncryptionFormat {
+    VIR_STORAGE_ENCRYPTION_FORMAT_UNENCRYPTED = 0,
+    VIR_STORAGE_ENCRYPTION_FORMAT_QCOW, /* Both qcow and qcow2 */
+
+    VIR_STORAGE_ENCRYPTION_FORMAT_LAST,
+};
+VIR_ENUM_DECL(virStorageEncryptionFormat)
+
+typedef struct _virStorageEncryption virStorageEncryption;
+typedef virStorageEncryption *virStorageEncryptionPtr;
+struct _virStorageEncryption {
+    int format;            /* enum virStorageEncryptionFormat */
+
+    union {                /* Format-specific data */
+        struct {
+            char *passphrase;
+        } qcow;
+    } v;
+};
+
+void virStorageEncryptionFree(virStorageEncryptionPtr enc);
+void virStorageEncryptionDropSecrets(virStorageEncryptionPtr enc);
+virStorageEncryptionPtr virStorageEncryptionParseNode(virConnectPtr conn,
+                                                      xmlDocPtr xml,
+                                                      xmlNodePtr root);
+int virStorageEncryptionFormat(virConnectPtr conn, virBufferPtr buf,
+                               virStorageEncryptionPtr enc,
+                               bool with_secrets);
+
+#endif /* __VIR_STORAGE_ENCRYPTION_H__ */
-- 
1.6.2.5




More information about the libvir-list mailing list