/* * 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 . * */ #include "qemu/osdep.h" #include "crypto/secret_common.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_decrypt(QCryptoSecretCommon *secret, const uint8_t *input, size_t inputlen, uint8_t **output, size_t *outputlen, Error **errp) { g_autofree uint8_t *iv = NULL; g_autofree uint8_t *key = NULL; g_autofree uint8_t *ciphertext = 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, &key, &keylen, 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, &ivlen, 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, &ciphertextlen, 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]; plaintext[ciphertextlen] = '\0'; *output = g_steal_pointer(&plaintext); *outputlen = ciphertextlen; } static void qcrypto_secret_decode(const uint8_t *input, size_t inputlen, uint8_t **output, size_t *outputlen, Error **errp) { *output = qbase64_decode((const gchar *)input, inputlen, outputlen, errp); } static void qcrypto_secret_prop_set_loaded(Object *obj, bool value, Error **errp) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); QCryptoSecretCommonClass *sec_class = QCRYPTO_SECRET_COMMON_GET_CLASS(obj); if (value) { Error *local_err = NULL; uint8_t *input = NULL; size_t inputlen = 0; uint8_t *output = NULL; size_t outputlen = 0; if (sec_class->load_data) { sec_class->load_data(secret, &input, &inputlen, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } else { error_setg(errp, "%s provides no 'load_data' method'", object_get_typename(obj)); return; } if (secret->keyid) { qcrypto_secret_decrypt(secret, input, inputlen, &output, &outputlen, &local_err); g_free(input); if (local_err) { error_propagate(errp, local_err); return; } input = output; inputlen = outputlen; } else { if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) { qcrypto_secret_decode(input, inputlen, &output, &outputlen, &local_err); g_free(input); if (local_err) { error_propagate(errp, local_err); return; } input = output; inputlen = outputlen; } } secret->rawdata = input; secret->rawlen = inputlen; } else if (secret->rawdata) { error_setg(errp, "Cannot unload secret"); return; } } static bool qcrypto_secret_prop_get_loaded(Object *obj, Error **errp G_GNUC_UNUSED) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); return secret->rawdata != NULL; } static void qcrypto_secret_prop_set_format(Object *obj, int value, Error **errp G_GNUC_UNUSED) { QCryptoSecretCommon *creds = QCRYPTO_SECRET_COMMON(obj); creds->format = value; } static int qcrypto_secret_prop_get_format(Object *obj, Error **errp G_GNUC_UNUSED) { QCryptoSecretCommon *creds = QCRYPTO_SECRET_COMMON(obj); return creds->format; } static void qcrypto_secret_prop_set_iv(Object *obj, const char *value, Error **errp) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); g_free(secret->iv); secret->iv = g_strdup(value); } static char * qcrypto_secret_prop_get_iv(Object *obj, Error **errp) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); return g_strdup(secret->iv); } static void qcrypto_secret_prop_set_keyid(Object *obj, const char *value, Error **errp) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); g_free(secret->keyid); secret->keyid = g_strdup(value); } static char * qcrypto_secret_prop_get_keyid(Object *obj, Error **errp) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); return g_strdup(secret->keyid); } static void qcrypto_secret_complete(UserCreatable *uc, Error **errp) { object_property_set_bool(OBJECT(uc), "loaded", true, errp); } static void qcrypto_secret_finalize(Object *obj) { QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); g_free(secret->iv); g_free(secret->keyid); g_free(secret->rawdata); } static void qcrypto_secret_class_init(ObjectClass *oc, void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); ucc->complete = qcrypto_secret_complete; object_class_property_add_bool(oc, "loaded", qcrypto_secret_prop_get_loaded, qcrypto_secret_prop_set_loaded); object_class_property_add_enum(oc, "format", "QCryptoSecretFormat", &QCryptoSecretFormat_lookup, qcrypto_secret_prop_get_format, qcrypto_secret_prop_set_format); object_class_property_add_str(oc, "keyid", qcrypto_secret_prop_get_keyid, qcrypto_secret_prop_set_keyid); object_class_property_add_str(oc, "iv", qcrypto_secret_prop_get_iv, qcrypto_secret_prop_set_iv); } int qcrypto_secret_lookup(const char *secretid, uint8_t **data, size_t *datalen, Error **errp) { Object *obj; QCryptoSecretCommon *secret; obj = object_resolve_path_component( object_get_objects_root(), secretid); if (!obj) { error_setg(errp, "No secret with id '%s'", secretid); return -1; } secret = (QCryptoSecretCommon *) object_dynamic_cast(obj, TYPE_QCRYPTO_SECRET_COMMON); if (!secret) { error_setg(errp, "Object with id '%s' is not a secret", secretid); return -1; } if (!secret->rawdata) { error_setg(errp, "Secret with id '%s' has no data", secretid); return -1; } *data = g_new0(uint8_t, secret->rawlen + 1); memcpy(*data, secret->rawdata, secret->rawlen); (*data)[secret->rawlen] = '\0'; *datalen = secret->rawlen; return 0; } char *qcrypto_secret_lookup_as_utf8(const char *secretid, Error **errp) { uint8_t *data; size_t datalen; if (qcrypto_secret_lookup(secretid, &data, &datalen, errp) < 0) { return NULL; } if (!g_utf8_validate((const gchar *)data, datalen, NULL)) { error_setg(errp, "Data from secret %s is not valid UTF-8", secretid); g_free(data); return NULL; } return (char *)data; } char *qcrypto_secret_lookup_as_base64(const char *secretid, Error **errp) { uint8_t *data; size_t datalen; char *ret; if (qcrypto_secret_lookup(secretid, &data, &datalen, errp) < 0) { return NULL; } ret = g_base64_encode(data, datalen); g_free(data); return ret; } static const TypeInfo qcrypto_secret_info = { .parent = TYPE_OBJECT, .name = TYPE_QCRYPTO_SECRET_COMMON, .instance_size = sizeof(QCryptoSecretCommon), .instance_finalize = qcrypto_secret_finalize, .class_size = sizeof(QCryptoSecretCommonClass), .class_init = qcrypto_secret_class_init, .abstract = true, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } }; static void qcrypto_secret_register_types(void) { type_register_static(&qcrypto_secret_info); } type_init(qcrypto_secret_register_types);