qemu/crypto/secret_common.c
Daniel P. Berrangé 62eb377e0a crypto: purge 'loaded' property that was not fully removed
The 'loaded' property on TLS creds and secret objects was marked as
deprecated in 6.0.0. In 7.1.0 the deprecation info was moved into
the 'removed-features.rst' file, but the property was not actually
removed, just made read-only. This was a highly unusual practice,
so finish the long overdue removal job.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2024-11-05 18:37:15 +00:00

393 lines
11 KiB
C

/*
* 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_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_ALGO_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_complete(UserCreatable *uc, Error **errp)
{
QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(uc);
QCryptoSecretCommonClass *sec_class
= QCRYPTO_SECRET_COMMON_GET_CLASS(uc);
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(OBJECT(uc)));
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;
}
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_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_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);