diff mbox

[1/1] virtio-rng: device to send host entropy to guest

Message ID aa4c6913ca2bc37e049f04f498c6414a4ec544bd.1337167727.git.amit.shah@redhat.com
State New
Headers show

Commit Message

Amit Shah May 16, 2012, 11:30 a.m. UTC
The Linux kernel already has a virtio-rng driver, this is the device
implementation.

When Linux needs more entropy, it puts a buffer in the vq.  We then put
entropy into that buffer, and push it back to the guest.

Feeding randomness from host's /dev/urandom into the guest is
sufficient, so this is a simple implementation that opens /dev/urandom
and reads from it whenever required.

Invocation is simple:

  qemu ... -device virtio-rng-pci

In the guest, we see

  $ cat /sys/devices/virtual/misc/hw_random/rng_available
  virtio

  $ cat /sys/devices/virtual/misc/hw_random/rng_current
  virtio

There are ways to extend the device to be more generic and collect
entropy from other sources, but this is simple enough and works for now.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 Makefile.objs   |    1 +
 hw/pci.h        |    1 +
 hw/virtio-pci.c |   50 +++++++++++++++++++++
 hw/virtio-rng.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/virtio-rng.h |   18 ++++++++
 hw/virtio.h     |    2 +
 6 files changed, 202 insertions(+), 0 deletions(-)
 create mode 100644 hw/virtio-rng.c
 create mode 100644 hw/virtio-rng.h

Comments

Paolo Bonzini May 16, 2012, 11:38 a.m. UTC | #1
Il 16/05/2012 13:30, Amit Shah ha scritto:
>  hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
> +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o

This needs to be conditional on CONFIG_LINUX too.

Paolo
Amit Shah May 16, 2012, 11:54 a.m. UTC | #2
On (Wed) 16 May 2012 [13:38:06], Paolo Bonzini wrote:
> Il 16/05/2012 13:30, Amit Shah ha scritto:
> >  hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
> > +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o
> 
> This needs to be conditional on CONFIG_LINUX too.

Right.

Thanks,

		Amit
Anthony Liguori May 16, 2012, 1:24 p.m. UTC | #3
On 05/16/2012 06:30 AM, Amit Shah wrote:
> The Linux kernel already has a virtio-rng driver, this is the device
> implementation.
>
> When Linux needs more entropy, it puts a buffer in the vq.  We then put
> entropy into that buffer, and push it back to the guest.
>
> Feeding randomness from host's /dev/urandom into the guest is
> sufficient, so this is a simple implementation that opens /dev/urandom
> and reads from it whenever required.
>
> Invocation is simple:
>
>    qemu ... -device virtio-rng-pci
>
> In the guest, we see
>
>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>    virtio
>
>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>    virtio
>
> There are ways to extend the device to be more generic and collect
> entropy from other sources, but this is simple enough and works for now.
>
> Signed-off-by: Amit Shah<amit.shah@redhat.com>

It's not this simple unfortunately.

If you did this with libvirt, one guest could exhaust the available entropy for 
the remaining guests.  This could be used as a mechanism for one guest to attack 
another (reducing the available entropy for key generation).

You need to rate limit the amount of entropy that a guest can obtain to allow 
management tools to mitigate this attack.

Regards,

Anthony Liguori

> ---
>   Makefile.objs   |    1 +
>   hw/pci.h        |    1 +
>   hw/virtio-pci.c |   50 +++++++++++++++++++++
>   hw/virtio-rng.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   hw/virtio-rng.h |   18 ++++++++
>   hw/virtio.h     |    2 +
>   6 files changed, 202 insertions(+), 0 deletions(-)
>   create mode 100644 hw/virtio-rng.c
>   create mode 100644 hw/virtio-rng.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 70c5c79..5850762 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -210,6 +210,7 @@ user-obj-y += $(qom-obj-twice-y)
>   hw-obj-y =
>   hw-obj-y += vl.o loader.o
>   hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
> +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o
>   hw-obj-y += usb/libhw.o
>   hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
>   hw-obj-y += fw_cfg.o
> diff --git a/hw/pci.h b/hw/pci.h
> index 8d0aa49..0a22f91 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -76,6 +76,7 @@
>   #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
>   #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
>   #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
> +#define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
>
>   #define FMT_PCIBUS                      PRIx64
>
> diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
> index 4a4413d..7f2d630 100644
> --- a/hw/virtio-pci.c
> +++ b/hw/virtio-pci.c
> @@ -812,6 +812,28 @@ static int virtio_balloon_exit_pci(PCIDevice *pci_dev)
>       return virtio_exit_pci(pci_dev);
>   }
>
> +static int virtio_rng_init_pci(PCIDevice *pci_dev)
> +{
> +    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_rng_init(&pci_dev->qdev);
> +    if (!vdev) {
> +        return -1;
> +    }
> +    virtio_init_pci(proxy, vdev);
> +    return 0;
> +}
> +
> +static int virtio_rng_exit_pci(PCIDevice *pci_dev)
> +{
> +    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
> +
> +    virtio_pci_stop_ioeventfd(proxy);
> +    virtio_rng_exit(proxy->vdev);
> +    return virtio_exit_pci(pci_dev);
> +}
> +
>   static Property virtio_blk_properties[] = {
>       DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
>       DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block),
> @@ -937,6 +959,33 @@ static TypeInfo virtio_balloon_info = {
>       .class_init    = virtio_balloon_class_init,
>   };
>
> +static Property virtio_rng_properties[] = {
> +    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_rng_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_rng_init_pci;
> +    k->exit = virtio_rng_exit_pci;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
> +    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
> +    k->revision = VIRTIO_PCI_ABI_VERSION;
> +    k->class_id = PCI_CLASS_OTHERS;
> +    dc->reset = virtio_pci_reset;
> +    dc->props = virtio_rng_properties;
> +}
> +
> +static TypeInfo virtio_rng_info = {
> +    .name          = "virtio-rng-pci",
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(VirtIOPCIProxy),
> +    .class_init    = virtio_rng_class_init,
> +};
> +
>   static int virtio_scsi_init_pci(PCIDevice *pci_dev)
>   {
>       VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
> @@ -998,6 +1047,7 @@ static void virtio_pci_register_types(void)
>       type_register_static(&virtio_serial_info);
>       type_register_static(&virtio_balloon_info);
>       type_register_static(&virtio_scsi_info);
> +    type_register_static(&virtio_rng_info);
>   }
>
>   type_init(virtio_pci_register_types)
> diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
> new file mode 100644
> index 0000000..e1f3d1c
> --- /dev/null
> +++ b/hw/virtio-rng.c
> @@ -0,0 +1,130 @@
> +/* A virtio device for feeding entropy into a guest.
> + *
> + * Copyright 2012 Red Hat, Inc.
> + * Copyright 2012 Amit Shah<amit.shah@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> + * (at your option) any later version.  See the COPYING file in the
> + * top-level directory.
> + */
> +
> +#include "iov.h"
> +#include "qdev.h"
> +#include "virtio.h"
> +#include "virtio-rng.h"
> +
> +typedef struct VirtIORNG {
> +    VirtIODevice vdev;
> +
> +    DeviceState *qdev;
> +
> +    /* Only one vq - guest puts a buffer on it when it needs entropy */
> +    VirtQueue *vq;
> +
> +    int input_fd;
> +} VirtIORNG;
> +
> +static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +    VirtQueueElement elem;
> +    char *buf;
> +    ssize_t size, offset, ret;
> +
> +    if (!virtqueue_pop(vq,&elem)) {
> +        return;
> +    }
> +    size = iov_size(elem.in_sg, elem.in_num);
> +
> +    buf = g_malloc(size);
> +    do {
> +        ret = read(vrng->input_fd, buf, size);
> +    } while (ret == -1&&  errno == EINTR);
> +    if (ret<  0) {
> +        /* We can't get randomness -- give up for now. */
> +        virtqueue_push(vq,&elem, 0);
> +        goto skip;
> +    }
> +
> +    offset = 0;
> +    size = ret;
> +    while (offset<  size) {
> +        size_t len;
> +
> +        /* We've already popped the first elem */
> +        if (offset&&  !virtqueue_pop(vq,&elem)) {
> +            break;
> +        }
> +
> +        len = iov_from_buf(elem.in_sg, elem.in_num,
> +                           buf + offset, 0, size - offset);
> +        offset += len;
> +
> +        virtqueue_push(vq,&elem, len);
> +    }
> +skip:
> +    g_free(buf);
> +    virtio_notify(vdev, vq);
> +}
> +
> +static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
> +{
> +    return f;
> +}
> +
> +static void virtio_rng_save(QEMUFile *f, void *opaque)
> +{
> +    VirtIORNG *vrng = opaque;
> +
> +    virtio_save(&vrng->vdev, f);
> +}
> +
> +static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
> +{
> +    VirtIORNG *vrng = opaque;
> +
> +    if (version_id != 1) {
> +        return -EINVAL;
> +    }
> +    virtio_load(&vrng->vdev, f);
> +
> +    return 0;
> +}
> +
> +VirtIODevice *virtio_rng_init(DeviceState *dev)
> +{
> +    VirtIORNG *vrng;
> +    VirtIODevice *vdev;
> +    int input_fd;
> +
> +    input_fd = open("/dev/urandom", O_RDONLY);
> +    if (input_fd<  0) {
> +        error_report("error %d opening /dev/urandom", errno);
> +        return NULL;
> +    }
> +
> +    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
> +                              sizeof(VirtIORNG));
> +
> +    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +
> +    vrng->input_fd = input_fd;
> +
> +    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
> +    vrng->vdev.get_features = get_features;
> +
> +    vrng->qdev = dev;
> +    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
> +                    virtio_rng_load, vrng);
> +
> +    return vdev;
> +}
> +
> +void virtio_rng_exit(VirtIODevice *vdev)
> +{
> +    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +
> +    close(vrng->input_fd);
> +    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
> +    virtio_cleanup(vdev);
> +}
> diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h
> new file mode 100644
> index 0000000..2e1eba3
> --- /dev/null
> +++ b/hw/virtio-rng.h
> @@ -0,0 +1,18 @@
> +/*
> + * Virtio RNG Support
> + *
> + * Copyright Red Hat, Inc. 2012
> + * Copyright Amit Shah<amit.shah@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> + * (at your option) any later version.  See the COPYING file in the
> + * top-level directory.
> + */
> +
> +#ifndef _QEMU_VIRTIO_RNG_H
> +#define _QEMU_VIRTIO_RNG_H
> +
> +/* The Virtio ID for the virtio rng device */
> +#define VIRTIO_ID_RNG    4
> +
> +#endif
> diff --git a/hw/virtio.h b/hw/virtio.h
> index 0aef7d1..0315e0c 100644
> --- a/hw/virtio.h
> +++ b/hw/virtio.h
> @@ -201,6 +201,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
>   VirtIODevice *virtio_balloon_init(DeviceState *dev);
>   typedef struct VirtIOSCSIConf VirtIOSCSIConf;
>   VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
> +VirtIODevice *virtio_rng_init(DeviceState *dev);
>   #ifdef CONFIG_LINUX
>   VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
>   #endif
> @@ -211,6 +212,7 @@ void virtio_blk_exit(VirtIODevice *vdev);
>   void virtio_serial_exit(VirtIODevice *vdev);
>   void virtio_balloon_exit(VirtIODevice *vdev);
>   void virtio_scsi_exit(VirtIODevice *vdev);
> +void virtio_rng_exit(VirtIODevice *vdev);
>
>   #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
>   	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
Daniel P. Berrangé May 16, 2012, 1:45 p.m. UTC | #4
On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> On 05/16/2012 06:30 AM, Amit Shah wrote:
> >The Linux kernel already has a virtio-rng driver, this is the device
> >implementation.
> >
> >When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >entropy into that buffer, and push it back to the guest.
> >
> >Feeding randomness from host's /dev/urandom into the guest is
> >sufficient, so this is a simple implementation that opens /dev/urandom
> >and reads from it whenever required.
> >
> >Invocation is simple:
> >
> >   qemu ... -device virtio-rng-pci
> >
> >In the guest, we see
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >   virtio
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >   virtio
> >
> >There are ways to extend the device to be more generic and collect
> >entropy from other sources, but this is simple enough and works for now.
> >
> >Signed-off-by: Amit Shah<amit.shah@redhat.com>
> 
> It's not this simple unfortunately.
> 
> If you did this with libvirt, one guest could exhaust the available
> entropy for the remaining guests.  This could be used as a mechanism
> for one guest to attack another (reducing the available entropy for
> key generation).
> 
> You need to rate limit the amount of entropy that a guest can obtain
> to allow management tools to mitigate this attack.

Ultimately I think you need to have a push mechanism, where an external
process feeds entropy to QEMU, rather than a pull mechanism where QEMU
grabs entropy itself.

I tend to think that virtio-rng should have a chardev backend associated
with it. The driver should just read from this chardev to get its entropy.
Either libvirtd, or better yet a separate virt-entropyd daemonm would
connect to each guest & feed the entropy into each guest according to
some desired metrics.

Regards,
Daniel
Anthony Liguori May 16, 2012, 1:48 p.m. UTC | #5
On 05/16/2012 08:45 AM, Daniel P. Berrange wrote:
> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
>> On 05/16/2012 06:30 AM, Amit Shah wrote:
>>> The Linux kernel already has a virtio-rng driver, this is the device
>>> implementation.
>>>
>>> When Linux needs more entropy, it puts a buffer in the vq.  We then put
>>> entropy into that buffer, and push it back to the guest.
>>>
>>> Feeding randomness from host's /dev/urandom into the guest is
>>> sufficient, so this is a simple implementation that opens /dev/urandom
>>> and reads from it whenever required.
>>>
>>> Invocation is simple:
>>>
>>>    qemu ... -device virtio-rng-pci
>>>
>>> In the guest, we see
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>    virtio
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>    virtio
>>>
>>> There are ways to extend the device to be more generic and collect
>>> entropy from other sources, but this is simple enough and works for now.
>>>
>>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>
>> It's not this simple unfortunately.
>>
>> If you did this with libvirt, one guest could exhaust the available
>> entropy for the remaining guests.  This could be used as a mechanism
>> for one guest to attack another (reducing the available entropy for
>> key generation).
>>
>> You need to rate limit the amount of entropy that a guest can obtain
>> to allow management tools to mitigate this attack.
>
> Ultimately I think you need to have a push mechanism, where an external
> process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> grabs entropy itself.

A previous patch didn't open urandom directly but instead talked to an entropy 
daemon.  This approach would allow libvirt to hand out entropy as it saw fit 
without requiring a new driver.

Regards,

Anthony Liguori

>
> I tend to think that virtio-rng should have a chardev backend associated
> with it. The driver should just read from this chardev to get its entropy.
> Either libvirtd, or better yet a separate virt-entropyd daemonm would
> connect to each guest&  feed the entropy into each guest according to
> some desired metrics.
>
> Regards,
> Daniel
Daniel P. Berrangé May 16, 2012, 1:53 p.m. UTC | #6
On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote:
> On 05/16/2012 08:45 AM, Daniel P. Berrange wrote:
> >On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> >>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>implementation.
> >>>
> >>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>entropy into that buffer, and push it back to the guest.
> >>>
> >>>Feeding randomness from host's /dev/urandom into the guest is
> >>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>and reads from it whenever required.
> >>>
> >>>Invocation is simple:
> >>>
> >>>   qemu ... -device virtio-rng-pci
> >>>
> >>>In the guest, we see
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>   virtio
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>   virtio
> >>>
> >>>There are ways to extend the device to be more generic and collect
> >>>entropy from other sources, but this is simple enough and works for now.
> >>>
> >>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>
> >>It's not this simple unfortunately.
> >>
> >>If you did this with libvirt, one guest could exhaust the available
> >>entropy for the remaining guests.  This could be used as a mechanism
> >>for one guest to attack another (reducing the available entropy for
> >>key generation).
> >>
> >>You need to rate limit the amount of entropy that a guest can obtain
> >>to allow management tools to mitigate this attack.
> >
> >Ultimately I think you need to have a push mechanism, where an external
> >process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> >grabs entropy itself.
> 
> A previous patch didn't open urandom directly but instead talked to
> an entropy daemon.  This approach would allow libvirt to hand out
> entropy as it saw fit without requiring a new driver.

The nice thing about just using a plain chardev backend for virtiorng
is that it would let you have the flexibility to integrate with any kind
of entropy daemon, or even just run without a daemon & rely on some other
process to periodically open the chardev & feed in data.

> >I tend to think that virtio-rng should have a chardev backend associated
> >with it. The driver should just read from this chardev to get its entropy.
> >Either libvirtd, or better yet a separate virt-entropyd daemonm would
> >connect to each guest&  feed the entropy into each guest according to
> >some desired metrics.

Regards,
Daniel
Anthony Liguori May 16, 2012, 2:02 p.m. UTC | #7
On 05/16/2012 08:53 AM, Daniel P. Berrange wrote:
> On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote:
>> On 05/16/2012 08:45 AM, Daniel P. Berrange wrote:
>>> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
>>>> On 05/16/2012 06:30 AM, Amit Shah wrote:
>>>>> The Linux kernel already has a virtio-rng driver, this is the device
>>>>> implementation.
>>>>>
>>>>> When Linux needs more entropy, it puts a buffer in the vq.  We then put
>>>>> entropy into that buffer, and push it back to the guest.
>>>>>
>>>>> Feeding randomness from host's /dev/urandom into the guest is
>>>>> sufficient, so this is a simple implementation that opens /dev/urandom
>>>>> and reads from it whenever required.
>>>>>
>>>>> Invocation is simple:
>>>>>
>>>>>    qemu ... -device virtio-rng-pci
>>>>>
>>>>> In the guest, we see
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>>>    virtio
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>>>    virtio
>>>>>
>>>>> There are ways to extend the device to be more generic and collect
>>>>> entropy from other sources, but this is simple enough and works for now.
>>>>>
>>>>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>>>
>>>> It's not this simple unfortunately.
>>>>
>>>> If you did this with libvirt, one guest could exhaust the available
>>>> entropy for the remaining guests.  This could be used as a mechanism
>>>> for one guest to attack another (reducing the available entropy for
>>>> key generation).
>>>>
>>>> You need to rate limit the amount of entropy that a guest can obtain
>>>> to allow management tools to mitigate this attack.
>>>
>>> Ultimately I think you need to have a push mechanism, where an external
>>> process feeds entropy to QEMU, rather than a pull mechanism where QEMU
>>> grabs entropy itself.
>>
>> A previous patch didn't open urandom directly but instead talked to
>> an entropy daemon.  This approach would allow libvirt to hand out
>> entropy as it saw fit without requiring a new driver.
>
> The nice thing about just using a plain chardev backend for virtiorng
> is that it would let you have the flexibility to integrate with any kind
> of entropy daemon, or even just run without a daemon&  rely on some other
> process to periodically open the chardev&  feed in data.

Ack.

But there is an entropy daemon that does use a protocol.  The protocol may be 
"just read raw random data" but we should at least check to make sure that is 
the protocol.

Regards,

Anthony Liguori

>
>>> I tend to think that virtio-rng should have a chardev backend associated
>>> with it. The driver should just read from this chardev to get its entropy.
>>> Either libvirtd, or better yet a separate virt-entropyd daemonm would
>>> connect to each guest&   feed the entropy into each guest according to
>>> some desired metrics.
>
> Regards,
> Daniel
Amit Shah May 16, 2012, 5:21 p.m. UTC | #8
On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote:
> On 05/16/2012 06:30 AM, Amit Shah wrote:
> >The Linux kernel already has a virtio-rng driver, this is the device
> >implementation.
> >
> >When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >entropy into that buffer, and push it back to the guest.
> >
> >Feeding randomness from host's /dev/urandom into the guest is
> >sufficient, so this is a simple implementation that opens /dev/urandom
> >and reads from it whenever required.
> >
> >Invocation is simple:
> >
> >   qemu ... -device virtio-rng-pci
> >
> >In the guest, we see
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >   virtio
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >   virtio
> >
> >There are ways to extend the device to be more generic and collect
> >entropy from other sources, but this is simple enough and works for now.
> >
> >Signed-off-by: Amit Shah<amit.shah@redhat.com>
> 
> It's not this simple unfortunately.
> 
> If you did this with libvirt, one guest could exhaust the available
> entropy for the remaining guests.  This could be used as a mechanism
> for one guest to attack another (reducing the available entropy for
> key generation).
> 
> You need to rate limit the amount of entropy that a guest can obtain
> to allow management tools to mitigate this attack.

Hm, rate-limiting is a good point.  However, we're using /dev/urandom
here, which is nonblocking, and will keep on providing data as long as
we keep reading.

		Amit
Amit Shah May 16, 2012, 5:26 p.m. UTC | #9
On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote:
> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> > On 05/16/2012 06:30 AM, Amit Shah wrote:
> > >The Linux kernel already has a virtio-rng driver, this is the device
> > >implementation.
> > >
> > >When Linux needs more entropy, it puts a buffer in the vq.  We then put
> > >entropy into that buffer, and push it back to the guest.
> > >
> > >Feeding randomness from host's /dev/urandom into the guest is
> > >sufficient, so this is a simple implementation that opens /dev/urandom
> > >and reads from it whenever required.
> > >
> > >Invocation is simple:
> > >
> > >   qemu ... -device virtio-rng-pci
> > >
> > >In the guest, we see
> > >
> > >   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> > >   virtio
> > >
> > >   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> > >   virtio
> > >
> > >There are ways to extend the device to be more generic and collect
> > >entropy from other sources, but this is simple enough and works for now.
> > >
> > >Signed-off-by: Amit Shah<amit.shah@redhat.com>
> > 
> > It's not this simple unfortunately.
> > 
> > If you did this with libvirt, one guest could exhaust the available
> > entropy for the remaining guests.  This could be used as a mechanism
> > for one guest to attack another (reducing the available entropy for
> > key generation).
> > 
> > You need to rate limit the amount of entropy that a guest can obtain
> > to allow management tools to mitigate this attack.
> 
> Ultimately I think you need to have a push mechanism, where an external
> process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> grabs entropy itself.

Yes, that's the goal.

That was my first instinct as well.  However, we already have guests
which have the current virtio-rng driver that works only in pull
mode.  Also, Linux's hw-rng interface doesn't have a pull mechanism at
all -- it asks the h/w for more entropy when the OS is low on it.

> I tend to think that virtio-rng should have a chardev backend associated
> with it. The driver should just read from this chardev to get its entropy.

I even started with this approach.  Adapting the chardev layer to
actually read from a source of data, only when needed (for the
pull-based mechanism) quickly turned ugly.

When we do get a push-based mechanism working, we'll then also have to
think of buffering the data from the daemon somewhere.  It's not going
to be ideal.

> Either libvirtd, or better yet a separate virt-entropyd daemonm would
> connect to each guest & feed the entropy into each guest according to
> some desired metrics.

I'd prefer a separate daemon.  There already is egd, which we can use.
However, there are restrictions with certification (as always).
Adding new daemons to the mix increases complexity and the time it
takes for certification, so doing it in libvirt may end up to be the
preferred approach.

		Amit
Amit Shah May 16, 2012, 5:28 p.m. UTC | #10
On (Wed) 16 May 2012 [09:02:03], Anthony Liguori wrote:
> On 05/16/2012 08:53 AM, Daniel P. Berrange wrote:
> >On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote:
> >>On 05/16/2012 08:45 AM, Daniel P. Berrange wrote:
> >>>On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> >>>>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>>>implementation.
> >>>>>
> >>>>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>>>entropy into that buffer, and push it back to the guest.
> >>>>>
> >>>>>Feeding randomness from host's /dev/urandom into the guest is
> >>>>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>>>and reads from it whenever required.
> >>>>>
> >>>>>Invocation is simple:
> >>>>>
> >>>>>   qemu ... -device virtio-rng-pci
> >>>>>
> >>>>>In the guest, we see
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>>>   virtio
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>>>   virtio
> >>>>>
> >>>>>There are ways to extend the device to be more generic and collect
> >>>>>entropy from other sources, but this is simple enough and works for now.
> >>>>>
> >>>>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>>>
> >>>>It's not this simple unfortunately.
> >>>>
> >>>>If you did this with libvirt, one guest could exhaust the available
> >>>>entropy for the remaining guests.  This could be used as a mechanism
> >>>>for one guest to attack another (reducing the available entropy for
> >>>>key generation).
> >>>>
> >>>>You need to rate limit the amount of entropy that a guest can obtain
> >>>>to allow management tools to mitigate this attack.
> >>>
> >>>Ultimately I think you need to have a push mechanism, where an external
> >>>process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> >>>grabs entropy itself.
> >>
> >>A previous patch didn't open urandom directly but instead talked to
> >>an entropy daemon.  This approach would allow libvirt to hand out
> >>entropy as it saw fit without requiring a new driver.
> >
> >The nice thing about just using a plain chardev backend for virtiorng
> >is that it would let you have the flexibility to integrate with any kind
> >of entropy daemon, or even just run without a daemon&  rely on some other
> >process to periodically open the chardev&  feed in data.
> 
> Ack.
> 
> But there is an entropy daemon that does use a protocol.  The
> protocol may be "just read raw random data" but we should at least
> check to make sure that is the protocol.

Yes, that's egd.  Ultimately, we'll use that, but there are more
problems to be solved, both in qemu and in Linux, before we get there
(as described in the previous mails).

		Amit
Anthony Liguori May 16, 2012, 6:23 p.m. UTC | #11
On 05/16/2012 12:21 PM, Amit Shah wrote:
> On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote:
>> On 05/16/2012 06:30 AM, Amit Shah wrote:
>>> The Linux kernel already has a virtio-rng driver, this is the device
>>> implementation.
>>>
>>> When Linux needs more entropy, it puts a buffer in the vq.  We then put
>>> entropy into that buffer, and push it back to the guest.
>>>
>>> Feeding randomness from host's /dev/urandom into the guest is
>>> sufficient, so this is a simple implementation that opens /dev/urandom
>>> and reads from it whenever required.
>>>
>>> Invocation is simple:
>>>
>>>    qemu ... -device virtio-rng-pci
>>>
>>> In the guest, we see
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>    virtio
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>    virtio
>>>
>>> There are ways to extend the device to be more generic and collect
>>> entropy from other sources, but this is simple enough and works for now.
>>>
>>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>
>> It's not this simple unfortunately.
>>
>> If you did this with libvirt, one guest could exhaust the available
>> entropy for the remaining guests.  This could be used as a mechanism
>> for one guest to attack another (reducing the available entropy for
>> key generation).
>>
>> You need to rate limit the amount of entropy that a guest can obtain
>> to allow management tools to mitigate this attack.
>
> Hm, rate-limiting is a good point.  However, we're using /dev/urandom
> here, which is nonblocking, and will keep on providing data as long as
> we keep reading.

But you're still exhausting the entropy pool (which is a global resource). 
That's the problem.

Regards,

Anthony Liguori

>
> 		Amit
Anthony Liguori May 16, 2012, 6:24 p.m. UTC | #12
On 05/16/2012 12:26 PM, Amit Shah wrote:
> On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote:
>> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
>>> On 05/16/2012 06:30 AM, Amit Shah wrote:
>>>> The Linux kernel already has a virtio-rng driver, this is the device
>>>> implementation.
>>>>
>>>> When Linux needs more entropy, it puts a buffer in the vq.  We then put
>>>> entropy into that buffer, and push it back to the guest.
>>>>
>>>> Feeding randomness from host's /dev/urandom into the guest is
>>>> sufficient, so this is a simple implementation that opens /dev/urandom
>>>> and reads from it whenever required.
>>>>
>>>> Invocation is simple:
>>>>
>>>>    qemu ... -device virtio-rng-pci
>>>>
>>>> In the guest, we see
>>>>
>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>>    virtio
>>>>
>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>>    virtio
>>>>
>>>> There are ways to extend the device to be more generic and collect
>>>> entropy from other sources, but this is simple enough and works for now.
>>>>
>>>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>>
>>> It's not this simple unfortunately.
>>>
>>> If you did this with libvirt, one guest could exhaust the available
>>> entropy for the remaining guests.  This could be used as a mechanism
>>> for one guest to attack another (reducing the available entropy for
>>> key generation).
>>>
>>> You need to rate limit the amount of entropy that a guest can obtain
>>> to allow management tools to mitigate this attack.
>>
>> Ultimately I think you need to have a push mechanism, where an external
>> process feeds entropy to QEMU, rather than a pull mechanism where QEMU
>> grabs entropy itself.
>
> Yes, that's the goal.
>
> That was my first instinct as well.  However, we already have guests
> which have the current virtio-rng driver that works only in pull
> mode.  Also, Linux's hw-rng interface doesn't have a pull mechanism at
> all -- it asks the h/w for more entropy when the OS is low on it.
>
>> I tend to think that virtio-rng should have a chardev backend associated
>> with it. The driver should just read from this chardev to get its entropy.
>
> I even started with this approach.  Adapting the chardev layer to
> actually read from a source of data, only when needed (for the
> pull-based mechanism) quickly turned ugly.
>
> When we do get a push-based mechanism working, we'll then also have to
> think of buffering the data from the daemon somewhere.  It's not going
> to be ideal.

push == pull with flow control.  There's no need to implement a different guest 
interface.

>
>> Either libvirtd, or better yet a separate virt-entropyd daemonm would
>> connect to each guest&  feed the entropy into each guest according to
>> some desired metrics.
>
> I'd prefer a separate daemon.  There already is egd, which we can use.
> However, there are restrictions with certification (as always).
> Adding new daemons to the mix increases complexity and the time it
> takes for certification, so doing it in libvirt may end up to be the
> preferred approach.

I don't think time to certify is a reasonable technical consideration.

Regards,

Anthony Liguori

>
> 		Amit
Amit Shah May 21, 2012, 7:32 p.m. UTC | #13
On (Wed) 16 May 2012 [08:48:20], Anthony Liguori wrote:
> On 05/16/2012 08:45 AM, Daniel P. Berrange wrote:
> >On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> >>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>implementation.
> >>>
> >>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>entropy into that buffer, and push it back to the guest.
> >>>
> >>>Feeding randomness from host's /dev/urandom into the guest is
> >>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>and reads from it whenever required.
> >>>
> >>>Invocation is simple:
> >>>
> >>>   qemu ... -device virtio-rng-pci
> >>>
> >>>In the guest, we see
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>   virtio
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>   virtio
> >>>
> >>>There are ways to extend the device to be more generic and collect
> >>>entropy from other sources, but this is simple enough and works for now.
> >>>
> >>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>
> >>It's not this simple unfortunately.
> >>
> >>If you did this with libvirt, one guest could exhaust the available
> >>entropy for the remaining guests.  This could be used as a mechanism
> >>for one guest to attack another (reducing the available entropy for
> >>key generation).
> >>
> >>You need to rate limit the amount of entropy that a guest can obtain
> >>to allow management tools to mitigate this attack.
> >
> >Ultimately I think you need to have a push mechanism, where an external
> >process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> >grabs entropy itself.
> 
> A previous patch didn't open urandom directly but instead talked to
> an entropy daemon.  This approach would allow libvirt to hand out
> entropy as it saw fit without requiring a new driver.

Using egd isn't necessary -- libvirt could still read urandom and pass
on to qemu at configured intervals.

		Amit
Amit Shah May 21, 2012, 7:37 p.m. UTC | #14
On (Wed) 16 May 2012 [13:24:10], Anthony Liguori wrote:
> On 05/16/2012 12:26 PM, Amit Shah wrote:
> >On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote:
> >>On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote:
> >>>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>>implementation.
> >>>>
> >>>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>>entropy into that buffer, and push it back to the guest.
> >>>>
> >>>>Feeding randomness from host's /dev/urandom into the guest is
> >>>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>>and reads from it whenever required.
> >>>>
> >>>>Invocation is simple:
> >>>>
> >>>>   qemu ... -device virtio-rng-pci
> >>>>
> >>>>In the guest, we see
> >>>>
> >>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>>   virtio
> >>>>
> >>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>>   virtio
> >>>>
> >>>>There are ways to extend the device to be more generic and collect
> >>>>entropy from other sources, but this is simple enough and works for now.
> >>>>
> >>>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>>
> >>>It's not this simple unfortunately.
> >>>
> >>>If you did this with libvirt, one guest could exhaust the available
> >>>entropy for the remaining guests.  This could be used as a mechanism
> >>>for one guest to attack another (reducing the available entropy for
> >>>key generation).
> >>>
> >>>You need to rate limit the amount of entropy that a guest can obtain
> >>>to allow management tools to mitigate this attack.
> >>
> >>Ultimately I think you need to have a push mechanism, where an external
> >>process feeds entropy to QEMU, rather than a pull mechanism where QEMU
> >>grabs entropy itself.
> >
> >Yes, that's the goal.
> >
> >That was my first instinct as well.  However, we already have guests
> >which have the current virtio-rng driver that works only in pull
> >mode.  Also, Linux's hw-rng interface doesn't have a pull mechanism at
> >all -- it asks the h/w for more entropy when the OS is low on it.
> >
> >>I tend to think that virtio-rng should have a chardev backend associated
> >>with it. The driver should just read from this chardev to get its entropy.
> >
> >I even started with this approach.  Adapting the chardev layer to
> >actually read from a source of data, only when needed (for the
> >pull-based mechanism) quickly turned ugly.
> >
> >When we do get a push-based mechanism working, we'll then also have to
> >think of buffering the data from the daemon somewhere.  It's not going
> >to be ideal.
> 
> push == pull with flow control.  There's no need to implement a
> different guest interface.

Though, the current implementation can be easily extended -- if we do
go without a chardev for v1 for virtio-rng, with qemu directly opening
urandom and feeding entropy, libvirt just has to add a
'virtio-rng-pci' device.  When the ',chardev' property becomes
available, it can connect the chardev to itself or a daemon and start
feeding data.

> >>Either libvirtd, or better yet a separate virt-entropyd daemonm would
> >>connect to each guest&  feed the entropy into each guest according to
> >>some desired metrics.
> >
> >I'd prefer a separate daemon.  There already is egd, which we can use.
> >However, there are restrictions with certification (as always).
> >Adding new daemons to the mix increases complexity and the time it
> >takes for certification, so doing it in libvirt may end up to be the
> >preferred approach.
> 
> I don't think time to certify is a reasonable technical consideration.

Absolutely -- but if it's as easy to implement it in libvirt vs
another standalone program, the libvirt approach should be preferred
considering the above.

		Amit
Amit Shah May 21, 2012, 7:39 p.m. UTC | #15
On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote:
> On 05/16/2012 12:21 PM, Amit Shah wrote:
> >On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote:
> >>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>implementation.
> >>>
> >>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>entropy into that buffer, and push it back to the guest.
> >>>
> >>>Feeding randomness from host's /dev/urandom into the guest is
> >>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>and reads from it whenever required.
> >>>
> >>>Invocation is simple:
> >>>
> >>>   qemu ... -device virtio-rng-pci
> >>>
> >>>In the guest, we see
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>   virtio
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>   virtio
> >>>
> >>>There are ways to extend the device to be more generic and collect
> >>>entropy from other sources, but this is simple enough and works for now.
> >>>
> >>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>
> >>It's not this simple unfortunately.
> >>
> >>If you did this with libvirt, one guest could exhaust the available
> >>entropy for the remaining guests.  This could be used as a mechanism
> >>for one guest to attack another (reducing the available entropy for
> >>key generation).
> >>
> >>You need to rate limit the amount of entropy that a guest can obtain
> >>to allow management tools to mitigate this attack.
> >
> >Hm, rate-limiting is a good point.  However, we're using /dev/urandom
> >here, which is nonblocking, and will keep on providing data as long as
> >we keep reading.
> 
> But you're still exhausting the entropy pool (which is a global
> resource). That's the problem.

I understand.  It's been shown, however, that /dev/urandom isn't
easily exhausted, and can be used as a reliable random source for
quite a few years without new seeding.  And even if a guest (or more)
is malicious, the guest doing such activities would itself continue to
generate some seed for the host's pool, strengthening /dev/urandom.

I don't know where to cite the data from, but I'll pass on that info
when I have a reference.

In the meantime, I'll add a rate-limiting option to the device, it
does seem like a good idea to implement nevertheless.

		Amit
Anthony Liguori May 21, 2012, 8:34 p.m. UTC | #16
On 05/21/2012 02:39 PM, Amit Shah wrote:
> On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote:
>> On 05/16/2012 12:21 PM, Amit Shah wrote:
>>> On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote:
>>>> On 05/16/2012 06:30 AM, Amit Shah wrote:
>>>>> The Linux kernel already has a virtio-rng driver, this is the device
>>>>> implementation.
>>>>>
>>>>> When Linux needs more entropy, it puts a buffer in the vq.  We then put
>>>>> entropy into that buffer, and push it back to the guest.
>>>>>
>>>>> Feeding randomness from host's /dev/urandom into the guest is
>>>>> sufficient, so this is a simple implementation that opens /dev/urandom
>>>>> and reads from it whenever required.
>>>>>
>>>>> Invocation is simple:
>>>>>
>>>>>    qemu ... -device virtio-rng-pci
>>>>>
>>>>> In the guest, we see
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>>>    virtio
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>>>    virtio
>>>>>
>>>>> There are ways to extend the device to be more generic and collect
>>>>> entropy from other sources, but this is simple enough and works for now.
>>>>>
>>>>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>>>
>>>> It's not this simple unfortunately.
>>>>
>>>> If you did this with libvirt, one guest could exhaust the available
>>>> entropy for the remaining guests.  This could be used as a mechanism
>>>> for one guest to attack another (reducing the available entropy for
>>>> key generation).
>>>>
>>>> You need to rate limit the amount of entropy that a guest can obtain
>>>> to allow management tools to mitigate this attack.
>>>
>>> Hm, rate-limiting is a good point.  However, we're using /dev/urandom
>>> here, which is nonblocking, and will keep on providing data as long as
>>> we keep reading.
>>
>> But you're still exhausting the entropy pool (which is a global
>> resource). That's the problem.
>
> I understand.  It's been shown, however, that /dev/urandom isn't
> easily exhausted, and can be used as a reliable random source for
> quite a few years without new seeding.  And even if a guest (or more)
> is malicious, the guest doing such activities would itself continue to
> generate some seed for the host's pool, strengthening /dev/urandom.
>
> I don't know where to cite the data from, but I'll pass on that info
> when I have a reference.

Okay, I need to some type of reference here.

Not implementing virtio-rng for all of these years wasn't a simple oversight. 
It was specifically out of fear of exhausting the entropy pool.

I'm pretty opposed to merging this without addressing entropy exhaustion because 
it's a subtle enough issue that I don't think most users would be capable of 
understanding the ramifications.

Regards,

Anthony Liguori

>
> In the meantime, I'll add a rate-limiting option to the device, it
> does seem like a good idea to implement nevertheless.
>
> 		Amit
Amit Shah May 22, 2012, 12:57 p.m. UTC | #17
On (Mon) 21 May 2012 [15:34:59], Anthony Liguori wrote:
> On 05/21/2012 02:39 PM, Amit Shah wrote:
> >On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote:
> >>On 05/16/2012 12:21 PM, Amit Shah wrote:
> >>>On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote:
> >>>>On 05/16/2012 06:30 AM, Amit Shah wrote:
> >>>>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>>>implementation.
> >>>>>
> >>>>>When Linux needs more entropy, it puts a buffer in the vq.  We then put
> >>>>>entropy into that buffer, and push it back to the guest.
> >>>>>
> >>>>>Feeding randomness from host's /dev/urandom into the guest is
> >>>>>sufficient, so this is a simple implementation that opens /dev/urandom
> >>>>>and reads from it whenever required.
> >>>>>
> >>>>>Invocation is simple:
> >>>>>
> >>>>>   qemu ... -device virtio-rng-pci
> >>>>>
> >>>>>In the guest, we see
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>>>   virtio
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>>>   virtio
> >>>>>
> >>>>>There are ways to extend the device to be more generic and collect
> >>>>>entropy from other sources, but this is simple enough and works for now.
> >>>>>
> >>>>>Signed-off-by: Amit Shah<amit.shah@redhat.com>
> >>>>
> >>>>It's not this simple unfortunately.
> >>>>
> >>>>If you did this with libvirt, one guest could exhaust the available
> >>>>entropy for the remaining guests.  This could be used as a mechanism
> >>>>for one guest to attack another (reducing the available entropy for
> >>>>key generation).
> >>>>
> >>>>You need to rate limit the amount of entropy that a guest can obtain
> >>>>to allow management tools to mitigate this attack.
> >>>
> >>>Hm, rate-limiting is a good point.  However, we're using /dev/urandom
> >>>here, which is nonblocking, and will keep on providing data as long as
> >>>we keep reading.
> >>
> >>But you're still exhausting the entropy pool (which is a global
> >>resource). That's the problem.
> >
> >I understand.  It's been shown, however, that /dev/urandom isn't
> >easily exhausted, and can be used as a reliable random source for
> >quite a few years without new seeding.  And even if a guest (or more)
> >is malicious, the guest doing such activities would itself continue to
> >generate some seed for the host's pool, strengthening /dev/urandom.
> >
> >I don't know where to cite the data from, but I'll pass on that info
> >when I have a reference.
> 
> Okay, I need to some type of reference here.
> 
> Not implementing virtio-rng for all of these years wasn't a simple
> oversight. It was specifically out of fear of exhausting the entropy
> pool.
> 
> I'm pretty opposed to merging this without addressing entropy
> exhaustion because it's a subtle enough issue that I don't think
> most users would be capable of understanding the ramifications.

Yes, I understand.  We just have to wait for the references.

There is /proc/sys/kernel/random/entropy_avail and qemu can be
configured to never let that number go below a certain watermark.
However, libvirt is in a better position to understand how the system
works, and how much demand for entropy comes from multiple guests.

Having thought more about it, qemu can't really make the call
rate-limiting.

So I think we should leave this to libvirt: qemu can emit an event,
like ENTROPY_NEEDED along with the number of bytes that got requested
by the guest.  libvirt can then fill in the bytes via a chardev.  If
libvirt wants to rate-limit this particular guest, it can just not
provide any data (for a certain amount of time).

This also then doesn't depend on the chardev flow control work to land
before this gets implemented.

Also, the patch carried in this thread can be enabled for developer
debugging (i.e. -device virtio-rng-pci w/o any chardev).

Does this sound OK?

		Amit
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 70c5c79..5850762 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -210,6 +210,7 @@  user-obj-y += $(qom-obj-twice-y)
 hw-obj-y =
 hw-obj-y += vl.o loader.o
 hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
+hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o
 hw-obj-y += usb/libhw.o
 hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 hw-obj-y += fw_cfg.o
diff --git a/hw/pci.h b/hw/pci.h
index 8d0aa49..0a22f91 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -76,6 +76,7 @@ 
 #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
 #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
 #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
+#define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
 
 #define FMT_PCIBUS                      PRIx64
 
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 4a4413d..7f2d630 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -812,6 +812,28 @@  static int virtio_balloon_exit_pci(PCIDevice *pci_dev)
     return virtio_exit_pci(pci_dev);
 }
 
+static int virtio_rng_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev;
+
+    vdev = virtio_rng_init(&pci_dev->qdev);
+    if (!vdev) {
+        return -1;
+    }
+    virtio_init_pci(proxy, vdev);
+    return 0;
+}
+
+static int virtio_rng_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_rng_exit(proxy->vdev);
+    return virtio_exit_pci(pci_dev);
+}
+
 static Property virtio_blk_properties[] = {
     DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
     DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block),
@@ -937,6 +959,33 @@  static TypeInfo virtio_balloon_info = {
     .class_init    = virtio_balloon_class_init,
 };
 
+static Property virtio_rng_properties[] = {
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_rng_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_rng_init_pci;
+    k->exit = virtio_rng_exit_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_OTHERS;
+    dc->reset = virtio_pci_reset;
+    dc->props = virtio_rng_properties;
+}
+
+static TypeInfo virtio_rng_info = {
+    .name          = "virtio-rng-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_rng_class_init,
+};
+
 static int virtio_scsi_init_pci(PCIDevice *pci_dev)
 {
     VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
@@ -998,6 +1047,7 @@  static void virtio_pci_register_types(void)
     type_register_static(&virtio_serial_info);
     type_register_static(&virtio_balloon_info);
     type_register_static(&virtio_scsi_info);
+    type_register_static(&virtio_rng_info);
 }
 
 type_init(virtio_pci_register_types)
diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
new file mode 100644
index 0000000..e1f3d1c
--- /dev/null
+++ b/hw/virtio-rng.c
@@ -0,0 +1,130 @@ 
+/* A virtio device for feeding entropy into a guest.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright 2012 Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "iov.h"
+#include "qdev.h"
+#include "virtio.h"
+#include "virtio-rng.h"
+
+typedef struct VirtIORNG {
+    VirtIODevice vdev;
+
+    DeviceState *qdev;
+
+    /* Only one vq - guest puts a buffer on it when it needs entropy */
+    VirtQueue *vq;
+
+    int input_fd;
+} VirtIORNG;
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+    VirtQueueElement elem;
+    char *buf;
+    ssize_t size, offset, ret;
+
+    if (!virtqueue_pop(vq, &elem)) {
+        return;
+    }
+    size = iov_size(elem.in_sg, elem.in_num);
+
+    buf = g_malloc(size);
+    do {
+        ret = read(vrng->input_fd, buf, size);
+    } while (ret == -1 && errno == EINTR);
+    if (ret < 0) {
+        /* We can't get randomness -- give up for now. */
+        virtqueue_push(vq, &elem, 0);
+        goto skip;
+    }
+
+    offset = 0;
+    size = ret;
+    while (offset < size) {
+        size_t len;
+
+        /* We've already popped the first elem */
+        if (offset && !virtqueue_pop(vq, &elem)) {
+            break;
+        }
+
+        len = iov_from_buf(elem.in_sg, elem.in_num,
+                           buf + offset, 0, size - offset);
+        offset += len;
+
+        virtqueue_push(vq, &elem, len);
+    }
+skip:
+    g_free(buf);
+    virtio_notify(vdev, vq);
+}
+
+static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
+{
+    return f;
+}
+
+static void virtio_rng_save(QEMUFile *f, void *opaque)
+{
+    VirtIORNG *vrng = opaque;
+
+    virtio_save(&vrng->vdev, f);
+}
+
+static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
+{
+    VirtIORNG *vrng = opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+    virtio_load(&vrng->vdev, f);
+
+    return 0;
+}
+
+VirtIODevice *virtio_rng_init(DeviceState *dev)
+{
+    VirtIORNG *vrng;
+    VirtIODevice *vdev;
+    int input_fd;
+
+    input_fd = open("/dev/urandom", O_RDONLY);
+    if (input_fd < 0) {
+        error_report("error %d opening /dev/urandom", errno);
+        return NULL;
+    }
+
+    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
+                              sizeof(VirtIORNG));
+
+    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    vrng->input_fd = input_fd;
+
+    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
+    vrng->vdev.get_features = get_features;
+
+    vrng->qdev = dev;
+    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
+                    virtio_rng_load, vrng);
+
+    return vdev;
+}
+
+void virtio_rng_exit(VirtIODevice *vdev)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    close(vrng->input_fd);
+    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
+    virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h
new file mode 100644
index 0000000..2e1eba3
--- /dev/null
+++ b/hw/virtio-rng.h
@@ -0,0 +1,18 @@ 
+/*
+ * Virtio RNG Support
+ *
+ * Copyright Red Hat, Inc. 2012
+ * Copyright Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#ifndef _QEMU_VIRTIO_RNG_H
+#define _QEMU_VIRTIO_RNG_H
+
+/* The Virtio ID for the virtio rng device */
+#define VIRTIO_ID_RNG    4
+
+#endif
diff --git a/hw/virtio.h b/hw/virtio.h
index 0aef7d1..0315e0c 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -201,6 +201,7 @@  VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
 VirtIODevice *virtio_balloon_init(DeviceState *dev);
 typedef struct VirtIOSCSIConf VirtIOSCSIConf;
 VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
+VirtIODevice *virtio_rng_init(DeviceState *dev);
 #ifdef CONFIG_LINUX
 VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
 #endif
@@ -211,6 +212,7 @@  void virtio_blk_exit(VirtIODevice *vdev);
 void virtio_serial_exit(VirtIODevice *vdev);
 void virtio_balloon_exit(VirtIODevice *vdev);
 void virtio_scsi_exit(VirtIODevice *vdev);
+void virtio_rng_exit(VirtIODevice *vdev);
 
 #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
 	DEFINE_PROP_BIT("indirect_desc", _state, _field, \