* [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:18 ` Paolo Bonzini
2015-10-19 15:09 ` [Qemu-devel] [PATCH 02/17] crypto: add support for loading encrypted x509 keys Daniel P. Berrange
` (18 subsequent siblings)
19 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Introduce a new QCryptoSecret object class which will be used
for providing passwords and keys to other objects which need
sensitive credentials.
The new object can provide secret values directly as properties,
or indirectly via a file. The latter includes support for file
descriptor passing syntax on UNIX platforms. Ordinarily passing
secret values directly as properties is insecure, since they
are visible in process listings, or in log files showing the
CLI args / QMP commands. It is possible to use AES-256-CBC to
encrypt the secret values though, in which case all that is
visible is the ciphertext. For adhoc developer testing though,
it is fine to provide the secrets directly without encryption
so this is not explicitly forbidden.
The anticipated scenario is that libvirtd will create a random
master key per QEMU instance (eg /var/run/libvirt/qemu/$VMNAME.key)
and will use that key to encrypt all passwords it provides to
QEMU via '-object secret,....'. This avoids the need for libvirt
(or other mgmt apps) to worry about file descriptor passing.
It also makes life easier for people who are scripting the
management of QEMU, for whom FD passing is significantly more
complex.
Providing data inline (insecure, only for adhoc dev tetsing)
$QEMU -object secret,id=sec0,data=letmein
Providing data indirectly
echo -n "letmein" > mypasswd.txt
$QEMU -object secret,id=sec0,file=mypasswd.txt
Providing binary data
$QEMU -object secret,id=sec0,file=mykey.bin,format=base64
Providing data with encyption
$QEMU -object secret,id=master0,file=mykey.bin,format=base64 \
-object secret,id=sec0,data=[base64 ciphertext],\
keyid=master0,iv=[base64 IV],format=utf8
Note that 'format' here refers to the format of the decrypted
data, which is independant of the ciphertext which must always
be in base64.
More examples are shown in the updated docs.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
crypto/Makefile.objs | 1 +
crypto/secret.c | 513 +++++++++++++++++++++++++++++++++++++++++++++
include/crypto/secret.h | 139 ++++++++++++
qapi/crypto.json | 14 ++
qemu-options.hx | 76 +++++++
tests/.gitignore | 1 +
tests/Makefile | 2 +
tests/test-crypto-secret.c | 440 ++++++++++++++++++++++++++++++++++++++
8 files changed, 1186 insertions(+)
create mode 100644 crypto/secret.c
create mode 100644 include/crypto/secret.h
create mode 100644 tests/test-crypto-secret.c
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index b2a0e0b..a3135f1 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -7,6 +7,7 @@ crypto-obj-y += tlscreds.o
crypto-obj-y += tlscredsanon.o
crypto-obj-y += tlscredsx509.o
crypto-obj-y += tlssession.o
+crypto-obj-y += secret.o
# Let the userspace emulators avoid linking gnutls/etc
crypto-aes-obj-y = aes.o
diff --git a/crypto/secret.c b/crypto/secret.c
new file mode 100644
index 0000000..e1ee4fa
--- /dev/null
+++ b/crypto/secret.c
@@ -0,0 +1,513 @@
+/*
+ * QEMU crypto secret support
+ *
+ * Copyright (c) 2015 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 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "crypto/secret.h"
+#include "crypto/cipher.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+
+static void
+qcrypto_secret_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ int fd;
+ gchar *data = NULL;
+ size_t offset = 0;
+ size_t length = 0;
+
+ if (value) {
+ if (secret->file) {
+ if (secret->data) {
+ error_setg(errp,
+ "'file' and 'data' are mutually exclusive");
+ return;
+ }
+ fd = qemu_open(secret->file, O_RDONLY);
+ if (fd < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to open %s", secret->file);
+ return;
+ }
+ while (length < (1024 * 1024)) { /* Limit secrets to 1 MB */
+ if ((length - offset) < 1024) {
+ length += 1024;
+ data = g_renew(gchar, data, length);
+ }
+ ssize_t ret = read(fd, data + offset, length - offset);
+ if (ret == 0) {
+ break;
+ }
+ if (ret < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to read from %s", secret->file);
+ close(fd);
+ g_free(data);
+ return;
+ }
+ offset += ret;
+ }
+ if (offset) {
+ data = g_renew(gchar, data, offset + 1);
+ data[offset] = '\0';
+ } else {
+ g_free(data);
+ data = NULL;
+ }
+ close(fd);
+ secret->data = data;
+ }
+ } else {
+ if (secret->file) {
+ g_free(secret->data);
+ secret->data = NULL;
+ }
+ }
+}
+
+
+static bool
+qcrypto_secret_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return secret->data != NULL;
+}
+
+
+static void
+qcrypto_secret_prop_set_format(Object *obj,
+ int value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+
+ creds->format = value;
+}
+
+
+static int
+qcrypto_secret_prop_get_format(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+
+ return creds->format;
+}
+
+
+static void
+qcrypto_secret_prop_set_data(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->data);
+ secret->data = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_data(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->data);
+}
+
+
+static void
+qcrypto_secret_prop_set_file(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->file);
+ secret->file = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_file(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->file);
+}
+
+
+static void
+qcrypto_secret_prop_set_iv(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->iv);
+ secret->iv = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_iv(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->iv);
+}
+
+
+static void
+qcrypto_secret_prop_set_keyid(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->keyid);
+ secret->keyid = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_keyid(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->keyid);
+}
+
+
+static void
+qcrypto_secret_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_secret_init(Object *obj)
+{
+ object_property_add_bool(obj, "loaded",
+ qcrypto_secret_prop_get_loaded,
+ qcrypto_secret_prop_set_loaded,
+ NULL);
+ object_property_add_enum(obj, "format",
+ "QCryptoSecretFormat",
+ QCryptoSecretFormat_lookup,
+ qcrypto_secret_prop_get_format,
+ qcrypto_secret_prop_set_format,
+ NULL);
+ object_property_add_str(obj, "data",
+ qcrypto_secret_prop_get_data,
+ qcrypto_secret_prop_set_data,
+ NULL);
+ object_property_add_str(obj, "file",
+ qcrypto_secret_prop_get_file,
+ qcrypto_secret_prop_set_file,
+ NULL);
+ object_property_add_str(obj, "keyid",
+ qcrypto_secret_prop_get_keyid,
+ qcrypto_secret_prop_set_keyid,
+ NULL);
+ object_property_add_str(obj, "iv",
+ qcrypto_secret_prop_get_iv,
+ qcrypto_secret_prop_set_iv,
+ NULL);
+}
+
+
+static void
+qcrypto_secret_finalize(Object *obj)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->iv);
+ g_free(secret->file);
+ g_free(secret->keyid);
+ g_free(secret->data);
+}
+
+static void
+qcrypto_secret_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_secret_complete;
+}
+
+
+static QCryptoSecret *qcrypto_secret_lookup(const char *secretid,
+ Error **errp)
+{
+ Object *obj;
+ QCryptoSecret *secret;
+
+ obj = object_resolve_path_component(
+ object_get_objects_root(), secretid);
+ if (!obj) {
+ error_setg(errp, "No secret with id '%s'", secretid);
+ return NULL;
+ }
+
+ secret = (QCryptoSecret *)
+ object_dynamic_cast(obj,
+ TYPE_QCRYPTO_SECRET);
+ if (!secret) {
+ error_setg(errp, "Object with id '%s' is not a secret",
+ secretid);
+ return NULL;
+ }
+
+ if (!secret->data) {
+ error_setg(errp, "Secret with id '%s' has no data",
+ secretid);
+ return NULL;
+ }
+
+ return secret;
+}
+
+
+static gchar *qcrypto_secret_decrypt(const char *input,
+ const char *secretid,
+ const char *iv64,
+ Error **errp)
+{
+ gchar *key64 = qcrypto_secret_lookup_as_base64(secretid, errp);
+ guchar *key = NULL, *ciphertext = NULL, *iv = NULL;
+ gsize keylen, ciphertextlen, ivlen;
+ QCryptoCipher *aes = NULL;
+ gchar *plaintext = NULL;
+
+ if (!key64) {
+ return NULL;
+ }
+
+ if (!iv64) {
+ error_setg(errp, "IV is required to decrypt secret");
+ goto cleanup;
+ }
+
+ key = g_base64_decode(key64, &keylen);
+ if (keylen != 32) {
+ error_setg(errp, "Key should be 32 bytes in length");
+ goto cleanup;
+ }
+
+ iv = g_base64_decode(iv64, &ivlen);
+ if (ivlen != 16) {
+ error_setg(errp, "IV should be 16 bytes in length not %zu",
+ ivlen);
+ goto cleanup;
+ }
+
+ aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
+ QCRYPTO_CIPHER_MODE_CBC,
+ key, keylen,
+ errp);
+ if (!aes) {
+ goto cleanup;
+ }
+
+ if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
+ goto cleanup;
+ }
+
+ ciphertext = g_base64_decode(input, &ciphertextlen);
+ plaintext = g_new0(gchar, ciphertextlen + 1);
+ if (qcrypto_cipher_decrypt(aes,
+ ciphertext,
+ plaintext,
+ ciphertextlen,
+ errp) < 0) {
+ plaintext = NULL;
+ goto cleanup;
+ }
+
+ if (plaintext[ciphertextlen - 1] > 16 ||
+ plaintext[ciphertextlen - 1] > ciphertextlen) {
+ error_setg(errp, "Incorrect number of padding bytes (%d) "
+ "found on decrypted data",
+ (int)plaintext[ciphertextlen - 1]);
+ g_free(plaintext);
+ plaintext = NULL;
+ goto cleanup;
+ }
+
+ plaintext[ciphertextlen - plaintext[ciphertextlen - 1]] = '\0';
+
+ cleanup:
+ g_free(ciphertext);
+ g_free(iv);
+ g_free(key);
+ g_free(key64);
+ qcrypto_cipher_free(aes);
+ return plaintext;
+}
+
+
+gchar *qcrypto_secret_lookup_as_utf8(const char *secretid,
+ Error **errp)
+{
+ QCryptoSecret *secret = qcrypto_secret_lookup(secretid,
+ errp);
+ gchar *input;
+ gsize len;
+ gchar *output = NULL;
+ if (!secret) {
+ return NULL;
+ }
+
+ if (secret->keyid) {
+ input = qcrypto_secret_decrypt(secret->data,
+ secret->keyid,
+ secret->iv,
+ errp);
+ if (!input) {
+ return NULL;
+ }
+ } else {
+ input = g_strdup(secret->data);
+ }
+
+ switch (secret->format) {
+ case QCRYPTO_SECRET_FORMAT_UTF8:
+ if (!g_utf8_validate(input, strlen(input), NULL)) {
+ error_setg(errp,
+ "Data from secret %s is not valid UTF-8",
+ secretid);
+ goto cleanup;
+ }
+ output = input;
+ input = NULL;
+ break;
+
+ case QCRYPTO_SECRET_FORMAT_BASE64:
+ output = (gchar *)g_base64_decode(input,
+ &len);
+ if (!g_utf8_validate(output, len, NULL)) {
+ error_setg(errp,
+ "Decoded base64 data from secret %s is not valid UTF-8",
+ secretid);
+ g_free(output);
+ output = NULL;
+ goto cleanup;
+ }
+ if (output[len - 1] != '\0') {
+ output = g_renew(gchar, output, len + 1);
+ output[len] = '\0';
+ }
+ break;
+
+ default:
+ error_setg(errp, "Unexpected secret data format for %s",
+ secretid);
+ break;
+ }
+
+ cleanup:
+ g_free(input);
+
+ return output;
+}
+
+
+static const char *base64_valid_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+gchar *qcrypto_secret_lookup_as_base64(const char *secretid,
+ Error **errp)
+{
+ QCryptoSecret *secret = qcrypto_secret_lookup(secretid,
+ errp);
+ gchar *input;
+ gchar *output = NULL;
+ if (!secret) {
+ return NULL;
+ }
+
+ if (secret->keyid) {
+ input = qcrypto_secret_decrypt(secret->data,
+ secret->keyid,
+ secret->iv,
+ errp);
+ if (!input) {
+ return NULL;
+ }
+ } else {
+ input = g_strdup(secret->data);
+ }
+
+ switch (secret->format) {
+ case QCRYPTO_SECRET_FORMAT_UTF8:
+ output = g_base64_encode((const guchar *)input, strlen(input));
+ break;
+
+ case QCRYPTO_SECRET_FORMAT_BASE64:
+ if (strspn(input, base64_valid_chars) != strlen(input)) {
+ error_setg(errp,
+ "Data for secret %s not a valid base64 string",
+ secretid);
+ goto cleanup;
+ }
+ output = input;
+ input = NULL;
+ break;
+
+ default:
+ error_setg(errp, "Unexpected secret data format for %s",
+ secretid);
+ break;
+ }
+
+ cleanup:
+ g_free(input);
+
+ return output;
+}
+
+
+static const TypeInfo qcrypto_secret_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QCRYPTO_SECRET,
+ .instance_size = sizeof(QCryptoSecret),
+ .instance_init = qcrypto_secret_init,
+ .instance_finalize = qcrypto_secret_finalize,
+ .class_size = sizeof(QCryptoSecretClass),
+ .class_init = qcrypto_secret_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_secret_register_types(void)
+{
+ type_register_static(&qcrypto_secret_info);
+}
+
+
+type_init(qcrypto_secret_register_types);
diff --git a/include/crypto/secret.h b/include/crypto/secret.h
new file mode 100644
index 0000000..5c0801d
--- /dev/null
+++ b/include/crypto/secret.h
@@ -0,0 +1,139 @@
+/*
+ * QEMU crypto secret support
+ *
+ * Copyright (c) 2015 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 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QCRYPTO_SECRET_H__
+#define QCRYPTO_SECRET_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#define TYPE_QCRYPTO_SECRET "secret"
+#define QCRYPTO_SECRET(obj) \
+ OBJECT_CHECK(QCryptoSecret, (obj), TYPE_QCRYPTO_SECRET)
+
+typedef struct QCryptoSecret QCryptoSecret;
+typedef struct QCryptoSecretClass QCryptoSecretClass;
+
+/**
+ * QCryptoSecret:
+ *
+ * The QCryptoSecret object provides storage of secrets,
+ * which may be user passwords, encryption keys or any
+ * other kind of sensitive data that is represented as
+ * a sequence of bytes.
+ *
+ * The sensitive data associated with the secret can
+ * be provided directly via the 'data' property, or
+ * indirectly via the 'file' property. In the latter
+ * case there is support for file descriptor passing
+ * via the usual /dev/fdset/NN syntax that QEMU uses.
+ *
+ * The data for a secret can be provided in two formats,
+ * either as a UTF-8 string (the default), or as base64
+ * encoded 8-bit binary data. The latter is appropriate
+ * for raw encrypton keys, while the former is appropriate
+ * for user entered passwords.
+ *
+ * The data may be optionally encrypted with AES-256-CBC,
+ * and the decryption key provided by another
+ * QCryptoSecret instance identified by the 'keyid'
+ * property. When passing sensitive data directly
+ * via the 'data' property it is strongly recommended
+ * to use the AES encryption facility to prevent the
+ * sensitive data being exposed in the process listing
+ * or system log files.
+ *
+ * Providing data directly, insecurely (suitable for
+ * adhoc developer testing only)
+ *
+ * $QEMU -object secret,id=sec0,data=letmein
+ *
+ * Providing data indirectly:
+ *
+ * # echo -n "letmein" > password.txt
+ * # $QEMU \
+ * -object secret,id=sec0,file=password.txt
+ *
+ * Using a master encryption key with data.
+ *
+ * The master key needs to be created as 32 secure
+ * random bytes, base64 encoded.
+ *
+ * # openssl rand -base64 32 > key.b64
+ * # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"')
+ *
+ * Each secret to be encrypted needs to have a random
+ * initialization vector generated. These do not need
+ * to be kept secret
+ *
+ * # openssl rand -base64 16 > iv.b64
+ * # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"')
+ *
+ * A secret to be defined can now be encrypted
+ *
+ * # SECRET=$(echo -n "letmein" |
+ * openssl enc -aes-256-cbc -a -K $KEY -iv $IV)
+ *
+ * When launching QEMU, create a master secret pointing
+ * to key.b64 and specify that to be used to decrypt
+ * the user password
+ *
+ * # $QEMU \
+ * -object secret,id=secmaster0,format=base64,file=key.b64 \
+ * -object secret,id=sec0,keyid=secmaster0,format=utf8,\
+ * data=$SECRET,iv=$(<iv.b64)
+ *
+ * When encrypting, the data can still be provided via an
+ * external base64 encoded file
+ *
+ * # echo -n "letmein" |
+ * openssl enc -aes-256-cbc -a -K $KEY -iv $IV -o pw.aes
+ * # $QEMU \
+ * -object secret,id=secmaster0,format=base64,file=key.b64 \
+ * -object secret,id=sec0,keyid=secmaster0,format=utf8,\
+ * file=pw.aes,iv=$(<iv.b64)
+ *
+ * Note that format=utf8 for sec0, because the format refers to
+ * the format of the data *after* decryption. The encrypted
+ * data itself must always be in base64 format.
+ */
+
+struct QCryptoSecret {
+ Object parent_obj;
+ gchar *data;
+ QCryptoSecretFormat format;
+ gchar *file;
+ gchar *keyid;
+ gchar *iv;
+};
+
+
+struct QCryptoSecretClass {
+ ObjectClass parent_class;
+};
+
+
+extern gchar *qcrypto_secret_lookup_as_utf8(const char *secretid,
+ Error **errp);
+extern gchar *qcrypto_secret_lookup_as_base64(const char *secretid,
+ Error **errp);
+
+#endif /* QCRYPTO_SECRET_H__ */
diff --git a/qapi/crypto.json b/qapi/crypto.json
index b058b14..d498dc3 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -19,3 +19,17 @@
{ 'enum': 'QCryptoTLSCredsEndpoint',
'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT',
'data': ['client', 'server']}
+
+
+##
+# QCryptoSecretFormat:
+#
+# The data format that the secret is provided in
+#
+# @utf8: printable UTF-8 encoded text
+# @base64: arbitrary base64 encoded binary data
+# Since: 2.5
+##
+{ 'enum': 'QCryptoSecretFormat',
+ 'prefix': 'QCRYPTO_SECRET_FORMAT',
+ 'data': ['utf8', 'base64']}
diff --git a/qemu-options.hx b/qemu-options.hx
index 2485b94..831213b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3660,6 +3660,82 @@ queue @var{all|rx|tx} is an option that can be applied to any netfilter.
@option{tx}: the filter is attached to the transmit queue of the netdev,
where it will receive packets sent by the netdev.
+@item -object secret,id=@var{id},data=@var{string},format=@var{utf8|base64}[,keyid=@var{secretid},iv=@var{string}]
+@item -object secret,id=@var{id},file=@var{filename},format=@var{utf8|base64}[,keyid=@var{secretid},iv=@var{string}]
+
+Defines a secret to store a password, encryption key, or some other sensitive
+data. The senstive data can either be passed directly via the @var{data}
+parameter, or indirectly via the @var{file} parameter. Using the @var{data}
+parameter is insecure unless the sensitive data is encrypted.
+
+The sensitive data can be provided in UTF-8 format, which is suitable for
+strings which only need to contain printable valid UTF-8 characters. For
+data which needs the full range of 8-bit values, the base64 encoded format
+can be used. QEMU will convert from which ever format is provided to the
+format it needs internally. eg, an RBD password can be provided in UTF8
+format, even though it will be base64 encoded when passed onto the RBD
+sever.
+
+For added protection, it is possible to encrypt the data associated with
+a secret using the AES-256-CBC cipher. Use of encryption is indicated
+by providing the @var{keyid} and @var{iv} parameters. The @var{keyid}
+parameter provides the ID of a previously defined secret that contains
+the AES-256 decryption key. This key should be 32-bytes long and be
+base64 encoded. The @var{iv} parameter provides the random initialization
+vector used for encryption of this particular secret and should be a
+base64 encrypted string of the 32-byte IV.
+
+The simplest (insecure) usage is to provide the secret inline
+
+@example
+
+ # $QEMU -object secret,id=sec0,data=letmein,format=utf8
+
+@end example
+
+The simplest secure usage is to provide the secret via a file
+
+ # echo -n "letmein" > mypasswd.txt
+ # $QEMU -object secret,id=sec0,file=mypasswd.txt,format=utf8
+
+For greater security, AES-256-CBC should be used. To illustrate usage,
+consider the openssl command line tool which can encrypt the data. Note
+that when encrypting, the plaintext must be padded to the cipher block
+size (32 bytes) using the standard PKCS#5/6 compatible padding algorithm.
+
+First a master key needs to be created in base64 encoding:
+
+@example
+ # openssl rand -base64 32 > key.b64
+ # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"')
+@end example
+
+Each secret to be encrypted needs to have a random initialization vector
+generated. These do not need to be kept secret
+
+@example
+ # openssl rand -base64 16 > iv.b64
+ # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"')
+@end example
+
+The secret to be defined can now be encrypted
+
+@example
+ # SECRET=$(echo -n "letmein" |
+ openssl enc -aes-256-cbc -a -K $KEY -iv $IV)
+@end example
+
+When launching QEMU, create a master secret pointing to @code{key.b64}
+and specify that to be used to decrypt the user password. Pass the
+contents of @code{iv.b64} to the second secret
+
+@example
+ # $QEMU \
+ -object secret,id=secmaster0,format=base64,file=key.b64 \
+ -object secret,id=sec0,keyid=secmaster0,format=utf8,\
+ data=$SECRET,iv=$(<iv.b64)
+@end example
+
@end table
ETEXI
diff --git a/tests/.gitignore b/tests/.gitignore
index 65496aa..b4e2a0a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -12,6 +12,7 @@ test-bitops
test-coroutine
test-crypto-cipher
test-crypto-hash
+test-crypto-secret
test-crypto-tlscredsx509
test-crypto-tlscredsx509-work/
test-crypto-tlscredsx509-certs/
diff --git a/tests/Makefile b/tests/Makefile
index cb221de..5e58b52 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -78,6 +78,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF)
gcov-files-test-write-threshold-y = block/write-threshold.c
check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
check-unit-y += tests/test-crypto-cipher$(EXESUF)
+check-unit-y += tests/test-crypto-secret$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
@@ -443,6 +444,7 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y)
tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y)
+tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y)
tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c
new file mode 100644
index 0000000..9c6121e
--- /dev/null
+++ b/tests/test-crypto-secret.c
@@ -0,0 +1,440 @@
+/*
+ * QEMU Crypto secret handling
+ *
+ * Copyright (c) 2015 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 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib.h>
+
+#include "crypto/init.h"
+#include "crypto/secret.h"
+
+static void test_secret_direct(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "123456",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "123456");
+
+ object_unparent(sec);
+ g_free(pw);
+}
+
+
+static void test_secret_indirect_good(void)
+{
+ Object *sec;
+ char *fname = NULL;
+ int fd = g_file_open_tmp("secretXXXXXX",
+ &fname,
+ NULL);
+
+ g_assert(fd >= 0);
+ g_assert_nonnull(fname);
+
+ g_assert(write(fd, "123456", 6) == 6);
+
+ sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "file", fname,
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "123456");
+
+ object_unparent(sec);
+ g_free(pw);
+ close(fd);
+ g_free(fname);
+}
+
+
+static void test_secret_indirect_badfile(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ NULL,
+ "file", "does-not-exist",
+ NULL);
+
+ g_assert(sec == NULL);
+}
+
+
+static void test_secret_indirect_emptyfile(void)
+{
+ Object *sec;
+ char *fname = NULL;
+ int fd = g_file_open_tmp("secretXXXXXX",
+ &fname,
+ NULL);
+
+ g_assert(fd >= 0);
+ g_assert_nonnull(fname);
+
+ sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "file", fname,
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+ close(fd);
+ g_free(fname);
+}
+
+
+static void test_secret_noconv_base64_good(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "MTIzNDU2",
+ "format", "base64",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_base64("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "MTIzNDU2");
+
+ object_unparent(sec);
+ g_free(pw);
+}
+
+
+static void test_secret_noconv_base64_bad(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "MTI$NDU2",
+ "format", "base64",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_base64("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+ object_unparent(sec);
+}
+
+
+static void test_secret_noconv_utf8(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "123456",
+ "format", "utf8",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "123456");
+
+ object_unparent(sec);
+ g_free(pw);
+}
+
+
+static void test_secret_conv_base64_utf8valid(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "MTIzNDU2",
+ "format", "base64",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "123456");
+
+ object_unparent(sec);
+ g_free(pw);
+}
+
+
+static void test_secret_conv_base64_utf8invalid(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "f0VMRgIBAQAAAA==",
+ "format", "base64",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+}
+
+
+static void test_secret_conv_utf8_base64(void)
+{
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "123456",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_base64("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "MTIzNDU2");
+
+ object_unparent(sec);
+ g_free(pw);
+}
+
+
+static void test_secret_crypt_good(void)
+{
+ Object *master = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "master",
+ &error_abort,
+ "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
+ "format", "base64",
+ NULL);
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "zL/3CUYZC1IqOrRrzXqwsA==",
+ "format", "utf8",
+ "keyid", "master",
+ "iv", "0I7Gw/TKuA+Old2W2apQ3g==",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ &error_abort);
+
+ g_assert_cmpstr(pw, ==, "123456");
+
+ object_unparent(sec);
+ object_unparent(master);
+ g_free(pw);
+}
+
+
+static void test_secret_crypt_short_key(void)
+{
+ Object *master = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "master",
+ &error_abort,
+ "data", "9miloPQCzGy+TL6aonfzVc",
+ "format", "base64",
+ NULL);
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ &error_abort,
+ "data", "zL/3CUYZC1IqOrRrzXqwsA==",
+ "format", "utf8",
+ "keyid", "master",
+ "iv", "0I7Gw/TKuA+Old2W2apQ3g==",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+ object_unparent(master);
+}
+
+
+static void test_secret_crypt_short_iv(void)
+{
+ Object *master = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "master",
+ &error_abort,
+ "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
+ "format", "base64",
+ NULL);
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ NULL,
+ "data", "zL/3CUYZC1IqOrRrzXqwsA==",
+ "format", "utf8",
+ "keyid", "master",
+ "iv", "0I7Gw/TKuA+Old2W2a",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+ object_unparent(master);
+}
+
+
+static void test_secret_crypt_missing_iv(void)
+{
+ Object *master = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "master",
+ &error_abort,
+ "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
+ "format", "base64",
+ NULL);
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ NULL,
+ "data", "zL/3CUYZC1IqOrRrzXqwsA==",
+ "format", "utf8",
+ "keyid", "master",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+ object_unparent(master);
+}
+
+
+static void test_secret_crypt_bad_iv(void)
+{
+ Object *master = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "master",
+ &error_abort,
+ "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
+ "format", "base64",
+ NULL);
+ Object *sec = object_new_with_props(
+ TYPE_QCRYPTO_SECRET,
+ object_get_objects_root(),
+ "sec0",
+ NULL,
+ "data", "zL/3CUYZC1IqOrRrzXqwsA==",
+ "format", "utf8",
+ "keyid", "master",
+ "iv", "0I7Gw/TK$$uA+Old2W2a",
+ NULL);
+
+ char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ NULL);
+
+ g_assert(pw == NULL);
+
+ object_unparent(sec);
+ object_unparent(master);
+}
+
+
+int main(int argc, char **argv)
+{
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ g_test_add_func("/crypto/secret/direct",
+ test_secret_direct);
+ g_test_add_func("/crypto/secret/indirect/good",
+ test_secret_indirect_good);
+ g_test_add_func("/crypto/secret/indirect/badfile",
+ test_secret_indirect_badfile);
+ g_test_add_func("/crypto/secret/indirect/emptyfile",
+ test_secret_indirect_emptyfile);
+
+ g_test_add_func("/crypto/secret/noconv/base64/good",
+ test_secret_noconv_base64_good);
+ g_test_add_func("/crypto/secret/noconv/base64/bad",
+ test_secret_noconv_base64_bad);
+ g_test_add_func("/crypto/secret/noconv/utf8",
+ test_secret_noconv_utf8);
+ g_test_add_func("/crypto/secret/conv/base64/utf8valid",
+ test_secret_conv_base64_utf8valid);
+ g_test_add_func("/crypto/secret/conv/base64/utf8invalid",
+ test_secret_conv_base64_utf8invalid);
+ g_test_add_func("/crypto/secret/conv/utf8/base64",
+ test_secret_conv_utf8_base64);
+
+ g_test_add_func("/crypto/secret/crypt/good",
+ test_secret_crypt_good);
+ g_test_add_func("/crypto/secret/crypt/shortkey",
+ test_secret_crypt_short_key);
+ g_test_add_func("/crypto/secret/crypt/shortiv",
+ test_secret_crypt_short_iv);
+ g_test_add_func("/crypto/secret/crypt/missingiv",
+ test_secret_crypt_missing_iv);
+ g_test_add_func("/crypto/secret/crypt/badiv",
+ test_secret_crypt_bad_iv);
+
+ return g_test_run();
+}
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:09 ` [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling Daniel P. Berrange
@ 2015-10-19 15:18 ` Paolo Bonzini
2015-10-19 15:24 ` Daniel P. Berrange
0 siblings, 1 reply; 36+ messages in thread
From: Paolo Bonzini @ 2015-10-19 15:18 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel
Cc: Kevin Wolf, Josh Durgin, Stefan Hajnoczi, qemu-block,
Markus Armbruster, Ronnie Sahlberg
On 19/10/2015 17:09, Daniel P. Berrange wrote:
> +
> + switch (secret->format) {
> + case QCRYPTO_SECRET_FORMAT_UTF8:
> + if (!g_utf8_validate(input, strlen(input), NULL)) {
> + error_setg(errp,
> + "Data from secret %s is not valid UTF-8",
> + secretid);
> + goto cleanup;
> + }
> + output = input;
> + input = NULL;
> + break;
Why validate secrets as UTF-8? In other words why have "utf8" instead
of "binary" as a possible QCryptoSecretFormat?
Paolo
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:18 ` Paolo Bonzini
@ 2015-10-19 15:24 ` Daniel P. Berrange
2015-10-19 15:40 ` Paolo Bonzini
0 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:24 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block, qemu-devel,
Markus Armbruster, Stefan Hajnoczi
On Mon, Oct 19, 2015 at 05:18:56PM +0200, Paolo Bonzini wrote:
>
>
> On 19/10/2015 17:09, Daniel P. Berrange wrote:
> > +
> > + switch (secret->format) {
> > + case QCRYPTO_SECRET_FORMAT_UTF8:
> > + if (!g_utf8_validate(input, strlen(input), NULL)) {
> > + error_setg(errp,
> > + "Data from secret %s is not valid UTF-8",
> > + secretid);
> > + goto cleanup;
> > + }
> > + output = input;
> > + input = NULL;
> > + break;
>
> Why validate secrets as UTF-8? In other words why have "utf8" instead
> of "binary" as a possible QCryptoSecretFormat?
JSON doesn't accept arbitrary 8-bit binary data, so the alternative
'base64' is effectively providing binary data facility. Having to
use base64 for plain passwords is rather tedious though, so allowing
utf8 is a much more developer friendly approach for people using QEMU
without a mgmt tool like libvirt.
NB, this dual-format utf8-or-base64 approach matches the approach used
in QEMU guest agent for the guest-file-read/write commands for the same
reason.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:24 ` Daniel P. Berrange
@ 2015-10-19 15:40 ` Paolo Bonzini
2015-10-19 15:46 ` Daniel P. Berrange
0 siblings, 1 reply; 36+ messages in thread
From: Paolo Bonzini @ 2015-10-19 15:40 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On 19/10/2015 17:24, Daniel P. Berrange wrote:
> JSON doesn't accept arbitrary 8-bit binary data, so the alternative
> 'base64' is effectively providing binary data facility. Having to
> use base64 for plain passwords is rather tedious though, so allowing
> utf8 is a much more developer friendly approach for people using QEMU
> without a mgmt tool like libvirt.
>
> NB, this dual-format utf8-or-base64 approach matches the approach used
> in QEMU guest agent for the guest-file-read/write commands for the same
> reason.
The difference is that guest-file-read/write have the payload in JSON;
for file-based secrets the payload is not JSON.
So I think that "binary" (which is the default anyway) would fit all the
usecases (direct over JSON, file-based, direct over command line).
Direct over JSON would be limited to valid UTF-8, but that's just a
limitation of the transport.
Paolo
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:40 ` Paolo Bonzini
@ 2015-10-19 15:46 ` Daniel P. Berrange
2015-10-19 16:12 ` Paolo Bonzini
0 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:46 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On Mon, Oct 19, 2015 at 05:40:08PM +0200, Paolo Bonzini wrote:
>
>
> On 19/10/2015 17:24, Daniel P. Berrange wrote:
> > JSON doesn't accept arbitrary 8-bit binary data, so the alternative
> > 'base64' is effectively providing binary data facility. Having to
> > use base64 for plain passwords is rather tedious though, so allowing
> > utf8 is a much more developer friendly approach for people using QEMU
> > without a mgmt tool like libvirt.
> >
> > NB, this dual-format utf8-or-base64 approach matches the approach used
> > in QEMU guest agent for the guest-file-read/write commands for the same
> > reason.
>
> The difference is that guest-file-read/write have the payload in JSON;
> for file-based secrets the payload is not JSON.
For non-file based secrets though, the payload *is* in the JSON,
and per the cover letter, I actually anticipate passing all
secrets inline in the JSON and only using the file backend for
loading the initial master key. This avoids the need to do
file handle passing and/or create lots of temporary files, when
hotplugging resources.
> So I think that "binary" (which is the default anyway) would fit all the
> usecases (direct over JSON, file-based, direct over command line).
> Direct over JSON would be limited to valid UTF-8, but that's just a
> limitation of the transport.
I don't think that's actually an acceptable limitation - I want the
inline data passing to be fully usable for non-UTF-8 data too.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 15:46 ` Daniel P. Berrange
@ 2015-10-19 16:12 ` Paolo Bonzini
2015-10-19 16:24 ` Daniel P. Berrange
0 siblings, 1 reply; 36+ messages in thread
From: Paolo Bonzini @ 2015-10-19 16:12 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On 19/10/2015 17:46, Daniel P. Berrange wrote:
>> > The difference is that guest-file-read/write have the payload in JSON;
>> > for file-based secrets the payload is not JSON.
> For non-file based secrets though, the payload *is* in the JSON,
> and per the cover letter, I actually anticipate passing all
> secrets inline in the JSON and only using the file backend for
> loading the initial master key. This avoids the need to do
> file handle passing and/or create lots of temporary files, when
> hotplugging resources.
>
> > So I think that "binary" (which is the default anyway) would fit all the
> > usecases (direct over JSON, file-based, direct over command line).
> > Direct over JSON would be limited to valid UTF-8, but that's just a
> > limitation of the transport.
>
> I don't think that's actually an acceptable limitation - I want the
> inline data passing to be fully usable for non-UTF-8 data too.
Of course, there's base64 for that. On the other hand, I think there's
no reason for unencoded file-based data passing to be usable for UTF-8
data only.
Paolo
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 16:12 ` Paolo Bonzini
@ 2015-10-19 16:24 ` Daniel P. Berrange
2015-10-19 16:28 ` Paolo Bonzini
0 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 16:24 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On Mon, Oct 19, 2015 at 06:12:53PM +0200, Paolo Bonzini wrote:
>
>
> On 19/10/2015 17:46, Daniel P. Berrange wrote:
> >> > The difference is that guest-file-read/write have the payload in JSON;
> >> > for file-based secrets the payload is not JSON.
> > For non-file based secrets though, the payload *is* in the JSON,
> > and per the cover letter, I actually anticipate passing all
> > secrets inline in the JSON and only using the file backend for
> > loading the initial master key. This avoids the need to do
> > file handle passing and/or create lots of temporary files, when
> > hotplugging resources.
> >
> > > So I think that "binary" (which is the default anyway) would fit all the
> > > usecases (direct over JSON, file-based, direct over command line).
> > > Direct over JSON would be limited to valid UTF-8, but that's just a
> > > limitation of the transport.
> >
> > I don't think that's actually an acceptable limitation - I want the
> > inline data passing to be fully usable for non-UTF-8 data too.
>
> Of course, there's base64 for that. On the other hand, I think there's
> no reason for unencoded file-based data passing to be usable for UTF-8
> data only.
Ok, so there's really two separate formats at play here.
The input format, eg the encoding of the data=XXXX value, or the contents
of the file, and the output format, which is that required by the consumer
inside QEMU. We convert between the two. eg you can provide data in base64
even if QEMU ultimately needs to use it in plain utf-8 format, or vica-verca.
IIUC, you're suggesting that for the input format, the data=XXX value
should allow a choice of utf8 or base64, while the external file could
just take raw or base64 data. That's easy enough to wire up - just add
a 3rd option to the format enum and make raw be the default for files.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 16:24 ` Daniel P. Berrange
@ 2015-10-19 16:28 ` Paolo Bonzini
2015-10-19 16:30 ` Daniel P. Berrange
0 siblings, 1 reply; 36+ messages in thread
From: Paolo Bonzini @ 2015-10-19 16:28 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On 19/10/2015 18:24, Daniel P. Berrange wrote:
> The input format, eg the encoding of the data=XXXX value, or the contents
> of the file, and the output format, which is that required by the consumer
> inside QEMU. We convert between the two. eg you can provide data in base64
> even if QEMU ultimately needs to use it in plain utf-8 format, or vica-verca.
Right. In the end QCryptoSecret only needs to provide a raw output;
converting it to something else, and possibly applying restrictions such
as UTF-8, should depend on the user. Of course the API can include
helper functions for common restrictions, but in general a "secret
storage" module is independent of them.
> IIUC, you're suggesting that for the input format, the data=XXX value
> should allow a choice of utf8 or base64, while the external file could
> just take raw or base64 data. That's easy enough to wire up - just add
> a 3rd option to the format enum and make raw be the default for files.
Almost.
I am also saying that the utf8 case for data=XXX actually should be raw,
because utf8 is just a limitation of JSON and not of the data=XXX
interface. Non-UTF8 data=XXX would then be accepted for the -object
command line option.
Paolo
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling
2015-10-19 16:28 ` Paolo Bonzini
@ 2015-10-19 16:30 ` Daniel P. Berrange
0 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 16:30 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
qemu-devel, Stefan Hajnoczi
On Mon, Oct 19, 2015 at 06:28:26PM +0200, Paolo Bonzini wrote:
>
>
> On 19/10/2015 18:24, Daniel P. Berrange wrote:
> > The input format, eg the encoding of the data=XXXX value, or the contents
> > of the file, and the output format, which is that required by the consumer
> > inside QEMU. We convert between the two. eg you can provide data in base64
> > even if QEMU ultimately needs to use it in plain utf-8 format, or vica-verca.
>
> Right. In the end QCryptoSecret only needs to provide a raw output;
> converting it to something else, and possibly applying restrictions such
> as UTF-8, should depend on the user. Of course the API can include
> helper functions for common restrictions, but in general a "secret
> storage" module is independent of them.
>
> > IIUC, you're suggesting that for the input format, the data=XXX value
> > should allow a choice of utf8 or base64, while the external file could
> > just take raw or base64 data. That's easy enough to wire up - just add
> > a 3rd option to the format enum and make raw be the default for files.
>
> Almost.
>
> I am also saying that the utf8 case for data=XXX actually should be raw,
> because utf8 is just a limitation of JSON and not of the data=XXX
> interface. Non-UTF8 data=XXX would then be accepted for the -object
> command line option.
Ah ok, I see what you mean now
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 02/17] crypto: add support for loading encrypted x509 keys
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object Daniel P. Berrange
` (17 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Make use of the QCryptoSecret object to support loading of
encrypted x509 keys. The optional 'passwordid' parameter
to the tls-creds-x509 object type, provides the ID of a
secret object instance that holds the decryption password
for the PEM file.
# echo "123456" > mypasswd.txt
# $QEMU \
-object secret,id=sec0,filename=mypasswd.txt \
-object tls-creds-x509,passwordid=sec0,id=creds0,\
dir=/home/berrange/.pki/qemu,endpoint=server \
-vnc :1,tls-creds=creds0
This requires QEMU to be linked to GNUTLS >= 3.1.11. If
GNUTLS is too old an error will be reported if an attempt
is made to pass a decryption password.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
crypto/tlscredsx509.c | 47 +++++++++++++++++++++++++++++++++++++++++++
include/crypto/tlscredsx509.h | 1 +
qemu-options.hx | 8 +++++++-
3 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index dc46bc4..637465f 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -20,6 +20,7 @@
#include "crypto/tlscredsx509.h"
#include "crypto/tlscredspriv.h"
+#include "crypto/secret.h"
#include "qom/object_interfaces.h"
#include "trace.h"
@@ -605,9 +606,29 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
}
if (cert != NULL && key != NULL) {
+#if GNUTLS_VERSION_NUMBER >= 0x030111
+ char *password = NULL;
+ if (creds->passwordid) {
+ password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
+ errp);
+ if (!password) {
+ goto cleanup;
+ }
+ }
+ ret = gnutls_certificate_set_x509_key_file2(creds->data,
+ cert, key,
+ GNUTLS_X509_FMT_PEM,
+ password,
+ 0);
+#else /* GNUTLS_VERSION_NUMBER < 0x030111 */
+ if (creds->passwordid) {
+ error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
+ goto cleanup;
+ }
ret = gnutls_certificate_set_x509_key_file(creds->data,
cert, key,
GNUTLS_X509_FMT_PEM);
+#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */
if (ret < 0) {
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
cert, key, gnutls_strerror(ret));
@@ -731,6 +752,27 @@ qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
}
+static void
+qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->passwordid = g_strdup(value);
+}
+
+
+static char *
+qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return g_strdup(creds->passwordid);
+}
+
+
static bool
qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
Error **errp G_GNUC_UNUSED)
@@ -763,6 +805,10 @@ qcrypto_tls_creds_x509_init(Object *obj)
qcrypto_tls_creds_x509_prop_get_sanity,
qcrypto_tls_creds_x509_prop_set_sanity,
NULL);
+ object_property_add_str(obj, "passwordid",
+ qcrypto_tls_creds_x509_prop_get_passwordid,
+ qcrypto_tls_creds_x509_prop_set_passwordid,
+ NULL);
}
@@ -771,6 +817,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
{
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+ g_free(creds->passwordid);
qcrypto_tls_creds_x509_unload(creds);
}
diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h
index b9785fd..25796d7 100644
--- a/include/crypto/tlscredsx509.h
+++ b/include/crypto/tlscredsx509.h
@@ -101,6 +101,7 @@ struct QCryptoTLSCredsX509 {
gnutls_certificate_credentials_t data;
#endif
bool sanityCheck;
+ char *passwordid;
};
diff --git a/qemu-options.hx b/qemu-options.hx
index 831213b..c43893a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3616,7 +3616,7 @@ expensive operation that consumes random pool entropy, so it is
recommended that a persistent set of parameters be generated
upfront and saved.
-@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}
+@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off},passwordid=@var{id}
Creates a TLS anonymous credentials object, which can be used to provide
TLS support on network backends. The @option{id} parameter is a unique
@@ -3643,6 +3643,12 @@ in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional),
@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers),
@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients).
+For the @var{server-key.pem} and @var{client-key.pem} files which
+contain sensitive private keys, it is possible to use an encrypted
+version by providing the @var{passwordid} parameter. This provides
+the ID of a previously created @code{secret} object containing the
+password for decryption.
+
@item -object filter-buffer,id=@var{id},netdev=@var{netdevid},interval=@var{t}[,queue=@var{all|rx|tx}]
Interval @var{t} can't be 0, this filter batches the packet delivery: all
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 01/17] crypto: add QCryptoSecret object class for password/key handling Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 02/17] crypto: add support for loading encrypted x509 keys Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 22:57 ` Josh Durgin
2015-10-19 15:09 ` [Qemu-devel] [PATCH 04/17] curl: add support for HTTP authentication parameters Daniel P. Berrange
` (16 subsequent siblings)
19 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Currently RBD passwords must be provided on the command line
via
$QEMU -drive file=rbd:pool/image:id=myname:\
key=QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=:\
auth_supported=cephx
This is insecure because the key is visible in the OS process
listing.
This adds support for an 'authsecret' parameter in the RBD
parameters that can be used with the QCryptoSecret object to
provide the password via a file:
echo "QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=" > poolkey.b64
$QEMU -object secret,id=secret0,file=poolkey.b64,format=base64 \
-drive file=rbd:pool/image:id=myname:\
auth_supported=cephx,authsecret=secret0
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/rbd.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/block/rbd.c b/block/rbd.c
index a60a19d..0acf777 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -16,6 +16,7 @@
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "block/block_int.h"
+#include "crypto/secret.h"
#include <rbd/librbd.h>
@@ -228,6 +229,23 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
return NULL;
}
+
+static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
+ Error **errp)
+{
+ gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
+ errp);
+ if (!secret) {
+ return -1;
+ }
+
+ rados_conf_set(cluster, "key", secret);
+ g_free(secret);
+
+ return 0;
+}
+
+
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
bool only_read_conf_file,
Error **errp)
@@ -299,10 +317,13 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
char conf[RBD_MAX_CONF_SIZE];
char clientname_buf[RBD_MAX_CONF_SIZE];
char *clientname;
+ const char *secretid;
rados_t cluster;
rados_ioctx_t io_ctx;
int ret;
+ secretid = qemu_opt_get(opts, "authsecret");
+
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
name, sizeof(name),
@@ -350,6 +371,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
return -EIO;
}
+ if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
+ rados_shutdown(cluster);
+ return -EIO;
+ }
+
if (rados_connect(cluster) < 0) {
error_setg(errp, "error connecting");
rados_shutdown(cluster);
@@ -423,6 +449,11 @@ static QemuOptsList runtime_opts = {
.type = QEMU_OPT_STRING,
.help = "Specification of the rbd image",
},
+ {
+ .name = "authsecret",
+ .type = QEMU_OPT_STRING,
+ .help = "ID of secret providing the password",
+ },
{ /* end of list */ }
},
};
@@ -436,6 +467,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
char conf[RBD_MAX_CONF_SIZE];
char clientname_buf[RBD_MAX_CONF_SIZE];
char *clientname;
+ const char *secretid;
QemuOpts *opts;
Error *local_err = NULL;
const char *filename;
@@ -450,6 +482,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
}
filename = qemu_opt_get(opts, "filename");
+ secretid = qemu_opt_get(opts, "authsecret");
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
@@ -488,6 +521,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
}
}
+ if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
+ goto failed_shutdown;
+ }
+
/*
* Fallback to more conservative semantics if setting cache
* options fails. Ignore errors from setting rbd_cache because the
@@ -919,6 +956,11 @@ static QemuOptsList qemu_rbd_create_opts = {
.type = QEMU_OPT_SIZE,
.help = "RBD object size"
},
+ {
+ .name = "authsecret",
+ .type = QEMU_OPT_STRING,
+ .help = "ID of secret providing the password",
+ },
{ /* end of list */ }
}
};
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object
2015-10-19 15:09 ` [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object Daniel P. Berrange
@ 2015-10-19 22:57 ` Josh Durgin
2015-10-20 8:35 ` Daniel P. Berrange
0 siblings, 1 reply; 36+ messages in thread
From: Josh Durgin @ 2015-10-19 22:57 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, Markus Armbruster,
Stefan Hajnoczi, Paolo Bonzini
On 10/19/2015 08:09 AM, Daniel P. Berrange wrote:
> Currently RBD passwords must be provided on the command line
> via
>
> $QEMU -drive file=rbd:pool/image:id=myname:\
> key=QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=:\
> auth_supported=cephx
>
> This is insecure because the key is visible in the OS process
> listing.
>
> This adds support for an 'authsecret' parameter in the RBD
> parameters that can be used with the QCryptoSecret object to
> provide the password via a file:
>
> echo "QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=" > poolkey.b64
> $QEMU -object secret,id=secret0,file=poolkey.b64,format=base64 \
> -drive file=rbd:pool/image:id=myname:\
> auth_supported=cephx,authsecret=secret0
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
Looks good in general, thanks for fixing this! Just one thing to fix
below.
> block/rbd.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
>
> diff --git a/block/rbd.c b/block/rbd.c
> index a60a19d..0acf777 100644
> --- a/block/rbd.c
> +++ b/block/rbd.c
> @@ -16,6 +16,7 @@
> #include "qemu-common.h"
> #include "qemu/error-report.h"
> #include "block/block_int.h"
> +#include "crypto/secret.h"
>
> #include <rbd/librbd.h>
>
> @@ -228,6 +229,23 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
> return NULL;
> }
>
> +
> +static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
> + Error **errp)
> +{
> + gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
> + errp);
> + if (!secret) {
> + return -1;
> + }
It looks like this fails if no authsecret is provided. Ceph auth can be
disabled, so it seems like we should skip the qemu_rbd_set_auth() calls
in this case.
> +
> + rados_conf_set(cluster, "key", secret);
> + g_free(secret);
> +
> + return 0;
> +}
> +
> +
> static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
> bool only_read_conf_file,
> Error **errp)
> @@ -299,10 +317,13 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
> char conf[RBD_MAX_CONF_SIZE];
> char clientname_buf[RBD_MAX_CONF_SIZE];
> char *clientname;
> + const char *secretid;
> rados_t cluster;
> rados_ioctx_t io_ctx;
> int ret;
>
> + secretid = qemu_opt_get(opts, "authsecret");
> +
> if (qemu_rbd_parsename(filename, pool, sizeof(pool),
> snap_buf, sizeof(snap_buf),
> name, sizeof(name),
> @@ -350,6 +371,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
> return -EIO;
> }
>
> + if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
> + rados_shutdown(cluster);
> + return -EIO;
> + }
> +
> if (rados_connect(cluster) < 0) {
> error_setg(errp, "error connecting");
> rados_shutdown(cluster);
> @@ -423,6 +449,11 @@ static QemuOptsList runtime_opts = {
> .type = QEMU_OPT_STRING,
> .help = "Specification of the rbd image",
> },
> + {
> + .name = "authsecret",
> + .type = QEMU_OPT_STRING,
> + .help = "ID of secret providing the password",
> + },
> { /* end of list */ }
> },
> };
> @@ -436,6 +467,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
> char conf[RBD_MAX_CONF_SIZE];
> char clientname_buf[RBD_MAX_CONF_SIZE];
> char *clientname;
> + const char *secretid;
> QemuOpts *opts;
> Error *local_err = NULL;
> const char *filename;
> @@ -450,6 +482,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
> }
>
> filename = qemu_opt_get(opts, "filename");
> + secretid = qemu_opt_get(opts, "authsecret");
>
> if (qemu_rbd_parsename(filename, pool, sizeof(pool),
> snap_buf, sizeof(snap_buf),
> @@ -488,6 +521,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
> }
> }
>
> + if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
> + goto failed_shutdown;
> + }
> +
> /*
> * Fallback to more conservative semantics if setting cache
> * options fails. Ignore errors from setting rbd_cache because the
> @@ -919,6 +956,11 @@ static QemuOptsList qemu_rbd_create_opts = {
> .type = QEMU_OPT_SIZE,
> .help = "RBD object size"
> },
> + {
> + .name = "authsecret",
> + .type = QEMU_OPT_STRING,
> + .help = "ID of secret providing the password",
> + },
> { /* end of list */ }
> }
> };
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object
2015-10-19 22:57 ` Josh Durgin
@ 2015-10-20 8:35 ` Daniel P. Berrange
0 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-20 8:35 UTC (permalink / raw)
To: Josh Durgin
Cc: Kevin Wolf, Ronnie Sahlberg, qemu-block, qemu-devel,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
On Mon, Oct 19, 2015 at 03:57:29PM -0700, Josh Durgin wrote:
> On 10/19/2015 08:09 AM, Daniel P. Berrange wrote:
> >Currently RBD passwords must be provided on the command line
> >via
> >
> > $QEMU -drive file=rbd:pool/image:id=myname:\
> > key=QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=:\
> > auth_supported=cephx
> >
> >This is insecure because the key is visible in the OS process
> >listing.
> >
> >This adds support for an 'authsecret' parameter in the RBD
> >parameters that can be used with the QCryptoSecret object to
> >provide the password via a file:
> >
> > echo "QVFDVm41aE82SHpGQWhBQXEwTkN2OGp0SmNJY0UrSE9CbE1RMUE=" > poolkey.b64
> > $QEMU -object secret,id=secret0,file=poolkey.b64,format=base64 \
> > -drive file=rbd:pool/image:id=myname:\
> > auth_supported=cephx,authsecret=secret0
> >
> >Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> >---
>
> Looks good in general, thanks for fixing this! Just one thing to fix
> below.
>
> > block/rbd.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 42 insertions(+)
> >
> >diff --git a/block/rbd.c b/block/rbd.c
> >index a60a19d..0acf777 100644
> >--- a/block/rbd.c
> >+++ b/block/rbd.c
> >@@ -16,6 +16,7 @@
> > #include "qemu-common.h"
> > #include "qemu/error-report.h"
> > #include "block/block_int.h"
> >+#include "crypto/secret.h"
> >
> > #include <rbd/librbd.h>
> >
> >@@ -228,6 +229,23 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
> > return NULL;
> > }
> >
> >+
> >+static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
> >+ Error **errp)
> >+{
> >+ gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
> >+ errp);
> >+ if (!secret) {
> >+ return -1;
> >+ }
>
> It looks like this fails if no authsecret is provided. Ceph auth can be
> disabled, so it seems like we should skip the qemu_rbd_set_auth() calls
> in this case.
This failure scenario happens if the user provides a key ID, but the
corresponding QCryptoSecret does not exist. This is a usage error
by the user, so it is appropriate to have it be a fatal error.
In the case that they don't want any auth, they can just leave out
the keyid parameter.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 04/17] curl: add support for HTTP authentication parameters
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (2 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 03/17] rbd: add support for getting password from QCryptoSecret object Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 05/17] iscsi: add support for getting CHAP password via QCryptoSecret API Daniel P. Berrange
` (15 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
If connecting to a web server which has authentication
turned on, QEMU gets a 401 as curl has not been configured
with any authentication credentials.
This adds 4 new parameters to the curl block driver
options, username, passwordid, proxyusername and
proxypasswordid.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/example.pw \
-object secret,id=sec1,filename=/home/berrange/proxy.pw \
-drive driver=http,url=http://example.com/some.img,\
username=dan,passwordid=sec0,\
proxyusername=dan,passwordid=sec1
Of course it is possible to use the same secret for both the
proxy & server passwords if desired.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/curl.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/block/curl.c b/block/curl.c
index 032cc8a..5ad5704 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -26,6 +26,7 @@
#include "block/block_int.h"
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qstring.h"
+#include "crypto/secret.h"
#include <curl/curl.h>
// #define DEBUG_CURL
@@ -77,6 +78,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
#define CURL_BLOCK_OPT_COOKIE "cookie"
+#define CURL_BLOCK_OPT_USERNAME "username"
+#define CURL_BLOCK_OPT_PASSWORDID "passwordid"
+#define CURL_BLOCK_OPT_PROXY_USERNAME "proxyusername"
+#define CURL_BLOCK_OPT_PROXY_PASSWORDID "proxypasswordid"
struct BDRVCURLState;
@@ -119,6 +124,10 @@ typedef struct BDRVCURLState {
char *cookie;
bool accept_range;
AioContext *aio_context;
+ char *username;
+ char *password;
+ char *proxyusername;
+ char *proxypassword;
} BDRVCURLState;
static void curl_clean_state(CURLState *s);
@@ -416,6 +425,21 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
+ if (s->username) {
+ curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username);
+ }
+ if (s->password) {
+ curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password);
+ }
+ if (s->proxyusername) {
+ curl_easy_setopt(state->curl,
+ CURLOPT_PROXYUSERNAME, s->proxyusername);
+ }
+ if (s->proxypassword) {
+ curl_easy_setopt(state->curl,
+ CURLOPT_PROXYPASSWORD, s->proxypassword);
+ }
+
/* Restrict supported protocols to avoid security issues in the more
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
* CVE-2013-0249.
@@ -522,10 +546,31 @@ static QemuOptsList runtime_opts = {
.type = QEMU_OPT_STRING,
.help = "Pass the cookie or list of cookies with each request"
},
+ {
+ .name = CURL_BLOCK_OPT_USERNAME,
+ .type = QEMU_OPT_STRING,
+ .help = "Username for HTTP auth"
+ },
+ {
+ .name = CURL_BLOCK_OPT_PASSWORDID,
+ .type = QEMU_OPT_STRING,
+ .help = "ID of secret used as password for HTTP auth",
+ },
+ {
+ .name = CURL_BLOCK_OPT_PROXY_USERNAME,
+ .type = QEMU_OPT_STRING,
+ .help = "Username for HTTP proxy auth"
+ },
+ {
+ .name = CURL_BLOCK_OPT_PROXY_PASSWORDID,
+ .type = QEMU_OPT_STRING,
+ .help = "ID of secret used as password for HTTP proxy auth",
+ },
{ /* end of list */ }
},
};
+
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -536,6 +581,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
const char *file;
const char *cookie;
double d;
+ const char *passwordid;
static int inited = 0;
@@ -577,6 +623,26 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
goto out_noclean;
}
+ s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
+ passwordid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORDID);
+
+ if (passwordid) {
+ s->password = qcrypto_secret_lookup_as_utf8(passwordid, errp);
+ if (!s->password) {
+ goto out_noclean;
+ }
+ }
+
+ s->proxyusername = g_strdup(
+ qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
+ passwordid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORDID);
+ if (passwordid) {
+ s->proxypassword = qcrypto_secret_lookup_as_utf8(passwordid, errp);
+ if (!s->proxypassword) {
+ goto out_noclean;
+ }
+ }
+
if (!inited) {
curl_global_init(CURL_GLOBAL_ALL);
inited = 1;
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 05/17] iscsi: add support for getting CHAP password via QCryptoSecret API
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (3 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 04/17] curl: add support for HTTP authentication parameters Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 06/17] qcow: add a 'keyid' parameter to qcow options Daniel P. Berrange
` (14 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
The iSCSI driver currently accepts the CHAP password in plain text
as a block driver property. This change adds a new "passwordid"
property that accepts the ID of a QCryptoSecret instance.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/example.pw \
-drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,\
user=dan,passwordid=sec0
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/iscsi.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/block/iscsi.c b/block/iscsi.c
index 93f1ee4..5209964 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -39,6 +39,7 @@
#include "sysemu/sysemu.h"
#include "qmp-commands.h"
#include "qapi/qmp/qstring.h"
+#include "crypto/secret.h"
#include <iscsi/iscsi.h>
#include <iscsi/scsi-lowlevel.h>
@@ -1018,6 +1019,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
QemuOpts *opts;
const char *user = NULL;
const char *password = NULL;
+ const char *passwordid;
+ char *secret = NULL;
list = qemu_find_opts("iscsi");
if (!list) {
@@ -1037,8 +1040,20 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
return;
}
+ passwordid = qemu_opt_get(opts, "passwordid");
password = qemu_opt_get(opts, "password");
- if (!password) {
+ if (passwordid && password) {
+ error_setg(errp, "'password' and 'passwordid' properties are "
+ "mutually exclusive");
+ return;
+ }
+ if (passwordid) {
+ secret = qcrypto_secret_lookup_as_utf8(passwordid, errp);
+ if (!secret) {
+ return;
+ }
+ password = secret;
+ } else if (!password) {
error_setg(errp, "CHAP username specified but no password was given");
return;
}
@@ -1046,6 +1061,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
error_setg(errp, "Failed to set initiator username and password");
}
+
+ g_free(secret);
}
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
@@ -1793,6 +1810,11 @@ static QemuOptsList qemu_iscsi_opts = {
.type = QEMU_OPT_STRING,
.help = "password for CHAP authentication to target",
},{
+ .name = "passwordid",
+ .type = QEMU_OPT_STRING,
+ .help = "ID of the secret providing password for CHAP "
+ "authentication to target",
+ },{
.name = "header-digest",
.type = QEMU_OPT_STRING,
.help = "HeaderDigest setting. "
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 06/17] qcow: add a 'keyid' parameter to qcow options
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (4 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 05/17] iscsi: add support for getting CHAP password via QCryptoSecret API Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-28 13:56 ` Eric Blake
2015-10-19 15:09 ` [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options Daniel P. Berrange
` (13 subsequent siblings)
19 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Add a 'keyid' parameter that refers to the ID of a
QCryptoSecret instance that provides the encryption key.
eg
$QEMU \
-object secret,id=sec0,filename=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow,keyid=sec0
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/qcow.c | 94 +++++++++++++++++++++++++++++++++++++++-------------
qapi/block-core.json | 17 +++++++++-
2 files changed, 87 insertions(+), 24 deletions(-)
diff --git a/block/qcow.c b/block/qcow.c
index 635085e..719ed7c 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -27,6 +27,7 @@
#include <zlib.h>
#include "qapi/qmp/qerror.h"
#include "crypto/cipher.h"
+#include "crypto/secret.h"
#include "migration/migration.h"
/**************************************************************/
@@ -40,6 +41,8 @@
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+#define QCOW_OPT_KEY_ID "keyid"
+
typedef struct QCowHeader {
uint32_t magic;
uint32_t version;
@@ -92,6 +95,43 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0;
}
+static QCryptoCipher *qcow_get_cipher_from_key(const char *key,
+ Error **errp)
+{
+ uint8_t keybuf[16];
+ int len, i;
+
+ memset(keybuf, 0, 16);
+ len = strlen(key);
+ if (len > 16) {
+ len = 16;
+ }
+ /* XXX: we could compress the chars to 7 bits to increase
+ entropy */
+ for (i = 0; i < len; i++) {
+ keybuf[i] = key[i];
+ }
+
+ return qcrypto_cipher_new(
+ QCRYPTO_CIPHER_ALG_AES_128,
+ QCRYPTO_CIPHER_MODE_CBC,
+ keybuf, G_N_ELEMENTS(keybuf),
+ errp);
+}
+
+static QemuOptsList qcow_runtime_opts = {
+ .name = "qcow",
+ .head = QTAILQ_HEAD_INITIALIZER(qcow_runtime_opts.head),
+ .desc = {
+ {
+ .name = QCOW_OPT_KEY_ID,
+ .type = QEMU_OPT_STRING,
+ .help = "ID of the secret that provides the encryption key",
+ },
+ { /* end of list */ }
+ },
+};
+
static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -99,6 +139,10 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
unsigned int len, i, shift;
int ret;
QCowHeader header;
+ QemuOpts *opts = NULL;
+ const char *keyid;
+ char *key;
+ Error *local_err = NULL;
ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header));
if (ret < 0) {
@@ -147,6 +191,32 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
+ opts = qemu_opts_create(&qcow_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ keyid = qemu_opt_get(opts, QCOW_OPT_KEY_ID);
+ if (keyid) {
+ key = qcrypto_secret_lookup_as_utf8(keyid,
+ errp);
+ if (!key) {
+ ret = -ENOENT;
+ goto fail;
+ }
+
+ s->cipher = qcow_get_cipher_from_key(key,
+ errp);
+ g_free(key);
+ if (!s->cipher) {
+ ret = -ENOSYS;
+ goto fail;
+ }
+ }
+
if (header.crypt_method > QCOW_CRYPT_AES) {
error_setg(errp, "invalid encryption method in qcow header");
ret = -EINVAL;
@@ -261,33 +331,11 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
static int qcow_set_key(BlockDriverState *bs, const char *key)
{
BDRVQcowState *s = bs->opaque;
- uint8_t keybuf[16];
- int len, i;
- Error *err;
- memset(keybuf, 0, 16);
- len = strlen(key);
- if (len > 16)
- len = 16;
- /* XXX: we could compress the chars to 7 bits to increase
- entropy */
- for(i = 0;i < len;i++) {
- keybuf[i] = key[i];
- }
assert(bs->encrypted);
-
qcrypto_cipher_free(s->cipher);
- s->cipher = qcrypto_cipher_new(
- QCRYPTO_CIPHER_ALG_AES_128,
- QCRYPTO_CIPHER_MODE_CBC,
- keybuf, G_N_ELEMENTS(keybuf),
- &err);
-
+ s->cipher = qcow_get_cipher_from_key(key, NULL);
if (!s->cipher) {
- /* XXX would be nice if errors in this method could
- * be properly propagate to the caller. Would need
- * the bdrv_set_key() API signature to be fixed. */
- error_free(err);
return -1;
}
return 0;
diff --git a/qapi/block-core.json b/qapi/block-core.json
index bb2189e..513fe93 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1562,6 +1562,21 @@
'mode': 'Qcow2OverlapCheckMode' } }
##
+# @BlockdevOptionsQcow
+#
+# Driver specific block device options for qcow.
+#
+# @keyid: #optional ID of the "secret" object providing the
+# AES decryption key.
+#
+# Since: 2.5
+##
+{ 'struct': 'BlockdevOptionsQcow',
+ 'base': 'BlockdevOptionsGenericCOWFormat',
+ 'data': { '*keyid': 'str' } }
+
+
+##
# @BlockdevOptionsQcow2
#
# Driver specific block device options for qcow2.
@@ -1826,7 +1841,7 @@
'null-co': 'BlockdevOptionsNull',
'parallels': 'BlockdevOptionsGenericFormat',
'qcow2': 'BlockdevOptionsQcow2',
- 'qcow': 'BlockdevOptionsGenericCOWFormat',
+ 'qcow': 'BlockdevOptionsQcow',
'qed': 'BlockdevOptionsGenericCOWFormat',
'quorum': 'BlockdevOptionsQuorum',
'raw': 'BlockdevOptionsGenericFormat',
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/17] qcow: add a 'keyid' parameter to qcow options
2015-10-19 15:09 ` [Qemu-devel] [PATCH 06/17] qcow: add a 'keyid' parameter to qcow options Daniel P. Berrange
@ 2015-10-28 13:56 ` Eric Blake
0 siblings, 0 replies; 36+ messages in thread
From: Eric Blake @ 2015-10-28 13:56 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2378 bytes --]
On 10/19/2015 09:09 AM, Daniel P. Berrange wrote:
> Add a 'keyid' parameter that refers to the ID of a
> QCryptoSecret instance that provides the encryption key.
> eg
>
> $QEMU \
> -object secret,id=sec0,filename=/home/berrange/encrypted.pw \
> -drive file=/home/berrange/encrypted.qcow,keyid=sec0
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> block/qcow.c | 94 +++++++++++++++++++++++++++++++++++++++-------------
> qapi/block-core.json | 17 +++++++++-
> 2 files changed, 87 insertions(+), 24 deletions(-)
>
> +static QCryptoCipher *qcow_get_cipher_from_key(const char *key,
> + Error **errp)
> +{
> + uint8_t keybuf[16];
> + int len, i;
> +
> + memset(keybuf, 0, 16);
> + len = strlen(key);
> + if (len > 16) {
> + len = 16;
> + }
> + /* XXX: we could compress the chars to 7 bits to increase
> + entropy */
> + for (i = 0; i < len; i++) {
> + keybuf[i] = key[i];
> + }
Would memcpy() be more efficient?
> @@ -261,33 +331,11 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
> static int qcow_set_key(BlockDriverState *bs, const char *key)
> {
> BDRVQcowState *s = bs->opaque;
> - uint8_t keybuf[16];
> - int len, i;
> - Error *err;
>
> - memset(keybuf, 0, 16);
> - len = strlen(key);
> - if (len > 16)
> - len = 16;
> - /* XXX: we could compress the chars to 7 bits to increase
> - entropy */
> - for(i = 0;i < len;i++) {
> - keybuf[i] = key[i];
> - }
Oh, I see - code motion.
> +++ b/qapi/block-core.json
> @@ -1562,6 +1562,21 @@
> 'mode': 'Qcow2OverlapCheckMode' } }
>
> ##
> +# @BlockdevOptionsQcow
> +#
> +# Driver specific block device options for qcow.
> +#
> +# @keyid: #optional ID of the "secret" object providing the
> +# AES decryption key.
> +#
That's a lot of whitespace, but it doesn't hurt.
> +# Since: 2.5
> +##
> +{ 'struct': 'BlockdevOptionsQcow',
> + 'base': 'BlockdevOptionsGenericCOWFormat',
> + 'data': { '*keyid': 'str' } }
Interface looks fine.
Reviewed-by: Eric Blake <eblake@redhat.com>
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (5 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 06/17] qcow: add a 'keyid' parameter to qcow options Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 23:29 ` Eric Blake
2015-10-19 15:09 ` [Qemu-devel] [PATCH 08/17] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
` (12 subsequent siblings)
19 siblings, 1 reply; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Add a 'keyid' parameter that refers to the ID of a
QCryptoSecret instance that provides the encryption key.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow2,keyid=sec0
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/qcow2.c | 80 +++++++++++++++++++++++++++++++++++++---------------
block/qcow2.h | 1 +
qapi/block-core.json | 8 ++++--
3 files changed, 64 insertions(+), 25 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index bacc4f2..3b108b0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -34,6 +34,7 @@
#include "qapi-event.h"
#include "trace.h"
#include "qemu/option_int.h"
+#include "crypto/secret.h"
/*
Differences with QCOW:
@@ -472,6 +473,11 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_NUMBER,
.help = "Clean unused cache entries after this time (in seconds)",
},
+ {
+ .name = QCOW2_OPT_KEY_ID,
+ .type = QEMU_OPT_STRING,
+ .help = "ID of the secret that provides the encryption key",
+ },
{ /* end of list */ }
},
};
@@ -589,6 +595,31 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
}
}
+static QCryptoCipher *qcow2_get_cipher_from_key(const char *key,
+ Error **errp)
+{
+ uint8_t keybuf[16];
+ int len, i;
+
+ memset(keybuf, 0, 16);
+ len = strlen(key);
+ if (len > 16) {
+ len = 16;
+ }
+ /* XXX: we could compress the chars to 7 bits to increase
+ entropy */
+ for (i = 0; i < len; i++) {
+ keybuf[i] = key[i];
+ }
+
+ return qcrypto_cipher_new(
+ QCRYPTO_CIPHER_ALG_AES_128,
+ QCRYPTO_CIPHER_MODE_CBC,
+ keybuf, G_N_ELEMENTS(keybuf),
+ errp);
+}
+
+
typedef struct Qcow2ReopenState {
Qcow2Cache *l2_table_cache;
Qcow2Cache *refcount_block_cache;
@@ -596,6 +627,7 @@ typedef struct Qcow2ReopenState {
int overlap_check;
bool discard_passthrough[QCOW2_DISCARD_MAX];
uint64_t cache_clean_interval;
+ QCryptoCipher *cipher;
} Qcow2ReopenState;
static int qcow2_update_options_prepare(BlockDriverState *bs,
@@ -611,6 +643,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
int i;
Error *local_err = NULL;
int ret;
+ const char *keyid;
+ char *key;
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
@@ -754,6 +788,24 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
r->discard_passthrough[QCOW2_DISCARD_OTHER] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
+ keyid = qemu_opt_get(opts, QCOW2_OPT_KEY_ID);
+ if (keyid) {
+ key = qcrypto_secret_lookup_as_utf8(keyid,
+ errp);
+ if (!key) {
+ ret = -ENOENT;
+ goto fail;
+ }
+
+ r->cipher = qcow2_get_cipher_from_key(key,
+ errp);
+ g_free(key);
+ if (!r->cipher) {
+ ret = -ENOSYS;
+ goto fail;
+ }
+ }
+
ret = 0;
fail:
qemu_opts_del(opts);
@@ -788,6 +840,9 @@ static void qcow2_update_options_commit(BlockDriverState *bs,
s->cache_clean_interval = r->cache_clean_interval;
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
}
+
+ qcrypto_cipher_free(s->cipher);
+ s->cipher = r->cipher;
}
static void qcow2_update_options_abort(BlockDriverState *bs,
@@ -799,6 +854,7 @@ static void qcow2_update_options_abort(BlockDriverState *bs,
if (r->refcount_block_cache) {
qcow2_cache_destroy(bs, r->refcount_block_cache);
}
+ qcrypto_cipher_free(r->cipher);
}
static int qcow2_update_options(BlockDriverState *bs, QDict *options,
@@ -1202,33 +1258,11 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
static int qcow2_set_key(BlockDriverState *bs, const char *key)
{
BDRVQcow2State *s = bs->opaque;
- uint8_t keybuf[16];
- int len, i;
- Error *err = NULL;
- memset(keybuf, 0, 16);
- len = strlen(key);
- if (len > 16)
- len = 16;
- /* XXX: we could compress the chars to 7 bits to increase
- entropy */
- for(i = 0;i < len;i++) {
- keybuf[i] = key[i];
- }
assert(bs->encrypted);
-
qcrypto_cipher_free(s->cipher);
- s->cipher = qcrypto_cipher_new(
- QCRYPTO_CIPHER_ALG_AES_128,
- QCRYPTO_CIPHER_MODE_CBC,
- keybuf, G_N_ELEMENTS(keybuf),
- &err);
-
+ s->cipher = qcow2_get_cipher_from_key(key, NULL);
if (!s->cipher) {
- /* XXX would be nice if errors in this method could
- * be properly propagate to the caller. Would need
- * the bdrv_set_key() API signature to be fixed. */
- error_free(err);
return -1;
}
return 0;
diff --git a/block/qcow2.h b/block/qcow2.h
index 3512263..7e2f046 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -97,6 +97,7 @@
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"
#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval"
+#define QCOW2_OPT_KEY_ID "keyid"
typedef struct QCowHeader {
uint32_t magic;
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 513fe93..9f0ec68 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1567,7 +1567,7 @@
# Driver specific block device options for qcow.
#
# @keyid: #optional ID of the "secret" object providing the
-# AES decryption key.
+# AES decryption key (since 2.5)
#
# Since: 2.5
##
@@ -1611,6 +1611,9 @@
# caches. The interval is in seconds. The default value
# is 0 and it disables this feature (since 2.5)
#
+# @keyid: #optional ID of the "secret" object providing the
+# AES decryption key.
+#
# Since: 1.7
##
{ 'struct': 'BlockdevOptionsQcow2',
@@ -1623,7 +1626,8 @@
'*cache-size': 'int',
'*l2-cache-size': 'int',
'*refcount-cache-size': 'int',
- '*cache-clean-interval': 'int' } }
+ '*cache-clean-interval': 'int',
+ '*keyid': 'str' } }
##
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options
2015-10-19 15:09 ` [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options Daniel P. Berrange
@ 2015-10-19 23:29 ` Eric Blake
2015-10-28 13:58 ` Eric Blake
0 siblings, 1 reply; 36+ messages in thread
From: Eric Blake @ 2015-10-19 23:29 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 1600 bytes --]
On 10/19/2015 09:09 AM, Daniel P. Berrange wrote:
> Add a 'keyid' parameter that refers to the ID of a
> QCryptoSecret instance that provides the encryption key.
>
> $QEMU \
> -object secret,id=sec0,filename=/home/berrange/encrypted.pw \
> -drive file=/home/berrange/encrypted.qcow2,keyid=sec0
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> block/qcow2.c | 80 +++++++++++++++++++++++++++++++++++++---------------
> block/qcow2.h | 1 +
> qapi/block-core.json | 8 ++++--
> 3 files changed, 64 insertions(+), 25 deletions(-)
>
> +++ b/qapi/block-core.json
> @@ -1567,7 +1567,7 @@
> # Driver specific block device options for qcow.
> #
> # @keyid: #optional ID of the "secret" object providing the
> -# AES decryption key.
> +# AES decryption key (since 2.5)
Looks like this line...
> #
> # Since: 2.5
> ##
> @@ -1611,6 +1611,9 @@
> # caches. The interval is in seconds. The default value
> # is 0 and it disables this feature (since 2.5)
> #
> +# @keyid: #optional ID of the "secret" object providing the
> +# AES decryption key.
...and this line should be swapped.
> +#
> # Since: 1.7
(For qcow, the entire struct is new so @keyid doesn't need versioning;
for qcow2, the struct existed since 1.7 and we are extending it in 2.5)
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options
2015-10-19 23:29 ` Eric Blake
@ 2015-10-28 13:58 ` Eric Blake
0 siblings, 0 replies; 36+ messages in thread
From: Eric Blake @ 2015-10-28 13:58 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 1816 bytes --]
On 10/19/2015 05:29 PM, Eric Blake wrote:
> On 10/19/2015 09:09 AM, Daniel P. Berrange wrote:
>> Add a 'keyid' parameter that refers to the ID of a
>> QCryptoSecret instance that provides the encryption key.
>>
>> $QEMU \
>> -object secret,id=sec0,filename=/home/berrange/encrypted.pw \
>> -drive file=/home/berrange/encrypted.qcow2,keyid=sec0
>>
>> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
>> ---
>> block/qcow2.c | 80 +++++++++++++++++++++++++++++++++++++---------------
>> block/qcow2.h | 1 +
>> qapi/block-core.json | 8 ++++--
>> 3 files changed, 64 insertions(+), 25 deletions(-)
>>
>
>> +++ b/qapi/block-core.json
>> @@ -1567,7 +1567,7 @@
>> # Driver specific block device options for qcow.
>> #
>> # @keyid: #optional ID of the "secret" object providing the
>> -# AES decryption key.
>> +# AES decryption key (since 2.5)
>
> Looks like this line...
>
>> #
>> # Since: 2.5
>> ##
>> @@ -1611,6 +1611,9 @@
>> # caches. The interval is in seconds. The default value
>> # is 0 and it disables this feature (since 2.5)
>> #
>> +# @keyid: #optional ID of the "secret" object providing the
>> +# AES decryption key.
>
> ...and this line should be swapped.
>
Also, do you want to change BlockdevOptionsQcow2 to have a base class of
BlockdevOptionsQcow, and get keyid by inheritance rather than by direct
declaration? Doesn't matter in the long run (once my qapi patches land
that provide the information without going through an extra 'base->' layer).
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 08/17] qom: add user_creatable_add & user_creatable_del methods
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (6 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 07/17] qcow2: add a 'keyid' parameter to qcow2 options Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 09/17] qemu-img: add support for --object command line arg Daniel P. Berrange
` (11 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
The QMP monitor code has two helper methods object_add
and qmp_object_del that are called from several places
in the code (QMP, HMP and main emulator startup).
We soon need to use this code from qemu-img, qemu-io
and qemu-nbd too, but don't want those to depend on
the monitor.
To avoid this, move object_add to user_creatable_add
an qmp_object_del to user_creatable_del, in the
object_interfaces.c file
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
hmp.c | 11 ++++--
include/monitor/monitor.h | 3 --
include/qom/object_interfaces.h | 31 +++++++++++++++++
qmp.c | 75 ++++------------------------------------
qom/object_interfaces.c | 76 +++++++++++++++++++++++++++++++++++++++++
vl.c | 8 +++--
6 files changed, 127 insertions(+), 77 deletions(-)
diff --git a/hmp.c b/hmp.c
index 5048eee..409d05d 100644
--- a/hmp.c
+++ b/hmp.c
@@ -28,6 +28,7 @@
#include "qapi/qmp/qerror.h"
#include "qapi/string-output-visitor.h"
#include "qapi-visit.h"
+#include "qom/object_interfaces.h"
#include "ui/console.h"
#include "block/qapi.h"
#include "qemu-io.h"
@@ -1630,6 +1631,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
void *dummy = NULL;
OptsVisitor *ov;
QDict *pdict;
+ Object *obj = NULL;
opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
if (err) {
@@ -1656,12 +1658,12 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
goto out_end;
}
- object_add(type, id, pdict, opts_get_visitor(ov), &err);
+ obj = user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
out_end:
visit_end_struct(opts_get_visitor(ov), &err_end);
if (!err && err_end) {
- qmp_object_del(id, NULL);
+ user_creatable_del(id, NULL);
}
error_propagate(&err, err_end);
out_clean:
@@ -1672,6 +1674,9 @@ out_clean:
g_free(id);
g_free(type);
g_free(dummy);
+ if (obj) {
+ object_unref(obj);
+ }
out:
hmp_handle_error(mon, &err);
@@ -1904,7 +1909,7 @@ void hmp_object_del(Monitor *mon, const QDict *qdict)
const char *id = qdict_get_str(qdict, "id");
Error *err = NULL;
- qmp_object_del(id, &err);
+ user_creatable_del(id, &err);
hmp_handle_error(mon, &err);
}
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 91b95ae..aa0f373 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -43,9 +43,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
void *opaque);
-void object_add(const char *type, const char *id, const QDict *qdict,
- Visitor *v, Error **errp);
-
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
bool has_opaque, const char *opaque,
Error **errp);
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 283ae0d..3e2afeb 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -2,6 +2,8 @@
#define OBJECT_INTERFACES_H
#include "qom/object.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/visitor.h"
#define TYPE_USER_CREATABLE "user-creatable"
@@ -72,4 +74,33 @@ void user_creatable_complete(Object *obj, Error **errp);
* from implements USER_CREATABLE interface.
*/
bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp);
+
+/**
+ * user_creatable_add:
+ * @type: the object type name
+ * @id: the unique ID for the object
+ * @qdict: the object parameters
+ * @v: the visitor
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Create an instance of the user creatable object @type, placing
+ * it in the object composition tree with name @id, initializing
+ * it with properties from @qdict
+ *
+ * Returns: the newly created object or NULL on error
+ */
+Object *user_creatable_add(const char *type, const char *id,
+ const QDict *qdict,
+ Visitor *v, Error **errp);
+
+/**
+ * user_creatable_del:
+ * @id: the unique ID for the object
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Delete an instance of the user creatable object identified
+ * by @id.
+ */
+void user_creatable_del(const char *id, Error **errp);
+
#endif
diff --git a/qmp.c b/qmp.c
index d9ecede..feea847 100644
--- a/qmp.c
+++ b/qmp.c
@@ -613,65 +613,13 @@ void qmp_add_client(const char *protocol, const char *fdname,
close(fd);
}
-void object_add(const char *type, const char *id, const QDict *qdict,
- Visitor *v, Error **errp)
-{
- Object *obj;
- ObjectClass *klass;
- const QDictEntry *e;
- Error *local_err = NULL;
-
- klass = object_class_by_name(type);
- if (!klass) {
- error_setg(errp, "invalid object type: %s", type);
- return;
- }
-
- if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
- error_setg(errp, "object type '%s' isn't supported by object-add",
- type);
- return;
- }
-
- if (object_class_is_abstract(klass)) {
- error_setg(errp, "object type '%s' is abstract", type);
- return;
- }
-
- obj = object_new(type);
- if (qdict) {
- for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
- object_property_set(obj, v, e->key, &local_err);
- if (local_err) {
- goto out;
- }
- }
- }
-
- object_property_add_child(object_get_objects_root(),
- id, obj, &local_err);
- if (local_err) {
- goto out;
- }
-
- user_creatable_complete(obj, &local_err);
- if (local_err) {
- object_property_del(object_get_objects_root(),
- id, &error_abort);
- goto out;
- }
-out:
- if (local_err) {
- error_propagate(errp, local_err);
- }
- object_unref(obj);
-}
void qmp_object_add(const char *type, const char *id,
bool has_props, QObject *props, Error **errp)
{
const QDict *pdict = NULL;
QmpInputVisitor *qiv;
+ Object *obj;
if (props) {
pdict = qobject_to_qdict(props);
@@ -682,27 +630,16 @@ void qmp_object_add(const char *type, const char *id,
}
qiv = qmp_input_visitor_new(props);
- object_add(type, id, pdict, qmp_input_get_visitor(qiv), errp);
+ obj = user_creatable_add(type, id, pdict, qmp_input_get_visitor(qiv), errp);
qmp_input_visitor_cleanup(qiv);
+ if (obj) {
+ object_unref(obj);
+ }
}
void qmp_object_del(const char *id, Error **errp)
{
- Object *container;
- Object *obj;
-
- container = object_get_objects_root();
- obj = object_resolve_path_component(container, id);
- if (!obj) {
- error_setg(errp, "object id not found");
- return;
- }
-
- if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
- error_setg(errp, "%s is in use, can not be deleted", id);
- return;
- }
- object_unparent(obj);
+ user_creatable_del(id, errp);
}
MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp)
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index a66cd60..d94995f 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -30,6 +30,82 @@ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp)
}
}
+Object *user_creatable_add(const char *type, const char *id,
+ const QDict *qdict,
+ Visitor *v, Error **errp)
+{
+ Object *obj;
+ ObjectClass *klass;
+ const QDictEntry *e;
+ Error *local_err = NULL;
+
+ klass = object_class_by_name(type);
+ if (!klass) {
+ error_setg(errp, "invalid object type: %s", type);
+ return NULL;
+ }
+
+ if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
+ error_setg(errp, "object type '%s' isn't supported by object-add",
+ type);
+ return NULL;
+ }
+
+ if (object_class_is_abstract(klass)) {
+ error_setg(errp, "object type '%s' is abstract", type);
+ return NULL;
+ }
+
+ obj = object_new(type);
+ if (qdict) {
+ for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+ object_property_set(obj, v, e->key, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+ }
+
+ object_property_add_child(object_get_objects_root(),
+ id, obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ user_creatable_complete(obj, &local_err);
+ if (local_err) {
+ object_property_del(object_get_objects_root(),
+ id, &error_abort);
+ goto out;
+ }
+out:
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(obj);
+ return NULL;
+ }
+ return obj;
+}
+
+void user_creatable_del(const char *id, Error **errp)
+{
+ Object *container;
+ Object *obj;
+
+ container = object_get_objects_root();
+ obj = object_resolve_path_component(container, id);
+ if (!obj) {
+ error_setg(errp, "object id not found");
+ return;
+ }
+
+ if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
+ error_setg(errp, "%s is in use, can not be deleted", id);
+ return;
+ }
+ object_unparent(obj);
+}
+
static void register_types(void)
{
static const TypeInfo uc_interface_info = {
diff --git a/vl.c b/vl.c
index 7c806a2..e0b571e 100644
--- a/vl.c
+++ b/vl.c
@@ -2775,6 +2775,7 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
OptsVisitor *ov;
QDict *pdict;
bool (*type_predicate)(const char *) = opaque;
+ Object *obj = NULL;
ov = opts_visitor_new(opts);
pdict = qemu_opts_to_qdict(opts, NULL);
@@ -2799,13 +2800,13 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
goto out;
}
- object_add(type, id, pdict, opts_get_visitor(ov), &err);
+ obj = user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
if (err) {
goto out;
}
visit_end_struct(opts_get_visitor(ov), &err);
if (err) {
- qmp_object_del(id, NULL);
+ user_creatable_del(id, NULL);
}
out:
@@ -2815,6 +2816,9 @@ out:
g_free(id);
g_free(type);
g_free(dummy);
+ if (obj) {
+ object_unref(obj);
+ }
if (err) {
error_report_err(err);
return -1;
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 09/17] qemu-img: add support for --object command line arg
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (7 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 08/17] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 10/17] qemu-nbd: " Daniel P. Berrange
` (10 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Allow creation of user creatable object types with qemu-img
via a --object command line arg. This will be used to supply
passwords and/or encryption keys to the various block driver
backends via the recently added 'secret' object type.
# echo -n letmein > mypasswd.txt
# qemu-img info --object secret,id=sec0,file=mypasswd.txt \
...other info args...
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
qemu-img-cmds.hx | 44 ++++----
qemu-img.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
qemu-img.texi | 8 ++
3 files changed, 322 insertions(+), 30 deletions(-)
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 9567774..5bb1de7 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -10,68 +10,68 @@ STEXI
ETEXI
DEF("check", img_check,
- "check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename")
+ "check [-q] [--object objectdef] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename")
STEXI
-@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
+@item check [--object objectdef] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
ETEXI
DEF("create", img_create,
- "create [-q] [-f fmt] [-o options] filename [size]")
+ "create [-q] [--object objectdef] [-f fmt] [-o options] filename [size]")
STEXI
-@item create [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
+@item create [--object objectdef] [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
ETEXI
DEF("commit", img_commit,
- "commit [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename")
+ "commit [-q] [--object objectdef] [-f fmt] [-t cache] [-b base] [-d] [-p] filename")
STEXI
-@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename}
+@item commit [--object objectdef] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename}
ETEXI
DEF("compare", img_compare,
- "compare [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2")
+ "compare [--object objectdef] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2")
STEXI
-@item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2}
+@item compare [--object objectdef] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2}
ETEXI
DEF("convert", img_convert,
- "convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename")
+ "convert [--object objectdef] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename")
STEXI
-@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [--object objectdef] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("info", img_info,
- "info [-f fmt] [--output=ofmt] [--backing-chain] filename")
+ "info [--object objectdef] [-f fmt] [--output=ofmt] [--backing-chain] filename")
STEXI
-@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
+@item info [--object objectdef] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
ETEXI
DEF("map", img_map,
- "map [-f fmt] [--output=ofmt] filename")
+ "map [--object objectdef] [-f fmt] [--output=ofmt] filename")
STEXI
-@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
+@item map [--object objectdef] [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
ETEXI
DEF("snapshot", img_snapshot,
- "snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
+ "snapshot [--object objectdef] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
STEXI
-@item snapshot [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
+@item snapshot [--object objectdef] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
ETEXI
DEF("rebase", img_rebase,
- "rebase [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
+ "rebase [--object objectdef] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI
-@item rebase [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [--object objectdef] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI
DEF("resize", img_resize,
- "resize [-q] filename [+ | -]size")
+ "resize [--object objectdef] [-q] filename [+ | -]size")
STEXI
-@item resize [-q] @var{filename} [+ | -]@var{size}
+@item resize [--object objectdef] [-q] @var{filename} [+ | -]@var{size}
ETEXI
DEF("amend", img_amend,
- "amend [-p] [-q] [-f fmt] [-t cache] -o options filename")
+ "amend [--object objectdef] [-p] [-q] [-f fmt] [-t cache] -o options filename")
STEXI
-@item amend [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
+@item amend [--object objectdef] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
@end table
ETEXI
diff --git a/qemu-img.c b/qemu-img.c
index 3025776..1578e0b 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -23,12 +23,15 @@
*/
#include "qapi-visit.h"
#include "qapi/qmp-output-visitor.h"
+#include "qapi/opts-visitor.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
#include "qemu-common.h"
+#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/error-report.h"
#include "qemu/osdep.h"
+#include "qom/object_interfaces.h"
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
@@ -47,6 +50,7 @@ typedef struct img_cmd_t {
enum {
OPTION_OUTPUT = 256,
OPTION_BACKING_CHAIN = 257,
+ OPTION_OBJECT = 258,
};
typedef enum OutputFormat {
@@ -94,6 +98,11 @@ static void QEMU_NORETURN help(void)
"\n"
"Command parameters:\n"
" 'filename' is a disk image filename\n"
+ " 'objectdef' is a QEMU user creatable object definition. See \n"
+ " the @code{qemu(1)} manual page for a description of the object\n"
+ " properties. The only object type that it makes sense to define\n"
+ " is the @code{secret} object, which is used to supply passwords\n"
+ " and/or encryption keys.\n"
" 'fmt' is the disk image format. It is guessed automatically in most cases\n"
" 'cache' is the cache mode used to write the output disk image, the valid\n"
" options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n"
@@ -154,6 +163,67 @@ static void QEMU_NORETURN help(void)
exit(EXIT_SUCCESS);
}
+static QemuOptsList qemu_object_opts = {
+ .name = "object",
+ .implied_opt_name = "qom-type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
+ .desc = {
+ { }
+ },
+};
+
+static int object_create(void *opaque, QemuOpts *opts, Error **errp)
+{
+ Error *err = NULL;
+ char *type = NULL;
+ char *id = NULL;
+ void *dummy = NULL;
+ OptsVisitor *ov;
+ QDict *pdict;
+
+ ov = opts_visitor_new(opts);
+ pdict = qemu_opts_to_qdict(opts, NULL);
+
+ visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "qom-type");
+ visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "id");
+ visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+ if (err) {
+ goto out;
+ }
+
+ user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
+ if (err) {
+ goto out;
+ }
+ visit_end_struct(opts_get_visitor(ov), &err);
+ if (err) {
+ user_creatable_del(id, NULL);
+ }
+
+out:
+ opts_visitor_cleanup(ov);
+
+ QDECREF(pdict);
+ g_free(id);
+ g_free(type);
+ g_free(dummy);
+ if (err) {
+ error_report_err(err);
+ return -1;
+ }
+ return 0;
+}
+
static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...)
{
int ret = 0;
@@ -275,9 +345,17 @@ static int img_create(int argc, char **argv)
char *options = NULL;
Error *local_err = NULL;
bool quiet = false;
+ QemuOpts *opts;
for(;;) {
- c = getopt(argc, argv, "F:b:f:he6o:q");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "F:b:f:he6o:q",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -319,6 +397,13 @@ static int img_create(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -334,6 +419,12 @@ static int img_create(int argc, char **argv)
}
optind++;
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* Get image size, if specified */
if (optind < argc) {
int64_t sval;
@@ -492,6 +583,7 @@ static int img_check(int argc, char **argv)
int flags = BDRV_O_FLAGS | BDRV_O_CHECK;
ImageCheck *check;
bool quiet = false;
+ QemuOpts *opts;
fmt = NULL;
output = NULL;
@@ -503,6 +595,7 @@ static int img_check(int argc, char **argv)
{"format", required_argument, 0, 'f'},
{"repair", required_argument, 0, 'r'},
{"output", required_argument, 0, OPTION_OUTPUT},
+ {"object", required_argument, 0, OPTION_OBJECT},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hf:r:T:q",
@@ -539,6 +632,13 @@ static int img_check(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
if (optind != argc - 1) {
@@ -555,6 +655,12 @@ static int img_check(int argc, char **argv)
return 1;
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
ret = bdrv_parse_cache_flags(cache, &flags);
if (ret < 0) {
error_report("Invalid source cache option: %s", cache);
@@ -675,12 +781,20 @@ static int img_commit(int argc, char **argv)
bool progress = false, quiet = false, drop = false;
Error *local_err = NULL;
CommonBlockJobCBInfo cbi;
+ QemuOpts *opts;
fmt = NULL;
cache = BDRV_DEFAULT_CACHE;
base = NULL;
for(;;) {
- c = getopt(argc, argv, "f:ht:b:dpq");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "f:ht:b:dpq",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -709,6 +823,13 @@ static int img_commit(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -722,6 +843,12 @@ static int img_commit(int argc, char **argv)
}
filename = argv[optind++];
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
flags = BDRV_O_RDWR | BDRV_O_UNMAP;
ret = bdrv_parse_cache_flags(cache, &flags);
if (ret < 0) {
@@ -978,10 +1105,18 @@ static int img_compare(int argc, char **argv)
int64_t nb_sectors;
int c, pnum;
uint64_t progress_base;
+ QemuOpts *opts;
cache = BDRV_DEFAULT_CACHE;
for (;;) {
- c = getopt(argc, argv, "hf:F:T:pqs");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "hf:F:T:pqs",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -1008,6 +1143,13 @@ static int img_compare(int argc, char **argv)
case 's':
strict = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -1023,6 +1165,12 @@ static int img_compare(int argc, char **argv)
filename1 = argv[optind++];
filename2 = argv[optind++];
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* Initialize before goto out */
qemu_progress_init(progress, 2.0);
@@ -1542,7 +1690,14 @@ static int img_convert(int argc, char **argv)
compress = 0;
skip_create = 0;
for(;;) {
- c = getopt(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -1633,9 +1788,22 @@ static int img_convert(int argc, char **argv)
case 'n':
skip_create = 1;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* Initialize before goto out */
if (quiet) {
progress = 0;
@@ -2065,6 +2233,7 @@ static int img_info(int argc, char **argv)
bool chain = false;
const char *filename, *fmt, *output;
ImageInfoList *list;
+ QemuOpts *opts;
fmt = NULL;
output = NULL;
@@ -2075,6 +2244,7 @@ static int img_info(int argc, char **argv)
{"format", required_argument, 0, 'f'},
{"output", required_argument, 0, OPTION_OUTPUT},
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
+ {"object", required_argument, 0, OPTION_OBJECT},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
@@ -2096,6 +2266,13 @@ static int img_info(int argc, char **argv)
case OPTION_BACKING_CHAIN:
chain = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
if (optind != argc - 1) {
@@ -2112,6 +2289,12 @@ static int img_info(int argc, char **argv)
return 1;
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
list = collect_image_info_list(filename, fmt, chain);
if (!list) {
return 1;
@@ -2235,6 +2418,7 @@ static int img_map(int argc, char **argv)
int64_t length;
MapEntry curr = { .length = 0 }, next;
int ret = 0;
+ QemuOpts *opts;
fmt = NULL;
output = NULL;
@@ -2244,6 +2428,7 @@ static int img_map(int argc, char **argv)
{"help", no_argument, 0, 'h'},
{"format", required_argument, 0, 'f'},
{"output", required_argument, 0, OPTION_OUTPUT},
+ {"object", required_argument, 0, OPTION_OBJECT},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
@@ -2262,6 +2447,13 @@ static int img_map(int argc, char **argv)
case OPTION_OUTPUT:
output = optarg;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
if (optind != argc - 1) {
@@ -2278,6 +2470,12 @@ static int img_map(int argc, char **argv)
return 1;
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false);
if (!blk) {
return 1;
@@ -2343,11 +2541,19 @@ static int img_snapshot(int argc, char **argv)
qemu_timeval tv;
bool quiet = false;
Error *err = NULL;
+ QemuOpts *opts;
bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
/* Parse commandline parameters */
for(;;) {
- c = getopt(argc, argv, "la:c:d:hq");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "la:c:d:hq",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -2391,6 +2597,13 @@ static int img_snapshot(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -2399,6 +2612,12 @@ static int img_snapshot(int argc, char **argv)
}
filename = argv[optind++];
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* Open the image */
blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet);
if (!blk) {
@@ -2465,6 +2684,7 @@ static int img_rebase(int argc, char **argv)
int progress = 0;
bool quiet = false;
Error *local_err = NULL;
+ QemuOpts *opts;
/* Parse commandline parameters */
fmt = NULL;
@@ -2473,7 +2693,14 @@ static int img_rebase(int argc, char **argv)
out_baseimg = NULL;
out_basefmt = NULL;
for(;;) {
- c = getopt(argc, argv, "hf:F:b:upt:T:q");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "hf:F:b:upt:T:q",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -2506,6 +2733,13 @@ static int img_rebase(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -2521,6 +2755,12 @@ static int img_rebase(int argc, char **argv)
}
filename = argv[optind++];
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
qemu_progress_init(progress, 2.0);
qemu_progress_print(0, 100);
@@ -2781,6 +3021,7 @@ static int img_resize(int argc, char **argv)
bool quiet = false;
BlockBackend *blk = NULL;
QemuOpts *param;
+ QemuOpts *opts;
static QemuOptsList resize_options = {
.name = "resize_options",
.head = QTAILQ_HEAD_INITIALIZER(resize_options.head),
@@ -2807,7 +3048,14 @@ static int img_resize(int argc, char **argv)
/* Parse getopt arguments */
fmt = NULL;
for(;;) {
- c = getopt(argc, argv, "f:hq");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "f:hq",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -2822,6 +3070,13 @@ static int img_resize(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
if (optind != argc - 1) {
@@ -2829,6 +3084,12 @@ static int img_resize(int argc, char **argv)
}
filename = argv[optind++];
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* Choose grow, shrink, or absolute resize mode */
switch (size[0]) {
case '+':
@@ -2918,7 +3179,14 @@ static int img_amend(int argc, char **argv)
cache = BDRV_DEFAULT_CACHE;
for (;;) {
- c = getopt(argc, argv, "ho:f:t:pq");
+ int option_index = 0;
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"object", required_argument, 0, OPTION_OBJECT},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long(argc, argv, "ho:f:t:pq",
+ long_options, &option_index);
if (c == -1) {
break;
}
@@ -2954,6 +3222,13 @@ static int img_amend(int argc, char **argv)
case 'q':
quiet = true;
break;
+ case OPTION_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -2961,6 +3236,12 @@ static int img_amend(int argc, char **argv)
error_exit("Must specify options (-o)");
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
if (quiet) {
progress = false;
}
@@ -3083,6 +3364,9 @@ int main(int argc, char **argv)
}
cmdname = argv[1];
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_object_opts);
+
/* find the command */
for (cmd = img_cmds; cmd->name != NULL; cmd++) {
if (!strcmp(cmdname, cmd->name)) {
diff --git a/qemu-img.texi b/qemu-img.texi
index 55c6be3..8e7c17c 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -24,6 +24,14 @@ Command parameters:
@table @var
@item filename
is a disk image filename
+
+@item objectdef
+
+is a QEMU user creatable object definition. See the @code{qemu(1)} manual
+page for a description of the object properties. The only object type that
+it makes sense to define is the @code{secret} object, which is used to
+supply passwords and/or encryption keys.
+
@item fmt
is the disk image format. It is guessed automatically in most cases. See below
for a description of the supported disk formats.
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 10/17] qemu-nbd: add support for --object command line arg
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (8 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 09/17] qemu-img: add support for --object command line arg Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 11/17] qemu-io: " Daniel P. Berrange
` (9 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Allow creation of user creatable object types with qemu-nbd
via a --object command line arg. This will be used to supply
passwords and/or encryption keys to the various block driver
backends via the recently added 'secret' object type.
# echo -n letmein > mypasswd.txt
# qemu-nbd --object secret,id=sec0,file=mypasswd.txt \
...other nbd args...
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
qemu-nbd.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-nbd.texi | 7 +++++
2 files changed, 92 insertions(+)
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 422a607..7eca167 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -23,9 +23,12 @@
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
+#include "qemu/config-file.h"
#include "block/snapshot.h"
#include "qapi/util.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/opts-visitor.h"
+#include "qom/object_interfaces.h"
#include <stdarg.h>
#include <stdio.h>
@@ -45,6 +48,7 @@
#define QEMU_NBD_OPT_AIO 2
#define QEMU_NBD_OPT_DISCARD 3
#define QEMU_NBD_OPT_DETECT_ZEROES 4
+#define QEMU_NBD_OPT_OBJECT 5
static NBDExport *exp;
static int verbose;
@@ -78,6 +82,9 @@ static void usage(const char *name)
" -o, --offset=OFFSET offset into the image\n"
" -P, --partition=NUM only expose partition NUM\n"
"\n"
+"General purpose options:\n"
+" --object type,id=ID,... define an object such as 'secret' for providing\n"
+" passwords and/or encryption keys\n"
#ifdef __linux__
"Kernel NBD client support:\n"
" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
@@ -380,6 +387,67 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,
}
+static QemuOptsList qemu_object_opts = {
+ .name = "object",
+ .implied_opt_name = "qom-type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
+ .desc = {
+ { }
+ },
+};
+
+static int object_create(void *opaque, QemuOpts *opts, Error **errp)
+{
+ Error *err = NULL;
+ char *type = NULL;
+ char *id = NULL;
+ void *dummy = NULL;
+ OptsVisitor *ov;
+ QDict *pdict;
+
+ ov = opts_visitor_new(opts);
+ pdict = qemu_opts_to_qdict(opts, NULL);
+
+ visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "qom-type");
+ visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "id");
+ visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+ if (err) {
+ goto out;
+ }
+
+ user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
+ if (err) {
+ goto out;
+ }
+ visit_end_struct(opts_get_visitor(ov), &err);
+ if (err) {
+ user_creatable_del(id, NULL);
+ }
+
+out:
+ opts_visitor_cleanup(ov);
+
+ QDECREF(pdict);
+ g_free(id);
+ g_free(type);
+ g_free(dummy);
+ if (err) {
+ error_report_err(err);
+ return -1;
+ }
+ return 0;
+}
+
int main(int argc, char **argv)
{
BlockBackend *blk;
@@ -417,6 +485,7 @@ int main(int argc, char **argv)
{ "format", 1, NULL, 'f' },
{ "persistent", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
+ { "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
{ NULL, 0, NULL, 0 }
};
int ch;
@@ -434,6 +503,7 @@ int main(int argc, char **argv)
Error *local_err = NULL;
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
QDict *options = NULL;
+ QemuOpts *opts;
/* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -442,6 +512,8 @@ int main(int argc, char **argv)
memset(&sa_sigterm, 0, sizeof(sa_sigterm));
sa_sigterm.sa_handler = termsig_handler;
sigaction(SIGTERM, &sa_sigterm, NULL);
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_object_opts);
qemu_init_exec_dir(argv[0]);
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
@@ -578,6 +650,13 @@ int main(int argc, char **argv)
usage(argv[0]);
exit(0);
break;
+ case QEMU_NBD_OPT_OBJECT:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
case '?':
errx(EXIT_FAILURE, "Try `%s --help' for more information.",
argv[0]);
@@ -590,6 +669,12 @@ int main(int argc, char **argv)
argv[0]);
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
if (disconnect) {
fd = open(argv[optind], O_RDWR);
if (fd < 0) {
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 46fd483..3b2004e 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -14,6 +14,13 @@ Export QEMU disk image using NBD protocol.
@table @option
@item @var{filename}
is a disk image filename
+@item --object type,id=@var{id},...props...
+ define a new instance of the @var{type} object class
+ identified by @var{id}. See the @code{qemu(1)} manual
+ page for full details of the properties supported.
+ The only object type that it makes sense to define
+ is the @code{secret} object, which is used to supply
+ passwords and/or encryption keys.
@item -p, --port=@var{port}
port to listen on (default @samp{10809})
@item -o, --offset=@var{offset}
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 11/17] qemu-io: add support for --object command line arg
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (9 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 10/17] qemu-nbd: " Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 12/17] qemu-io: allow specifying image as a set of options args Daniel P. Berrange
` (8 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Allow creation of user creatable object types with qemu-io
via a --object command line arg. This will be used to supply
passwords and/or encryption keys to the various block driver
backends via the recently added 'secret' object type.
# echo -n letmein > mypasswd.txt
# qemu-io --object secret,id=sec0,file=mypasswd.txt \
...other args...
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
qemu-io.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/qemu-io.c b/qemu-io.c
index 269f17c..cf1dac6 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -21,6 +21,8 @@
#include "qemu/config-file.h"
#include "qemu/readline.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/opts-visitor.h"
+#include "qom/object_interfaces.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
#include "trace/control.h"
@@ -205,6 +207,9 @@ static void usage(const char *name)
"Usage: %s [-h] [-V] [-rsnm] [-f FMT] [-c STRING] ... [file]\n"
"QEMU Disk exerciser\n"
"\n"
+" --object OBJECTDEF define a object such as 'secret' for\n"
+" providing passwords and/or encryption\n"
+" keys\n"
" -c, --cmd STRING execute command with its arguments\n"
" from the given string\n"
" -f, --format FMT specifies the block driver to use\n"
@@ -366,6 +371,71 @@ static void reenable_tty_echo(void)
qemu_set_tty_echo(STDIN_FILENO, true);
}
+enum {
+ OPTION_OBJECT = 258,
+};
+
+static QemuOptsList qemu_object_opts = {
+ .name = "object",
+ .implied_opt_name = "qom-type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
+ .desc = {
+ { }
+ },
+};
+
+static int object_create(void *opaque, QemuOpts *opts, Error **errp)
+{
+ Error *err = NULL;
+ char *type = NULL;
+ char *id = NULL;
+ void *dummy = NULL;
+ OptsVisitor *ov;
+ QDict *pdict;
+
+ ov = opts_visitor_new(opts);
+ pdict = qemu_opts_to_qdict(opts, NULL);
+
+ visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "qom-type");
+ visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+ if (err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "id");
+ visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+ if (err) {
+ goto out;
+ }
+
+ user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
+ if (err) {
+ goto out;
+ }
+ visit_end_struct(opts_get_visitor(ov), &err);
+ if (err) {
+ user_creatable_del(id, NULL);
+ }
+
+out:
+ opts_visitor_cleanup(ov);
+
+ QDECREF(pdict);
+ g_free(id);
+ g_free(type);
+ g_free(dummy);
+ if (err) {
+ error_report_err(err);
+ return -1;
+ }
+ return 0;
+}
+
int main(int argc, char **argv)
{
int readonly = 0;
@@ -384,6 +454,7 @@ int main(int argc, char **argv)
{ "discard", 1, NULL, 'd' },
{ "cache", 1, NULL, 't' },
{ "trace", 1, NULL, 'T' },
+ { "object", 1, NULL, OPTION_OBJECT },
{ NULL, 0, NULL, 0 }
};
int c;
@@ -391,6 +462,7 @@ int main(int argc, char **argv)
int flags = BDRV_O_UNMAP;
Error *local_error = NULL;
QDict *opts = NULL;
+ QemuOpts *qopts = NULL;
#ifdef CONFIG_POSIX
signal(SIGPIPE, SIG_IGN);
@@ -399,6 +471,8 @@ int main(int argc, char **argv)
progname = basename(argv[0]);
qemu_init_exec_dir(argv[0]);
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_object_opts);
bdrv_init();
while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
@@ -450,6 +524,13 @@ int main(int argc, char **argv)
case 'h':
usage(progname);
exit(0);
+ case OPTION_OBJECT:
+ qopts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+ optarg, true);
+ if (!qopts) {
+ exit(1);
+ }
+ break;
default:
usage(progname);
exit(1);
@@ -466,6 +547,12 @@ int main(int argc, char **argv)
exit(1);
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ NULL, NULL)) {
+ exit(1);
+ }
+
/* initialize commands */
qemuio_add_command(&quit_cmd);
qemuio_add_command(&open_cmd);
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 12/17] qemu-io: allow specifying image as a set of options args
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (10 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 11/17] qemu-io: " Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 13/17] qemu-nbd: " Daniel P. Berrange
` (7 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Currently qemu-io allows an image filename to be passed on the
command line, but does not have a way to set any options except
the format eg
qemu-io https://127.0.0.1/images/centos7.iso
qemu-io /home/berrange/demo.qcow2
This adds a --source arg (that is mutually exclusive with a
positional filename arg and -f arg) that accepts a full option
string, as well as the original syntax eg
qemu-io --source driver=http,url=https://127.0.0.1/images,sslverify=off
qemu-io --source https://127.0.0.1/images/centos7.iso
qemu-io --source file=/home/berrange/demo.qcow2
qemu-io --source /home/berrange/demo.qcow2
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
qemu-io.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/qemu-io.c b/qemu-io.c
index cf1dac6..fc7f81b 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -373,6 +373,7 @@ static void reenable_tty_echo(void)
enum {
OPTION_OBJECT = 258,
+ OPTION_SOURCE = 259,
};
static QemuOptsList qemu_object_opts = {
@@ -436,6 +437,16 @@ out:
return 0;
}
+static QemuOptsList file_opts = {
+ .name = "file",
+ .implied_opt_name = "file",
+ .head = QTAILQ_HEAD_INITIALIZER(file_opts.head),
+ .desc = {
+ /* no elements => accept any params */
+ { /* end of list */ }
+ },
+};
+
int main(int argc, char **argv)
{
int readonly = 0;
@@ -455,6 +466,7 @@ int main(int argc, char **argv)
{ "cache", 1, NULL, 't' },
{ "trace", 1, NULL, 'T' },
{ "object", 1, NULL, OPTION_OBJECT },
+ { "source", 1, NULL, OPTION_SOURCE },
{ NULL, 0, NULL, 0 }
};
int c;
@@ -531,6 +543,12 @@ int main(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (!qemu_opts_parse_noisily(&file_opts, optarg, false)) {
+ qemu_opts_reset(&file_opts);
+ return 0;
+ }
+ break;
default:
usage(progname);
exit(1);
@@ -572,7 +590,24 @@ int main(int argc, char **argv)
flags |= BDRV_O_RDWR;
}
- if ((argc - optind) == 1) {
+ qopts = qemu_opts_find(&file_opts, NULL);
+ if (qopts) {
+ char *file;
+ if (opts) {
+ error_report("--source and -f are mutually exclusive");
+ exit(1);
+ }
+ if ((argc - optind) == 1) {
+ error_report("--source and filename are mutually exclusive");
+ exit(1);
+ }
+ file = g_strdup(qemu_opt_get(qopts, "file"));
+ qemu_opt_unset(qopts, "file");
+ opts = qemu_opts_to_qdict(qopts, NULL);
+ qemu_opts_reset(&file_opts);
+ openfile(file, flags, opts);
+ g_free(file);
+ } else if ((argc - optind) == 1) {
openfile(argv[optind], flags, opts);
}
command_loop();
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 13/17] qemu-nbd: allow specifying image as a set of options args
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (11 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 12/17] qemu-io: allow specifying image as a set of options args Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 14/17] qemu-img: " Daniel P. Berrange
` (6 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Currently qemu-nbd allows an image filename to be passed on the
command line, but does not have a way to set any options except
the format eg
qemu-nbd https://127.0.0.1/images/centos7.iso
qemu-nbd /home/berrange/demo.qcow2
This adds a --source arg (that is mutually exclusive with a
positional filename arg and -f arg) that accepts a full option
string, as well as the original syntax eg
qemu-nbd --source driver=http,url=https://127.0.0.1/images,sslverify=off
qemu-nbd --source https://127.0.0.1/images/centos7.iso
qemu-nbd --source file=/home/berrange/demo.qcow2
qemu-nbd --source /home/berrange/demo.qcow2
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
qemu-nbd.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 50 insertions(+), 7 deletions(-)
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 7eca167..1100c75 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -49,6 +49,7 @@
#define QEMU_NBD_OPT_DISCARD 3
#define QEMU_NBD_OPT_DETECT_ZEROES 4
#define QEMU_NBD_OPT_OBJECT 5
+#define QEMU_NBD_OPT_SOURCE 6
static NBDExport *exp;
static int verbose;
@@ -387,6 +388,16 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,
}
+static QemuOptsList file_opts = {
+ .name = "file",
+ .implied_opt_name = "file",
+ .head = QTAILQ_HEAD_INITIALIZER(file_opts.head),
+ .desc = {
+ /* no elements => accept any params */
+ { /* end of list */ }
+ },
+};
+
static QemuOptsList qemu_object_opts = {
.name = "object",
.implied_opt_name = "qom-type",
@@ -486,6 +497,7 @@ int main(int argc, char **argv)
{ "persistent", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
{ "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
+ { "source", 1, NULL, QEMU_NBD_OPT_SOURCE },
{ NULL, 0, NULL, 0 }
};
int ch;
@@ -657,13 +669,23 @@ int main(int argc, char **argv)
exit(1);
}
break;
+ case QEMU_NBD_OPT_SOURCE:
+ if (srcpath) {
+ errx(EXIT_FAILURE, "--source can only be used once");
+ }
+ if (!qemu_opts_parse_noisily(&file_opts, optarg, true)) {
+ qemu_opts_reset(&file_opts);
+ exit(EXIT_FAILURE);
+ }
+ srcpath = optarg;
+ break;
case '?':
errx(EXIT_FAILURE, "Try `%s --help' for more information.",
argv[0]);
}
}
- if ((argc - optind) != 1) {
+ if ((argc - optind) > 1) {
errx(EXIT_FAILURE, "Invalid number of argument.\n"
"Try `%s --help' for more information.",
argv[0]);
@@ -757,15 +779,36 @@ int main(int argc, char **argv)
bdrv_init();
atexit(bdrv_close_all);
- if (fmt) {
- options = qdict_new();
- qdict_put(options, "driver", qstring_from_str(fmt));
+ if (srcpath) {
+ char *file = NULL;
+ opts = qemu_opts_find(&file_opts, NULL);
+ if (fmt) {
+ errx(EXIT_FAILURE, "--source and -f are mutually exclusive");
+ }
+ if ((argc - optind) > 1) {
+ errx(EXIT_FAILURE, "--source and filename are mutually exclusive");
+ }
+ file = g_strdup(qemu_opt_get(opts, "file"));
+ qemu_opt_unset(opts, "file");
+ options = qemu_opts_to_qdict(opts, NULL);
+ qemu_opts_reset(&file_opts);
+ blk = blk_new_open("hda", file, NULL, options, flags, &local_err);
+ g_free(file);
+ } else {
+ if (fmt) {
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str(fmt));
+ }
+ if ((argc - optind) != 1) {
+ errx(EXIT_FAILURE, "one of --source or filename are expected");
+ }
+
+ srcpath = argv[optind];
+ blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err);
}
- srcpath = argv[optind];
- blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err);
if (!blk) {
- errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind],
+ errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", srcpath,
error_get_pretty(local_err));
}
bs = blk_bs(blk);
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 14/17] qemu-img: allow specifying image as a set of options args
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (12 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 13/17] qemu-nbd: " Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 15/17] block: rip out all traces of password prompting Daniel P. Berrange
` (5 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Currently qemu-img allows an image filename to be passed on the
command line, but does not have a way to set any options except
the format eg
qemu-img info https://127.0.0.1/images/centos7.iso
This adds a --source arg (that is mutually exclusive with a
positional filename arg and -f arg) that accepts a full option
string, as well as the original syntax eg
qemu-img info driver=http,url=https://127.0.0.1/images,sslverify=off
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
include/qemu/option.h | 1 +
qemu-img.c | 474 ++++++++++++++++++++++++++++++++++++++++++--------
util/qemu-option.c | 6 +
3 files changed, 407 insertions(+), 74 deletions(-)
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 71f5f27..caf5a3b 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -104,6 +104,7 @@ int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
Error **errp);
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
+QemuOpts *qemu_opts_next(QemuOpts *opts);
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
int fail_if_exists, Error **errp);
void qemu_opts_reset(QemuOptsList *list);
diff --git a/qemu-img.c b/qemu-img.c
index 1578e0b..30869fe 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -38,6 +38,7 @@
#include "block/blockjob.h"
#include "block/qapi.h"
#include <getopt.h>
+#include <err.h>
#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \
", Copyright (c) 2004-2008 Fabrice Bellard\n"
@@ -51,6 +52,8 @@ enum {
OPTION_OUTPUT = 256,
OPTION_BACKING_CHAIN = 257,
OPTION_OBJECT = 258,
+ OPTION_SOURCE = 259,
+ OPTION_TARGET = 260,
};
typedef enum OutputFormat {
@@ -172,6 +175,16 @@ static QemuOptsList qemu_object_opts = {
},
};
+static QemuOptsList qemu_source_opts = {
+ .name = "source",
+ .implied_opt_name = "file",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head),
+ .desc = {
+ { }
+ },
+};
+
+
static int object_create(void *opaque, QemuOpts *opts, Error **errp)
{
Error *err = NULL;
@@ -266,9 +279,31 @@ static int print_block_option_help(const char *filename, const char *fmt)
return 0;
}
-static BlockBackend *img_open(const char *id, const char *filename,
- const char *fmt, int flags,
- bool require_io, bool quiet)
+static BlockBackend *img_open_opts(const char *id,
+ QemuOpts *opts, int flags)
+{
+ QDict *options;
+ Error *local_err = NULL;
+ char *file = NULL;
+ BlockBackend *blk;
+ file = g_strdup(qemu_opt_get(opts, "file"));
+ qemu_opt_unset(opts, "file");
+ options = qemu_opts_to_qdict(opts, NULL);
+ blk = blk_new_open(id, file, NULL, options, flags, &local_err);
+ if (!blk) {
+ error_report("Could not open '%s': %s", file ? file : "",
+ error_get_pretty(local_err));
+ g_free(file);
+ error_free(local_err);
+ return NULL;
+ }
+ g_free(file);
+ return blk;
+}
+
+static BlockBackend *img_open_file(const char *id, const char *filename,
+ const char *fmt, int flags,
+ bool require_io, bool quiet)
{
BlockBackend *blk;
BlockDriverState *bs;
@@ -576,7 +611,7 @@ static int img_check(int argc, char **argv)
{
int c, ret;
OutputFormat output_format = OFORMAT_HUMAN;
- const char *filename, *fmt, *output, *cache;
+ const char *filename = NULL, *fmt, *output, *cache;
BlockBackend *blk;
BlockDriverState *bs;
int fix = 0;
@@ -596,6 +631,7 @@ static int img_check(int argc, char **argv)
{"repair", required_argument, 0, 'r'},
{"output", required_argument, 0, OPTION_OUTPUT},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hf:r:T:q",
@@ -639,12 +675,29 @@ static int img_check(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename) {
+ errx(EXIT_FAILURE, "--source can only be specified once");
+ }
+ filename = optarg;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
- filename = argv[optind++];
if (output && !strcmp(output, "json")) {
output_format = OFORMAT_JSON;
@@ -667,7 +720,15 @@ static int img_check(int argc, char **argv)
return 1;
}
- blk = img_open("image", filename, fmt, flags, true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ errx(EXIT_FAILURE, "--source and --format are mutually exclusive");
+ }
+ blk = img_open_opts("image", opts, flags);
+ } else {
+ blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ }
if (!blk) {
return 1;
}
@@ -775,7 +836,7 @@ static void run_block_job(BlockJob *job, Error **errp)
static int img_commit(int argc, char **argv)
{
int c, ret, flags;
- const char *filename, *fmt, *cache, *base;
+ const char *filename = NULL, *fmt, *cache, *base;
BlockBackend *blk;
BlockDriverState *bs, *base_bs;
bool progress = false, quiet = false, drop = false;
@@ -791,6 +852,7 @@ static int img_commit(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:ht:b:dpq",
@@ -830,6 +892,17 @@ static int img_commit(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename) {
+ errx(EXIT_FAILURE, "--source can only be specified once");
+ }
+ filename = optarg;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -838,10 +911,16 @@ static int img_commit(int argc, char **argv)
progress = false;
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
- filename = argv[optind++];
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
@@ -856,7 +935,15 @@ static int img_commit(int argc, char **argv)
return 1;
}
- blk = img_open("image", filename, fmt, flags, true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ errx(EXIT_FAILURE, "--source and --format are mutually exclusive");
+ }
+ blk = img_open_opts("image", opts, flags);
+ } else {
+ blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ }
if (!blk) {
return 1;
}
@@ -1090,7 +1177,8 @@ static int check_empty_sectors(BlockBackend *blk, int64_t sect_num,
*/
static int img_compare(int argc, char **argv)
{
- const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2;
+ const char *fmt1 = NULL, *fmt2 = NULL, *cache,
+ *filename1 = NULL, *filename2 = NULL;
BlockBackend *blk1, *blk2;
BlockDriverState *bs1, *bs2;
int64_t total_sectors1, total_sectors2;
@@ -1113,6 +1201,7 @@ static int img_compare(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hf:F:T:pqs",
@@ -1150,6 +1239,20 @@ static int img_compare(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename2) {
+ errx(EXIT_FAILURE, "--source can only be specified twice");
+ } else if (filename1) {
+ filename2 = optarg;
+ } else {
+ filename1 = optarg;
+ }
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -1158,12 +1261,20 @@ static int img_compare(int argc, char **argv)
progress = false;
}
-
- if (optind != argc - 2) {
- error_exit("Expecting two image file names");
+ if (filename1) {
+ if (optind != argc) {
+ error_exit("--source and filenames are mutually exclusive");
+ }
+ if (!filename2) {
+ error_exit("Expecting two --source arguments");
+ }
+ } else {
+ if (optind != argc - 2) {
+ error_exit("Expecting two image file names");
+ }
+ filename1 = argv[optind++];
+ filename2 = argv[optind++];
}
- filename1 = argv[optind++];
- filename2 = argv[optind++];
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
@@ -1182,18 +1293,38 @@ static int img_compare(int argc, char **argv)
goto out3;
}
- blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet);
- if (!blk1) {
- ret = 2;
- goto out3;
- }
- bs1 = blk_bs(blk1);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt1 || fmt2) {
+ error_report("--source and -f or -F are mutuall exclusive");
+ goto out3;
+ }
- blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet);
- if (!blk2) {
- ret = 2;
- goto out2;
+ blk1 = img_open_opts("image_1", opts, flags);
+ if (!blk1) {
+ ret = 2;
+ goto out3;
+ }
+
+ blk2 = img_open_opts("image_2", qemu_opts_next(opts), flags);
+ if (!blk2) {
+ ret = 2;
+ goto out3;
+ }
+ } else {
+ blk1 = img_open_file("image_1", filename1, fmt1, flags, true, quiet);
+ if (!blk1) {
+ ret = 2;
+ goto out3;
+ }
+
+ blk2 = img_open_file("image_2", filename2, fmt2, flags, true, quiet);
+ if (!blk2) {
+ ret = 2;
+ goto out2;
+ }
}
+ bs1 = blk_bs(blk1);
bs2 = blk_bs(blk2);
buf1 = blk_blockalign(blk1, IO_BUF_SIZE);
@@ -1660,10 +1791,11 @@ fail:
static int img_convert(int argc, char **argv)
{
- int c, bs_n, bs_i, compress, cluster_sectors, skip_create;
+ int c, bs_n = 0, bs_i, compress, cluster_sectors, skip_create;
int64_t ret = 0;
int progress = 0, flags, src_flags;
- const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename;
+ const char *fmt, *out_fmt, *cache, *src_cache,
+ *out_baseimg, *out_filename = NULL;
BlockDriver *drv, *proto_drv;
BlockBackend **blk = NULL, *out_blk = NULL;
BlockDriverState **bs = NULL, *out_bs = NULL;
@@ -1694,6 +1826,7 @@ static int img_convert(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn",
@@ -1795,6 +1928,14 @@ static int img_convert(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ bs_n++;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -1810,20 +1951,33 @@ static int img_convert(int argc, char **argv)
}
qemu_progress_init(progress, 1.0);
-
- bs_n = argc - optind - 1;
- out_filename = bs_n >= 1 ? argv[argc - 1] : NULL;
+ if (!bs_n) {
+ out_filename = (argc - optind - 1) >= 1 ? argv[argc - 1] : NULL;
+ }
if (options && has_help_option(options)) {
ret = print_block_option_help(out_filename, out_fmt);
goto out;
}
- if (bs_n < 1) {
- error_exit("Must specify image file name");
+ if (bs_n) {
+ if (argc > (optind + 1)) {
+ error_exit("--source and filenames are mutually exclusive");
+ }
+ if (argc != (optind + 1)) {
+ error_exit("Must specify target image file name");
+ }
+ if (!bs_n) {
+ error_exit("At least one --source arg is expected with --target");
+ }
+ out_filename = argv[argc - 1];
+ } else {
+ bs_n = argc - optind - 1;
+ if (bs_n < 1) {
+ error_exit("Must specify image file name");
+ }
}
-
if (bs_n > 1 && out_baseimg) {
error_report("-B makes no sense when concatenating multiple input "
"images");
@@ -1845,11 +1999,21 @@ static int img_convert(int argc, char **argv)
bs_sectors = g_new(int64_t, bs_n);
total_sectors = 0;
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
for (bs_i = 0; bs_i < bs_n; bs_i++) {
char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i)
: g_strdup("source");
- blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags,
- true, quiet);
+ if (opts) {
+ if (fmt) {
+ error_report("--source and -f are mutually exclusive");
+ goto out;
+ }
+ blk[bs_i] = img_open_opts(id, opts, src_flags);
+ opts = qemu_opts_next(opts);
+ } else {
+ blk[bs_i] = img_open_file(id, argv[optind + bs_i], fmt, src_flags,
+ true, quiet);
+ }
g_free(id);
if (!blk[bs_i]) {
ret = -1;
@@ -1993,7 +2157,12 @@ static int img_convert(int argc, char **argv)
goto out;
}
- out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet);
+ /* XXX we could allow a --target OPTSRING and then use
+ * img_open_opts here, but then we have trouble with
+ * the bdrv_create() call which takes different params
+ */
+ out_blk = img_open_file("target", out_filename,
+ out_fmt, flags, true, quiet);
if (!out_blk) {
ret = -1;
goto out;
@@ -2160,7 +2329,8 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
* image file. If there was an error a message will have been printed to
* stderr.
*/
-static ImageInfoList *collect_image_info_list(const char *filename,
+static ImageInfoList *collect_image_info_list(QemuOpts *opts,
+ const char *filename,
const char *fmt,
bool chain)
{
@@ -2184,8 +2354,19 @@ static ImageInfoList *collect_image_info_list(const char *filename,
}
g_hash_table_insert(filenames, (gpointer)filename, NULL);
- blk = img_open("image", filename, fmt,
- BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false);
+ if (opts) {
+ if (fmt) {
+ error_report("--source and -f are mutually exclusive");
+ goto err;
+ }
+ blk = img_open_opts("image", opts,
+ BDRV_O_FLAGS | BDRV_O_NO_BACKING);
+ opts = NULL;
+ } else {
+ blk = img_open_file("image", filename, fmt,
+ BDRV_O_FLAGS | BDRV_O_NO_BACKING,
+ false, false);
+ }
if (!blk) {
goto err;
}
@@ -2231,7 +2412,7 @@ static int img_info(int argc, char **argv)
int c;
OutputFormat output_format = OFORMAT_HUMAN;
bool chain = false;
- const char *filename, *fmt, *output;
+ const char *filename = NULL, *fmt, *output;
ImageInfoList *list;
QemuOpts *opts;
@@ -2245,6 +2426,7 @@ static int img_info(int argc, char **argv)
{"output", required_argument, 0, OPTION_OUTPUT},
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
@@ -2273,12 +2455,29 @@ static int img_info(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename) {
+ error_exit("--source can only be specified once");
+ }
+ filename = optarg;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
- filename = argv[optind++];
if (output && !strcmp(output, "json")) {
output_format = OFORMAT_JSON;
@@ -2295,7 +2494,8 @@ static int img_info(int argc, char **argv)
exit(1);
}
- list = collect_image_info_list(filename, fmt, chain);
+ list = collect_image_info_list(qemu_opts_find(&qemu_source_opts, NULL),
+ filename, fmt, chain);
if (!list) {
return 1;
}
@@ -2414,7 +2614,7 @@ static int img_map(int argc, char **argv)
OutputFormat output_format = OFORMAT_HUMAN;
BlockBackend *blk;
BlockDriverState *bs;
- const char *filename, *fmt, *output;
+ const char *filename = NULL, *fmt, *output;
int64_t length;
MapEntry curr = { .length = 0 }, next;
int ret = 0;
@@ -2429,6 +2629,7 @@ static int img_map(int argc, char **argv)
{"format", required_argument, 0, 'f'},
{"output", required_argument, 0, OPTION_OUTPUT},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
@@ -2454,12 +2655,29 @@ static int img_map(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename) {
+ error_exit("--source can only be specified once");
+ }
+ filename = optarg;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind];
}
- filename = argv[optind];
if (output && !strcmp(output, "json")) {
output_format = OFORMAT_JSON;
@@ -2476,7 +2694,15 @@ static int img_map(int argc, char **argv)
exit(1);
}
- blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ errx(EXIT_FAILURE, "--source and --format are mutually exclusive");
+ }
+ blk = img_open_opts("image", opts, BDRV_O_FLAGS);
+ } else {
+ blk = img_open_file("image", filename, fmt, BDRV_O_FLAGS, true, false);
+ }
if (!blk) {
return 1;
}
@@ -2535,7 +2761,7 @@ static int img_snapshot(int argc, char **argv)
BlockBackend *blk;
BlockDriverState *bs;
QEMUSnapshotInfo sn;
- char *filename, *snapshot_name = NULL;
+ char *filename = NULL, *snapshot_name = NULL;
int c, ret = 0, bdrv_oflags;
int action = 0;
qemu_timeval tv;
@@ -2550,6 +2776,7 @@ static int img_snapshot(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "la:c:d:hq",
@@ -2604,13 +2831,30 @@ static int img_snapshot(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ if (filename) {
+ error_exit("--source can only be specified once");
+ }
+ filename = optarg;
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
- filename = argv[optind++];
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
@@ -2619,7 +2863,12 @@ static int img_snapshot(int argc, char **argv)
}
/* Open the image */
- blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ blk = img_open_opts("image", opts, bdrv_oflags);
+ } else {
+ blk = img_open_file("image", filename, NULL, bdrv_oflags, true, quiet);
+ }
if (!blk) {
return 1;
}
@@ -2677,7 +2926,7 @@ static int img_rebase(int argc, char **argv)
{
BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL;
BlockDriverState *bs = NULL;
- char *filename;
+ char *filename = NULL;
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
int c, flags, src_flags, ret;
int unsafe = 0;
@@ -2697,6 +2946,7 @@ static int img_rebase(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hf:F:b:upt:T:q",
@@ -2740,6 +2990,13 @@ static int img_rebase(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -2747,13 +3004,20 @@ static int img_rebase(int argc, char **argv)
progress = 0;
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
+
if (!unsafe && !out_baseimg) {
error_exit("Must specify backing file (-b) or use unsafe mode (-u)");
}
- filename = argv[optind++];
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
@@ -2784,7 +3048,17 @@ static int img_rebase(int argc, char **argv)
* Ignore the old backing file for unsafe rebase in case we want to correct
* the reference to a renamed or moved backing file.
*/
- blk = img_open("image", filename, fmt, flags, true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ error_report("--source and --format are mutually exclusive");
+ ret = -1;
+ goto out;
+ }
+ blk = img_open_opts("image", opts, flags);
+ } else {
+ blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ }
if (!blk) {
ret = -1;
goto out;
@@ -3016,7 +3290,7 @@ static int img_resize(int argc, char **argv)
{
Error *err = NULL;
int c, ret, relative;
- const char *filename, *fmt, *size;
+ const char *filename = NULL, *fmt, *size;
int64_t n, total_size;
bool quiet = false;
BlockBackend *blk = NULL;
@@ -3052,6 +3326,7 @@ static int img_resize(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:hq",
@@ -3077,12 +3352,25 @@ static int img_resize(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
- if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ } else {
+ if (optind != argc - 1) {
+ error_exit("Expecting one image file name");
+ }
+ filename = argv[optind++];
}
- filename = argv[optind++];
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
@@ -3117,8 +3405,18 @@ static int img_resize(int argc, char **argv)
n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0);
qemu_opts_del(param);
- blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
- true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ error_report("--source and --format are mutually exclusive");
+ ret = -1;
+ goto out;
+ }
+ blk = img_open_opts("image", opts, BDRV_O_FLAGS | BDRV_O_RDWR);
+ } else {
+ blk = img_open_file("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
+ true, quiet);
+ }
if (!blk) {
ret = -1;
goto out;
@@ -3171,7 +3469,7 @@ static int img_amend(int argc, char **argv)
char *options = NULL;
QemuOptsList *create_opts = NULL;
QemuOpts *opts = NULL;
- const char *fmt = NULL, *filename, *cache;
+ const char *fmt = NULL, *filename = NULL, *cache;
int flags;
bool quiet = false, progress = false;
BlockBackend *blk = NULL;
@@ -3183,6 +3481,7 @@ static int img_amend(int argc, char **argv)
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
+ {"source", required_argument, 0, OPTION_SOURCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "ho:f:t:pq",
@@ -3229,6 +3528,13 @@ static int img_amend(int argc, char **argv)
exit(1);
}
break;
+ case OPTION_SOURCE:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("source"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
}
}
@@ -3236,6 +3542,12 @@ static int img_amend(int argc, char **argv)
error_exit("Must specify options (-o)");
}
+ if (filename) {
+ if (optind != argc) {
+ error_exit("--source and filename are mutually exclusive");
+ }
+ }
+
if (qemu_opts_foreach(qemu_find_opts("object"),
object_create,
NULL, NULL)) {
@@ -3247,7 +3559,9 @@ static int img_amend(int argc, char **argv)
}
qemu_progress_init(progress, 1.0);
- filename = (optind == argc - 1) ? argv[argc - 1] : NULL;
+ if (!filename) {
+ filename = (optind == argc - 1) ? argv[argc - 1] : NULL;
+ }
if (fmt && has_help_option(options)) {
/* If a format is explicitly specified (and possibly no filename is
* given), print option help here */
@@ -3255,8 +3569,9 @@ static int img_amend(int argc, char **argv)
goto out;
}
- if (optind != argc - 1) {
- error_report("Expecting one image file name");
+ if (!filename &&
+ (optind != argc - 1)) {
+ error_exit("Expecting one image file name");
ret = -1;
goto out;
}
@@ -3268,7 +3583,17 @@ static int img_amend(int argc, char **argv)
goto out;
}
- blk = img_open("image", filename, fmt, flags, true, quiet);
+ opts = qemu_opts_find(&qemu_source_opts, NULL);
+ if (opts) {
+ if (fmt) {
+ error_report("--source and --format are mutually exclusive");
+ ret = -1;
+ goto out;
+ }
+ blk = img_open_opts("image", opts, BDRV_O_FLAGS | BDRV_O_RDWR);
+ } else {
+ blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ }
if (!blk) {
ret = -1;
goto out;
@@ -3366,6 +3691,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
qemu_add_opts(&qemu_object_opts);
+ qemu_add_opts(&qemu_source_opts);
/* find the command */
for (cmd = img_cmds; cmd->name != NULL; cmd++) {
diff --git a/util/qemu-option.c b/util/qemu-option.c
index a50ecea..48e1cc5 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -637,6 +637,12 @@ QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id)
return NULL;
}
+
+QemuOpts *qemu_opts_next(QemuOpts *opts)
+{
+ return QTAILQ_NEXT(opts, next);
+}
+
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
int fail_if_exists, Error **errp)
{
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 15/17] block: rip out all traces of password prompting
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (13 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 14/17] qemu-img: " Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 16/17] block: remove all encryption handling APIs Daniel P. Berrange
` (4 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Now that qcow & qcow2 are wired up to get encryption keys
via the QCryptoSecret object, all traces of code which
had to deal with prompting for passwords can be ripped
out.
When the image is initially opened, the encryption key
must be available immediately, or an error will be
reported.
$ qemu-system-x86_64 -drive file=secret.qcow2
qemu-system-x86_64: -drive file=secret.qcow2: Image is encrypted but no secret is provided
Users must provide the secret with -object
$ echo 123456 > mypasswd.txt
qemu-system-x86_64 -drive file=secret.qcow2,keysecret=sec0 -object secret,file=mypasswd.txt,id=sec0
The BDRV_O_NO_IO flag allows this error to be skipped,
for use when 'qemu-img info' wants to open the file
to query the headers, but not perform any actual I/O
operations.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/qcow.c | 7 +++++
block/qcow2.c | 13 +++++++--
hmp.c | 31 ---------------------
hw/usb/dev-storage.c | 32 ---------------------
include/block/block.h | 1 +
include/monitor/monitor.h | 7 -----
include/qemu/osdep.h | 2 --
monitor.c | 65 -------------------------------------------
qemu-img.c | 46 ++++++++++++------------------
qemu-io.c | 21 --------------
qmp.c | 8 ------
tests/qemu-iotests/087 | 20 ++++++++++++++
tests/qemu-iotests/087.out | 2 ++
tests/qemu-iotests/134 | 17 ++++++++----
tests/qemu-iotests/134.out | 8 ------
tests/qemu-iotests/common.rc | 4 +--
util/oslib-posix.c | 66 --------------------------------------------
util/oslib-win32.c | 24 ----------------
18 files changed, 72 insertions(+), 302 deletions(-)
diff --git a/block/qcow.c b/block/qcow.c
index 719ed7c..ff80ef5 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -231,6 +231,13 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
if (s->crypt_method_header) {
bs->encrypted = 1;
}
+ if (!(flags & BDRV_O_NO_IO) &&
+ bs->encrypted && !s->cipher) {
+ error_setg(errp, "Image is encrypted but no secret is provided");
+ ret = -EINVAL;
+ goto fail;
+ }
+
s->cluster_bits = header.cluster_bits;
s->cluster_size = 1 << s->cluster_bits;
s->cluster_sectors = 1 << (s->cluster_bits - 9);
diff --git a/block/qcow2.c b/block/qcow2.c
index 3b108b0..6ca2b25 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1136,6 +1136,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
+ if (!(flags & BDRV_O_NO_IO) &&
+ bs->encrypted && !s->cipher) {
+ error_setg(errp, "Image is encrypted but no secret is provided");
+ ret = -EINVAL;
+ goto fail;
+ }
+
s->cluster_cache = g_malloc(s->cluster_size);
/* one more sector for decompressed data alignment */
s->cluster_data = qemu_try_blockalign(bs->file->bs, QCOW_MAX_CRYPT_CLUSTERS
@@ -2207,7 +2214,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
options = qdict_new();
qdict_put(options, "driver", qstring_from_str("qcow2"));
ret = bdrv_open(&bs, filename, NULL, options,
- BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH |
+ BDRV_O_NO_IO,
&local_err);
if (ret < 0) {
error_propagate(errp, local_err);
@@ -2261,7 +2269,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
options = qdict_new();
qdict_put(options, "driver", qstring_from_str("qcow2"));
ret = bdrv_open(&bs, filename, NULL, options,
- BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING |
+ BDRV_O_NO_IO,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
diff --git a/hmp.c b/hmp.c
index 409d05d..8036642 100644
--- a/hmp.c
+++ b/hmp.c
@@ -959,37 +959,12 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict)
g_free(data);
}
-static void hmp_cont_cb(void *opaque, int err)
-{
- if (!err) {
- qmp_cont(NULL);
- }
-}
-
-static bool key_is_missing(const BlockInfo *bdev)
-{
- return (bdev->inserted && bdev->inserted->encryption_key_missing);
-}
-
void hmp_cont(Monitor *mon, const QDict *qdict)
{
- BlockInfoList *bdev_list, *bdev;
Error *err = NULL;
- bdev_list = qmp_query_block(NULL);
- for (bdev = bdev_list; bdev; bdev = bdev->next) {
- if (key_is_missing(bdev->value)) {
- monitor_read_block_device_key(mon, bdev->value->device,
- hmp_cont_cb, NULL);
- goto out;
- }
- }
-
qmp_cont(&err);
hmp_handle_error(mon, &err);
-
-out:
- qapi_free_BlockInfoList(bdev_list);
}
void hmp_system_wakeup(Monitor *mon, const QDict *qdict)
@@ -1349,12 +1324,6 @@ void hmp_change(Monitor *mon, const QDict *qdict)
}
qmp_change(device, target, !!arg, arg, &err);
- if (err &&
- error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
- error_free(err);
- monitor_read_block_device_key(mon, device, NULL, NULL);
- return;
- }
hmp_handle_error(mon, &err);
}
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 9a4e7dc..2122f4f 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -553,21 +553,6 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
}
-static void usb_msd_password_cb(void *opaque, int err)
-{
- MSDState *s = opaque;
- Error *local_err = NULL;
-
- if (!err) {
- usb_device_attach(&s->dev, &local_err);
- }
-
- if (local_err) {
- error_report_err(local_err);
- qdev_unplug(&s->dev.qdev, NULL);
- }
-}
-
static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -613,23 +598,6 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
return;
}
- bdrv_add_key(blk_bs(blk), NULL, &err);
- if (err) {
- if (monitor_cur_is_qmp()) {
- error_propagate(errp, err);
- return;
- }
- error_free(err);
- err = NULL;
- if (cur_mon) {
- monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
- usb_msd_password_cb, s);
- s->dev.auto_attach = 0;
- } else {
- autostart = 0;
- }
- }
-
blkconf_serial(&s->conf, &dev->serial);
blkconf_blocksizes(&s->conf);
diff --git a/include/block/block.h b/include/block/block.h
index 6d70eb4..ac776b2 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -90,6 +90,7 @@ typedef struct HDGeometry {
#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given:
select an appropriate protocol driver,
ignoring the format layer */
+#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index aa0f373..cd38020 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -21,13 +21,6 @@ void monitor_init(CharDriverState *chr, int flags);
int monitor_suspend(Monitor *mon);
void monitor_resume(Monitor *mon);
-int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
- BlockCompletionFunc *completion_cb,
- void *opaque);
-int monitor_read_block_device_key(Monitor *mon, const char *device,
- BlockCompletionFunc *completion_cb,
- void *opaque);
-
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp);
int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp);
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index ef21efb..65aa1ea 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -284,6 +284,4 @@ void qemu_set_tty_echo(int fd, bool echo);
void os_mem_prealloc(int fd, char *area, size_t sz);
-int qemu_read_password(char *buf, int buf_size);
-
#endif
diff --git a/monitor.c b/monitor.c
index 4f1ba2f..73c0633 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4093,71 +4093,6 @@ void monitor_init(CharDriverState *chr, int flags)
qemu_mutex_unlock(&monitor_lock);
}
-static void bdrv_password_cb(void *opaque, const char *password,
- void *readline_opaque)
-{
- Monitor *mon = opaque;
- BlockDriverState *bs = readline_opaque;
- int ret = 0;
- Error *local_err = NULL;
-
- bdrv_add_key(bs, password, &local_err);
- if (local_err) {
- monitor_printf(mon, "%s\n", error_get_pretty(local_err));
- error_free(local_err);
- ret = -EPERM;
- }
- if (mon->password_completion_cb)
- mon->password_completion_cb(mon->password_opaque, ret);
-
- monitor_read_command(mon, 1);
-}
-
-int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
- BlockCompletionFunc *completion_cb,
- void *opaque)
-{
- int err;
-
- monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs),
- bdrv_get_encrypted_filename(bs));
-
- mon->password_completion_cb = completion_cb;
- mon->password_opaque = opaque;
-
- err = monitor_read_password(mon, bdrv_password_cb, bs);
-
- if (err && completion_cb)
- completion_cb(opaque, err);
-
- return err;
-}
-
-int monitor_read_block_device_key(Monitor *mon, const char *device,
- BlockCompletionFunc *completion_cb,
- void *opaque)
-{
- Error *err = NULL;
- BlockBackend *blk;
-
- blk = blk_by_name(device);
- if (!blk) {
- monitor_printf(mon, "Device not found %s\n", device);
- return -1;
- }
-
- bdrv_add_key(blk_bs(blk), NULL, &err);
- if (err) {
- error_free(err);
- return monitor_read_bdrv_key_start(mon, blk_bs(blk), completion_cb, opaque);
- }
-
- if (completion_cb) {
- completion_cb(opaque, 0);
- }
- return 0;
-}
-
QemuOptsList qemu_mon_opts = {
.name = "mon",
.implied_opt_name = "chardev",
diff --git a/qemu-img.c b/qemu-img.c
index 30869fe..76766f2 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -303,11 +303,9 @@ static BlockBackend *img_open_opts(const char *id,
static BlockBackend *img_open_file(const char *id, const char *filename,
const char *fmt, int flags,
- bool require_io, bool quiet)
+ bool quiet)
{
BlockBackend *blk;
- BlockDriverState *bs;
- char password[256];
Error *local_err = NULL;
QDict *options = NULL;
@@ -324,18 +322,6 @@ static BlockBackend *img_open_file(const char *id, const char *filename,
goto fail;
}
- bs = blk_bs(blk);
- if (bdrv_is_encrypted(bs) && require_io) {
- qprintf(quiet, "Disk image '%s' is encrypted.\n", filename);
- if (qemu_read_password(password, sizeof(password)) < 0) {
- error_report("No password given");
- goto fail;
- }
- if (bdrv_set_key(bs, password) < 0) {
- error_report("invalid password");
- goto fail;
- }
- }
return blk;
fail:
blk_unref(blk);
@@ -727,7 +713,7 @@ static int img_check(int argc, char **argv)
}
blk = img_open_opts("image", opts, flags);
} else {
- blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ blk = img_open_file("image", filename, fmt, flags, quiet);
}
if (!blk) {
return 1;
@@ -942,7 +928,7 @@ static int img_commit(int argc, char **argv)
}
blk = img_open_opts("image", opts, flags);
} else {
- blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ blk = img_open_file("image", filename, fmt, flags, quiet);
}
if (!blk) {
return 1;
@@ -1312,13 +1298,13 @@ static int img_compare(int argc, char **argv)
goto out3;
}
} else {
- blk1 = img_open_file("image_1", filename1, fmt1, flags, true, quiet);
+ blk1 = img_open_file("image_1", filename1, fmt1, flags, quiet);
if (!blk1) {
ret = 2;
goto out3;
}
- blk2 = img_open_file("image_2", filename2, fmt2, flags, true, quiet);
+ blk2 = img_open_file("image_2", filename2, fmt2, flags, quiet);
if (!blk2) {
ret = 2;
goto out2;
@@ -2012,7 +1998,7 @@ static int img_convert(int argc, char **argv)
opts = qemu_opts_next(opts);
} else {
blk[bs_i] = img_open_file(id, argv[optind + bs_i], fmt, src_flags,
- true, quiet);
+ quiet);
}
g_free(id);
if (!blk[bs_i]) {
@@ -2162,7 +2148,7 @@ static int img_convert(int argc, char **argv)
* the bdrv_create() call which takes different params
*/
out_blk = img_open_file("target", out_filename,
- out_fmt, flags, true, quiet);
+ out_fmt, flags, quiet);
if (!out_blk) {
ret = -1;
goto out;
@@ -2360,12 +2346,14 @@ static ImageInfoList *collect_image_info_list(QemuOpts *opts,
goto err;
}
blk = img_open_opts("image", opts,
- BDRV_O_FLAGS | BDRV_O_NO_BACKING);
+ BDRV_O_FLAGS | BDRV_O_NO_BACKING |
+ BDRV_O_NO_IO);
opts = NULL;
} else {
blk = img_open_file("image", filename, fmt,
- BDRV_O_FLAGS | BDRV_O_NO_BACKING,
- false, false);
+ BDRV_O_FLAGS | BDRV_O_NO_BACKING |
+ BDRV_O_NO_IO,
+ false);
}
if (!blk) {
goto err;
@@ -2701,7 +2689,7 @@ static int img_map(int argc, char **argv)
}
blk = img_open_opts("image", opts, BDRV_O_FLAGS);
} else {
- blk = img_open_file("image", filename, fmt, BDRV_O_FLAGS, true, false);
+ blk = img_open_file("image", filename, fmt, BDRV_O_FLAGS, false);
}
if (!blk) {
return 1;
@@ -2867,7 +2855,7 @@ static int img_snapshot(int argc, char **argv)
if (opts) {
blk = img_open_opts("image", opts, bdrv_oflags);
} else {
- blk = img_open_file("image", filename, NULL, bdrv_oflags, true, quiet);
+ blk = img_open_file("image", filename, NULL, bdrv_oflags, quiet);
}
if (!blk) {
return 1;
@@ -3057,7 +3045,7 @@ static int img_rebase(int argc, char **argv)
}
blk = img_open_opts("image", opts, flags);
} else {
- blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ blk = img_open_file("image", filename, fmt, flags, quiet);
}
if (!blk) {
ret = -1;
@@ -3415,7 +3403,7 @@ static int img_resize(int argc, char **argv)
blk = img_open_opts("image", opts, BDRV_O_FLAGS | BDRV_O_RDWR);
} else {
blk = img_open_file("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
- true, quiet);
+ quiet);
}
if (!blk) {
ret = -1;
@@ -3592,7 +3580,7 @@ static int img_amend(int argc, char **argv)
}
blk = img_open_opts("image", opts, BDRV_O_FLAGS | BDRV_O_RDWR);
} else {
- blk = img_open_file("image", filename, fmt, flags, true, quiet);
+ blk = img_open_file("image", filename, fmt, flags, quiet);
}
if (!blk) {
ret = -1;
diff --git a/qemu-io.c b/qemu-io.c
index fc7f81b..0018bfa 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -56,7 +56,6 @@ static const cmdinfo_t close_cmd = {
static int openfile(char *name, int flags, QDict *opts)
{
Error *local_err = NULL;
- BlockDriverState *bs;
if (qemuio_blk) {
fprintf(stderr, "file open already, try 'help close'\n");
@@ -73,27 +72,7 @@ static int openfile(char *name, int flags, QDict *opts)
return 1;
}
- bs = blk_bs(qemuio_blk);
- if (bdrv_is_encrypted(bs)) {
- char password[256];
- printf("Disk image '%s' is encrypted.\n", name);
- if (qemu_read_password(password, sizeof(password)) < 0) {
- error_report("No password given");
- goto error;
- }
- if (bdrv_set_key(bs, password) < 0) {
- error_report("invalid password");
- goto error;
- }
- }
-
-
return 0;
-
- error:
- blk_unref(qemuio_blk);
- qemuio_blk = NULL;
- return 1;
}
static void open_help(void)
diff --git a/qmp.c b/qmp.c
index feea847..3a9940f 100644
--- a/qmp.c
+++ b/qmp.c
@@ -169,7 +169,6 @@ SpiceInfo *qmp_query_spice(Error **errp)
void qmp_cont(Error **errp)
{
- Error *local_err = NULL;
BlockDriverState *bs;
if (runstate_needs_reset()) {
@@ -182,13 +181,6 @@ void qmp_cont(Error **errp)
for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
bdrv_iostatus_reset(bs);
}
- for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
- bdrv_add_key(bs, NULL, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- }
if (runstate_check(RUN_STATE_INMIGRATE)) {
autostart = 1;
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
index 8694749..e386267 100755
--- a/tests/qemu-iotests/087
+++ b/tests/qemu-iotests/087
@@ -187,11 +187,21 @@ echo
_make_test_img -o encryption=on $size
run_qemu -S <<EOF
{ "execute": "qmp_capabilities" }
+{ "execute": "object-add",
+ "arguments": {
+ "qom-type": "secret",
+ "id": "sec0",
+ "props": {
+ "data": "123456"
+ }
+ }
+}
{ "execute": "blockdev-add",
"arguments": {
"options": {
"driver": "$IMGFMT",
"id": "disk",
+ "keyid": "sec0",
"file": {
"driver": "file",
"filename": "$TEST_IMG"
@@ -204,11 +214,21 @@ EOF
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
+{ "execute": "object-add",
+ "arguments": {
+ "qom-type": "secret",
+ "id": "sec0",
+ "props": {
+ "data": "123456"
+ }
+ }
+}
{ "execute": "blockdev-add",
"arguments": {
"options": {
"driver": "$IMGFMT",
"id": "disk",
+ "keyid": "sec0",
"file": {
"driver": "file",
"filename": "$TEST_IMG"
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index c509a40..8299f81 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -48,6 +48,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
Testing: -S
QMP_VERSION
{"return": {}}
+{"return": {}}
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
@@ -58,6 +59,7 @@ You can use 'qemu-img convert' to convert your image to an unencrypted one.
Testing:
QMP_VERSION
{"return": {}}
+{"return": {}}
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
index 1c3820b..a247473 100755
--- a/tests/qemu-iotests/134
+++ b/tests/qemu-iotests/134
@@ -44,23 +44,30 @@ _supported_os Linux
size=128M
-IMGOPTS="encryption=on" _make_test_img $size
+SECRET1=secret,id=sec0,data=astrochicken
+SECRET2=secret,id=sec0,data=platypus
+
+EXTRA_IMG_ARGS="--object $SECRET1" IMGOPTS="encryption=on" _make_test_img $size
+
+
+QEMU_IO_OPTIONS=`echo $QEMU_IO_OPTIONS | sed -e 's/-f qcow2//'`
+TEST_IMG="driver=qcow2,file=$TEST_IMG,keyid=sec0"
echo
echo "== reading whole image =="
-echo "astrochicken" | $QEMU_IO -c "read 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "read 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
echo
echo "== rewriting whole image =="
-echo "astrochicken" | $QEMU_IO -c "write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "write -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern =="
-echo "astrochicken" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "read -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern failure with wrong password =="
-echo "platypus" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET2 -c "read -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
# success, all done
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
index a16acb8..845aa57 100644
--- a/tests/qemu-iotests/134.out
+++ b/tests/qemu-iotests/134.out
@@ -11,8 +11,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-Disk image 'TEST_DIR/t.qcow2' is encrypted.
-password:
read 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -20,8 +18,6 @@ read 134217728/134217728 bytes at offset 0
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-Disk image 'TEST_DIR/t.qcow2' is encrypted.
-password:
wrote 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -29,8 +25,6 @@ wrote 134217728/134217728 bytes at offset 0
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-Disk image 'TEST_DIR/t.qcow2' is encrypted.
-password:
read 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -38,8 +32,6 @@ read 134217728/134217728 bytes at offset 0
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-Disk image 'TEST_DIR/t.qcow2' is encrypted.
-password:
Pattern verification failed at offset 0, 134217728 bytes
read 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 28e4bea..405a932 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -145,9 +145,9 @@ _make_test_img()
# XXX(hch): have global image options?
(
if [ $use_backing = 1 ]; then
- $QEMU_IMG create -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" $image_size 2>&1
+ $QEMU_IMG create $EXTRA_IMG_ARGS -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" $image_size 2>&1
else
- $QEMU_IMG create -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1
+ $QEMU_IMG create $EXTRA_IMG_ARGS -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1
fi
) | _filter_img_create
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index a0fcdc2..9121ed5 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -424,69 +424,3 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
}
}
-
-
-static struct termios oldtty;
-
-static void term_exit(void)
-{
- tcsetattr(0, TCSANOW, &oldtty);
-}
-
-static void term_init(void)
-{
- struct termios tty;
-
- tcgetattr(0, &tty);
- oldtty = tty;
-
- tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
- |INLCR|IGNCR|ICRNL|IXON);
- tty.c_oflag |= OPOST;
- tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
- tty.c_cflag &= ~(CSIZE|PARENB);
- tty.c_cflag |= CS8;
- tty.c_cc[VMIN] = 1;
- tty.c_cc[VTIME] = 0;
-
- tcsetattr(0, TCSANOW, &tty);
-
- atexit(term_exit);
-}
-
-int qemu_read_password(char *buf, int buf_size)
-{
- uint8_t ch;
- int i, ret;
-
- printf("password: ");
- fflush(stdout);
- term_init();
- i = 0;
- for (;;) {
- ret = read(0, &ch, 1);
- if (ret == -1) {
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- } else {
- break;
- }
- } else if (ret == 0) {
- ret = -1;
- break;
- } else {
- if (ch == '\r' ||
- ch == '\n') {
- ret = 0;
- break;
- }
- if (i < (buf_size - 1)) {
- buf[i++] = ch;
- }
- }
- }
- term_exit();
- buf[i] = '\0';
- printf("\n");
- return ret;
-}
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 08f5a9c..e27f391 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -472,27 +472,3 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
memset(area + pagesize * i, 0, 1);
}
}
-
-
-/* XXX: put correct support for win32 */
-int qemu_read_password(char *buf, int buf_size)
-{
- int c, i;
-
- printf("Password: ");
- fflush(stdout);
- i = 0;
- for (;;) {
- c = getchar();
- if (c < 0) {
- buf[i] = '\0';
- return -1;
- } else if (c == '\n') {
- break;
- } else if (i < (buf_size - 1)) {
- buf[i++] = c;
- }
- }
- buf[i] = '\0';
- return 0;
-}
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 16/17] block: remove all encryption handling APIs
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (14 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 15/17] block: rip out all traces of password prompting Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 15:09 ` [Qemu-devel] [PATCH 17/17] block: remove support for writing to qcow/qcow2 encrypted images Daniel P. Berrange
` (3 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Now that all encryption keys must be provided upfront via
the QCryptoSecret API and associated block driver properties
there is no need for any explicit encryption handling APIs
in the block layer. Encryption can be handled transparently
within the block driver. We only retain an API for querying
whether an image is encrypted or not, since that is a
potentially useful piece of metadata to report to the user.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block.c | 81 ++--------------------------------------------
block/qapi.c | 2 +-
block/qcow.c | 19 -----------
block/qcow2.c | 19 -----------
blockdev.c | 69 +++++++++------------------------------
include/block/block.h | 4 ---
tests/qemu-iotests/087.out | 4 +--
7 files changed, 21 insertions(+), 177 deletions(-)
diff --git a/block.c b/block.c
index 09f2a75..bb8cb96 100644
--- a/block.c
+++ b/block.c
@@ -1542,17 +1542,8 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
goto close_and_fail;
}
- if (!bdrv_key_required(bs)) {
- if (bs->blk) {
- blk_dev_change_media_cb(bs->blk, true);
- }
- } else if (!runstate_check(RUN_STATE_PRELAUNCH)
- && !runstate_check(RUN_STATE_INMIGRATE)
- && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
- error_setg(errp,
- "Guest must be stopped for opening of encrypted image");
- ret = -EBUSY;
- goto close_and_fail;
+ if (bs->blk) {
+ blk_dev_change_media_cb(bs->blk, true);
}
QDECREF(options);
@@ -2608,74 +2599,6 @@ int bdrv_is_encrypted(BlockDriverState *bs)
return bs->encrypted;
}
-int bdrv_key_required(BlockDriverState *bs)
-{
- BdrvChild *backing = bs->backing;
-
- if (backing && backing->bs->encrypted && !backing->bs->valid_key) {
- return 1;
- }
- return (bs->encrypted && !bs->valid_key);
-}
-
-int bdrv_set_key(BlockDriverState *bs, const char *key)
-{
- int ret;
- if (bs->backing && bs->backing->bs->encrypted) {
- ret = bdrv_set_key(bs->backing->bs, key);
- if (ret < 0)
- return ret;
- if (!bs->encrypted)
- return 0;
- }
- if (!bs->encrypted) {
- return -EINVAL;
- } else if (!bs->drv || !bs->drv->bdrv_set_key) {
- return -ENOMEDIUM;
- }
- ret = bs->drv->bdrv_set_key(bs, key);
- if (ret < 0) {
- bs->valid_key = 0;
- } else if (!bs->valid_key) {
- bs->valid_key = 1;
- if (bs->blk) {
- /* call the change callback now, we skipped it on open */
- blk_dev_change_media_cb(bs->blk, true);
- }
- }
- return ret;
-}
-
-/*
- * Provide an encryption key for @bs.
- * If @key is non-null:
- * If @bs is not encrypted, fail.
- * Else if the key is invalid, fail.
- * Else set @bs's key to @key, replacing the existing key, if any.
- * If @key is null:
- * If @bs is encrypted and still lacks a key, fail.
- * Else do nothing.
- * On failure, store an error object through @errp if non-null.
- */
-void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp)
-{
- if (key) {
- if (!bdrv_is_encrypted(bs)) {
- error_setg(errp, "Node '%s' is not encrypted",
- bdrv_get_device_or_node_name(bs));
- } else if (bdrv_set_key(bs, key) < 0) {
- error_setg(errp, QERR_INVALID_PASSWORD);
- }
- } else {
- if (bdrv_key_required(bs)) {
- error_set(errp, ERROR_CLASS_DEVICE_ENCRYPTED,
- "'%s' (%s) is encrypted",
- bdrv_get_device_or_node_name(bs),
- bdrv_get_encrypted_filename(bs));
- }
- }
-}
-
const char *bdrv_get_format_name(BlockDriverState *bs)
{
return bs->drv ? bs->drv->format_name : NULL;
diff --git a/block/qapi.c b/block/qapi.c
index 355ba32..a38258f 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -42,7 +42,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
info->ro = bs->read_only;
info->drv = g_strdup(bs->drv->format_name);
info->encrypted = bs->encrypted;
- info->encryption_key_missing = bdrv_key_required(bs);
+ info->encryption_key_missing = false;
info->cache = g_new(BlockdevCacheInfo, 1);
*info->cache = (BlockdevCacheInfo) {
diff --git a/block/qcow.c b/block/qcow.c
index ff80ef5..ccf6de1 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -222,11 +222,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
goto fail;
}
- if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
- error_setg(errp, "AES cipher not available");
- ret = -EINVAL;
- goto fail;
- }
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
bs->encrypted = 1;
@@ -335,19 +330,6 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
return 0;
}
-static int qcow_set_key(BlockDriverState *bs, const char *key)
-{
- BDRVQcowState *s = bs->opaque;
-
- assert(bs->encrypted);
- qcrypto_cipher_free(s->cipher);
- s->cipher = qcow_get_cipher_from_key(key, NULL);
- if (!s->cipher) {
- return -1;
- }
- return 0;
-}
-
/* The crypt function is compatible with the linux cryptoloop
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
supported */
@@ -1078,7 +1060,6 @@ static BlockDriver bdrv_qcow = {
.bdrv_co_writev = qcow_co_writev,
.bdrv_co_get_block_status = qcow_co_get_block_status,
- .bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty,
.bdrv_write_compressed = qcow_write_compressed,
.bdrv_get_info = qcow_get_info,
diff --git a/block/qcow2.c b/block/qcow2.c
index 6ca2b25..c93df92 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1026,11 +1026,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
goto fail;
}
- if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
- error_setg(errp, "AES cipher not available");
- ret = -EINVAL;
- goto fail;
- }
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
bs->encrypted = 1;
@@ -1262,19 +1257,6 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
bs->bl.write_zeroes_alignment = s->cluster_sectors;
}
-static int qcow2_set_key(BlockDriverState *bs, const char *key)
-{
- BDRVQcow2State *s = bs->opaque;
-
- assert(bs->encrypted);
- qcrypto_cipher_free(s->cipher);
- s->cipher = qcow2_get_cipher_from_key(key, NULL);
- if (!s->cipher) {
- return -1;
- }
- return 0;
-}
-
static int qcow2_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
@@ -3193,7 +3175,6 @@ BlockDriver bdrv_qcow2 = {
.bdrv_create = qcow2_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_get_block_status = qcow2_co_get_block_status,
- .bdrv_set_key = qcow2_set_key,
.bdrv_co_readv = qcow2_co_readv,
.bdrv_co_writev = qcow2_co_writev,
diff --git a/blockdev.c b/blockdev.c
index 8141b6b..3e8d7ca 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -564,10 +564,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
bdrv_set_io_limits(bs, &cfg);
}
- if (bdrv_key_required(bs)) {
- autostart = 0;
- }
-
err_no_bs_opts:
qemu_opts_del(opts);
return blk;
@@ -1865,48 +1861,10 @@ void qmp_block_passwd(bool has_device, const char *device,
bool has_node_name, const char *node_name,
const char *password, Error **errp)
{
- Error *local_err = NULL;
- BlockDriverState *bs;
- AioContext *aio_context;
-
- bs = bdrv_lookup_bs(has_device ? device : NULL,
- has_node_name ? node_name : NULL,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
- bdrv_add_key(bs, password, errp);
-
- aio_context_release(aio_context);
+ error_setg_errno(errp, -ENOSYS,
+ "Setting block passwords directly is no longer supported");
}
-/* Assumes AioContext is held */
-static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
- int bdrv_flags, const char *format,
- const char *password, Error **errp)
-{
- Error *local_err = NULL;
- QDict *options = NULL;
- int ret;
-
- if (format) {
- options = qdict_new();
- qdict_put(options, "driver", qstring_from_str(format));
- }
-
- ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- return;
- }
-
- bdrv_add_key(bs, password, errp);
-}
void qmp_change_blockdev(const char *device, const char *filename,
const char *format, Error **errp)
@@ -1916,6 +1874,8 @@ void qmp_change_blockdev(const char *device, const char *filename,
AioContext *aio_context;
int bdrv_flags;
Error *err = NULL;
+ QDict *options = NULL;
+ int ret;
blk = blk_by_name(device);
if (!blk) {
@@ -1937,7 +1897,17 @@ void qmp_change_blockdev(const char *device, const char *filename,
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
- qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, format, NULL, errp);
+
+ if (format) {
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str(format));
+ }
+
+ ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &err);
+ if (ret < 0) {
+ error_propagate(errp, err);
+ return;
+ }
out:
aio_context_release(aio_context);
@@ -3030,7 +3000,6 @@ out:
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
QmpOutputVisitor *ov = qmp_output_visitor_new();
- BlockBackend *blk;
QObject *obj;
QDict *qdict;
Error *local_err = NULL;
@@ -3068,18 +3037,12 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
qdict_flatten(qdict);
- blk = blockdev_init(NULL, qdict, &local_err);
+ blockdev_init(NULL, qdict, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto fail;
}
- if (bdrv_key_required(blk_bs(blk))) {
- blk_unref(blk);
- error_setg(errp, "blockdev-add doesn't support encrypted devices");
- goto fail;
- }
-
fail:
qmp_output_visitor_cleanup(ov);
}
diff --git a/include/block/block.h b/include/block/block.h
index ac776b2..b612146 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -414,10 +414,6 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);
BlockDriverState *bdrv_next_node(BlockDriverState *bs);
BlockDriverState *bdrv_next(BlockDriverState *bs);
int bdrv_is_encrypted(BlockDriverState *bs);
-int bdrv_key_required(BlockDriverState *bs);
-int bdrv_set_key(BlockDriverState *bs, const char *key);
-void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp);
-int bdrv_query_missing_keys(void);
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
void *opaque);
const char *bdrv_get_node_name(const BlockDriverState *bs);
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index 8299f81..50ce50d 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -52,7 +52,7 @@ QMP_VERSION
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-{"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}}
+{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
@@ -63,7 +63,7 @@ QMP_VERSION
Encrypted images are deprecated
Support for them will be removed in a future release.
You can use 'qemu-img convert' to convert your image to an unencrypted one.
-{"error": {"class": "GenericError", "desc": "Guest must be stopped for opening of encrypted image"}}
+{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 17/17] block: remove support for writing to qcow/qcow2 encrypted images
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (15 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 16/17] block: remove all encryption handling APIs Daniel P. Berrange
@ 2015-10-19 15:09 ` Daniel P. Berrange
2015-10-19 16:05 ` [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Alex Bennée
` (2 subsequent siblings)
19 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 15:09 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Josh Durgin, Ronnie Sahlberg, qemu-block,
Markus Armbruster, Stefan Hajnoczi, Paolo Bonzini
Refuse to open a qcow/qcow2 image with encryption if write
access has been requested. To enable historic data to be
liberated support for reading images is retained, as it
does not pose an unreasonable support burden now that the
new key handling infrastructure is inplace.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block.c | 7 -------
block/qcow.c | 6 ++++++
block/qcow2.c | 6 ++++++
tests/qemu-iotests/087.out | 24 ++++--------------------
tests/qemu-iotests/134 | 8 ++++----
tests/qemu-iotests/134.out | 36 +++++++++---------------------------
6 files changed, 29 insertions(+), 58 deletions(-)
diff --git a/block.c b/block.c
index bb8cb96..afb4003 100644
--- a/block.c
+++ b/block.c
@@ -923,13 +923,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
goto free_and_fail;
}
- if (bs->encrypted) {
- error_report("Encrypted images are deprecated");
- error_printf("Support for them will be removed in a future release.\n"
- "You can use 'qemu-img convert' to convert your image"
- " to an unencrypted one.\n");
- }
-
ret = refresh_total_sectors(bs, bs->total_sectors);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector count");
diff --git a/block/qcow.c b/block/qcow.c
index ccf6de1..1914e5e 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -224,6 +224,12 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
}
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
+ if (flags & BDRV_O_RDWR) {
+ error_setg(errp,
+ "Writing of encrypted qcow images is no longer supported");
+ ret = -ENOSYS;
+ goto fail;
+ }
bs->encrypted = 1;
}
if (!(flags & BDRV_O_NO_IO) &&
diff --git a/block/qcow2.c b/block/qcow2.c
index c93df92..a2559f8 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1028,6 +1028,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
}
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
+ if (flags & BDRV_O_RDWR) {
+ error_setg(errp,
+ "Writing of encrypted qcow2 images is no longer supported");
+ ret = -ENOSYS;
+ goto fail;
+ }
bs->encrypted = 1;
}
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index 50ce50d..0cd656a 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -38,21 +38,13 @@ QMP_VERSION
=== Encrypted image ===
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
+qemu-img: TEST_DIR/t.IMGFMT: Writing of encrypted IMGFMT images is no longer supported
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
Testing: -S
QMP_VERSION
{"return": {}}
{"return": {}}
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-{"return": {}}
+{"error": {"class": "GenericError", "desc": "Writing of encrypted qcow2 images is no longer supported"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
@@ -60,22 +52,14 @@ Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-{"return": {}}
+{"error": {"class": "GenericError", "desc": "Writing of encrypted qcow2 images is no longer supported"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
=== Missing driver ===
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
+qemu-img: TEST_DIR/t.IMGFMT: Writing of encrypted IMGFMT images is no longer supported
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
Testing: -S
QMP_VERSION
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
index a247473..5b094bc 100755
--- a/tests/qemu-iotests/134
+++ b/tests/qemu-iotests/134
@@ -55,19 +55,19 @@ TEST_IMG="driver=qcow2,file=$TEST_IMG,keyid=sec0"
echo
echo "== reading whole image =="
-$QEMU_IO --object $SECRET1 -c "read 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "read 0 $size" --source "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== rewriting whole image =="
-$QEMU_IO --object $SECRET1 -c "write -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "write -P 0xa 0 $size" --source "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern =="
-$QEMU_IO --object $SECRET1 -c "read -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET1 -c "read -P 0xa 0 $size" --source "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern failure with wrong password =="
-$QEMU_IO --object $SECRET2 -c "read -P 0xa 0 $size" --source "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET2 -c "read -P 0xa 0 $size" --source "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
# success, all done
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
index 845aa57..f386ebf 100644
--- a/tests/qemu-iotests/134.out
+++ b/tests/qemu-iotests/134.out
@@ -1,38 +1,20 @@
QA output created by 134
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-qemu-img: Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
+qemu-img: TEST_DIR/t.IMGFMT: Writing of encrypted IMGFMT images is no longer supported
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
== reading whole image ==
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-read 134217728/134217728 bytes at offset 0
-128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open device TEST_DIR/t.qcow2: Writing of encrypted qcow2 images is no longer supported
+no file open, try 'help open'
== rewriting whole image ==
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-wrote 134217728/134217728 bytes at offset 0
-128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open device TEST_DIR/t.qcow2: Writing of encrypted qcow2 images is no longer supported
+no file open, try 'help open'
== verify pattern ==
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-read 134217728/134217728 bytes at offset 0
-128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open device TEST_DIR/t.qcow2: Writing of encrypted qcow2 images is no longer supported
+no file open, try 'help open'
== verify pattern failure with wrong password ==
-Encrypted images are deprecated
-Support for them will be removed in a future release.
-You can use 'qemu-img convert' to convert your image to an unencrypted one.
-Pattern verification failed at offset 0, 134217728 bytes
-read 134217728/134217728 bytes at offset 0
-128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open device TEST_DIR/t.qcow2: Writing of encrypted qcow2 images is no longer supported
+no file open, try 'help open'
*** done
--
2.4.3
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (16 preceding siblings ...)
2015-10-19 15:09 ` [Qemu-devel] [PATCH 17/17] block: remove support for writing to qcow/qcow2 encrypted images Daniel P. Berrange
@ 2015-10-19 16:05 ` Alex Bennée
2015-10-19 16:14 ` Daniel P. Berrange
2015-10-19 17:13 ` Dr. David Alan Gilbert
2015-10-23 15:31 ` Stefan Hajnoczi
19 siblings, 1 reply; 36+ messages in thread
From: Alex Bennée @ 2015-10-19 16:05 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Josh Durgin, Stefan Hajnoczi, qemu-block, qemu-devel,
Markus Armbruster, Ronnie Sahlberg, Paolo Bonzini
Daniel P. Berrange <berrange@redhat.com> writes:
> There are a variety of places where QEMU needs to have access
> to passwords, encryption keys or similar kinds of secrets.
>
<snip>
>
> Example usage for creating secrets...
>
> Direct password, insecure, for ad-hoc developer testing only
>
> $QEMU -object secret,id=sec0,data=letmein
>
> Indirect password via a file, good for production
>
> echo -n "letmein" > mypasswd.txt
> $QEMU -object secret,id=sec0,file=mypasswd.txt
>
> The file based approach supports file descriptor passing,
> so mgmt apps can use that to dynamically add passwords to
> running QEMU.
>
> There is a better way though, which is to use an encrypted
> secret. The intent here is that a mgmt app (like libvirt)
> will generate a random AES-256 key for each virtual machine
> it starts (saved in eg /var/run/libvirt/qemu/$GUEST.key)
> It can then use the direct password passing, but encrypt
> the data.
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,data=[base64 ciphertext],\
> keyid=secmaster0,iv=[base64 initialization vector]
>
> This means that the mgmt app does not need to worry about
> file descriptor passing at all. It can just use regular
> object properties, safe in the knowledge that the data is
> protected by a secret AES key shared only between QEMU
> and the mgmt app.
>
> Use of encrypted secrets is not restricted to directly
> provided inline data. If the secret is stored in an
> external file, that can be encrypted too
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,file=/some/secret/file.aes,\
> keyid=secmaster0,iv=[base64 initialization vector]
>
>
>
> Example usage for referencing secrets...
>
> CURL:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0
>
> $QEMU -object secret,id=sec0... -object secret,id=sec1 \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0,proxyuser=dan,passwordid=sec1
>
> iSCSI:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,\
> user=dan,passwordid=sec0
>
> RBD:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=rbd,file=rbd:pool/image:id=myname,\
> auth-supported-cephx,authsecret=sec0
>
> QCow/Qcow2 encryption
>
> $QEMU -object secret,id=sec0... \
> -drive file=someimage.qcow2,keyid=sec0
So one use case which I don't see here but do on other programs that
need to access secrets is calling an external program and reading its
stdout. This is simpler than having to mess around with passing FDs
although there may be security concerns having QEMU create new tasks on
the system.
--
Alex Bennée
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU
2015-10-19 16:05 ` [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Alex Bennée
@ 2015-10-19 16:14 ` Daniel P. Berrange
0 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 16:14 UTC (permalink / raw)
To: Alex Bennée
Cc: Kevin Wolf, Josh Durgin, Stefan Hajnoczi, qemu-block, qemu-devel,
Markus Armbruster, Ronnie Sahlberg, Paolo Bonzini
On Mon, Oct 19, 2015 at 05:05:58PM +0100, Alex Bennée wrote:
> Daniel P. Berrange <berrange@redhat.com> writes:
>
> > There are a variety of places where QEMU needs to have access
> > to passwords, encryption keys or similar kinds of secrets.
> >
> <snip>
> >
> > Example usage for creating secrets...
> >
> > Direct password, insecure, for ad-hoc developer testing only
> >
> > $QEMU -object secret,id=sec0,data=letmein
> >
> > Indirect password via a file, good for production
> >
> > echo -n "letmein" > mypasswd.txt
> > $QEMU -object secret,id=sec0,file=mypasswd.txt
> >
> > The file based approach supports file descriptor passing,
> > so mgmt apps can use that to dynamically add passwords to
> > running QEMU.
> >
> > There is a better way though, which is to use an encrypted
> > secret. The intent here is that a mgmt app (like libvirt)
> > will generate a random AES-256 key for each virtual machine
> > it starts (saved in eg /var/run/libvirt/qemu/$GUEST.key)
> > It can then use the direct password passing, but encrypt
> > the data.
> >
> > $QEMU \
> > -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> > -object secret,id=sec0,data=[base64 ciphertext],\
> > keyid=secmaster0,iv=[base64 initialization vector]
> >
> > This means that the mgmt app does not need to worry about
> > file descriptor passing at all. It can just use regular
> > object properties, safe in the knowledge that the data is
> > protected by a secret AES key shared only between QEMU
> > and the mgmt app.
> >
> > Use of encrypted secrets is not restricted to directly
> > provided inline data. If the secret is stored in an
> > external file, that can be encrypted too
> >
> > $QEMU \
> > -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> > -object secret,id=sec0,file=/some/secret/file.aes,\
> > keyid=secmaster0,iv=[base64 initialization vector]
> >
> >
> >
> > Example usage for referencing secrets...
> >
> > CURL:
> >
> > $QEMU -object secret,id=sec0... \
> > -drive driver=http,url=http://example.com/someimg.qcow2,\
> > user=dan,passwordid=sec0
> >
> > $QEMU -object secret,id=sec0... -object secret,id=sec1 \
> > -drive driver=http,url=http://example.com/someimg.qcow2,\
> > user=dan,passwordid=sec0,proxyuser=dan,passwordid=sec1
> >
> > iSCSI:
> >
> > $QEMU -object secret,id=sec0... \
> > -drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,\
> > user=dan,passwordid=sec0
> >
> > RBD:
> >
> > $QEMU -object secret,id=sec0... \
> > -drive driver=rbd,file=rbd:pool/image:id=myname,\
> > auth-supported-cephx,authsecret=sec0
> >
> > QCow/Qcow2 encryption
> >
> > $QEMU -object secret,id=sec0... \
> > -drive file=someimage.qcow2,keyid=sec0
>
> So one use case which I don't see here but do on other programs that
> need to access secrets is calling an external program and reading its
> stdout. This is simpler than having to mess around with passing FDs
> although there may be security concerns having QEMU create new tasks on
> the system.
Spawning external programs is a rather heavyweight approach. I can see
it being useful if you were locked into an existing API/syntax which
you couldn't modify, but we don't have that restriction here. I'm also
not a huge fan of having QEMU spawn a program that potentially has
access to a large number of passwords that QEMU should not be able
to access. I think passing in the required passwords explicitly as
done here is a better approach.
As noted earlier, FD passing is supported, but I don't think it is
going to be commonly needed or used. At least libvirt would not use
it, as providing the passwords directly, with encryption, is a more
straightforward approach to implement.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (17 preceding siblings ...)
2015-10-19 16:05 ` [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Alex Bennée
@ 2015-10-19 17:13 ` Dr. David Alan Gilbert
2015-10-19 17:46 ` Daniel P. Berrange
2015-10-23 15:31 ` Stefan Hajnoczi
19 siblings, 1 reply; 36+ messages in thread
From: Dr. David Alan Gilbert @ 2015-10-19 17:13 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Josh Durgin, Stefan Hajnoczi, qemu-block, qemu-devel,
Markus Armbruster, Ronnie Sahlberg, Paolo Bonzini
* Daniel P. Berrange (berrange@redhat.com) wrote:
<snip>
> It is obvious there there is a wide variety of functionality
> in QEMU that needs access to "secrets". This need will only
> grow over time. We need to stop having everyone invent their
> own dangerous wheels and provide a standard mechanism for
> securely passing secrets to QEMU.
Agreed.
>
> To this end, this series introduces a QCryptoSecret object
> class with short name "secret". All the places which needs
> passwords/keys are then converted to get their via this
> API, except VNC/SPICE which are a future exercise.
>
> Example usage for creating secrets...
>
> Direct password, insecure, for ad-hoc developer testing only
>
> $QEMU -object secret,id=sec0,data=letmein
>
> Indirect password via a file, good for production
>
> echo -n "letmein" > mypasswd.txt
> $QEMU -object secret,id=sec0,file=mypasswd.txt
>
> The file based approach supports file descriptor passing,
> so mgmt apps can use that to dynamically add passwords to
> running QEMU.
Would it make any sense to support the Linux kernel key system?
That seems to have a way of passing keys between a limited
set of processes and protecting them using SELinux and the like.
(I can also imagine that people might want to feed it with keys
from a TPM or other hardware security device, but fortunately
I can't remember enough about TPMs to remember how getting
keys worked).
Dave
>
> There is a better way though, which is to use an encrypted
> secret. The intent here is that a mgmt app (like libvirt)
> will generate a random AES-256 key for each virtual machine
> it starts (saved in eg /var/run/libvirt/qemu/$GUEST.key)
> It can then use the direct password passing, but encrypt
> the data.
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,data=[base64 ciphertext],\
> keyid=secmaster0,iv=[base64 initialization vector]
>
> This means that the mgmt app does not need to worry about
> file descriptor passing at all. It can just use regular
> object properties, safe in the knowledge that the data is
> protected by a secret AES key shared only between QEMU
> and the mgmt app.
>
> Use of encrypted secrets is not restricted to directly
> provided inline data. If the secret is stored in an
> external file, that can be encrypted too
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,file=/some/secret/file.aes,\
> keyid=secmaster0,iv=[base64 initialization vector]
>
>
>
> Example usage for referencing secrets...
>
> CURL:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0
>
> $QEMU -object secret,id=sec0... -object secret,id=sec1 \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0,proxyuser=dan,passwordid=sec1
>
> iSCSI:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,\
> user=dan,passwordid=sec0
>
> RBD:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=rbd,file=rbd:pool/image:id=myname,\
> auth-supported-cephx,authsecret=sec0
>
> QCow/Qcow2 encryption
>
> $QEMU -object secret,id=sec0... \
> -drive file=someimage.qcow2,keyid=sec0
>
>
> Finally, this extends qemu-img, qemu-nbd and qemu-io. All of
> them gain a new '--object' parameter which provides the same
> functionality as '-object' with QEMU system emulators. This
> lets us create QCryptoSecret object instances in those tools
>
> The tools also then get support for a new '--source IMG-OPTS'
> parameter to allow a full set of image options to be specified,
> instead of just separate hardcoded args for format + filename
> which they currently permit. This is probably the area I am
> least sure of. I struggled to understand what the "best
> practice" is for turning a QemuOpts into something you can
> use to instantiate block backends. So I may well have not
> done the right thing.
>
> Towards the end I rip out the current encryption key handling
> from the block layer so all the hairy code for dealing
> with encrypted block devices disappears, and encryption
> can be a 100% private matter for the block driver internal
> impl. This is obviously not backwards compatible, but we
> have been warning users we're dropping qcow2 encryption
> support for a while.
>
> Finally I disable support for writing to encrypted qcow2
> files, but keep the ability to read them, so users can
> liberate data. Originally we were intending to fully
> delete encryption support, due to the burden it places
> on the internal boock API. Since I removed that burden
> I figured it is reasonable to keep read-only support
> around.
>
> The only real missing thing is wiring up the VNC/SPICE
> servers. There is one complication here in that it is
> common to change the VNC/SPICE password at runtime, and
> I'm not sure what the best way to deal with this is.
>
> There are two obvious choices
>
> a. Create a new secret, tell the VNC server to use
> the new secret, delete the old secret. This will
> need a new 'graphics_secret_update' command in
> the monitor, to use alongside object_add/del.
>
> b. Allow the existing secret to be updated via some
> new 'object_update' method, and internally notify
> the VNC/SPICE server when the secret is updated.
> This would probably need a new QOM interface
> UserUpdatableObject to be defined, as an refinement
> of UserCreatableObject.
>
> Daniel P. Berrange (17):
> crypto: add QCryptoSecret object class for password/key handling
> crypto: add support for loading encrypted x509 keys
> rbd: add support for getting password from QCryptoSecret object
> curl: add support for HTTP authentication parameters
> iscsi: add support for getting CHAP password via QCryptoSecret API
> qcow: add a 'keyid' parameter to qcow options
> qcow2: add a 'keyid' parameter to qcow2 options
> qom: add user_creatable_add & user_creatable_del methods
> qemu-img: add support for --object command line arg
> qemu-nbd: add support for --object command line arg
> qemu-io: add support for --object command line arg
> qemu-io: allow specifying image as a set of options args
> qemu-nbd: allow specifying image as a set of options args
> qemu-img: allow specifying image as a set of options args
> block: rip out all traces of password prompting
> block: remove all encryption handling APIs
> block: remove support for writing to qcow/qcow2 encrypted images
>
> block.c | 88 +----
> block/curl.c | 66 ++++
> block/iscsi.c | 24 +-
> block/qapi.c | 2 +-
> block/qcow.c | 122 +++++--
> block/qcow2.c | 116 +++---
> block/qcow2.h | 1 +
> block/rbd.c | 42 +++
> blockdev.c | 69 +---
> crypto/Makefile.objs | 1 +
> crypto/secret.c | 513 ++++++++++++++++++++++++++
> crypto/tlscredsx509.c | 47 +++
> hmp.c | 42 +--
> hw/usb/dev-storage.c | 32 --
> include/block/block.h | 5 +-
> include/crypto/secret.h | 139 +++++++
> include/crypto/tlscredsx509.h | 1 +
> include/monitor/monitor.h | 10 -
> include/qemu/option.h | 1 +
> include/qemu/osdep.h | 2 -
> include/qom/object_interfaces.h | 31 ++
> monitor.c | 65 ----
> qapi/block-core.json | 23 +-
> qapi/crypto.json | 14 +
> qemu-img-cmds.hx | 44 +--
> qemu-img.c | 788 +++++++++++++++++++++++++++++++++++-----
> qemu-img.texi | 8 +
> qemu-io.c | 145 ++++++--
> qemu-nbd.c | 142 +++++++-
> qemu-nbd.texi | 7 +
> qemu-options.hx | 84 ++++-
> qmp.c | 83 +----
> qom/object_interfaces.c | 76 ++++
> tests/.gitignore | 1 +
> tests/Makefile | 2 +
> tests/qemu-iotests/087 | 20 +
> tests/qemu-iotests/087.out | 26 +-
> tests/qemu-iotests/134 | 17 +-
> tests/qemu-iotests/134.out | 44 +--
> tests/qemu-iotests/common.rc | 4 +-
> tests/test-crypto-secret.c | 440 ++++++++++++++++++++++
> util/oslib-posix.c | 66 ----
> util/oslib-win32.c | 24 --
> util/qemu-option.c | 6 +
> vl.c | 8 +-
> 45 files changed, 2740 insertions(+), 751 deletions(-)
> create mode 100644 crypto/secret.c
> create mode 100644 include/crypto/secret.h
> create mode 100644 tests/test-crypto-secret.c
>
> --
> 2.4.3
>
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU
2015-10-19 17:13 ` Dr. David Alan Gilbert
@ 2015-10-19 17:46 ` Daniel P. Berrange
0 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrange @ 2015-10-19 17:46 UTC (permalink / raw)
To: Dr. David Alan Gilbert
Cc: Kevin Wolf, Josh Durgin, Stefan Hajnoczi, qemu-block, qemu-devel,
Markus Armbruster, Ronnie Sahlberg, Paolo Bonzini
On Mon, Oct 19, 2015 at 06:13:24PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
>
> <snip>
>
> > It is obvious there there is a wide variety of functionality
> > in QEMU that needs access to "secrets". This need will only
> > grow over time. We need to stop having everyone invent their
> > own dangerous wheels and provide a standard mechanism for
> > securely passing secrets to QEMU.
>
> Agreed.
>
> >
> > To this end, this series introduces a QCryptoSecret object
> > class with short name "secret". All the places which needs
> > passwords/keys are then converted to get their via this
> > API, except VNC/SPICE which are a future exercise.
> >
> > Example usage for creating secrets...
> >
> > Direct password, insecure, for ad-hoc developer testing only
> >
> > $QEMU -object secret,id=sec0,data=letmein
> >
> > Indirect password via a file, good for production
> >
> > echo -n "letmein" > mypasswd.txt
> > $QEMU -object secret,id=sec0,file=mypasswd.txt
> >
> > The file based approach supports file descriptor passing,
> > so mgmt apps can use that to dynamically add passwords to
> > running QEMU.
>
> Would it make any sense to support the Linux kernel key system?
> That seems to have a way of passing keys between a limited
> set of processes and protecting them using SELinux and the like.
>
> (I can also imagine that people might want to feed it with keys
> from a TPM or other hardware security device, but fortunately
> I can't remember enough about TPMs to remember how getting
> keys worked).
I don't really know enough about the Linux key system to answer
that very well.
It does of course have the obvious downside that it is Linux
specific, so it would have to be an optional further extension
to the basic portable framework I've implemented so. Given that
I'll say this is an area someone else can explore in the future
if they have desire todo so :-)
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU
2015-10-19 15:09 [Qemu-devel] [PATCH 00/17] Framework for securely passing secrets to QEMU Daniel P. Berrange
` (18 preceding siblings ...)
2015-10-19 17:13 ` Dr. David Alan Gilbert
@ 2015-10-23 15:31 ` Stefan Hajnoczi
19 siblings, 0 replies; 36+ messages in thread
From: Stefan Hajnoczi @ 2015-10-23 15:31 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Kevin Wolf, Josh Durgin, qemu-block, qemu-devel,
Markus Armbruster, Ronnie Sahlberg, Paolo Bonzini
On Mon, Oct 19, 2015 at 04:09:32PM +0100, Daniel P. Berrange wrote:
> There are a variety of places where QEMU needs to have access
> to passwords, encryption keys or similar kinds of secrets.
>
> - VNC / SPICE user passwords
> - Curl block http / proxy passwords
> - RBD auth password
> - iSCSI CHAP password
> - x509 private key password
> - QCow/QCow2 encryption key
>
> QEMU has a variety of ways of dealing with this problem, some
> good, some ugly, some bad.
>
> - The RBD block driver accepts the password in plaintext
> via a private RBD config option. This is a pending CVE
>
> https://security-tracker.debian.org/tracker/CVE-2015-5160
>
> - The iSCSI driver accepts the password in plaintext as
> a block driver option. This is the same as the RBD CVE
> essentially, just a QEMU option, rather than a librbd
> option
>
> - The VNC / SPICE servers only accept the passwords via
> the QEMU monitor. This is secure, but it inconvenient
> for adhoc developer usage where security of CLI args
> does not matter.
>
> - QCow/QCow2 encryption keys can be provided by the monitor
> but this is not available for qemu-img, qemu-io and
> qemu-nbd. QEMU falls back to doing interactive
> console prompting to get keys.
>
> - x509 privte key passwords are not supported at all by
> QEMU which forces users to store their key in plaintext
> on their host FS.
>
> - The CURL driver doesn't support HTTP auth at all
> currently.
>
> It is obvious there there is a wide variety of functionality
> in QEMU that needs access to "secrets". This need will only
> grow over time. We need to stop having everyone invent their
> own dangerous wheels and provide a standard mechanism for
> securely passing secrets to QEMU.
>
> To this end, this series introduces a QCryptoSecret object
> class with short name "secret". All the places which needs
> passwords/keys are then converted to get their via this
> API, except VNC/SPICE which are a future exercise.
>
> Example usage for creating secrets...
>
> Direct password, insecure, for ad-hoc developer testing only
>
> $QEMU -object secret,id=sec0,data=letmein
>
> Indirect password via a file, good for production
>
> echo -n "letmein" > mypasswd.txt
> $QEMU -object secret,id=sec0,file=mypasswd.txt
>
> The file based approach supports file descriptor passing,
> so mgmt apps can use that to dynamically add passwords to
> running QEMU.
>
> There is a better way though, which is to use an encrypted
> secret. The intent here is that a mgmt app (like libvirt)
> will generate a random AES-256 key for each virtual machine
> it starts (saved in eg /var/run/libvirt/qemu/$GUEST.key)
> It can then use the direct password passing, but encrypt
> the data.
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,data=[base64 ciphertext],\
> keyid=secmaster0,iv=[base64 initialization vector]
>
> This means that the mgmt app does not need to worry about
> file descriptor passing at all. It can just use regular
> object properties, safe in the knowledge that the data is
> protected by a secret AES key shared only between QEMU
> and the mgmt app.
>
> Use of encrypted secrets is not restricted to directly
> provided inline data. If the secret is stored in an
> external file, that can be encrypted too
>
> $QEMU \
> -object secret,id=secmaster0,file=/var/run/libvirt/qemu/$GUEST.key,format=base64 \
> -object secret,id=sec0,file=/some/secret/file.aes,\
> keyid=secmaster0,iv=[base64 initialization vector]
>
>
>
> Example usage for referencing secrets...
>
> CURL:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0
>
> $QEMU -object secret,id=sec0... -object secret,id=sec1 \
> -drive driver=http,url=http://example.com/someimg.qcow2,\
> user=dan,passwordid=sec0,proxyuser=dan,passwordid=sec1
>
> iSCSI:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,\
> user=dan,passwordid=sec0
>
> RBD:
>
> $QEMU -object secret,id=sec0... \
> -drive driver=rbd,file=rbd:pool/image:id=myname,\
> auth-supported-cephx,authsecret=sec0
>
> QCow/Qcow2 encryption
>
> $QEMU -object secret,id=sec0... \
> -drive file=someimage.qcow2,keyid=sec0
>
>
> Finally, this extends qemu-img, qemu-nbd and qemu-io. All of
> them gain a new '--object' parameter which provides the same
> functionality as '-object' with QEMU system emulators. This
> lets us create QCryptoSecret object instances in those tools
>
> The tools also then get support for a new '--source IMG-OPTS'
> parameter to allow a full set of image options to be specified,
> instead of just separate hardcoded args for format + filename
> which they currently permit. This is probably the area I am
> least sure of. I struggled to understand what the "best
> practice" is for turning a QemuOpts into something you can
> use to instantiate block backends. So I may well have not
> done the right thing.
>
> Towards the end I rip out the current encryption key handling
> from the block layer so all the hairy code for dealing
> with encrypted block devices disappears, and encryption
> can be a 100% private matter for the block driver internal
> impl. This is obviously not backwards compatible, but we
> have been warning users we're dropping qcow2 encryption
> support for a while.
>
> Finally I disable support for writing to encrypted qcow2
> files, but keep the ability to read them, so users can
> liberate data. Originally we were intending to fully
> delete encryption support, due to the burden it places
> on the internal boock API. Since I removed that burden
> I figured it is reasonable to keep read-only support
> around.
>
> The only real missing thing is wiring up the VNC/SPICE
> servers. There is one complication here in that it is
> common to change the VNC/SPICE password at runtime, and
> I'm not sure what the best way to deal with this is.
>
> There are two obvious choices
>
> a. Create a new secret, tell the VNC server to use
> the new secret, delete the old secret. This will
> need a new 'graphics_secret_update' command in
> the monitor, to use alongside object_add/del.
>
> b. Allow the existing secret to be updated via some
> new 'object_update' method, and internally notify
> the VNC/SPICE server when the secret is updated.
> This would probably need a new QOM interface
> UserUpdatableObject to be defined, as an refinement
> of UserCreatableObject.
>
> Daniel P. Berrange (17):
> crypto: add QCryptoSecret object class for password/key handling
> crypto: add support for loading encrypted x509 keys
> rbd: add support for getting password from QCryptoSecret object
> curl: add support for HTTP authentication parameters
> iscsi: add support for getting CHAP password via QCryptoSecret API
> qcow: add a 'keyid' parameter to qcow options
> qcow2: add a 'keyid' parameter to qcow2 options
> qom: add user_creatable_add & user_creatable_del methods
> qemu-img: add support for --object command line arg
> qemu-nbd: add support for --object command line arg
> qemu-io: add support for --object command line arg
> qemu-io: allow specifying image as a set of options args
> qemu-nbd: allow specifying image as a set of options args
> qemu-img: allow specifying image as a set of options args
> block: rip out all traces of password prompting
> block: remove all encryption handling APIs
> block: remove support for writing to qcow/qcow2 encrypted images
>
> block.c | 88 +----
> block/curl.c | 66 ++++
> block/iscsi.c | 24 +-
> block/qapi.c | 2 +-
> block/qcow.c | 122 +++++--
> block/qcow2.c | 116 +++---
> block/qcow2.h | 1 +
> block/rbd.c | 42 +++
> blockdev.c | 69 +---
> crypto/Makefile.objs | 1 +
> crypto/secret.c | 513 ++++++++++++++++++++++++++
> crypto/tlscredsx509.c | 47 +++
> hmp.c | 42 +--
> hw/usb/dev-storage.c | 32 --
> include/block/block.h | 5 +-
> include/crypto/secret.h | 139 +++++++
> include/crypto/tlscredsx509.h | 1 +
> include/monitor/monitor.h | 10 -
> include/qemu/option.h | 1 +
> include/qemu/osdep.h | 2 -
> include/qom/object_interfaces.h | 31 ++
> monitor.c | 65 ----
> qapi/block-core.json | 23 +-
> qapi/crypto.json | 14 +
> qemu-img-cmds.hx | 44 +--
> qemu-img.c | 788 +++++++++++++++++++++++++++++++++++-----
> qemu-img.texi | 8 +
> qemu-io.c | 145 ++++++--
> qemu-nbd.c | 142 +++++++-
> qemu-nbd.texi | 7 +
> qemu-options.hx | 84 ++++-
> qmp.c | 83 +----
> qom/object_interfaces.c | 76 ++++
> tests/.gitignore | 1 +
> tests/Makefile | 2 +
> tests/qemu-iotests/087 | 20 +
> tests/qemu-iotests/087.out | 26 +-
> tests/qemu-iotests/134 | 17 +-
> tests/qemu-iotests/134.out | 44 +--
> tests/qemu-iotests/common.rc | 4 +-
> tests/test-crypto-secret.c | 440 ++++++++++++++++++++++
> util/oslib-posix.c | 66 ----
> util/oslib-win32.c | 24 --
> util/qemu-option.c | 6 +
> vl.c | 8 +-
> 45 files changed, 2740 insertions(+), 751 deletions(-)
> create mode 100644 crypto/secret.c
> create mode 100644 include/crypto/secret.h
> create mode 100644 tests/test-crypto-secret.c
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
i.e. makes sense to me but I haven't reviewed patches in detail
^ permalink raw reply [flat|nested] 36+ messages in thread