firmware (and crypto) patches

- add the tls-cipher-suites object,
 - add the ability to QOM objects to produce data consumable
   by the fw_cfg device,
 - let the tls-cipher-suites object implement the
   FW_CFG_DATA_GENERATOR interface.
 
 This is required by EDK2 'HTTPS Boot' feature of OVMF to tell
 the guest which TLS ciphers it can use.
 
 CI jobs results:
   https://travis-ci.org/github/philmd/qemu/builds/704724619
   https://gitlab.com/philmd/qemu/-/pipelines/162938106
   https://cirrus-ci.com/build/4682977303068672
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl8AsDQACgkQ4+MsLN6t
 wN4HLA//Sxi7iYWp3OygX1F7A9XpWxuWNVjThBJzao00y0qxWQ7G6l/YTDeNYOoD
 dE/HsyUd3QtvC6/c79zoqyOb5m+WtQNsQmHAO4LZdR16TPNvTdCZ2lNgWYRIjuHJ
 obvYy2ingKpnlzd5V1bioTlUw0AOchk86pMnTVAsoKPXWqsOrPB+PYmE9tFRxqVY
 5WVW7rnhtcJ05ajeIWhgndhW7fM4lvoWdZyctTIhWttQ9WkhyKAYKHQrmgOcsbUp
 soOfK9oHRH1XuUsC0SHpPmUQbp/c5cyCS1D7nDuIe/lPNNfWCVeUKkny495Vgj4d
 nK6MI1PQ4Bw2VBfMMxO4dXl2rnzlVjSnXXUP56IdfZHlOr+5gPh7uZvfZXN3AmHC
 km9MBt2IWrDYAriMwHhqumzHMIFmNVdqdXaYZYzagwwFzy8VNLgzQEFQYHsIXtl3
 7pfvZVtIbSfXFGfktY3vu4sBZNAAk3mAOc04faF3CuTvXp5m0Duc0boO38eLDCbp
 5mV2uEUJ9zQzcSoLqfLGiYCBFX9C8XdjdX+u9gMHhNb04y5c6r/zZZKpH7ZfwEc1
 WdwXzEor+h3yW06dV1WdOGlMUqVt3Xx9GaJBW57XbdMQQ2O2m+TfMcYLeVLsXtvf
 aHUKOZdBxRKFcbA7zARhZ4xyzNQVddz3+aTGpzpP/xIL4TXrzHQ=
 =QArU
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd-gitlab/tags/fw_cfg-20200704' into staging

firmware (and crypto) patches

- add the tls-cipher-suites object,
- add the ability to QOM objects to produce data consumable
  by the fw_cfg device,
- let the tls-cipher-suites object implement the
  FW_CFG_DATA_GENERATOR interface.

This is required by EDK2 'HTTPS Boot' feature of OVMF to tell
the guest which TLS ciphers it can use.

CI jobs results:
  https://travis-ci.org/github/philmd/qemu/builds/704724619
  https://gitlab.com/philmd/qemu/-/pipelines/162938106
  https://cirrus-ci.com/build/4682977303068672

# gpg: Signature made Sat 04 Jul 2020 17:37:08 BST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/fw_cfg-20200704:
  crypto/tls-cipher-suites: Produce fw_cfg consumable blob
  softmmu/vl: Allow -fw_cfg 'gen_id' option to use the 'etc/' namespace
  softmmu/vl: Let -fw_cfg option take a 'gen_id' argument
  hw/nvram/fw_cfg: Add the FW_CFG_DATA_GENERATOR interface
  crypto: Add tls-cipher-suites object

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-07-09 20:01:43 +01:00
commit aecdfcc3f8
9 changed files with 326 additions and 10 deletions

View file

@ -13,6 +13,7 @@ crypto-obj-y += cipher.o
crypto-obj-$(CONFIG_AF_ALG) += afalg.o
crypto-obj-$(CONFIG_AF_ALG) += cipher-afalg.o
crypto-obj-$(CONFIG_AF_ALG) += hash-afalg.o
crypto-obj-$(CONFIG_GNUTLS) += tls-cipher-suites.o
crypto-obj-y += tlscreds.o
crypto-obj-y += tlscredsanon.o
crypto-obj-y += tlscredspsk.o

126
crypto/tls-cipher-suites.c Normal file
View file

@ -0,0 +1,126 @@
/*
* QEMU TLS Cipher Suites
*
* Copyright (c) 2018-2020 Red Hat, Inc.
*
* Author: Philippe Mathieu-Daudé <philmd@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qom/object_interfaces.h"
#include "crypto/tlscreds.h"
#include "crypto/tls-cipher-suites.h"
#include "hw/nvram/fw_cfg.h"
#include "trace.h"
/*
* IANA registered TLS ciphers:
* https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
*/
typedef struct {
uint8_t data[2];
} QEMU_PACKED IANA_TLS_CIPHER;
GByteArray *qcrypto_tls_cipher_suites_get_data(QCryptoTLSCipherSuites *obj,
Error **errp)
{
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
gnutls_priority_t pcache;
GByteArray *byte_array;
const char *err;
size_t i;
int ret;
trace_qcrypto_tls_cipher_suite_priority(creds->priority);
ret = gnutls_priority_init(&pcache, creds->priority, &err);
if (ret < 0) {
error_setg(errp, "Syntax error using priority '%s': %s",
creds->priority, gnutls_strerror(ret));
return NULL;
}
byte_array = g_byte_array_new();
for (i = 0;; i++) {
int ret;
unsigned idx;
const char *name;
IANA_TLS_CIPHER cipher;
gnutls_protocol_t protocol;
const char *version;
ret = gnutls_priority_get_cipher_suite_index(pcache, i, &idx);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
break;
}
if (ret == GNUTLS_E_UNKNOWN_CIPHER_SUITE) {
continue;
}
name = gnutls_cipher_suite_info(idx, (unsigned char *)&cipher,
NULL, NULL, NULL, &protocol);
if (name == NULL) {
continue;
}
version = gnutls_protocol_get_name(protocol);
g_byte_array_append(byte_array, cipher.data, 2);
trace_qcrypto_tls_cipher_suite_info(cipher.data[0],
cipher.data[1],
version, name);
}
trace_qcrypto_tls_cipher_suite_count(byte_array->len);
gnutls_priority_deinit(pcache);
return byte_array;
}
static void qcrypto_tls_cipher_suites_complete(UserCreatable *uc,
Error **errp)
{
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(uc);
if (!creds->priority) {
error_setg(errp, "'priority' property is not set");
return;
}
}
static GByteArray *qcrypto_tls_cipher_suites_fw_cfg_gen_data(Object *obj,
Error **errp)
{
return qcrypto_tls_cipher_suites_get_data(QCRYPTO_TLS_CIPHER_SUITES(obj),
errp);
}
static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(oc);
ucc->complete = qcrypto_tls_cipher_suites_complete;
fwgc->get_data = qcrypto_tls_cipher_suites_fw_cfg_gen_data;
}
static const TypeInfo qcrypto_tls_cipher_suites_info = {
.parent = TYPE_QCRYPTO_TLS_CREDS,
.name = TYPE_QCRYPTO_TLS_CIPHER_SUITES,
.instance_size = sizeof(QCryptoTLSCreds),
.class_size = sizeof(QCryptoTLSCredsClass),
.class_init = qcrypto_tls_cipher_suites_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ TYPE_FW_CFG_DATA_GENERATOR_INTERFACE },
{ }
}
};
static void qcrypto_tls_cipher_suites_register_types(void)
{
type_register_static(&qcrypto_tls_cipher_suites_info);
}
type_init(qcrypto_tls_cipher_suites_register_types);

View file

@ -21,3 +21,8 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds
# tlssession.c
qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
# tls-cipher-suites.c
qcrypto_tls_cipher_suite_priority(const char *name) "priority: %s"
qcrypto_tls_cipher_suite_info(uint8_t data0, uint8_t data1, const char *version, const char *name) "data=[0x%02x,0x%02x] version=%s name=%s"
qcrypto_tls_cipher_suite_count(unsigned count) "count: %u"

View file

@ -219,7 +219,7 @@ To check the result, read the "control" field:
= Externally Provided Items =
As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
Since v2.4, "file" fw_cfg items (i.e., items with selector keys above
FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
directory structure) may be inserted via the QEMU command line, using
the following syntax:
@ -230,6 +230,13 @@ Or
-fw_cfg [name=]<item_name>,string=<string>
Since v5.1, QEMU allows some objects to generate fw_cfg-specific content,
the content is then associated with a "file" item using the 'gen_id' option
in the command line, using the following syntax:
-object <generator-type>,id=<generated_id>,[generator-specific-options] \
-fw_cfg [name=]<item_name>,gen_id=<generated_id>
See QEMU man page for more documentation.
Using item_name with plain ASCII characters only is recommended.
@ -251,4 +258,8 @@ Prefix "opt/org.qemu/" is reserved for QEMU itself.
Use of names not beginning with "opt/" is potentially dangerous and
entirely unsupported. QEMU will warn if you try.
Use of names not beginning with "opt/" is tolerated with 'gen_id' (that
is, the warning is suppressed), but you must know exactly what you're
doing.
All externally provided fw_cfg items are read-only to the guest.

View file

@ -1032,6 +1032,35 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename,
return NULL;
}
void fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
const char *gen_id, Error **errp)
{
FWCfgDataGeneratorClass *klass;
Error *local_err = NULL;
GByteArray *array;
Object *obj;
gsize size;
obj = object_resolve_path_component(object_get_objects_root(), gen_id);
if (!obj) {
error_setg(errp, "Cannot find object ID '%s'", gen_id);
return;
}
if (!object_dynamic_cast(obj, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)) {
error_setg(errp, "Object ID '%s' is not a '%s' subclass",
gen_id, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE);
return;
}
klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj);
array = klass->get_data(obj, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
size = array->len;
fw_cfg_add_file(s, filename, g_byte_array_free(array, TRUE), size);
}
static void fw_cfg_machine_reset(void *opaque)
{
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
@ -1333,12 +1362,18 @@ static const TypeInfo fw_cfg_mem_info = {
.class_init = fw_cfg_mem_class_init,
};
static const TypeInfo fw_cfg_data_generator_interface_info = {
.parent = TYPE_INTERFACE,
.name = TYPE_FW_CFG_DATA_GENERATOR_INTERFACE,
.class_size = sizeof(FWCfgDataGeneratorClass),
};
static void fw_cfg_register_types(void)
{
type_register_static(&fw_cfg_info);
type_register_static(&fw_cfg_io_info);
type_register_static(&fw_cfg_mem_info);
type_register_static(&fw_cfg_data_generator_interface_info);
}
type_init(fw_cfg_register_types)

View file

@ -0,0 +1,39 @@
/*
* QEMU TLS Cipher Suites Registry (RFC8447)
*
* Copyright (c) 2018-2020 Red Hat, Inc.
*
* Author: Philippe Mathieu-Daudé <philmd@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef QCRYPTO_TLSCIPHERSUITES_H
#define QCRYPTO_TLSCIPHERSUITES_H
#include "qom/object.h"
#include "crypto/tlscreds.h"
#define TYPE_QCRYPTO_TLS_CIPHER_SUITES "tls-cipher-suites"
#define QCRYPTO_TLS_CIPHER_SUITES(obj) \
OBJECT_CHECK(QCryptoTLSCipherSuites, (obj), TYPE_QCRYPTO_TLS_CIPHER_SUITES)
typedef struct QCryptoTLSCipherSuites {
/* <private> */
QCryptoTLSCreds parent_obj;
/* <public> */
} QCryptoTLSCipherSuites;
/**
* qcrypto_tls_cipher_suites_get_data:
* @obj: pointer to a TLS cipher suites object
* @errp: pointer to a NULL-initialized error object
*
* Returns: reference to a byte array containing the data.
* The caller should release the reference when no longer
* required.
*/
GByteArray *qcrypto_tls_cipher_suites_get_data(QCryptoTLSCipherSuites *obj,
Error **errp);
#endif /* QCRYPTO_TLSCIPHERSUITES_H */

View file

@ -9,11 +9,36 @@
#define TYPE_FW_CFG "fw_cfg"
#define TYPE_FW_CFG_IO "fw_cfg_io"
#define TYPE_FW_CFG_MEM "fw_cfg_mem"
#define TYPE_FW_CFG_DATA_GENERATOR_INTERFACE "fw_cfg-data-generator"
#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG)
#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO)
#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM)
#define FW_CFG_DATA_GENERATOR_CLASS(class) \
OBJECT_CLASS_CHECK(FWCfgDataGeneratorClass, (class), \
TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)
#define FW_CFG_DATA_GENERATOR_GET_CLASS(obj) \
OBJECT_GET_CLASS(FWCfgDataGeneratorClass, (obj), \
TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)
typedef struct FWCfgDataGeneratorClass {
/*< private >*/
InterfaceClass parent_class;
/*< public >*/
/**
* get_data:
* @obj: the object implementing this interface
* @errp: pointer to a NULL-initialized error object
*
* Returns: reference to a byte array containing the data.
* The caller should release the reference when no longer
* required.
*/
GByteArray *(*get_data)(Object *obj, Error **errp);
} FWCfgDataGeneratorClass;
typedef struct fw_cfg_file FWCfgFile;
#define FW_CFG_ORDER_OVERRIDE_VGA 70
@ -263,6 +288,24 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data,
size_t len);
/**
* fw_cfg_add_from_generator:
* @s: fw_cfg device being modified
* @filename: name of new fw_cfg file item
* @gen_id: name of object implementing FW_CFG_DATA_GENERATOR interface
* @errp: pointer to a NULL initialized error object
*
* Add a new NAMED fw_cfg item with the content generated from the
* @gen_id object. The data generated by the @gen_id object is copied
* into the data structure of the fw_cfg device.
* The next available (unused) selector key starting at FW_CFG_FILE_FIRST
* will be used; also, a new entry will be added to the file directory
* structure residing at key value FW_CFG_FILE_DIR, containing the item name,
* data size, and assigned selector key value.
*/
void fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
const char *gen_id, Error **errp);
FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
AddressSpace *dma_as);
FWCfgState *fw_cfg_init_io(uint32_t iobase);

View file

@ -4579,6 +4579,43 @@ SRST
string as described at
https://gnutls.org/manual/html_node/Priority-Strings.html.
``-object tls-cipher-suites,id=id,priority=priority``
Creates a TLS cipher suites object, which can be used to control
the TLS cipher/protocol algorithms that applications are permitted
to use.
The ``id`` parameter is a unique ID which frontends will use to
access the ordered list of permitted TLS cipher suites from the
host.
The ``priority`` parameter allows to override the global default
priority used by gnutls. This can be useful if the system
administrator needs to use a weaker set of crypto priorities for
QEMU without potentially forcing the weakness onto all
applications. Or conversely if one wants wants a stronger
default for QEMU than for all other applications, they can do
this through this parameter. Its format is a gnutls priority
string as described at
https://gnutls.org/manual/html_node/Priority-Strings.html.
An example of use of this object is to control UEFI HTTPS Boot.
The tls-cipher-suites object exposes the ordered list of permitted
TLS cipher suites from the host side to the guest firmware, via
fw_cfg. The list is represented as an array of IANA_TLS_CIPHER
objects. The firmware uses the IANA_TLS_CIPHER array for configuring
guest-side TLS.
In the following example, the priority at which the host-side policy
is retrieved is given by the ``priority`` property.
Given that QEMU uses GNUTLS, ``priority=@SYSTEM`` may be used to
refer to /etc/crypto-policies/back-ends/gnutls.config.
.. parsed-literal::
# |qemu_system| \
-object tls-cipher-suites,id=mysuite0,priority=@SYSTEM \
-fw_cfg name=etc/edk2/https/ciphers,gen_id=mysuite0
``-object filter-buffer,id=id,netdev=netdevid,interval=t[,queue=all|rx|tx][,status=on|off][,position=head|tail|id=<id>][,insert=behind|before]``
Interval t can't be 0, this filter batches the packet delivery:
all packets arriving in a given interval on netdev netdevid are

View file

@ -489,6 +489,11 @@ static QemuOptsList qemu_fw_cfg_opts = {
.name = "string",
.type = QEMU_OPT_STRING,
.help = "Sets content of the blob to be inserted from a string",
}, {
.name = "gen_id",
.type = QEMU_OPT_STRING,
.help = "Sets id of the object generating the fw_cfg blob "
"to be inserted",
},
{ /* end of list */ }
},
@ -2020,7 +2025,7 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
{
gchar *buf;
size_t size;
const char *name, *file, *str;
const char *name, *file, *str, *gen_id;
FWCfgState *fw_cfg = (FWCfgState *) opaque;
if (fw_cfg == NULL) {
@ -2030,14 +2035,13 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
name = qemu_opt_get(opts, "name");
file = qemu_opt_get(opts, "file");
str = qemu_opt_get(opts, "string");
gen_id = qemu_opt_get(opts, "gen_id");
/* we need name and either a file or the content string */
if (!(nonempty_str(name) && (nonempty_str(file) || nonempty_str(str)))) {
error_setg(errp, "invalid argument(s)");
return -1;
}
if (nonempty_str(file) && nonempty_str(str)) {
error_setg(errp, "file and string are mutually exclusive");
/* we need the name, and exactly one of: file, content string, gen_id */
if (!nonempty_str(name) ||
nonempty_str(file) + nonempty_str(str) + nonempty_str(gen_id) != 1) {
error_setg(errp, "name, plus exactly one of file,"
" string and gen_id, are needed");
return -1;
}
if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) {
@ -2045,13 +2049,28 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
FW_CFG_MAX_FILE_PATH - 1);
return -1;
}
if (strncmp(name, "opt/", 4) != 0) {
if (nonempty_str(gen_id)) {
/*
* In this particular case where the content is populated
* internally, the "etc/" namespace protection is relaxed,
* so do not emit a warning.
*/
} else if (strncmp(name, "opt/", 4) != 0) {
warn_report("externally provided fw_cfg item names "
"should be prefixed with \"opt/\"");
}
if (nonempty_str(str)) {
size = strlen(str); /* NUL terminator NOT included in fw_cfg blob */
buf = g_memdup(str, size);
} else if (nonempty_str(gen_id)) {
Error *local_err = NULL;
fw_cfg_add_from_generator(fw_cfg, name, gen_id, errp);
if (local_err) {
error_propagate(errp, local_err);
return -1;
}
return 0;
} else {
GError *err = NULL;
if (!g_file_get_contents(file, &buf, &size, &err)) {