aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/u2f.txt9
-rw-r--r--hw/usb/meson.build2
-rw-r--r--hw/usb/u2f-passthru.c113
3 files changed, 110 insertions, 14 deletions
diff --git a/docs/u2f.txt b/docs/u2f.txt
index f60052882e..8f44994818 100644
--- a/docs/u2f.txt
+++ b/docs/u2f.txt
@@ -42,6 +42,10 @@ on libu2f-emu: configuring and building:
./configure --enable-u2f && make
+The pass-through mode is built by default on Linux. To take advantage
+of the autoscan option it provides, make sure you have a working libudev
+installed on the host.
+
3. Using u2f-emulated
@@ -90,6 +94,11 @@ On the host specify the u2f-passthru device with a suitable hidraw:
qemu -usb -device u2f-passthru,hidraw=/dev/hidraw0
+Alternately, the u2f-passthru device can autoscan to take the first
+U2F device it finds on the host (this requires a working libudev):
+
+ qemu -usb -device u2f-passthru
+
5. Libu2f-emu
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index a25109b88c..b7c7ff23bf 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -52,7 +52,7 @@ endif
# U2F
softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c'))
-softmmu_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: files('u2f-passthru.c'))
+softmmu_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: [libudev, files('u2f-passthru.c')])
if u2f.found()
softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')])
endif
diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c
index 74d4ae6e92..e9c8aa4595 100644
--- a/hw/usb/u2f-passthru.c
+++ b/hw/usb/u2f-passthru.c
@@ -378,6 +378,84 @@ static bool u2f_passthru_is_u2f_device(int fd)
sizeof(u2f_hid_report_desc_header)) == 0;
}
+#ifdef CONFIG_LIBUDEV
+static int u2f_passthru_open_from_device(struct udev_device *device)
+{
+ const char *devnode = udev_device_get_devnode(device);
+
+ int fd = qemu_open(devnode, O_RDWR);
+ if (fd < 0) {
+ return -1;
+ } else if (!u2f_passthru_is_u2f_device(fd)) {
+ qemu_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static int u2f_passthru_open_from_enumerate(struct udev *udev,
+ struct udev_enumerate *enumerate)
+{
+ struct udev_list_entry *devices, *entry;
+ int ret, fd;
+
+ ret = udev_enumerate_scan_devices(enumerate);
+ if (ret < 0) {
+ return -1;
+ }
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+ udev_list_entry_foreach(entry, devices) {
+ struct udev_device *device;
+ const char *syspath = udev_list_entry_get_name(entry);
+
+ if (syspath == NULL) {
+ continue;
+ }
+
+ device = udev_device_new_from_syspath(udev, syspath);
+ if (device == NULL) {
+ continue;
+ }
+
+ fd = u2f_passthru_open_from_device(device);
+ udev_device_unref(device);
+ if (fd >= 0) {
+ return fd;
+ }
+ }
+ return -1;
+}
+
+static int u2f_passthru_open_from_scan(void)
+{
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ int ret, fd = -1;
+
+ udev = udev_new();
+ if (udev == NULL) {
+ return -1;
+ }
+
+ enumerate = udev_enumerate_new(udev);
+ if (enumerate == NULL) {
+ udev_unref(udev);
+ return -1;
+ }
+
+ ret = udev_enumerate_add_match_subsystem(enumerate, "hidraw");
+ if (ret >= 0) {
+ fd = u2f_passthru_open_from_enumerate(udev, enumerate);
+ }
+
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return fd;
+}
+#endif
+
static void u2f_passthru_unrealize(U2FKeyState *base)
{
U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
@@ -392,22 +470,31 @@ static void u2f_passthru_realize(U2FKeyState *base, Error **errp)
int fd;
if (key->hidraw == NULL) {
+#ifdef CONFIG_LIBUDEV
+ fd = u2f_passthru_open_from_scan();
+ if (fd < 0) {
+ error_setg(errp, "%s: Failed to find a U2F USB device",
+ TYPE_U2F_PASSTHRU);
+ return;
+ }
+#else
error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU);
return;
- }
-
- fd = qemu_open(key->hidraw, O_RDWR);
- if (fd < 0) {
- error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
- key->hidraw);
- return;
- }
+#endif
+ } else {
+ fd = qemu_open(key->hidraw, O_RDWR);
+ if (fd < 0) {
+ error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
+ key->hidraw);
+ return;
+ }
- if (!u2f_passthru_is_u2f_device(fd)) {
- qemu_close(fd);
- error_setg(errp, "%s: Passed hidraw does not represent "
- "a U2F HID device", TYPE_U2F_PASSTHRU);
- return;
+ if (!u2f_passthru_is_u2f_device(fd)) {
+ qemu_close(fd);
+ error_setg(errp, "%s: Passed hidraw does not represent "
+ "a U2F HID device", TYPE_U2F_PASSTHRU);
+ return;
+ }
}
key->hidraw_fd = fd;
u2f_passthru_reset(key);