Merge QCryptoSecret object support
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWdDmJAAoJEL6G67QVEE/fJ4EP+gNC4ErBDpbg+I4RhLHv/FsF i2iEYkmBfFzUmiB8iSFlJY12XJ/CPnbrWks0WNHIoarUgtGuvfqH91KGkERxJQFE TkD65EOhNHWlP1zaI5r5ZMizhOdO6EPa0pbS/QH/UCy5qwu5IbG5EesOw00d7nL8 5gT39ehXAFljutRygWsa1JyOkDB04WNehVsly/l2t9v16aQUDJOC5mWVuoInrDEo ye1VG2Cx7Y1/FRo3fFCDwzYD+8jgxIBAu8Igwjk/95VbfBVl769PBAilQRc5zMHt //eMNdul6GooVKmu/K1JWkmjIZJFUiboEMgPJElWV1y8bWmhh++4J6EVb55owoDk VRv84cqiaYErVb+56gaImr92GSKezll0APWz6YlDsFZgPClCPnUjDSr39+23t1h4 LprirtbkAjw73T92kuQ7kzbXElWm7rSfcx5u1/S6YPP+EDzZpW9+h62lKGGnuS2M bzwFOOmWHe1MhbRSh+BOzGBf1wWhMSCKgLAOmPuRQ8slS91vfE66bIlqpIKBGgfn 42t0wZCEW8bqIe8xry5pC5UoDfm3cVDhgGHGyMLWWDMez0qDchaAkWNkIDtc8Juv a1WqE/0lP/sVb36yLVANvt1/Qvpg6M3JwMbTjVaJl2eTDDtwho4PK+Chxx0a8BGl Z6oGj1rmvCDD2Dsi/EXI =g+9G -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange/tags/pull-qcrypto-secrets-base-2015-12-18-1' into staging Merge QCryptoSecret object support # gpg: Signature made Fri 18 Dec 2015 16:51:21 GMT using RSA key ID 15104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" * remotes/berrange/tags/pull-qcrypto-secrets-base-2015-12-18-1: crypto: add support for loading encrypted x509 keys crypto: add QCryptoSecret object class for password/key handling qga: convert to use error checked base64 decode qemu-char: convert to use error checked base64 decode util: add base64 decoding function Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c688084506
@ -7,6 +7,7 @@ crypto-obj-y += tlscreds.o
|
||||
crypto-obj-y += tlscredsanon.o
|
||||
crypto-obj-y += tlscredsx509.o
|
||||
crypto-obj-y += tlssession.o
|
||||
crypto-obj-y += secret.o
|
||||
|
||||
# Let the userspace emulators avoid linking gnutls/etc
|
||||
crypto-aes-obj-y = aes.o
|
||||
|
513
crypto/secret.c
Normal file
513
crypto/secret.c
Normal file
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* 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 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 "crypto/secret.h"
|
||||
#include "crypto/cipher.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/base64.h"
|
||||
#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, &data, &length, &gerr)) {
|
||||
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,
|
||||
const uint8_t *input,
|
||||
size_t inputlen,
|
||||
uint8_t **output,
|
||||
size_t *outputlen,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
|
||||
size_t keylen, ciphertextlen, ivlen;
|
||||
QCryptoCipher *aes = NULL;
|
||||
uint8_t *plaintext = NULL;
|
||||
|
||||
*output = NULL;
|
||||
*outputlen = 0;
|
||||
|
||||
if (qcrypto_secret_lookup(secret->keyid,
|
||||
&key, &keylen,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (keylen != 32) {
|
||||
error_setg(errp, "Key should be 32 bytes in length");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!secret->iv) {
|
||||
error_setg(errp, "IV is required to decrypt secret");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
|
||||
if (!iv) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (ivlen != 16) {
|
||||
error_setg(errp, "IV should be 16 bytes in length not %zu",
|
||||
ivlen);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
|
||||
QCRYPTO_CIPHER_MODE_CBC,
|
||||
key, keylen,
|
||||
errp);
|
||||
if (!aes) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
|
||||
ciphertext = qbase64_decode((const gchar*)input,
|
||||
inputlen,
|
||||
&ciphertextlen,
|
||||
errp);
|
||||
if (!ciphertext) {
|
||||
goto cleanup;
|
||||
}
|
||||
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) {
|
||||
plaintext = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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]);
|
||||
g_free(plaintext);
|
||||
plaintext = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Even though plaintext may contain arbitrary NUL
|
||||
* ensure it is explicitly NUL terminated.
|
||||
*/
|
||||
ciphertextlen -= plaintext[ciphertextlen - 1];
|
||||
plaintext[ciphertextlen] = '\0';
|
||||
|
||||
*output = plaintext;
|
||||
*outputlen = ciphertextlen;
|
||||
|
||||
cleanup:
|
||||
g_free(ciphertext);
|
||||
g_free(iv);
|
||||
g_free(key);
|
||||
qcrypto_cipher_free(aes);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
if (value) {
|
||||
Error *local_err = NULL;
|
||||
uint8_t *input = NULL;
|
||||
size_t inputlen = 0;
|
||||
uint8_t *output = NULL;
|
||||
size_t outputlen = 0;
|
||||
|
||||
qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
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_RAW) {
|
||||
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 {
|
||||
g_free(secret->rawdata);
|
||||
secret->rawlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_secret_prop_get_loaded(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return secret->data != NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_format(Object *obj,
|
||||
int value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *creds = QCRYPTO_SECRET(obj);
|
||||
|
||||
creds->format = value;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_secret_prop_get_format(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *creds = QCRYPTO_SECRET(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 **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_prop_set_iv(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->iv);
|
||||
secret->iv = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_iv(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->iv);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_keyid(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->keyid);
|
||||
secret->keyid = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_keyid(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->keyid);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), true, "loaded", errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_init(Object *obj)
|
||||
{
|
||||
object_property_add_bool(obj, "loaded",
|
||||
qcrypto_secret_prop_get_loaded,
|
||||
qcrypto_secret_prop_set_loaded,
|
||||
NULL);
|
||||
object_property_add_enum(obj, "format",
|
||||
"QCryptoSecretFormat",
|
||||
QCryptoSecretFormat_lookup,
|
||||
qcrypto_secret_prop_get_format,
|
||||
qcrypto_secret_prop_set_format,
|
||||
NULL);
|
||||
object_property_add_str(obj, "data",
|
||||
qcrypto_secret_prop_get_data,
|
||||
qcrypto_secret_prop_set_data,
|
||||
NULL);
|
||||
object_property_add_str(obj, "file",
|
||||
qcrypto_secret_prop_get_file,
|
||||
qcrypto_secret_prop_set_file,
|
||||
NULL);
|
||||
object_property_add_str(obj, "keyid",
|
||||
qcrypto_secret_prop_get_keyid,
|
||||
qcrypto_secret_prop_set_keyid,
|
||||
NULL);
|
||||
object_property_add_str(obj, "iv",
|
||||
qcrypto_secret_prop_get_iv,
|
||||
qcrypto_secret_prop_set_iv,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_finalize(Object *obj)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->iv);
|
||||
g_free(secret->file);
|
||||
g_free(secret->keyid);
|
||||
g_free(secret->rawdata);
|
||||
g_free(secret->data);
|
||||
}
|
||||
|
||||
static void
|
||||
qcrypto_secret_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = qcrypto_secret_complete;
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_secret_lookup(const char *secretid,
|
||||
uint8_t **data,
|
||||
size_t *datalen,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj;
|
||||
QCryptoSecret *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 = (QCryptoSecret *)
|
||||
object_dynamic_cast(obj,
|
||||
TYPE_QCRYPTO_SECRET);
|
||||
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, 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,
|
||||
.instance_size = sizeof(QCryptoSecret),
|
||||
.instance_init = qcrypto_secret_init,
|
||||
.instance_finalize = qcrypto_secret_finalize,
|
||||
.class_size = sizeof(QCryptoSecretClass),
|
||||
.class_init = qcrypto_secret_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_register_types(void)
|
||||
{
|
||||
type_register_static(&qcrypto_secret_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qcrypto_secret_register_types);
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "crypto/tlscredsx509.h"
|
||||
#include "crypto/tlscredspriv.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
@ -607,9 +608,30 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
||||
}
|
||||
|
||||
if (cert != NULL && key != NULL) {
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030111
|
||||
char *password = NULL;
|
||||
if (creds->passwordid) {
|
||||
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
|
||||
errp);
|
||||
if (!password) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
ret = gnutls_certificate_set_x509_key_file2(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM,
|
||||
password,
|
||||
0);
|
||||
g_free(password);
|
||||
#else /* GNUTLS_VERSION_NUMBER < 0x030111 */
|
||||
if (creds->passwordid) {
|
||||
error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
|
||||
goto cleanup;
|
||||
}
|
||||
ret = gnutls_certificate_set_x509_key_file(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
|
||||
cert, key, gnutls_strerror(ret));
|
||||
@ -737,6 +759,27 @@ qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj,
|
||||
const char *value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
creds->passwordid = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
return g_strdup(creds->passwordid);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
@ -769,6 +812,10 @@ qcrypto_tls_creds_x509_init(Object *obj)
|
||||
qcrypto_tls_creds_x509_prop_get_sanity,
|
||||
qcrypto_tls_creds_x509_prop_set_sanity,
|
||||
NULL);
|
||||
object_property_add_str(obj, "passwordid",
|
||||
qcrypto_tls_creds_x509_prop_get_passwordid,
|
||||
qcrypto_tls_creds_x509_prop_set_passwordid,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -777,6 +824,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
g_free(creds->passwordid);
|
||||
qcrypto_tls_creds_x509_unload(creds);
|
||||
}
|
||||
|
||||
|
148
include/crypto/secret.h
Normal file
148
include/crypto/secret.h
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_SECRET_H__
|
||||
#define QCRYPTO_SECRET_H__
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_QCRYPTO_SECRET "secret"
|
||||
#define QCRYPTO_SECRET(obj) \
|
||||
OBJECT_CHECK(QCryptoSecret, (obj), TYPE_QCRYPTO_SECRET)
|
||||
|
||||
typedef struct QCryptoSecret QCryptoSecret;
|
||||
typedef struct QCryptoSecretClass QCryptoSecretClass;
|
||||
|
||||
/**
|
||||
* QCryptoSecret:
|
||||
*
|
||||
* The QCryptoSecret object provides storage of secrets,
|
||||
* which may be user passwords, encryption keys or any
|
||||
* other kind of sensitive data that is represented as
|
||||
* a sequence of bytes.
|
||||
*
|
||||
* The sensitive data associated with the secret can
|
||||
* be provided directly via the 'data' property, or
|
||||
* indirectly via the 'file' property. In the latter
|
||||
* case there is support for file descriptor passing
|
||||
* via the usual /dev/fdset/NN syntax that QEMU uses.
|
||||
*
|
||||
* The data for a secret can be provided in two formats,
|
||||
* either as a UTF-8 string (the default), or as base64
|
||||
* encoded 8-bit binary data. The latter is appropriate
|
||||
* for raw encryption keys, while the former is appropriate
|
||||
* for user entered passwords.
|
||||
*
|
||||
* The data may be optionally encrypted with AES-256-CBC,
|
||||
* and the decryption key provided by another
|
||||
* QCryptoSecret instance identified by the 'keyid'
|
||||
* property. When passing sensitive data directly
|
||||
* via the 'data' property it is strongly recommended
|
||||
* to use the AES encryption facility to prevent the
|
||||
* sensitive data being exposed in the process listing
|
||||
* or system log files.
|
||||
*
|
||||
* Providing data directly, insecurely (suitable for
|
||||
* ad hoc developer testing only)
|
||||
*
|
||||
* $QEMU -object secret,id=sec0,data=letmein
|
||||
*
|
||||
* Providing data indirectly:
|
||||
*
|
||||
* # printf "letmein" > password.txt
|
||||
* # $QEMU \
|
||||
* -object secret,id=sec0,file=password.txt
|
||||
*
|
||||
* Using a master encryption key with data.
|
||||
*
|
||||
* The master key needs to be created as 32 secure
|
||||
* random bytes (optionally base64 encoded)
|
||||
*
|
||||
* # openssl rand -base64 32 > key.b64
|
||||
* # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"')
|
||||
*
|
||||
* Each secret to be encrypted needs to have a random
|
||||
* initialization vector generated. These do not need
|
||||
* to be kept secret
|
||||
*
|
||||
* # openssl rand -base64 16 > iv.b64
|
||||
* # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"')
|
||||
*
|
||||
* A secret to be defined can now be encrypted
|
||||
*
|
||||
* # SECRET=$(printf "letmein" |
|
||||
* openssl enc -aes-256-cbc -a -K $KEY -iv $IV)
|
||||
*
|
||||
* When launching QEMU, create a master secret pointing
|
||||
* to key.b64 and specify that to be used to decrypt
|
||||
* the user password
|
||||
*
|
||||
* # $QEMU \
|
||||
* -object secret,id=secmaster0,format=base64,file=key.b64 \
|
||||
* -object secret,id=sec0,keyid=secmaster0,format=base64,\
|
||||
* data=$SECRET,iv=$(<iv.b64)
|
||||
*
|
||||
* When encrypting, the data can still be provided via an
|
||||
* external file, in which case it is possible to use either
|
||||
* raw binary data, or base64 encoded. This example uses
|
||||
* raw format
|
||||
*
|
||||
* # printf "letmein" |
|
||||
* openssl enc -aes-256-cbc -K $KEY -iv $IV -o pw.aes
|
||||
* # $QEMU \
|
||||
* -object secret,id=secmaster0,format=base64,file=key.b64 \
|
||||
* -object secret,id=sec0,keyid=secmaster0,\
|
||||
* file=pw.aes,iv=$(<iv.b64)
|
||||
*
|
||||
* Note that the ciphertext can be in either raw or base64
|
||||
* format, as indicated by the 'format' parameter, but the
|
||||
* plaintext resulting from decryption is expected to always
|
||||
* be in raw format.
|
||||
*/
|
||||
|
||||
struct QCryptoSecret {
|
||||
Object parent_obj;
|
||||
uint8_t *rawdata;
|
||||
size_t rawlen;
|
||||
QCryptoSecretFormat format;
|
||||
char *data;
|
||||
char *file;
|
||||
char *keyid;
|
||||
char *iv;
|
||||
};
|
||||
|
||||
|
||||
struct QCryptoSecretClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
extern int qcrypto_secret_lookup(const char *secretid,
|
||||
uint8_t **data,
|
||||
size_t *datalen,
|
||||
Error **errp);
|
||||
extern char *qcrypto_secret_lookup_as_utf8(const char *secretid,
|
||||
Error **errp);
|
||||
extern char *qcrypto_secret_lookup_as_base64(const char *secretid,
|
||||
Error **errp);
|
||||
|
||||
#endif /* QCRYPTO_SECRET_H__ */
|
@ -101,6 +101,7 @@ struct QCryptoTLSCredsX509 {
|
||||
gnutls_certificate_credentials_t data;
|
||||
#endif
|
||||
bool sanityCheck;
|
||||
char *passwordid;
|
||||
};
|
||||
|
||||
|
||||
|
58
include/qemu/base64.h
Normal file
58
include/qemu/base64.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QEMU base64 helpers
|
||||
*
|
||||
* 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 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_BASE64_H__
|
||||
#define QEMU_BASE64_H__
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
|
||||
/**
|
||||
* qbase64_decode:
|
||||
* @input: the (possibly) base64 encoded text
|
||||
* @in_len: length of @input or -1 if NUL terminated
|
||||
* @out_len: filled with length of decoded data
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Attempt to decode the (possibly) base64 encoded
|
||||
* text provided in @input. If the @input text may
|
||||
* contain embedded NUL characters, or may not be
|
||||
* NUL terminated, then @in_len must be set to the
|
||||
* known size of the @input buffer.
|
||||
*
|
||||
* Note that embedded NULs, or lack of a NUL terminator
|
||||
* are considered invalid base64 data and errors
|
||||
* will be reported to this effect.
|
||||
*
|
||||
* If decoding is successful, the decoded data will
|
||||
* be returned and @out_len set to indicate the
|
||||
* number of bytes in the decoded data. The caller
|
||||
* must use g_free() to free the returned data when
|
||||
* it is no longer required.
|
||||
*
|
||||
* Returns: the decoded data or NULL
|
||||
*/
|
||||
uint8_t *qbase64_decode(const char *input,
|
||||
size_t in_len,
|
||||
size_t *out_len,
|
||||
Error **errp);
|
||||
|
||||
|
||||
#endif /* QEMU_BUFFER_H__ */
|
@ -295,8 +295,6 @@
|
||||
# @format: #optional data encoding (default 'utf8').
|
||||
# - base64: data must be base64 encoded text. Its binary
|
||||
# decoding gets written.
|
||||
# Bug: invalid base64 is currently not rejected.
|
||||
# Whitespace *is* invalid.
|
||||
# - utf8: data's UTF-8 encoding is written
|
||||
# - data itself is always Unicode regardless of format, like
|
||||
# any other string.
|
||||
|
@ -19,3 +19,17 @@
|
||||
{ 'enum': 'QCryptoTLSCredsEndpoint',
|
||||
'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT',
|
||||
'data': ['client', 'server']}
|
||||
|
||||
|
||||
##
|
||||
# QCryptoSecretFormat:
|
||||
#
|
||||
# The data format that the secret is provided in
|
||||
#
|
||||
# @raw: raw bytes. When encoded in JSON only valid UTF-8 sequences can be used
|
||||
# @base64: arbitrary base64 encoded binary data
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'enum': 'QCryptoSecretFormat',
|
||||
'prefix': 'QCRYPTO_SECRET_FORMAT',
|
||||
'data': ['raw', 'base64']}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "qapi/qmp-input-visitor.h"
|
||||
#include "qapi/qmp-output-visitor.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -3264,7 +3265,12 @@ void qmp_ringbuf_write(const char *device, const char *data,
|
||||
}
|
||||
|
||||
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
||||
write_data = g_base64_decode(data, &write_count);
|
||||
write_data = qbase64_decode(data, -1,
|
||||
&write_count,
|
||||
errp);
|
||||
if (!write_data) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
write_data = (uint8_t *)data;
|
||||
write_count = strlen(data);
|
||||
|
@ -3627,7 +3627,7 @@ expensive operation that consumes random pool entropy, so it is
|
||||
recommended that a persistent set of parameters be generated
|
||||
upfront and saved.
|
||||
|
||||
@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}
|
||||
@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off},passwordid=@var{id}
|
||||
|
||||
Creates a TLS anonymous credentials object, which can be used to provide
|
||||
TLS support on network backends. The @option{id} parameter is a unique
|
||||
@ -3654,6 +3654,12 @@ in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional),
|
||||
@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers),
|
||||
@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients).
|
||||
|
||||
For the @var{server-key.pem} and @var{client-key.pem} files which
|
||||
contain sensitive private keys, it is possible to use an encrypted
|
||||
version by providing the @var{passwordid} parameter. This provides
|
||||
the ID of a previously created @code{secret} object containing the
|
||||
password for decryption.
|
||||
|
||||
@item -object filter-buffer,id=@var{id},netdev=@var{netdevid},interval=@var{t}[,queue=@var{all|rx|tx}]
|
||||
|
||||
Interval @var{t} can't be 0, this filter batches the packet delivery: all
|
||||
@ -3678,6 +3684,83 @@ Dump the network traffic on netdev @var{dev} to the file specified by
|
||||
The file format is libpcap, so it can be analyzed with tools such as tcpdump
|
||||
or Wireshark.
|
||||
|
||||
@item -object secret,id=@var{id},data=@var{string},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}]
|
||||
@item -object secret,id=@var{id},file=@var{filename},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}]
|
||||
|
||||
Defines a secret to store a password, encryption key, or some other sensitive
|
||||
data. The sensitive data can either be passed directly via the @var{data}
|
||||
parameter, or indirectly via the @var{file} parameter. Using the @var{data}
|
||||
parameter is insecure unless the sensitive data is encrypted.
|
||||
|
||||
The sensitive data can be provided in raw format (the default), or base64.
|
||||
When encoded as JSON, the raw format only supports valid UTF-8 characters,
|
||||
so base64 is recommended for sending binary data. QEMU will convert from
|
||||
which ever format is provided to the format it needs internally. eg, an
|
||||
RBD password can be provided in raw format, even though it will be base64
|
||||
encoded when passed onto the RBD sever.
|
||||
|
||||
For added protection, it is possible to encrypt the data associated with
|
||||
a secret using the AES-256-CBC cipher. Use of encryption is indicated
|
||||
by providing the @var{keyid} and @var{iv} parameters. The @var{keyid}
|
||||
parameter provides the ID of a previously defined secret that contains
|
||||
the AES-256 decryption key. This key should be 32-bytes long and be
|
||||
base64 encoded. The @var{iv} parameter provides the random initialization
|
||||
vector used for encryption of this particular secret and should be a
|
||||
base64 encrypted string of the 32-byte IV.
|
||||
|
||||
The simplest (insecure) usage is to provide the secret inline
|
||||
|
||||
@example
|
||||
|
||||
# $QEMU -object secret,id=sec0,data=letmein,format=raw
|
||||
|
||||
@end example
|
||||
|
||||
The simplest secure usage is to provide the secret via a file
|
||||
|
||||
# echo -n "letmein" > mypasswd.txt
|
||||
# $QEMU -object secret,id=sec0,file=mypasswd.txt,format=raw
|
||||
|
||||
For greater security, AES-256-CBC should be used. To illustrate usage,
|
||||
consider the openssl command line tool which can encrypt the data. Note
|
||||
that when encrypting, the plaintext must be padded to the cipher block
|
||||
size (32 bytes) using the standard PKCS#5/6 compatible padding algorithm.
|
||||
|
||||
First a master key needs to be created in base64 encoding:
|
||||
|
||||
@example
|
||||
# openssl rand -base64 32 > key.b64
|
||||
# KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"')
|
||||
@end example
|
||||
|
||||
Each secret to be encrypted needs to have a random initialization vector
|
||||
generated. These do not need to be kept secret
|
||||
|
||||
@example
|
||||
# openssl rand -base64 16 > iv.b64
|
||||
# IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"')
|
||||
@end example
|
||||
|
||||
The secret to be defined can now be encrypted, in this case we're
|
||||
telling openssl to base64 encode the result, but it could be left
|
||||
as raw bytes if desired.
|
||||
|
||||
@example
|
||||
# SECRET=$(echo -n "letmein" |
|
||||
openssl enc -aes-256-cbc -a -K $KEY -iv $IV)
|
||||
@end example
|
||||
|
||||
When launching QEMU, create a master secret pointing to @code{key.b64}
|
||||
and specify that to be used to decrypt the user password. Pass the
|
||||
contents of @code{iv.b64} to the second secret
|
||||
|
||||
@example
|
||||
# $QEMU \
|
||||
-object secret,id=secmaster0,format=base64,file=key.b64 \
|
||||
-object secret,id=sec0,keyid=secmaster0,format=base64,\
|
||||
data=$SECRET,iv=$(<iv.b64)
|
||||
@end example
|
||||
|
||||
@end table
|
||||
|
||||
ETEXI
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
#ifndef CONFIG_HAS_ENVIRON
|
||||
#ifdef __APPLE__
|
||||
@ -525,7 +526,10 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
|
||||
gfh->state = RW_STATE_NEW;
|
||||
}
|
||||
|
||||
buf = g_base64_decode(buf_b64, &buf_len);
|
||||
buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!has_count) {
|
||||
count = buf_len;
|
||||
@ -1963,7 +1967,10 @@ void qmp_guest_set_user_password(const char *username,
|
||||
char *chpasswddata = NULL;
|
||||
size_t chpasswdlen;
|
||||
|
||||
rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen);
|
||||
rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
|
||||
if (!rawpasswddata) {
|
||||
return;
|
||||
}
|
||||
rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
|
||||
rawpasswddata[rawpasswdlen] = '\0';
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
#ifndef SHTDN_REASON_FLAG_PLANNED
|
||||
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
|
||||
@ -357,7 +358,10 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
|
||||
return NULL;
|
||||
}
|
||||
fh = gfh->fh;
|
||||
buf = g_base64_decode(buf_b64, &buf_len);
|
||||
buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!has_count) {
|
||||
count = buf_len;
|
||||
@ -1294,7 +1298,10 @@ void qmp_guest_set_user_password(const char *username,
|
||||
return;
|
||||
}
|
||||
|
||||
rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen);
|
||||
rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
|
||||
if (!rawpasswddata) {
|
||||
return;
|
||||
}
|
||||
rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
|
||||
rawpasswddata[rawpasswdlen] = '\0';
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "qga/guest-agent-core.h"
|
||||
#include "qga-qmp-commands.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
/* Maximum captured guest-exec out_data/err_data - 16MB */
|
||||
#define GUEST_EXEC_MAX_OUTPUT (16*1024*1024)
|
||||
@ -393,10 +394,19 @@ GuestExec *qmp_guest_exec(const char *path,
|
||||
GIOChannel *in_ch, *out_ch, *err_ch;
|
||||
GSpawnFlags flags;
|
||||
bool has_output = (has_capture_output && capture_output);
|
||||
uint8_t *input = NULL;
|
||||
size_t ninput = 0;
|
||||
|
||||
arglist.value = (char *)path;
|
||||
arglist.next = has_arg ? arg : NULL;
|
||||
|
||||
if (has_input_data) {
|
||||
input = qbase64_decode(input_data, -1, &ninput, err);
|
||||
if (!input) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
argv = guest_exec_get_args(&arglist, true);
|
||||
envp = has_env ? guest_exec_get_args(env, false) : NULL;
|
||||
|
||||
@ -425,7 +435,8 @@ GuestExec *qmp_guest_exec(const char *path,
|
||||
g_child_watch_add(pid, guest_exec_child_watch, gei);
|
||||
|
||||
if (has_input_data) {
|
||||
gei->in.data = g_base64_decode(input_data, &gei->in.size);
|
||||
gei->in.data = input;
|
||||
gei->in.size = ninput;
|
||||
#ifdef G_OS_WIN32
|
||||
in_ch = g_io_channel_win32_new_fd(in_fd);
|
||||
#else
|
||||
|
@ -512,8 +512,6 @@ Arguments:
|
||||
- "data": data to write (json-string)
|
||||
- "format": data format (json-string, optional)
|
||||
- Possible values: "utf8" (default), "base64"
|
||||
Bug: invalid base64 is currently not rejected.
|
||||
Whitespace *is* invalid.
|
||||
|
||||
Example:
|
||||
|
||||
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -8,11 +8,13 @@ check-qom-interface
|
||||
check-qom-proplist
|
||||
rcutorture
|
||||
test-aio
|
||||
test-base64
|
||||
test-bitops
|
||||
test-blockjob-txn
|
||||
test-coroutine
|
||||
test-crypto-cipher
|
||||
test-crypto-hash
|
||||
test-crypto-secret
|
||||
test-crypto-tlscredsx509
|
||||
test-crypto-tlscredsx509-work/
|
||||
test-crypto-tlscredsx509-certs/
|
||||
|
@ -80,6 +80,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF)
|
||||
gcov-files-test-write-threshold-y = block/write-threshold.c
|
||||
check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-cipher$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-secret$(EXESUF)
|
||||
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
|
||||
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
|
||||
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
|
||||
@ -90,6 +91,7 @@ check-unit-y += tests/test-io-channel-file$(EXESUF)
|
||||
check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF)
|
||||
check-unit-y += tests/test-io-channel-command$(EXESUF)
|
||||
check-unit-y += tests/test-io-channel-buffer$(EXESUF)
|
||||
check-unit-y += tests/test-base64$(EXESUF)
|
||||
|
||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||
|
||||
@ -423,6 +425,8 @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
||||
$(test-qom-obj-y)
|
||||
tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
|
||||
$(test-util-obj-y)
|
||||
tests/test-base64$(EXESUF): tests/test-base64.o \
|
||||
libqemuutil.a libqemustub.a
|
||||
|
||||
tests/test-qapi-types.c tests/test-qapi-types.h :\
|
||||
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
@ -464,6 +468,7 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
|
||||
tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
|
||||
tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y)
|
||||
|
||||
tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS)
|
||||
tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS)
|
||||
|
109
tests/test-base64.c
Normal file
109
tests/test-base64.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* QEMU base64 helper test
|
||||
*
|
||||
* 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 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 <glib.h>
|
||||
|
||||
#include "qemu/base64.h"
|
||||
|
||||
|
||||
static void test_base64_good(void)
|
||||
{
|
||||
const char input[] =
|
||||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n"
|
||||
"lzc2VkIHRoZSBzY29ycGlvbi4=";
|
||||
const char expect[] = "Because we focused on the snake, "
|
||||
"we missed the scorpion.";
|
||||
|
||||
size_t len;
|
||||
uint8_t *actual = qbase64_decode(input,
|
||||
-1,
|
||||
&len,
|
||||
&error_abort);
|
||||
|
||||
g_assert(actual != NULL);
|
||||
g_assert_cmpint(len, ==, strlen(expect));
|
||||
g_assert_cmpstr((char *)actual, ==, expect);
|
||||
g_free(actual);
|
||||
}
|
||||
|
||||
|
||||
static void test_base64_bad(const char *input,
|
||||
size_t input_len)
|
||||
{
|
||||
size_t len;
|
||||
Error *err = NULL;
|
||||
uint8_t *actual = qbase64_decode(input,
|
||||
input_len,
|
||||
&len,
|
||||
&err);
|
||||
|
||||
g_assert(err != NULL);
|
||||
g_assert(actual == NULL);
|
||||
g_assert_cmpint(len, ==, 0);
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
|
||||
static void test_base64_embedded_nul(void)
|
||||
{
|
||||
/* We put a NUL character in the middle of the base64
|
||||
* text which is invalid data, given the expected length */
|
||||
const char input[] =
|
||||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\0"
|
||||
"lzc2VkIHRoZSBzY29ycGlvbi4=";
|
||||
|
||||
test_base64_bad(input, G_N_ELEMENTS(input) - 1);
|
||||
}
|
||||
|
||||
|
||||
static void test_base64_not_nul_terminated(void)
|
||||
{
|
||||
const char input[] =
|
||||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n"
|
||||
"lzc2VkIHRoZSBzY29ycGlvbi4=";
|
||||
|
||||
/* Using '-2' to make us drop the trailing NUL, thus
|
||||
* creating an invalid base64 sequence for decoding */
|
||||
test_base64_bad(input, G_N_ELEMENTS(input) - 2);
|
||||
}
|
||||
|
||||
|
||||
static void test_base64_invalid_chars(void)
|
||||
{
|
||||
/* We put a single quote character in the middle
|
||||
* of the base64 text which is invalid data */
|
||||
const char input[] =
|
||||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW'"
|
||||
"lzc2VkIHRoZSBzY29ycGlvbi4=";
|
||||
|
||||
test_base64_bad(input, strlen(input));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/util/base64/good", test_base64_good);
|
||||
g_test_add_func("/util/base64/embedded-nul", test_base64_embedded_nul);
|
||||
g_test_add_func("/util/base64/not-nul-terminated",
|
||||
test_base64_not_nul_terminated);
|
||||
g_test_add_func("/util/base64/invalid-chars", test_base64_invalid_chars);
|
||||
return g_test_run();
|
||||
}
|
452
tests/test-crypto-secret.c
Normal file
452
tests/test-crypto-secret.c
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* QEMU Crypto secret handling
|
||||
*
|
||||
* 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 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 <glib.h>
|
||||
|
||||
#include "crypto/init.h"
|
||||
#include "crypto/secret.h"
|
||||
|
||||
static void test_secret_direct(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "123456",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_indirect_good(void)
|
||||
{
|
||||
Object *sec;
|
||||
char *fname = NULL;
|
||||
int fd = g_file_open_tmp("secretXXXXXX",
|
||||
&fname,
|
||||
NULL);
|
||||
|
||||
g_assert(fd >= 0);
|
||||
g_assert_nonnull(fname);
|
||||
|
||||
g_assert(write(fd, "123456", 6) == 6);
|
||||
|
||||
sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"file", fname,
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
close(fd);
|
||||
g_free(fname);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_indirect_badfile(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"file", "does-not-exist",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_indirect_emptyfile(void)
|
||||
{
|
||||
Object *sec;
|
||||
char *fname = NULL;
|
||||
int fd = g_file_open_tmp("secretXXXXXX",
|
||||
&fname,
|
||||
NULL);
|
||||
|
||||
g_assert(fd >= 0);
|
||||
g_assert_nonnull(fname);
|
||||
|
||||
sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"file", fname,
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
close(fd);
|
||||
g_free(fname);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_noconv_base64_good(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "MTIzNDU2",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_base64("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "MTIzNDU2");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_noconv_base64_bad(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"data", "MTI$NDU2",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_noconv_utf8(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "123456",
|
||||
"format", "raw",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_conv_base64_utf8valid(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "MTIzNDU2",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_conv_base64_utf8invalid(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "f0VMRgIBAQAAAA==",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
NULL);
|
||||
g_assert(pw == NULL);
|
||||
|
||||
object_unparent(sec);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_conv_utf8_base64(void)
|
||||
{
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "123456",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_base64("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "MTIzNDU2");
|
||||
|
||||
object_unparent(sec);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_raw(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data",
|
||||
"\xCC\xBF\xF7\x09\x46\x19\x0B\x52\x2A\x3A\xB4\x6B\xCD\x7A\xB0\xB0",
|
||||
"format", "raw",
|
||||
"keyid", "master",
|
||||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
object_unparent(master);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_base64(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "zL/3CUYZC1IqOrRrzXqwsA==",
|
||||
"format", "base64",
|
||||
"keyid", "master",
|
||||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==",
|
||||
NULL);
|
||||
|
||||
char *pw = qcrypto_secret_lookup_as_utf8("sec0",
|
||||
&error_abort);
|
||||
|
||||
g_assert_cmpstr(pw, ==, "123456");
|
||||
|
||||
object_unparent(sec);
|
||||
object_unparent(master);
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_short_key(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVc",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"data", "zL/3CUYZC1IqOrRrzXqwsA==",
|
||||
"format", "raw",
|
||||
"keyid", "master",
|
||||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
object_unparent(master);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_short_iv(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"data", "zL/3CUYZC1IqOrRrzXqwsA==",
|
||||
"format", "raw",
|
||||
"keyid", "master",
|
||||
"iv", "0I7Gw/TKuA+Old2W2a",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
object_unparent(master);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_missing_iv(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"data", "zL/3CUYZC1IqOrRrzXqwsA==",
|
||||
"format", "raw",
|
||||
"keyid", "master",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
object_unparent(master);
|
||||
}
|
||||
|
||||
|
||||
static void test_secret_crypt_bad_iv(void)
|
||||
{
|
||||
Object *master = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"master",
|
||||
&error_abort,
|
||||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=",
|
||||
"format", "base64",
|
||||
NULL);
|
||||
Object *sec = object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
NULL,
|
||||
"data", "zL/3CUYZC1IqOrRrzXqwsA==",
|
||||
"format", "raw",
|
||||
"keyid", "master",
|
||||
"iv", "0I7Gw/TK$$uA+Old2W2a",
|
||||
NULL);
|
||||
|
||||
g_assert(sec == NULL);
|
||||
object_unparent(master);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_assert(qcrypto_init(NULL) == 0);
|
||||
|
||||
g_test_add_func("/crypto/secret/direct",
|
||||
test_secret_direct);
|
||||
g_test_add_func("/crypto/secret/indirect/good",
|
||||
test_secret_indirect_good);
|
||||
g_test_add_func("/crypto/secret/indirect/badfile",
|
||||
test_secret_indirect_badfile);
|
||||
g_test_add_func("/crypto/secret/indirect/emptyfile",
|
||||
test_secret_indirect_emptyfile);
|
||||
|
||||
g_test_add_func("/crypto/secret/noconv/base64/good",
|
||||
test_secret_noconv_base64_good);
|
||||
g_test_add_func("/crypto/secret/noconv/base64/bad",
|
||||
test_secret_noconv_base64_bad);
|
||||
g_test_add_func("/crypto/secret/noconv/utf8",
|
||||
test_secret_noconv_utf8);
|
||||
g_test_add_func("/crypto/secret/conv/base64/utf8valid",
|
||||
test_secret_conv_base64_utf8valid);
|
||||
g_test_add_func("/crypto/secret/conv/base64/utf8invalid",
|
||||
test_secret_conv_base64_utf8invalid);
|
||||
g_test_add_func("/crypto/secret/conv/utf8/base64",
|
||||
test_secret_conv_utf8_base64);
|
||||
|
||||
g_test_add_func("/crypto/secret/crypt/raw",
|
||||
test_secret_crypt_raw);
|
||||
g_test_add_func("/crypto/secret/crypt/base64",
|
||||
test_secret_crypt_base64);
|
||||
g_test_add_func("/crypto/secret/crypt/shortkey",
|
||||
test_secret_crypt_short_key);
|
||||
g_test_add_func("/crypto/secret/crypt/shortiv",
|
||||
test_secret_crypt_short_iv);
|
||||
g_test_add_func("/crypto/secret/crypt/missingiv",
|
||||
test_secret_crypt_missing_iv);
|
||||
g_test_add_func("/crypto/secret/crypt/badiv",
|
||||
test_secret_crypt_bad_iv);
|
||||
|
||||
return g_test_run();
|
||||
}
|
@ -30,3 +30,4 @@ util-obj-y += qemu-coroutine-sleep.o
|
||||
util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
|
||||
util-obj-y += buffer.o
|
||||
util-obj-y += timed-average.o
|
||||
util-obj-y += base64.o
|
||||
|
60
util/base64.c
Normal file
60
util/base64.c
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* QEMU base64 helpers
|
||||
*
|
||||
* 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 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 <config-host.h>
|
||||
|
||||
#include "qemu/base64.h"
|
||||
|
||||
static const char *base64_valid_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
|
||||
|
||||
uint8_t *qbase64_decode(const char *input,
|
||||
size_t in_len,
|
||||
size_t *out_len,
|
||||
Error **errp)
|
||||
{
|
||||
*out_len = 0;
|
||||
|
||||
if (in_len != -1) {
|
||||
/* Lack of NUL terminator is an error */
|
||||
if (input[in_len] != '\0') {
|
||||
error_setg(errp, "Base64 data is not NUL terminated");
|
||||
return NULL;
|
||||
}
|
||||
/* Check there's no NULs embedded since we expect
|
||||
* this to be valid base64 data */
|
||||
if (memchr(input, '\0', in_len) != NULL) {
|
||||
error_setg(errp, "Base64 data contains embedded NUL characters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now we know its a valid nul terminated string
|
||||
* strspn is safe to use... */
|
||||
} else {
|
||||
in_len = strlen(input);
|
||||
}
|
||||
|
||||
if (strspn(input, base64_valid_chars) != in_len) {
|
||||
error_setg(errp, "Base64 data contains invalid characters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_base64_decode(input, out_len);
|
||||
}
|
Loading…
Reference in New Issue
Block a user