From 173bfbf7886608a4a7abbfac6a42ac4bf4a3432d Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 20 Sep 2020 16:14:20 +0100 Subject: New upstream version 1.5.0 --- src/hid_osx.c | 323 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 93 deletions(-) (limited to 'src/hid_osx.c') diff --git a/src/hid_osx.c b/src/hid_osx.c index 5c40747..6be5cd7 100644 --- a/src/hid_osx.c +++ b/src/hid_osx.c @@ -19,11 +19,13 @@ #include "fido.h" -#define REPORT_LEN 65 - -struct dev { +struct hid_osx { IOHIDDeviceRef ref; CFStringRef loop_id; + int report_pipe[2]; + size_t report_in_len; + size_t report_out_len; + unsigned char report[CTAP_MAX_REPORT_LEN]; }; static int @@ -64,7 +66,8 @@ get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len) return (-1); } - if (CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8) == false) { + if (CFStringGetCString(ref, buf, (long)len, + kCFStringEncodingUTF8) == false) { fido_log_debug("%s: CFStringGetCString", __func__); return (-1); } @@ -72,30 +75,35 @@ get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len) return (0); } -static bool -is_fido(IOHIDDeviceRef dev) +static int +get_report_len(IOHIDDeviceRef dev, int dir, size_t *report_len) { - uint32_t usage_page; - int32_t report_len; + CFStringRef key; + int32_t v; - if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey), - (int32_t *)&usage_page) != 0 || usage_page != 0xf1d0) - return (false); + if (dir == 0) + key = CFSTR(kIOHIDMaxInputReportSizeKey); + else + key = CFSTR(kIOHIDMaxOutputReportSizeKey); - if (get_int32(dev, CFSTR(kIOHIDMaxInputReportSizeKey), - &report_len) < 0 || report_len != REPORT_LEN - 1) { - fido_log_debug("%s: unsupported report len", __func__); - return (false); + if (get_int32(dev, key, &v) < 0) { + fido_log_debug("%s: get_int32/%d", __func__, dir); + return (-1); } - return (true); + if ((*report_len = (size_t)v) > CTAP_MAX_REPORT_LEN) { + fido_log_debug("%s: report_len=%zu", __func__, *report_len); + return (-1); + } + + return (0); } static int get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id) { int32_t vendor; - int32_t product; + int32_t product; if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 || vendor > UINT16_MAX) { @@ -175,6 +183,31 @@ get_path(IOHIDDeviceRef dev) return (strdup(path)); } +static bool +is_fido(IOHIDDeviceRef dev) +{ + char buf[32]; + uint32_t usage_page; + + if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey), + (int32_t *)&usage_page) < 0 || usage_page != 0xf1d0) + return (false); + + if (get_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)) < 0) { + fido_log_debug("%s: get_utf8 transport", __func__); + return (false); + } + +#ifndef FIDO_HID_ANY + if (strcasecmp(buf, "usb") != 0) { + fido_log_debug("%s: transport", __func__); + return (false); + } +#endif + + return (true); +} + static int copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev) { @@ -199,11 +232,12 @@ copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev) int fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { - IOHIDManagerRef manager = NULL; - CFSetRef devset = NULL; - CFIndex devcnt; - IOHIDDeviceRef *devs = NULL; - int r = FIDO_ERR_INTERNAL; + IOHIDManagerRef manager = NULL; + CFSetRef devset = NULL; + size_t devcnt; + CFIndex n; + IOHIDDeviceRef *devs = NULL; + int r = FIDO_ERR_INTERNAL; *olen = 0; @@ -226,11 +260,13 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) goto fail; } - if ((devcnt = CFSetGetCount(devset)) < 0) { + if ((n = CFSetGetCount(devset)) < 0) { fido_log_debug("%s: CFSetGetCount", __func__); goto fail; } + devcnt = (size_t)n; + if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; @@ -238,7 +274,7 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) CFSetGetValues(devset, (void *)devs); - for (CFIndex i = 0; i < devcnt; i++) { + for (size_t i = 0; i < devcnt; i++) { if (copy_info(&devlist[*olen], devs[i]) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, @@ -263,157 +299,258 @@ fail: return (r); } +static void +report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type, + uint32_t id, uint8_t *ptr, CFIndex len) +{ + struct hid_osx *ctx = context; + ssize_t r; + + (void)dev; + + if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput || + id != 0 || len < 0 || (size_t)len != ctx->report_in_len) { + fido_log_debug("%s: io error", __func__); + return; + } + + if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) < 0 || + (size_t)r != (size_t)len) { + fido_log_debug("%s: write", __func__); + return; + } +} + +static void +removal_callback(void *context, IOReturn result, void *sender) +{ + (void)context; + (void)result; + (void)sender; + + CFRunLoopStop(CFRunLoopGetMain()); +} + +static int +set_nonblock(int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL)) == -1) { + fido_log_debug("%s: fcntl F_GETFL", __func__); + return (-1); + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + fido_log_debug("%s: fcntl, F_SETFL", __func__); + return (-1); + } + + return (0); +} + +static int +disable_sigpipe(int fd) +{ + int disabled = 1; + + if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) { + fido_log_debug("%s: fcntl F_SETNOSIGPIPE", __func__); + return (-1); + } + + return (0); +} + void * fido_hid_open(const char *path) { + struct hid_osx *ctx; io_registry_entry_t entry = MACH_PORT_NULL; - struct dev *dev = NULL; + char loop_id[32]; int ok = -1; int r; - char loop_id[32]; - if ((dev = calloc(1, sizeof(*dev))) == NULL) { + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } + ctx->report_pipe[0] = -1; + ctx->report_pipe[1] = -1; + + if (pipe(ctx->report_pipe) == -1) { + fido_log_debug("%s: pipe", __func__); + goto fail; + } + + if (set_nonblock(ctx->report_pipe[0]) < 0 || + set_nonblock(ctx->report_pipe[1]) < 0) { + fido_log_debug("%s: set_nonblock", __func__); + goto fail; + } + + if (disable_sigpipe(ctx->report_pipe[1]) < 0) { + fido_log_debug("%s: disable_sigpipe", __func__); + goto fail; + } + if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault, path)) == MACH_PORT_NULL) { fido_log_debug("%s: IORegistryEntryFromPath", __func__); goto fail; } - if ((dev->ref = IOHIDDeviceCreate(kCFAllocatorDefault, + if ((ctx->ref = IOHIDDeviceCreate(kCFAllocatorDefault, entry)) == NULL) { fido_log_debug("%s: IOHIDDeviceCreate", __func__); goto fail; } - if (IOHIDDeviceOpen(dev->ref, + if (get_report_len(ctx->ref, 0, &ctx->report_in_len) < 0 || + get_report_len(ctx->ref, 1, &ctx->report_out_len) < 0) { + fido_log_debug("%s: get_report_len", __func__); + goto fail; + } + + if (ctx->report_in_len > sizeof(ctx->report)) { + fido_log_debug("%s: report_in_len=%zu", __func__, + ctx->report_in_len); + goto fail; + } + + if (IOHIDDeviceOpen(ctx->ref, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { fido_log_debug("%s: IOHIDDeviceOpen", __func__); goto fail; } if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p", - (void *)dev->ref)) < 0 || (size_t)r >= sizeof(loop_id)) { + (void *)ctx->ref)) < 0 || (size_t)r >= sizeof(loop_id)) { fido_log_debug("%s: snprintf", __func__); goto fail; } - if ((dev->loop_id = CFStringCreateWithCString(NULL, loop_id, + if ((ctx->loop_id = CFStringCreateWithCString(NULL, loop_id, kCFStringEncodingASCII)) == NULL) { fido_log_debug("%s: CFStringCreateWithCString", __func__); goto fail; } + IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report, + (long)ctx->report_in_len, &report_callback, ctx); + IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx); + IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetMain(), + ctx->loop_id); + ok = 0; fail: if (entry != MACH_PORT_NULL) IOObjectRelease(entry); - if (ok < 0 && dev != NULL) { - if (dev->ref != NULL) - CFRelease(dev->ref); - if (dev->loop_id != NULL) - CFRelease(dev->loop_id); - free(dev); - dev = NULL; + if (ok < 0 && ctx != NULL) { + if (ctx->ref != NULL) + CFRelease(ctx->ref); + if (ctx->loop_id != NULL) + CFRelease(ctx->loop_id); + if (ctx->report_pipe[0] != -1) + close(ctx->report_pipe[0]); + if (ctx->report_pipe[1] != -1) + close(ctx->report_pipe[1]); + free(ctx); + ctx = NULL; } - return (dev); + return (ctx); } void fido_hid_close(void *handle) { - struct dev *dev = handle; + struct hid_osx *ctx = handle; + + IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report, + (long)ctx->report_in_len, NULL, ctx); + IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, NULL); + IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetMain(), + ctx->loop_id); - if (IOHIDDeviceClose(dev->ref, + if (IOHIDDeviceClose(ctx->ref, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) fido_log_debug("%s: IOHIDDeviceClose", __func__); - CFRelease(dev->ref); - CFRelease(dev->loop_id); - - free(dev); -} + CFRelease(ctx->ref); + CFRelease(ctx->loop_id); -static void -read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type, - uint32_t report_id, uint8_t *report, CFIndex report_len) -{ - (void)context; - (void)dev; - (void)report; + explicit_bzero(ctx->report, sizeof(ctx->report)); + close(ctx->report_pipe[0]); + close(ctx->report_pipe[1]); - if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput || - report_id != 0 || report_len != REPORT_LEN - 1) { - fido_log_debug("%s: io error", __func__); - } -} - -static void -removal_callback(void *context, IOReturn result, void *sender) -{ - (void)context; - (void)result; - (void)sender; - - CFRunLoopStop(CFRunLoopGetCurrent()); + free(ctx); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { - struct dev *dev = handle; - CFRunLoopRunResult r; + struct hid_osx *ctx = handle; + ssize_t r; - (void)ms; /* XXX */ + explicit_bzero(buf, len); + explicit_bzero(ctx->report, sizeof(ctx->report)); - if (len != REPORT_LEN - 1) { - fido_log_debug("%s: invalid len", __func__); + if (len != ctx->report_in_len || len > sizeof(ctx->report)) { + fido_log_debug("%s: len %zu", __func__, len); return (-1); } - explicit_bzero(buf, len); - - IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, - &read_callback, NULL); - IOHIDDeviceRegisterRemovalCallback(dev->ref, &removal_callback, dev); - IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(), - dev->loop_id); + if (ms == -1) + ms = 5000; /* wait 5 seconds by default */ - r = CFRunLoopRunInMode(dev->loop_id, 0.3, true); + if (CFRunLoopGetCurrent() != CFRunLoopGetMain()) + fido_log_debug("%s: CFRunLoopGetCurrent != CFRunLoopGetMain", + __func__); - IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL); - IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL); - IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(), - dev->loop_id); + CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true); - if (r != kCFRunLoopRunHandledSource) { - fido_log_debug("%s: CFRunLoopRunInMode=%d", __func__, (int)r); + if ((r = read(ctx->report_pipe[0], buf, len)) < 0 || (size_t)r != len) { + fido_log_debug("%s: read", __func__); return (-1); } - return (REPORT_LEN - 1); + return ((int)len); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { - struct dev *dev = handle; + struct hid_osx *ctx = handle; - if (len != REPORT_LEN) { - fido_log_debug("%s: invalid len", __func__); + if (len != ctx->report_out_len + 1 || len > LONG_MAX) { + fido_log_debug("%s: len %zu", __func__, len); return (-1); } - if (IOHIDDeviceSetReport(dev->ref, kIOHIDReportTypeOutput, 0, buf + 1, - len - 1) != kIOReturnSuccess) { + if (IOHIDDeviceSetReport(ctx->ref, kIOHIDReportTypeOutput, 0, buf + 1, + (long)(len - 1)) != kIOReturnSuccess) { fido_log_debug("%s: IOHIDDeviceSetReport", __func__); return (-1); } - return (REPORT_LEN); + return ((int)len); +} + +size_t +fido_hid_report_in_len(void *handle) +{ + struct hid_osx *ctx = handle; + + return (ctx->report_in_len); +} + +size_t +fido_hid_report_out_len(void *handle) +{ + struct hid_osx *ctx = handle; + + return (ctx->report_out_len); } -- cgit v1.2.3