Commit aecdfcc3 authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>
parents aff2caf6 69699f30
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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);
+5 −0
Original line number Diff line number Diff line
@@ -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"
+12 −1
Original line number Diff line number Diff line
@@ -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.
+35 −0
Original line number Diff line number Diff line
@@ -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)
Loading