[PATCH v4 2/3] crypto/linux_keyring: add 'secret_keyring' secret object.

2020-05-25 Thread Alexey Krasikov
Add the ability for the secret object to obtain secret data from the
Linux in-kernel key managment and retention facility, as an extra option
to the existing ones: reading from a file or passing directly as a
string.

The secret is identified by the key serial number. The upper layers
need to instantiate the key and make sure the QEMU process has access
permissions to read it.

Signed-off-by: Alexey Krasikov 
Reviewed-by: Daniel P. Berrangé 
---
 configure   |  38 
 crypto/Makefile.objs|   1 +
 crypto/secret_keyring.c | 148 
 include/crypto/secret_keyring.h |  52 +++
 4 files changed, 239 insertions(+)
 create mode 100644 crypto/secret_keyring.c
 create mode 100644 include/crypto/secret_keyring.h

diff --git a/configure b/configure
index 2fc05c4465..3c83504c95 100755
--- a/configure
+++ b/configure
@@ -509,6 +509,7 @@ libpmem=""
 default_devices="yes"
 plugins="no"
 fuzzing="no"
+secret_keyring="yes"
 
 supported_cpu="no"
 supported_os="no"
@@ -1601,6 +1602,10 @@ for opt do
   ;;
   --gdb=*) gdb_bin="$optarg"
   ;;
+  --enable-keyring) secret_keyring="yes"
+  ;;
+  --disable-keyring) secret_keyring="no"
+  ;;
   *)
   echo "ERROR: unknown option $opt"
   echo "Try '$0 --help' for more information"
@@ -6250,6 +6255,34 @@ case "$slirp" in
 ;;
 esac
 
+##
+# check for usable __NR_keyctl syscall
+
+if test "$linux" = "yes" ; then
+
+have_keyring=no
+cat > $TMPC << EOF
+#include 
+#include 
+#include 
+#include 
+int main(void) {
+return syscall(__NR_keyctl, KEYCTL_READ, 0, NULL, NULL, 0);
+}
+EOF
+if compile_prog "" "" ; then
+have_keyring=yes
+fi
+fi
+if test "$secret_keyring" = "yes"
+then
+if test "$have_keyring" != "yes"
+then
+error_exit "syscall __NR_keyctl requested, \
+but not implemented on your system"
+fi
+fi
+
 
 ##
 # End of CC checks
@@ -6733,6 +6766,7 @@ echo "default devices   $default_devices"
 echo "plugin support$plugins"
 echo "fuzzing support   $fuzzing"
 echo "gdb   $gdb_bin"
+echo "Linux keyring $secret_keyring"
 
 if test "$supported_cpu" = "no"; then
 echo
@@ -7614,6 +7648,10 @@ if test -n "$gdb_bin" ; then
 echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak
 fi
 
+if test "$secret_keyring" = "yes" ; then
+  echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 695da72dd1..872c928ac0 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -20,6 +20,7 @@ crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
 crypto-obj-y += secret_common.o
 crypto-obj-y += secret.o
+crypto-obj-$(CONFIG_SECRET_KEYRING) += secret_keyring.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
 crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o
diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c
new file mode 100644
index 00..aa29004639
--- /dev/null
+++ b/crypto/secret_keyring.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU crypto secret support
+ *
+ * Copyright 2020 Yandex N.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include 
+#include 
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+#include "crypto/secret_keyring.h"
+
+
+static inline
+long keyctl_read(int32_t key, uint8_t *buffer, size_t buflen)
+{
+return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen, 0);
+}
+
+
+static void
+qcrypto_secret_keyring_load_data(QCryptoSecretCommon *sec_common,
+ uint8_t **output,
+  

[PATCH v4 3/3] test-crypto-secret: add 'secret_keyring' object tests.

2020-05-25 Thread Alexey Krasikov
Add tests:
  test_secret_keyring_good;
  test_secret_keyring_revoked_key;
  test_secret_keyring_expired_key;
  test_secret_keyring_bad_serial_key;
  test_secret_keyring_bad_key_access_right;

Added tests require libkeyutils. The absence of this library is not
critical, because these tests will be skipped in this case.

Signed-off-by: Alexey Krasikov 
---
 configure  |  24 ++
 tests/Makefile.include |   4 +
 tests/test-crypto-secret.c | 158 +
 3 files changed, 186 insertions(+)

diff --git a/configure b/configure
index 3c83504c95..5a916ab33f 100755
--- a/configure
+++ b/configure
@@ -6283,6 +6283,27 @@ but not implemented on your system"
 fi
 fi
 
+##
+# check for usable keyutils.h
+
+if test "$linux" = "yes" ; then
+
+have_keyutils=no
+cat > $TMPC << EOF
+#include 
+#include 
+#include 
+#include 
+#include 
+int main(void) {
+return request_key("user", NULL, NULL, 0);
+}
+EOF
+if compile_prog "" "-lkeyutils"; then
+have_keyutils=yes
+fi
+fi
+
 
 ##
 # End of CC checks
@@ -7650,6 +7671,9 @@ fi
 
 if test "$secret_keyring" = "yes" ; then
   echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak
+  if test "$have_keyutils" = "yes" ; then
+echo "CONFIG_TEST_SECRET_KEYRING=y" >> $config_host_mak
+  fi
 fi
 
 if test "$tcg_interpreter" = "yes"; then
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 03a74b60f6..de13908701 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -538,6 +538,10 @@ tests/benchmark-crypto-cipher$(EXESUF): 
tests/benchmark-crypto-cipher.o $(test-c
 tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o 
$(test-crypto-obj-y)
 tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y)
 
+ifeq ($(CONFIG_TEST_SECRET_KEYRING),y)
+tests/test-crypto-secret.o-libs := -lkeyutils
+endif
+
 tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS)
 tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS)
 tests/pkix_asn1_tab.o-cflags := $(TASN1_CFLAGS)
diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c
index 13fc6c4c75..603a093f10 100644
--- a/tests/test-crypto-secret.c
+++ b/tests/test-crypto-secret.c
@@ -24,6 +24,10 @@
 #include "crypto/secret.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#ifdef CONFIG_TEST_SECRET_KEYRING
+#include "crypto/secret_keyring.h"
+#include 
+#endif
 
 static void test_secret_direct(void)
 {
@@ -124,6 +128,147 @@ static void test_secret_indirect_emptyfile(void)
 g_free(fname);
 }
 
+#ifdef CONFIG_TEST_SECRET_KEYRING
+
+#define DESCRIPTION "qemu_test_secret"
+#define PAYLOAD "Test Payload"
+
+
+static void test_secret_keyring_good(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+
+g_assert(key >= 0);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+"sec0",
+_abort,
+"serial", key_str,
+NULL);
+
+assert(0 <= keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING));
+char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ _abort);
+g_assert_cmpstr(pw, ==, PAYLOAD);
+
+object_unparent(sec);
+g_free(pw);
+}
+
+
+static void test_secret_keyring_revoked_key(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_revoke(key));
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", key_str,
+NULL);
+
+g_assert(errno == EKEYREVOKED);
+g_assert(sec == NULL);
+
+keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING);
+}
+
+
+static void test_secret_keyring_expired_key(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_set_timeout(key, 1));
+sleep(1);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+&qu

[PATCH v4 1/3] crypto/secret: move main logic from 'secret' to 'secret_common'.

2020-05-25 Thread Alexey Krasikov
Create base class 'common secret'. Move common data and logic from
'secret' to 'common_secret' class. This allowed adding abstraction layer
for easier adding new 'secret' objects in future.
Convert 'secret' class to child from basic 'secret_common' with 'data'
and 'file' properties.

Signed-off-by: Alexey Krasikov 
---
 crypto/Makefile.objs   |   1 +
 crypto/secret.c| 347 +---
 crypto/secret_common.c | 403 +
 include/crypto/secret.h|  20 +-
 include/crypto/secret_common.h |  68 ++
 5 files changed, 482 insertions(+), 357 deletions(-)
 create mode 100644 crypto/secret_common.c
 create mode 100644 include/crypto/secret_common.h

diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index c2a371b0b4..695da72dd1 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -18,6 +18,7 @@ crypto-obj-y += tlscredsanon.o
 crypto-obj-y += tlscredspsk.o
 crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
+crypto-obj-y += secret_common.o
 crypto-obj-y += secret.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
diff --git a/crypto/secret.c b/crypto/secret.c
index 3107aecb47..3447e2f64b 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -20,16 +20,14 @@
 
 #include "qemu/osdep.h"
 #include "crypto/secret.h"
-#include "crypto/cipher.h"
 #include "qapi/error.h"
 #include "qom/object_interfaces.h"
-#include "qemu/base64.h"
 #include "qemu/module.h"
 #include "trace.h"
 
 
 static void
-qcrypto_secret_load_data(QCryptoSecret *secret,
+qcrypto_secret_load_data(QCryptoSecretCommon *sec_common,
  uint8_t **output,
  size_t *outputlen,
  Error **errp)
@@ -38,6 +36,8 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 size_t length = 0;
 GError *gerr = NULL;
 
+QCryptoSecret *secret = QCRYPTO_SECRET(sec_common);
+
 *output = NULL;
 *outputlen = 0;
 
@@ -65,198 +65,6 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 }
 
 
-static void qcrypto_secret_decrypt(QCryptoSecret *secret,
-   const uint8_t *input,
-   size_t inputlen,
-   uint8_t **output,
-   size_t *outputlen,
-   Error **errp)
-{
-g_autofree uint8_t *key = NULL;
-g_autofree uint8_t *ciphertext = NULL;
-g_autofree uint8_t *iv = NULL;
-size_t keylen, ciphertextlen, ivlen;
-g_autoptr(QCryptoCipher) aes = NULL;
-g_autofree uint8_t *plaintext = NULL;
-
-*output = NULL;
-*outputlen = 0;
-
-if (qcrypto_secret_lookup(secret->keyid,
-  , ,
-  errp) < 0) {
-return;
-}
-
-if (keylen != 32) {
-error_setg(errp, "Key should be 32 bytes in length");
-return;
-}
-
-if (!secret->iv) {
-error_setg(errp, "IV is required to decrypt secret");
-return;
-}
-
-iv = qbase64_decode(secret->iv, -1, , errp);
-if (!iv) {
-return;
-}
-if (ivlen != 16) {
-error_setg(errp, "IV should be 16 bytes in length not %zu",
-   ivlen);
-return;
-}
-
-aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
- QCRYPTO_CIPHER_MODE_CBC,
- key, keylen,
- errp);
-if (!aes) {
-return;
-}
-
-if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
-return;
-}
-
-if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
-ciphertext = qbase64_decode((const gchar*)input,
-inputlen,
-,
-errp);
-if (!ciphertext) {
-return;
-}
-plaintext = g_new0(uint8_t, ciphertextlen + 1);
-} else {
-ciphertextlen = inputlen;
-plaintext = g_new0(uint8_t, inputlen + 1);
-}
-if (qcrypto_cipher_decrypt(aes,
-   ciphertext ? ciphertext : input,
-   plaintext,
-   ciphertextlen,
-   errp) < 0) {
-return;
-}
-
-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]);
-return;
-}
-
-/* Even though plaintext may contain arbitrary NUL
- * ensure it is explicitly NUL terminated.
- */
-ciphertextlen -= plaintext[ciphertextlen - 1];
-plainte

[PATCH v4 0/3] Add secret_keyring object

2020-05-25 Thread Alexey Krasikov
Add the ability to store encryption keys in the Linux keyring
facility.

For that, factor out common parts from secret to a new abstract class
secret_common, and introduce new user-creatable secret_keyring class
inheriting from it.
Use '--enable-keyring/--disable-keyring' configuration parameters
to provide this feature.

Example:

$QEMU -object secret_keyring,id=sec0,serial=0x15968230

v4 changes:
 - removed all extra white space aligment;
 - removed last NULL parameter from 'object_class_property_add_*' functions;
 - removed the word "support" from configure output summary.
 - removed the 'linux/keyctl.h' include from configure keyutils test;
 - temporary added 'g_test_skip()' at start of the metod
'test_secret_keyring_bad_key_access_right()'.

Alexey Krasikov (3):
  crypto/secret: move main logic from 'secret' to 'secret_common'.
  crypto/linux_keyring: add 'secret_keyring' secret object.
  test-crypto-secret: add 'secret_keyring' object tests.

 configure   |  62 +
 crypto/Makefile.objs|   2 +
 crypto/secret.c | 347 +--
 crypto/secret_common.c  | 403 
 crypto/secret_keyring.c | 148 
 include/crypto/secret.h |  20 +-
 include/crypto/secret_common.h  |  68 ++
 include/crypto/secret_keyring.h |  52 +
 tests/Makefile.include  |   4 +
 tests/test-crypto-secret.c  | 158 +
 10 files changed, 907 insertions(+), 357 deletions(-)
 create mode 100644 crypto/secret_common.c
 create mode 100644 crypto/secret_keyring.c
 create mode 100644 include/crypto/secret_common.h
 create mode 100644 include/crypto/secret_keyring.h

-- 
2.17.1




[PATCH v3 1/3] crypto/secret: move main logic from 'secret' to 'secret_common'.

2020-05-18 Thread Alexey Krasikov
Create base class 'common secret'. Move common data and logic from
'secret' to 'common_secret' class. This allowed adding abstraction layer
for easier adding new 'secret' objects in future.
Convert 'secret' class to child from basic 'secret_common' with 'data'
and 'file' properties.

Signed-off-by: Alexey Krasikov 
---
 crypto/Makefile.objs   |   1 +
 crypto/secret.c| 351 +---
 crypto/secret_common.c | 407 +
 include/crypto/secret.h|  20 +-
 include/crypto/secret_common.h |  68 ++
 5 files changed, 486 insertions(+), 361 deletions(-)
 create mode 100644 crypto/secret_common.c
 create mode 100644 include/crypto/secret_common.h

diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index c2a371b0b4..695da72dd1 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -18,6 +18,7 @@ crypto-obj-y += tlscredsanon.o
 crypto-obj-y += tlscredspsk.o
 crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
+crypto-obj-y += secret_common.o
 crypto-obj-y += secret.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
diff --git a/crypto/secret.c b/crypto/secret.c
index a846a3c87c..c1e1145232 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -20,16 +20,14 @@
 
 #include "qemu/osdep.h"
 #include "crypto/secret.h"
-#include "crypto/cipher.h"
 #include "qapi/error.h"
 #include "qom/object_interfaces.h"
-#include "qemu/base64.h"
 #include "qemu/module.h"
 #include "trace.h"
 
 
 static void
-qcrypto_secret_load_data(QCryptoSecret *secret,
+qcrypto_secret_load_data(QCryptoSecretCommon *sec_common,
  uint8_t **output,
  size_t *outputlen,
  Error **errp)
@@ -38,6 +36,8 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 size_t length = 0;
 GError *gerr = NULL;
 
+QCryptoSecret *secret = QCRYPTO_SECRET(sec_common);
+
 *output = NULL;
 *outputlen = 0;
 
@@ -65,198 +65,6 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 }
 
 
-static void qcrypto_secret_decrypt(QCryptoSecret *secret,
-   const uint8_t *input,
-   size_t inputlen,
-   uint8_t **output,
-   size_t *outputlen,
-   Error **errp)
-{
-g_autofree uint8_t *key = NULL;
-g_autofree uint8_t *ciphertext = NULL;
-g_autofree uint8_t *iv = NULL;
-size_t keylen, ciphertextlen, ivlen;
-g_autoptr(QCryptoCipher) aes = NULL;
-g_autofree uint8_t *plaintext = NULL;
-
-*output = NULL;
-*outputlen = 0;
-
-if (qcrypto_secret_lookup(secret->keyid,
-  , ,
-  errp) < 0) {
-return;
-}
-
-if (keylen != 32) {
-error_setg(errp, "Key should be 32 bytes in length");
-return;
-}
-
-if (!secret->iv) {
-error_setg(errp, "IV is required to decrypt secret");
-return;
-}
-
-iv = qbase64_decode(secret->iv, -1, , errp);
-if (!iv) {
-return;
-}
-if (ivlen != 16) {
-error_setg(errp, "IV should be 16 bytes in length not %zu",
-   ivlen);
-return;
-}
-
-aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
- QCRYPTO_CIPHER_MODE_CBC,
- key, keylen,
- errp);
-if (!aes) {
-return;
-}
-
-if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
-return;
-}
-
-if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
-ciphertext = qbase64_decode((const gchar*)input,
-inputlen,
-,
-errp);
-if (!ciphertext) {
-return;
-}
-plaintext = g_new0(uint8_t, ciphertextlen + 1);
-} else {
-ciphertextlen = inputlen;
-plaintext = g_new0(uint8_t, inputlen + 1);
-}
-if (qcrypto_cipher_decrypt(aes,
-   ciphertext ? ciphertext : input,
-   plaintext,
-   ciphertextlen,
-   errp) < 0) {
-return;
-}
-
-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]);
-return;
-}
-
-/* Even though plaintext may contain arbitrary NUL
- * ensure it is explicitly NUL terminated.
- */
-ciphertextlen -= plaintext[ciphertextlen - 1];
-plainte

[PATCH v3 3/3] test-crypto-secret: add 'secret_keyring' object tests.

2020-05-18 Thread Alexey Krasikov
Add tests:
  test_secret_keyring_good;
  test_secret_keyring_revoked_key;
  test_secret_keyring_expired_key;
  test_secret_keyring_bad_serial_key;
  test_secret_keyring_bad_key_access_right;

Added tests require libkeyutils. The absence of this library is not
critical, because these tests will be skipped in this case.

Signed-off-by: Alexey Krasikov 
---
 configure  |  25 ++
 tests/Makefile.include |   4 +
 tests/test-crypto-secret.c | 154 +
 3 files changed, 183 insertions(+)

diff --git a/configure b/configure
index 1bae5ec0a1..2ab7d2961c 100755
--- a/configure
+++ b/configure
@@ -6283,6 +6283,28 @@ but not implemented on your system"
 fi
 fi
 
+##
+# check for usable keyutils.h
+
+if test "$linux" = "yes" ; then
+
+have_keyutils=no
+cat > $TMPC << EOF
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+int main(void) {
+return request_key("user", NULL, NULL, 0);
+}
+EOF
+if compile_prog "" "-lkeyutils"; then
+have_keyutils=yes
+fi
+fi
+
 
 ##
 # End of CC checks
@@ -7650,6 +7672,9 @@ fi
 
 if test "$secret_keyring" = "yes" ; then
   echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak
+  if test "$have_keyutils" = "yes" ; then
+echo "CONFIG_TEST_SECRET_KEYRING=y" >> $config_host_mak
+  fi
 fi
 
 if test "$tcg_interpreter" = "yes"; then
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 03a74b60f6..de13908701 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -538,6 +538,10 @@ tests/benchmark-crypto-cipher$(EXESUF): 
tests/benchmark-crypto-cipher.o $(test-c
 tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o 
$(test-crypto-obj-y)
 tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y)
 
+ifeq ($(CONFIG_TEST_SECRET_KEYRING),y)
+tests/test-crypto-secret.o-libs := -lkeyutils
+endif
+
 tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS)
 tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS)
 tests/pkix_asn1_tab.o-cflags := $(TASN1_CFLAGS)
diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c
index 13fc6c4c75..34f5ed75ff 100644
--- a/tests/test-crypto-secret.c
+++ b/tests/test-crypto-secret.c
@@ -24,6 +24,10 @@
 #include "crypto/secret.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#ifdef CONFIG_TEST_SECRET_KEYRING
+#include "crypto/secret_keyring.h"
+#include 
+#endif
 
 static void test_secret_direct(void)
 {
@@ -124,6 +128,143 @@ static void test_secret_indirect_emptyfile(void)
 g_free(fname);
 }
 
+#ifdef CONFIG_TEST_SECRET_KEYRING
+
+#define DESCRIPTION "qemu_test_secret"
+#define PAYLOAD "Test Payload"
+
+
+static void test_secret_keyring_good(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+
+g_assert(key >= 0);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+"sec0",
+_abort,
+"serial", key_str,
+NULL);
+
+assert(0 <= keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING));
+char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ _abort);
+g_assert_cmpstr(pw, ==, PAYLOAD);
+
+object_unparent(sec);
+g_free(pw);
+}
+
+
+static void test_secret_keyring_revoked_key(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_revoke(key));
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", key_str,
+NULL);
+
+g_assert(errno == EKEYREVOKED);
+g_assert(sec == NULL);
+
+keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING);
+}
+
+
+static void test_secret_keyring_expired_key(void)
+{
+char key_str[16];
+Object *sec;
+int32_t key = add_key("user", DESCRIPTION, PAYLOAD,
+  strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_set_timeout(key, 1));
+sleep(1);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_KEYRING,
+object_get_objects_root(),
+&qu

[PATCH v3 2/3] crypto/linux_keyring: add 'secret_keyring' secret object.

2020-05-18 Thread Alexey Krasikov
Add the ability for the secret object to obtain secret data from the
Linux in-kernel key managment and retention facility, as an extra option
to the existing ones: reading from a file or passing directly as a
string.

The secret is identified by the key serial number. The upper layers
need to instantiate the key and make sure the QEMU process has access
permissions to read it.

Signed-off-by: Alexey Krasikov 
---
 configure   |  38 
 crypto/Makefile.objs|   1 +
 crypto/secret_keyring.c | 148 
 include/crypto/secret_keyring.h |  52 +++
 4 files changed, 239 insertions(+)
 create mode 100644 crypto/secret_keyring.c
 create mode 100644 include/crypto/secret_keyring.h

diff --git a/configure b/configure
index 0d69c360c0..1bae5ec0a1 100755
--- a/configure
+++ b/configure
@@ -509,6 +509,7 @@ libpmem=""
 default_devices="yes"
 plugins="no"
 fuzzing="no"
+secret_keyring="yes"
 
 supported_cpu="no"
 supported_os="no"
@@ -1601,6 +1602,10 @@ for opt do
   ;;
   --gdb=*) gdb_bin="$optarg"
   ;;
+  --enable-keyring) secret_keyring="yes"
+  ;;
+  --disable-keyring) secret_keyring="no"
+  ;;
   *)
   echo "ERROR: unknown option $opt"
   echo "Try '$0 --help' for more information"
@@ -6250,6 +6255,34 @@ case "$slirp" in
 ;;
 esac
 
+##
+# check for usable __NR_keyctl syscall
+
+if test "$linux" = "yes" ; then
+
+have_keyring=no
+cat > $TMPC << EOF
+#include 
+#include 
+#include 
+#include 
+int main(void) {
+return syscall(__NR_keyctl, KEYCTL_READ, 0, NULL, NULL, 0);
+}
+EOF
+if compile_prog "" "" ; then
+have_keyring=yes
+fi
+fi
+if test "$secret_keyring" = "yes"
+then
+if test "$have_keyring" != "yes"
+then
+error_exit "syscall __NR_keyctl requested, \
+but not implemented on your system"
+fi
+fi
+
 
 ##
 # End of CC checks
@@ -6733,6 +6766,7 @@ echo "default devices   $default_devices"
 echo "plugin support$plugins"
 echo "fuzzing support   $fuzzing"
 echo "gdb   $gdb_bin"
+echo "Linux keyring support $secret_keyring"
 
 if test "$supported_cpu" = "no"; then
 echo
@@ -7614,6 +7648,10 @@ if test -n "$gdb_bin" ; then
 echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak
 fi
 
+if test "$secret_keyring" = "yes" ; then
+  echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 695da72dd1..872c928ac0 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -20,6 +20,7 @@ crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
 crypto-obj-y += secret_common.o
 crypto-obj-y += secret.o
+crypto-obj-$(CONFIG_SECRET_KEYRING) += secret_keyring.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
 crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o
diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c
new file mode 100644
index 00..8f256ee3b8
--- /dev/null
+++ b/crypto/secret_keyring.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU crypto secret support
+ *
+ * Copyright 2020 Yandex N.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include 
+#include 
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+#include "crypto/secret_keyring.h"
+
+
+static inline
+long keyctl_read(int32_t key, uint8_t *buffer, size_t buflen)
+{
+return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen, 0);
+}
+
+
+static void
+qcrypto_secret_keyring_load_data(QCryptoSecretCommon *sec_common,
+ uint8_t **output,
+   

[PATCH v3 0/3] Add secret_keyring object

2020-05-18 Thread Alexey Krasikov
Add the ability to store encryption keys in the Linux keyring
facility.

For that, factor out common parts from secret to a new abstract class
secret_common, and introduce new user-creatable secret_keyring class
inheriting from it.
Use '--enable-keyring/--disable-keyring' configuration parameters
to provide this feature.

Example:

$QEMU -object secret_keyring,id=sec0,serial=0x15968230

Alexey Krasikov (3):
  crypto/secret: move main logic from 'secret' to 'secret_common'.
  crypto/linux_keyring: add 'secret_keyring' secret object.
  test-crypto-secret: add 'secret_keyring' object tests.

 configure   |  63 +
 crypto/Makefile.objs|   2 +
 crypto/secret.c | 351 +--
 crypto/secret_common.c  | 405 
 crypto/secret_keyring.c | 141 +++
 include/crypto/secret.h |  20 +-
 include/crypto/secret_common.h  |  68 ++
 include/crypto/secret_keyring.h |  45 
 tests/Makefile.include  |   4 +
 tests/test-crypto-secret.c  | 154 
 10 files changed, 892 insertions(+), 361 deletions(-)
 create mode 100644 crypto/secret_common.c
 create mode 100644 crypto/secret_keyring.c
 create mode 100644 include/crypto/secret_common.h
 create mode 100644 include/crypto/secret_keyring.h

-- 
2.17.1




[RFC PATCH v2 2/5] crypto/secret_interface: conversion to common basic class.

2020-04-15 Thread Alexey Krasikov
* Remove individual option fields. Common field have been left.

Signed-off-by: Alexey Krasikov 
---
 crypto/Makefile.objs  |   1 +
 crypto/secret_interface.c | 156 ++
 include/crypto/secret_interface.h | 119 ---
 3 files changed, 51 insertions(+), 225 deletions(-)

diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index c2a371b0b4..3ae0dfd1a4 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -18,6 +18,7 @@ crypto-obj-y += tlscredsanon.o
 crypto-obj-y += tlscredspsk.o
 crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
+crypto-obj-y += secret_interface.o
 crypto-obj-y += secret.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
diff --git a/crypto/secret_interface.c b/crypto/secret_interface.c
index 1cf0ad0ce8..9d8accdea3 100644
--- a/crypto/secret_interface.c
+++ b/crypto/secret_interface.c
@@ -19,7 +19,7 @@
  */
 
 #include "qemu/osdep.h"
-#include "crypto/secret.h"
+#include "crypto/secret_interface.h"
 #include "crypto/cipher.h"
 #include "qapi/error.h"
 #include "qom/object_interfaces.h"
@@ -28,44 +28,7 @@
 #include "trace.h"
 
 
-static void
-qcrypto_secret_load_data(QCryptoSecret *secret,
- uint8_t **output,
- size_t *outputlen,
- Error **errp)
-{
-char *data = NULL;
-size_t length = 0;
-GError *gerr = NULL;
-
-*output = NULL;
-*outputlen = 0;
-
-if (secret->file) {
-if (secret->data) {
-error_setg(errp,
-   "'file' and 'data' are mutually exclusive");
-return;
-}
-if (!g_file_get_contents(secret->file, , , )) {
-error_setg(errp,
-   "Unable to read %s: %s",
-   secret->file, gerr->message);
-g_error_free(gerr);
-return;
-}
-*output = (uint8_t *)data;
-*outputlen = length;
-} else if (secret->data) {
-*outputlen = strlen(secret->data);
-*output = (uint8_t *)g_strdup(secret->data);
-} else {
-error_setg(errp, "Either 'file' or 'data' must be provided");
-}
-}
-
-
-static void qcrypto_secret_decrypt(QCryptoSecret *secret,
+static void qcrypto_secret_decrypt(QCryptoSecretCommon *secret,
const uint8_t *input,
size_t inputlen,
uint8_t **output,
@@ -178,7 +141,9 @@ qcrypto_secret_prop_set_loaded(Object *obj,
bool value,
Error **errp)
 {
-QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj);
+QCryptoSecretCommonClass *sec_class
+= QCRYPTO_SECRET_COMMON_GET_CLASS(obj);
 
 if (value) {
 Error *local_err = NULL;
@@ -187,9 +152,14 @@ qcrypto_secret_prop_set_loaded(Object *obj,
 uint8_t *output = NULL;
 size_t outputlen = 0;
 
-qcrypto_secret_load_data(secret, , , _err);
-if (local_err) {
-error_propagate(errp, local_err);
+if (sec_class->load_data) {
+sec_class->load_data(obj, , , _err);
+if (local_err) {
+error_propagate(errp, local_err);
+return;
+}
+} else {
+error_setg(errp, "'load_data' metod has not been initiated");
 return;
 }
 
@@ -230,7 +200,7 @@ static bool
 qcrypto_secret_prop_get_loaded(Object *obj,
Error **errp G_GNUC_UNUSED)
 {
-QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj);
 return secret->data != NULL;
 }
 
@@ -240,7 +210,7 @@ qcrypto_secret_prop_set_format(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
 {
-QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+QCryptoSecretCommon *creds = QCRYPTO_SECRET_COMMON(obj);
 
 creds->format = value;
 }
@@ -250,60 +220,18 @@ static int
 qcrypto_secret_prop_get_format(Object *obj,
Error **errp G_GNUC_UNUSED)
 {
-QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+QCryptoSecretCommon *creds = QCRYPTO_SECRET_COMMON(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 **e

[RFC PATCH v2 3/5] crypto/secret: add secret class files.

2020-04-15 Thread Alexey Krasikov
* Add child 'secret' class from basic 'secret_common'
  with 'data' and 'file' properties.

Signed-off-by: Alexey Krasikov 
---
 crypto/secret.c | 167 
 include/crypto/secret.h | 133 
 2 files changed, 300 insertions(+)
 create mode 100644 crypto/secret.c
 create mode 100644 include/crypto/secret.h

diff --git a/crypto/secret.c b/crypto/secret.c
new file mode 100644
index 00..d9be0409e4
--- /dev/null
+++ b/crypto/secret.c
@@ -0,0 +1,167 @@
+/*
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/secret.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+
+static void
+qcrypto_secret_load_data(Object *obj,
+ uint8_t **output,
+ size_t *outputlen,
+ Error **errp)
+{
+char *data = NULL;
+size_t length = 0;
+GError *gerr = NULL;
+
+QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+*output = NULL;
+*outputlen = 0;
+
+if (secret->file) {
+if (secret->data) {
+error_setg(errp,
+   "'file' and 'data' are mutually exclusive");
+return;
+}
+if (!g_file_get_contents(secret->file, , , )) {
+error_setg(errp,
+   "Unable to read %s: %s",
+   secret->file, gerr->message);
+g_error_free(gerr);
+return;
+}
+*output = (uint8_t *)data;
+*outputlen = length;
+} else if (secret->data) {
+*outputlen = strlen(secret->data);
+*output = (uint8_t *)g_strdup(secret->data);
+} else {
+error_setg(errp, "Either 'file' or 'data' must be provided");
+}
+}
+
+
+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_complete(UserCreatable *uc, Error **errp)
+{
+object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_secret_finalize(Object *obj)
+{
+QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+g_free(secret->file);
+g_free(secret->data);
+}
+
+static void
+qcrypto_secret_class_init(ObjectClass *oc, void *data)
+{
+QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc);
+sic->load_data = qcrypto_secret_load_data;
+
+UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+ucc->complete = qcrypto_secret_complete;
+
+object_class_property_add_str(oc, "data",
+  qcrypto_secret_prop_get_data,
+  qcrypto_secret_prop_set_data,
+  NULL);
+object_class_property_add_str(oc, "file",
+  qcrypto_secret_prop_get_file,
+  qcrypto_secret_prop_set_file,
+  NULL);
+}
+
+
+static const TypeInfo qcrypto_secret_info = {
+.parent = TYPE_QCRYPTO_SECRET_COMMON,
+.name = TYPE_QCRYPTO_SECRET,
+.instance_size = sizeof(QCryptoSecret),
+.instance_finalize = qcrypto_secret_finalize,
+.class_size = sizeof(

[RFC PATCH v2 4/5] crypto/linux_keyring: add 'syskey' secret object.

2020-04-15 Thread Alexey Krasikov
* Add the ability for the secret object to obtain secret data from the
  Linux in-kernel key managment and retention facility, as an extra option
  to the existing ones: reading from a file or passing directly as a
  string.

  The secret is identified by the key serial number.  The upper layers
  need to instantiate the key and make sure the QEMU process has access
  rights to read it.

Signed-off-by: Alexey Krasikov 
---
 crypto/Makefile.objs   |   1 +
 crypto/linux_keyring.c | 140 +
 include/crypto/linux_keyring.h |  38 +
 3 files changed, 179 insertions(+)
 create mode 100644 crypto/linux_keyring.c
 create mode 100644 include/crypto/linux_keyring.h

diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 3ae0dfd1a4..7fc354a8d5 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -19,6 +19,7 @@ crypto-obj-y += tlscredspsk.o
 crypto-obj-y += tlscredsx509.o
 crypto-obj-y += tlssession.o
 crypto-obj-y += secret_interface.o
+crypto-obj-y += linux_keyring.o
 crypto-obj-y += secret.o
 crypto-obj-y += pbkdf.o
 crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
diff --git a/crypto/linux_keyring.c b/crypto/linux_keyring.c
new file mode 100644
index 00..7950d4c12d
--- /dev/null
+++ b/crypto/linux_keyring.c
@@ -0,0 +1,140 @@
+#ifdef __NR_keyctl
+
+#include "qemu/osdep.h"
+#include 
+#include 
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+#include "crypto/linux_keyring.h"
+
+
+static inline
+long keyctl_read(key_serial_t key, uint8_t *buffer, size_t buflen)
+{
+return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen, 0);
+}
+
+
+static
+long keyctl_read_alloc(key_serial_t key, uint8_t **buffer)
+{
+uint8_t *loc_buf;
+long retcode = keyctl_read(key, NULL, 0);
+if (retcode <= 0) {
+return retcode;
+}
+loc_buf = g_malloc(retcode);
+retcode = keyctl_read(key, loc_buf, retcode);
+
+if (retcode >= 0) {
+*buffer = loc_buf;
+} else {
+g_free(loc_buf);
+}
+return retcode;
+}
+
+
+static void
+qcrypto_secret_linux_load_data(Object   *obj,
+   uint8_t  **output,
+   size_t   *outputlen,
+   Error**errp)
+{
+QCryptoSecretLinuxKeyring *secret = QCRYPTO_SECRET_LINUX_KEYRING(obj);
+uint8_t  *buffer = NULL;
+long retcode;
+
+*output= NULL;
+*outputlen = 0;
+
+if (secret->serial) {
+retcode = keyctl_read_alloc(secret->serial, );
+if (retcode < 0) {
+  error_setg_errno(errp, errno,
+ "Unable to read serial key %08x",
+ secret->serial);
+  return;
+} else {
+  *outputlen = retcode;
+  *output= buffer;
+}
+} else {
+  error_setg(errp, "Either 'serial' must be provided");
+}
+}
+
+
+static void
+qcrypto_secret_prop_set_key(Object *obj,   Visitor *v,
+const char *name,  void*opaque,
+Error  **errp)
+{
+QCryptoSecretLinuxKeyring *secret = QCRYPTO_SECRET_LINUX_KEYRING(obj);
+int32_t value;
+visit_type_int32(v, name, , errp);
+if (!value) {
+error_setg(errp, "The 'serial' should be not equal 0");
+}
+secret->serial = value;
+}
+
+
+static void
+qcrypto_secret_prop_get_key(Object *obj,   Visitor *v,
+const char *name,  void*opaque,
+Error  **errp)
+{
+QCryptoSecretLinuxKeyring *secret = QCRYPTO_SECRET_LINUX_KEYRING(obj);
+int32_t value = secret->serial;
+visit_type_int32(v, name, , errp);
+}
+
+
+static void
+qcrypto_secret_linux_complete(UserCreatable *uc, Error **errp)
+{
+object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_secret_linux_class_init(ObjectClass *oc, void *data)
+{
+QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc);
+sic->load_data = qcrypto_secret_linux_load_data;
+
+UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+ucc->complete = qcrypto_secret_linux_complete;
+
+object_class_property_add(oc, "serial", "key_serial_t",
+  qcrypto_secret_prop_get_key,
+  qcrypto_secret_prop_set_key,
+  NULL, NULL, NULL);
+}
+
+
+static const TypeInfo qcrypto_secret_info = {
+.parent= TYPE_QCRYPTO_SECRET_COMMON,
+.name  = TYPE_QCRYPTO_SECRET_LINUX_KEYRING,
+.instance_size = sizeof(QCryptoSecretLinuxKeyring),
+.class_size= sizeof(QCryptoSecretLinuxKeyringClass),
+.class_init= qcrypto_secret_linux_class_init,
+.interfaces= (InterfaceInfo[]) {
+{ TYPE_USER_CREATABLE },

[RFC PATCH v2 1/5] crypto/secret: rename to secret_interface.

2020-04-15 Thread Alexey Krasikov
* Rename for future division into subclasses. Most part of the interface
  will remain in basic common class.

Signed-off-by: Alexey Krasikov 
---
 crypto/{secret.c => secret_interface.c} | 0
 include/crypto/{secret.h => secret_interface.h} | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename crypto/{secret.c => secret_interface.c} (100%)
 rename include/crypto/{secret.h => secret_interface.h} (100%)

diff --git a/crypto/secret.c b/crypto/secret_interface.c
similarity index 100%
rename from crypto/secret.c
rename to crypto/secret_interface.c
diff --git a/include/crypto/secret.h b/include/crypto/secret_interface.h
similarity index 100%
rename from include/crypto/secret.h
rename to include/crypto/secret_interface.h
-- 
2.17.1




[RFC PATCH v2 5/5] test-crypto-secret: add 'syskey' object tests.

2020-04-15 Thread Alexey Krasikov
* test_secret_seckey_bad_key_access_right() is not working yet.
  We don't know yet if this due a bag in the Linux kernel or
  whether it's normal syscall behavior.
  We've requested information from kernel maintainer.

Signed-off-by: Alexey Krasikov 
---
 tests/test-crypto-secret.c | 138 +
 1 file changed, 138 insertions(+)

diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c
index 13fc6c4c75..6b17fe3a81 100644
--- a/tests/test-crypto-secret.c
+++ b/tests/test-crypto-secret.c
@@ -22,8 +22,10 @@
 
 #include "crypto/init.h"
 #include "crypto/secret.h"
+#include "crypto/linux_keyring.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#include 
 
 static void test_secret_direct(void)
 {
@@ -125,6 +127,132 @@ static void test_secret_indirect_emptyfile(void)
 }
 
 
+#define DESCRIPTION "qemu_test_secret"
+#define PAYLOAD "Test Payload"
+
+
+static void test_secret_seckey_good(void)
+{
+char key_str[16];
+Object *sec;
+key_serial_t key = add_key("user", DESCRIPTION, PAYLOAD,
+   strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+
+g_assert(key >= 0);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_LINUX_KEYRING,
+object_get_objects_root(),
+"sec0",
+_abort,
+"serial", key_str,
+NULL);
+
+assert(0 <= keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING));
+char *pw = qcrypto_secret_lookup_as_utf8("sec0",
+ _abort);
+g_assert_cmpstr(pw, ==, PAYLOAD);
+
+object_unparent(sec);
+g_free(pw);
+}
+
+
+static void test_secret_seckey_revoked_key(void)
+{
+char key_str[16];
+Object *sec;
+key_serial_t key = add_key("user", DESCRIPTION, PAYLOAD,
+   strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_revoke(key));
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_LINUX_KEYRING,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", key_str,
+NULL);
+
+g_assert(errno == EKEYREVOKED);
+g_assert(sec == NULL);
+
+keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING);
+}
+
+
+static void test_secret_seckey_expired_key(void)
+{
+char key_str[16];
+Object *sec;
+key_serial_t key = add_key("user", DESCRIPTION, PAYLOAD,
+   strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_set_timeout(key, 1));
+sleep(1);
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_LINUX_KEYRING,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", key_str,
+NULL);
+
+g_assert(errno == EKEYEXPIRED);
+g_assert(sec == NULL);
+
+keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING);
+}
+
+
+static void test_secret_seckey_bad_serial_key(void)
+{
+Object *sec;
+
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", "1",
+NULL);
+
+g_assert(errno == ENOKEY);
+g_assert(sec == NULL);
+}
+
+
+static void test_secret_seckey_bad_key_access_right(void)
+{
+char key_str[16];
+Object *sec;
+key_serial_t key = add_key("user", DESCRIPTION, PAYLOAD,
+   strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING);
+g_assert(key >= 0);
+g_assert_false(keyctl_setperm(key, KEY_POS_ALL & (~KEY_POS_READ)));
+
+snprintf(key_str, sizeof(key_str), "0x%08x", key);
+
+sec = object_new_with_props(
+TYPE_QCRYPTO_SECRET_LINUX_KEYRING,
+object_get_objects_root(),
+"sec0",
+NULL,
+"serial", key_str,
+NULL);
+
+g_assert(errno == EACCES);
+g_assert(sec == NULL);
+
+keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING);
+}
+
+
 static void test_secret_noconv_base64_good(void)
 {
 Object *sec = object_new_with_props(
@@ -425,6 +553,16 @@ int main(int argc, char **argv)
 test_secret_indirect_badfile);
 g_test_add_func("/crypto/secret/indirect/emptyfile",
 test_secret_indirect_emptyfile);
+g_test_add_func("/crypto/secret/seckey/good",
+test_secret_seckey_good);
+g_test_add_func("/crypto/secret/seckey/revoked_key",
+test_secret_seckey_revoked_key);
+g_test_add_func("/crypto/secret/seckey/expired_key",
+ 

[PATCH 1/2] crypto/secret: fix inconsequential errors.

2020-04-15 Thread Alexey Krasikov
* change condition from QCRYPTO_SECRET_FORMAT_RAW
  to QCRYPTO_SECRET_FORMAT_BASE64 in if-operator, because
  this is potencial error if you add another format value.

Signed-off-by: Alexey Krasikov 
---
 crypto/secret.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crypto/secret.c b/crypto/secret.c
index 1cf0ad0ce8..546b965afe 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -204,7 +204,7 @@ qcrypto_secret_prop_set_loaded(Object *obj,
 input = output;
 inputlen = outputlen;
 } else {
-if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
+if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
 qcrypto_secret_decode(input, inputlen,
   , , _err);
 g_free(input);
-- 
2.17.1




[PATCH 2/2] crypto/secret: fix return logic of crypto_secret_prop_get_loaded()

2020-04-15 Thread Alexey Krasikov
* Get function returned value of properties 'data' insteed of returning
  value of raw data internal field. This error did not affect anyone,
  because no one called the get function.

Signed-off-by: Alexey Krasikov 
---
 crypto/secret.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crypto/secret.c b/crypto/secret.c
index 546b965afe..79b9b4ce0c 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -231,7 +231,7 @@ qcrypto_secret_prop_get_loaded(Object *obj,
Error **errp G_GNUC_UNUSED)
 {
 QCryptoSecret *secret = QCRYPTO_SECRET(obj);
-return secret->data != NULL;
+return secret->rawdata != NULL;
 }
 
 
-- 
2.17.1




[RFC PATCH] crypto/secret: support fetching secrets from Linux keyring

2020-03-28 Thread Alexey Krasikov
Add the ability for the secret object to obtain secret data from the
Linux in-kernel key managment and retention facility, as an extra option
to the existing ones: reading from a file or passing directly as a
string.

The secret is identified by the key serial number.  The upper layers
need to instantiate the key and make sure the QEMU process has access
rights to read it.

Signed-off-by: Alexey Krasikov 
---
 crypto/secret.c | 88 +++--
 include/crypto/secret.h |  3 ++
 2 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/crypto/secret.c b/crypto/secret.c
index 1cf0ad0ce8..2e8be6241c 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -19,6 +19,8 @@
  */
 
 #include "qemu/osdep.h"
+#include 
+#include 
 #include "crypto/secret.h"
 #include "crypto/cipher.h"
 #include "qapi/error.h"
@@ -28,6 +30,40 @@
 #include "trace.h"
 
 
+static inline
+long keyctl_read(key_serial_t key, uint8_t *buffer, size_t buflen)
+{
+#ifdef __NR_keyctl
+return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen, 0);
+#else
+errno = ENOSYS;
+return -1;
+#endif
+}
+
+static
+long keyctl_read_alloc(key_serial_t key, uint8_t **buffer)
+{
+uint8_t *loc_buf;
+long retcode = keyctl_read(key, NULL, 0);
+if (retcode < 0) {
+return retcode;
+}
+loc_buf = g_malloc(retcode + 1);
+retcode = keyctl_read(key, loc_buf, retcode + 1);
+   /*
+* We don't have key operations locks between syscalls.
+* For example, the key could have been removed or expired.
+*/
+if (retcode >= 0) {
+loc_buf[retcode] = '\0';
+*buffer = loc_buf;
+} else {
+g_free(loc_buf);
+}
+return retcode;
+}
+
 static void
 qcrypto_secret_load_data(QCryptoSecret *secret,
  uint8_t **output,
@@ -41,10 +77,28 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 *output = NULL;
 *outputlen = 0;
 
-if (secret->file) {
+if (secret->syskey) {
+uint8_t *buffer = NULL;
+long retcode;
+if (secret->data || secret->file) {
+error_setg(errp,
+   "'syskey', 'file' and 'data' are mutually exclusive");
+return;
+}
+retcode = keyctl_read_alloc(secret->syskey, );
+if (retcode < 0) {
+error_setg_errno(errp, errno,
+   "Unable to read serial key %08x",
+   secret->syskey);
+return;
+} else {
+*outputlen = retcode;
+*output = buffer;
+}
+} else if (secret->file) {
 if (secret->data) {
 error_setg(errp,
-   "'file' and 'data' are mutually exclusive");
+   "'syskey', 'file' and 'data' are mutually exclusive");
 return;
 }
 if (!g_file_get_contents(secret->file, , , )) {
@@ -60,7 +114,8 @@ qcrypto_secret_load_data(QCryptoSecret *secret,
 *outputlen = strlen(secret->data);
 *output = (uint8_t *)g_strdup(secret->data);
 } else {
-error_setg(errp, "Either 'file' or 'data' must be provided");
+error_setg(errp,
+   "Either 'syskey' or 'file' or 'data' must be provided");
 }
 }
 
@@ -298,6 +353,29 @@ qcrypto_secret_prop_get_file(Object *obj,
 }
 
 
+static void
+qcrypto_secret_prop_set_syskey(Object *obj, Visitor *v,
+   const char *name, void *opaque,
+   Error **errp)
+{
+QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+int32_t value;
+visit_type_int32(v, name, , errp);
+secret->syskey = value;
+}
+
+
+static void
+qcrypto_secret_prop_get_syskey(Object *obj, Visitor *v,
+   const char *name, void *opaque,
+   Error **errp)
+{
+QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+int32_t value = secret->syskey;
+visit_type_int32(v, name, , errp);
+}
+
+
 static void
 qcrypto_secret_prop_set_iv(Object *obj,
const char *value,
@@ -384,6 +462,10 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data)
   qcrypto_secret_prop_get_file,
   qcrypto_secret_prop_set_file,
   NULL);
+object_class_property_add(oc, "syskey", "key_serial_t",
+  qcrypto_secret_prop_get_syskey,
+  qcrypto_secret_prop_set_syskey,
+  NULL, NULL, NULL);
 object_class_property_add_str(oc, "keyid",
   qcrypto_secret_prop_get_keyid,
   qcrypto_secret_prop_set_keyid,
diff --git a/include/crypto/secret.h b/include/cr