|  | /* | 
|  | * 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_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_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 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_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, | 
|  | NULL); | 
|  | 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); |