[PATCH v4 2/3] crypto/linux_keyring: add 'secret_keyring' secret object.
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.
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'.
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
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'.
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.
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.
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
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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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()
* 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
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