10afa8e06SEd Maste /*
2*2ccfa855SEd Maste * Copyright (c) 2019-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste */
70afa8e06SEd Maste
80afa8e06SEd Maste #include <sys/types.h>
90afa8e06SEd Maste
100afa8e06SEd Maste #include <errno.h>
110afa8e06SEd Maste #include <fcntl.h>
120afa8e06SEd Maste #include <signal.h>
130afa8e06SEd Maste #include <unistd.h>
140afa8e06SEd Maste
15f540a430SEd Maste #include <Availability.h>
160afa8e06SEd Maste #include <CoreFoundation/CoreFoundation.h>
170afa8e06SEd Maste #include <IOKit/IOKitLib.h>
180afa8e06SEd Maste #include <IOKit/hid/IOHIDKeys.h>
190afa8e06SEd Maste #include <IOKit/hid/IOHIDManager.h>
200afa8e06SEd Maste
210afa8e06SEd Maste #include "fido.h"
220afa8e06SEd Maste
23f540a430SEd Maste #if __MAC_OS_X_VERSION_MIN_REQUIRED < 120000
24f540a430SEd Maste #define kIOMainPortDefault kIOMasterPortDefault
25f540a430SEd Maste #endif
26f540a430SEd Maste
273e696dfbSEd Maste #define IOREG "ioreg://"
283e696dfbSEd Maste
290afa8e06SEd Maste struct hid_osx {
300afa8e06SEd Maste IOHIDDeviceRef ref;
310afa8e06SEd Maste CFStringRef loop_id;
320afa8e06SEd Maste int report_pipe[2];
330afa8e06SEd Maste size_t report_in_len;
340afa8e06SEd Maste size_t report_out_len;
350afa8e06SEd Maste unsigned char report[CTAP_MAX_REPORT_LEN];
360afa8e06SEd Maste };
370afa8e06SEd Maste
380afa8e06SEd Maste static int
get_int32(IOHIDDeviceRef dev,CFStringRef key,int32_t * v)390afa8e06SEd Maste get_int32(IOHIDDeviceRef dev, CFStringRef key, int32_t *v)
400afa8e06SEd Maste {
410afa8e06SEd Maste CFTypeRef ref;
420afa8e06SEd Maste
430afa8e06SEd Maste if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
440afa8e06SEd Maste CFGetTypeID(ref) != CFNumberGetTypeID()) {
450afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
460afa8e06SEd Maste return (-1);
470afa8e06SEd Maste }
480afa8e06SEd Maste
490afa8e06SEd Maste if (CFNumberGetType(ref) != kCFNumberSInt32Type &&
500afa8e06SEd Maste CFNumberGetType(ref) != kCFNumberSInt64Type) {
510afa8e06SEd Maste fido_log_debug("%s: CFNumberGetType", __func__);
520afa8e06SEd Maste return (-1);
530afa8e06SEd Maste }
540afa8e06SEd Maste
550afa8e06SEd Maste if (CFNumberGetValue(ref, kCFNumberSInt32Type, v) == false) {
560afa8e06SEd Maste fido_log_debug("%s: CFNumberGetValue", __func__);
570afa8e06SEd Maste return (-1);
580afa8e06SEd Maste }
590afa8e06SEd Maste
600afa8e06SEd Maste return (0);
610afa8e06SEd Maste }
620afa8e06SEd Maste
630afa8e06SEd Maste static int
get_utf8(IOHIDDeviceRef dev,CFStringRef key,void * buf,size_t len)640afa8e06SEd Maste get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
650afa8e06SEd Maste {
660afa8e06SEd Maste CFTypeRef ref;
670afa8e06SEd Maste
680afa8e06SEd Maste memset(buf, 0, len);
690afa8e06SEd Maste
700afa8e06SEd Maste if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
710afa8e06SEd Maste CFGetTypeID(ref) != CFStringGetTypeID()) {
720afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
730afa8e06SEd Maste return (-1);
740afa8e06SEd Maste }
750afa8e06SEd Maste
760afa8e06SEd Maste if (CFStringGetCString(ref, buf, (long)len,
770afa8e06SEd Maste kCFStringEncodingUTF8) == false) {
780afa8e06SEd Maste fido_log_debug("%s: CFStringGetCString", __func__);
790afa8e06SEd Maste return (-1);
800afa8e06SEd Maste }
810afa8e06SEd Maste
820afa8e06SEd Maste return (0);
830afa8e06SEd Maste }
840afa8e06SEd Maste
850afa8e06SEd Maste static int
get_report_len(IOHIDDeviceRef dev,int dir,size_t * report_len)860afa8e06SEd Maste get_report_len(IOHIDDeviceRef dev, int dir, size_t *report_len)
870afa8e06SEd Maste {
880afa8e06SEd Maste CFStringRef key;
890afa8e06SEd Maste int32_t v;
900afa8e06SEd Maste
910afa8e06SEd Maste if (dir == 0)
920afa8e06SEd Maste key = CFSTR(kIOHIDMaxInputReportSizeKey);
930afa8e06SEd Maste else
940afa8e06SEd Maste key = CFSTR(kIOHIDMaxOutputReportSizeKey);
950afa8e06SEd Maste
960afa8e06SEd Maste if (get_int32(dev, key, &v) < 0) {
970afa8e06SEd Maste fido_log_debug("%s: get_int32/%d", __func__, dir);
980afa8e06SEd Maste return (-1);
990afa8e06SEd Maste }
1000afa8e06SEd Maste
1010afa8e06SEd Maste if ((*report_len = (size_t)v) > CTAP_MAX_REPORT_LEN) {
1020afa8e06SEd Maste fido_log_debug("%s: report_len=%zu", __func__, *report_len);
1030afa8e06SEd Maste return (-1);
1040afa8e06SEd Maste }
1050afa8e06SEd Maste
1060afa8e06SEd Maste return (0);
1070afa8e06SEd Maste }
1080afa8e06SEd Maste
1090afa8e06SEd Maste static int
get_id(IOHIDDeviceRef dev,int16_t * vendor_id,int16_t * product_id)1100afa8e06SEd Maste get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id)
1110afa8e06SEd Maste {
1120afa8e06SEd Maste int32_t vendor;
1130afa8e06SEd Maste int32_t product;
1140afa8e06SEd Maste
1150afa8e06SEd Maste if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 ||
1160afa8e06SEd Maste vendor > UINT16_MAX) {
1170afa8e06SEd Maste fido_log_debug("%s: get_int32 vendor", __func__);
1180afa8e06SEd Maste return (-1);
1190afa8e06SEd Maste }
1200afa8e06SEd Maste
1210afa8e06SEd Maste if (get_int32(dev, CFSTR(kIOHIDProductIDKey), &product) < 0 ||
1220afa8e06SEd Maste product > UINT16_MAX) {
1230afa8e06SEd Maste fido_log_debug("%s: get_int32 product", __func__);
1240afa8e06SEd Maste return (-1);
1250afa8e06SEd Maste }
1260afa8e06SEd Maste
1270afa8e06SEd Maste *vendor_id = (int16_t)vendor;
1280afa8e06SEd Maste *product_id = (int16_t)product;
1290afa8e06SEd Maste
1300afa8e06SEd Maste return (0);
1310afa8e06SEd Maste }
1320afa8e06SEd Maste
1330afa8e06SEd Maste static int
get_str(IOHIDDeviceRef dev,char ** manufacturer,char ** product)1340afa8e06SEd Maste get_str(IOHIDDeviceRef dev, char **manufacturer, char **product)
1350afa8e06SEd Maste {
1360afa8e06SEd Maste char buf[512];
1370afa8e06SEd Maste int ok = -1;
1380afa8e06SEd Maste
1390afa8e06SEd Maste *manufacturer = NULL;
1400afa8e06SEd Maste *product = NULL;
1410afa8e06SEd Maste
142f540a430SEd Maste if (get_utf8(dev, CFSTR(kIOHIDManufacturerKey), buf, sizeof(buf)) < 0)
143f540a430SEd Maste *manufacturer = strdup("");
144f540a430SEd Maste else
145f540a430SEd Maste *manufacturer = strdup(buf);
1460afa8e06SEd Maste
147f540a430SEd Maste if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0)
148f540a430SEd Maste *product = strdup("");
149f540a430SEd Maste else
150f540a430SEd Maste *product = strdup(buf);
1510afa8e06SEd Maste
152f540a430SEd Maste if (*manufacturer == NULL || *product == NULL) {
153f540a430SEd Maste fido_log_debug("%s: strdup", __func__);
1540afa8e06SEd Maste goto fail;
1550afa8e06SEd Maste }
1560afa8e06SEd Maste
1570afa8e06SEd Maste ok = 0;
1580afa8e06SEd Maste fail:
1590afa8e06SEd Maste if (ok < 0) {
1600afa8e06SEd Maste free(*manufacturer);
1610afa8e06SEd Maste free(*product);
1620afa8e06SEd Maste *manufacturer = NULL;
1630afa8e06SEd Maste *product = NULL;
1640afa8e06SEd Maste }
1650afa8e06SEd Maste
1660afa8e06SEd Maste return (ok);
1670afa8e06SEd Maste }
1680afa8e06SEd Maste
1690afa8e06SEd Maste static char *
get_path(IOHIDDeviceRef dev)1700afa8e06SEd Maste get_path(IOHIDDeviceRef dev)
1710afa8e06SEd Maste {
1720afa8e06SEd Maste io_service_t s;
1733e696dfbSEd Maste uint64_t id;
1743e696dfbSEd Maste char *path;
1750afa8e06SEd Maste
1760afa8e06SEd Maste if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) {
1770afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceGetService", __func__);
1780afa8e06SEd Maste return (NULL);
1790afa8e06SEd Maste }
1800afa8e06SEd Maste
1813e696dfbSEd Maste if (IORegistryEntryGetRegistryEntryID(s, &id) != KERN_SUCCESS) {
1823e696dfbSEd Maste fido_log_debug("%s: IORegistryEntryGetRegistryEntryID",
1833e696dfbSEd Maste __func__);
1840afa8e06SEd Maste return (NULL);
1850afa8e06SEd Maste }
1860afa8e06SEd Maste
1873e696dfbSEd Maste if (asprintf(&path, "%s%llu", IOREG, (unsigned long long)id) == -1) {
1883e696dfbSEd Maste fido_log_error(errno, "%s: asprintf", __func__);
1893e696dfbSEd Maste return (NULL);
1903e696dfbSEd Maste }
1913e696dfbSEd Maste
1923e696dfbSEd Maste return (path);
1930afa8e06SEd Maste }
1940afa8e06SEd Maste
1950afa8e06SEd Maste static bool
is_fido(IOHIDDeviceRef dev)1960afa8e06SEd Maste is_fido(IOHIDDeviceRef dev)
1970afa8e06SEd Maste {
1980afa8e06SEd Maste char buf[32];
1990afa8e06SEd Maste uint32_t usage_page;
2000afa8e06SEd Maste
2010afa8e06SEd Maste if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey),
2020afa8e06SEd Maste (int32_t *)&usage_page) < 0 || usage_page != 0xf1d0)
2030afa8e06SEd Maste return (false);
2040afa8e06SEd Maste
2050afa8e06SEd Maste if (get_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)) < 0) {
2060afa8e06SEd Maste fido_log_debug("%s: get_utf8 transport", __func__);
2070afa8e06SEd Maste return (false);
2080afa8e06SEd Maste }
2090afa8e06SEd Maste
2100afa8e06SEd Maste #ifndef FIDO_HID_ANY
2110afa8e06SEd Maste if (strcasecmp(buf, "usb") != 0) {
2120afa8e06SEd Maste fido_log_debug("%s: transport", __func__);
2130afa8e06SEd Maste return (false);
2140afa8e06SEd Maste }
2150afa8e06SEd Maste #endif
2160afa8e06SEd Maste
2170afa8e06SEd Maste return (true);
2180afa8e06SEd Maste }
2190afa8e06SEd Maste
2200afa8e06SEd Maste static int
copy_info(fido_dev_info_t * di,IOHIDDeviceRef dev)2210afa8e06SEd Maste copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
2220afa8e06SEd Maste {
2230afa8e06SEd Maste memset(di, 0, sizeof(*di));
2240afa8e06SEd Maste
2250afa8e06SEd Maste if (is_fido(dev) == false)
2260afa8e06SEd Maste return (-1);
2270afa8e06SEd Maste
2280afa8e06SEd Maste if (get_id(dev, &di->vendor_id, &di->product_id) < 0 ||
2290afa8e06SEd Maste get_str(dev, &di->manufacturer, &di->product) < 0 ||
2300afa8e06SEd Maste (di->path = get_path(dev)) == NULL) {
2310afa8e06SEd Maste free(di->path);
2320afa8e06SEd Maste free(di->manufacturer);
2330afa8e06SEd Maste free(di->product);
2340afa8e06SEd Maste explicit_bzero(di, sizeof(*di));
2350afa8e06SEd Maste return (-1);
2360afa8e06SEd Maste }
2370afa8e06SEd Maste
2380afa8e06SEd Maste return (0);
2390afa8e06SEd Maste }
2400afa8e06SEd Maste
2410afa8e06SEd Maste int
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)2420afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
2430afa8e06SEd Maste {
2440afa8e06SEd Maste IOHIDManagerRef manager = NULL;
2450afa8e06SEd Maste CFSetRef devset = NULL;
2460afa8e06SEd Maste size_t devcnt;
2470afa8e06SEd Maste CFIndex n;
2480afa8e06SEd Maste IOHIDDeviceRef *devs = NULL;
2490afa8e06SEd Maste int r = FIDO_ERR_INTERNAL;
2500afa8e06SEd Maste
2510afa8e06SEd Maste *olen = 0;
2520afa8e06SEd Maste
2530afa8e06SEd Maste if (ilen == 0)
2540afa8e06SEd Maste return (FIDO_OK); /* nothing to do */
2550afa8e06SEd Maste
2560afa8e06SEd Maste if (devlist == NULL)
2570afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT);
2580afa8e06SEd Maste
2590afa8e06SEd Maste if ((manager = IOHIDManagerCreate(kCFAllocatorDefault,
2600afa8e06SEd Maste kIOHIDManagerOptionNone)) == NULL) {
2610afa8e06SEd Maste fido_log_debug("%s: IOHIDManagerCreate", __func__);
2620afa8e06SEd Maste goto fail;
2630afa8e06SEd Maste }
2640afa8e06SEd Maste
2650afa8e06SEd Maste IOHIDManagerSetDeviceMatching(manager, NULL);
2660afa8e06SEd Maste
2670afa8e06SEd Maste if ((devset = IOHIDManagerCopyDevices(manager)) == NULL) {
2680afa8e06SEd Maste fido_log_debug("%s: IOHIDManagerCopyDevices", __func__);
2690afa8e06SEd Maste goto fail;
2700afa8e06SEd Maste }
2710afa8e06SEd Maste
2720afa8e06SEd Maste if ((n = CFSetGetCount(devset)) < 0) {
2730afa8e06SEd Maste fido_log_debug("%s: CFSetGetCount", __func__);
2740afa8e06SEd Maste goto fail;
2750afa8e06SEd Maste }
2760afa8e06SEd Maste
2770afa8e06SEd Maste devcnt = (size_t)n;
2780afa8e06SEd Maste
2790afa8e06SEd Maste if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) {
2800afa8e06SEd Maste fido_log_debug("%s: calloc", __func__);
2810afa8e06SEd Maste goto fail;
2820afa8e06SEd Maste }
2830afa8e06SEd Maste
2840afa8e06SEd Maste CFSetGetValues(devset, (void *)devs);
2850afa8e06SEd Maste
2860afa8e06SEd Maste for (size_t i = 0; i < devcnt; i++) {
2870afa8e06SEd Maste if (copy_info(&devlist[*olen], devs[i]) == 0) {
2880afa8e06SEd Maste devlist[*olen].io = (fido_dev_io_t) {
2890afa8e06SEd Maste fido_hid_open,
2900afa8e06SEd Maste fido_hid_close,
2910afa8e06SEd Maste fido_hid_read,
2920afa8e06SEd Maste fido_hid_write,
2930afa8e06SEd Maste };
2940afa8e06SEd Maste if (++(*olen) == ilen)
2950afa8e06SEd Maste break;
2960afa8e06SEd Maste }
2970afa8e06SEd Maste }
2980afa8e06SEd Maste
2990afa8e06SEd Maste r = FIDO_OK;
3000afa8e06SEd Maste fail:
3010afa8e06SEd Maste if (manager != NULL)
3020afa8e06SEd Maste CFRelease(manager);
3030afa8e06SEd Maste if (devset != NULL)
3040afa8e06SEd Maste CFRelease(devset);
3050afa8e06SEd Maste
3060afa8e06SEd Maste free(devs);
3070afa8e06SEd Maste
3080afa8e06SEd Maste return (r);
3090afa8e06SEd Maste }
3100afa8e06SEd Maste
3110afa8e06SEd Maste static void
report_callback(void * context,IOReturn result,void * dev,IOHIDReportType type,uint32_t id,uint8_t * ptr,CFIndex len)3120afa8e06SEd Maste report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
3130afa8e06SEd Maste uint32_t id, uint8_t *ptr, CFIndex len)
3140afa8e06SEd Maste {
3150afa8e06SEd Maste struct hid_osx *ctx = context;
3160afa8e06SEd Maste ssize_t r;
3170afa8e06SEd Maste
3180afa8e06SEd Maste (void)dev;
3190afa8e06SEd Maste
3200afa8e06SEd Maste if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
3210afa8e06SEd Maste id != 0 || len < 0 || (size_t)len != ctx->report_in_len) {
3220afa8e06SEd Maste fido_log_debug("%s: io error", __func__);
3230afa8e06SEd Maste return;
3240afa8e06SEd Maste }
3250afa8e06SEd Maste
3260afa8e06SEd Maste if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) == -1) {
3270afa8e06SEd Maste fido_log_error(errno, "%s: write", __func__);
3280afa8e06SEd Maste return;
3290afa8e06SEd Maste }
3300afa8e06SEd Maste
3310afa8e06SEd Maste if (r < 0 || (size_t)r != (size_t)len) {
3320afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, (size_t)len);
3330afa8e06SEd Maste return;
3340afa8e06SEd Maste }
3350afa8e06SEd Maste }
3360afa8e06SEd Maste
3370afa8e06SEd Maste static void
removal_callback(void * context,IOReturn result,void * sender)3380afa8e06SEd Maste removal_callback(void *context, IOReturn result, void *sender)
3390afa8e06SEd Maste {
3400afa8e06SEd Maste (void)context;
3410afa8e06SEd Maste (void)result;
3420afa8e06SEd Maste (void)sender;
3430afa8e06SEd Maste
3440afa8e06SEd Maste CFRunLoopStop(CFRunLoopGetCurrent());
3450afa8e06SEd Maste }
3460afa8e06SEd Maste
3470afa8e06SEd Maste static int
set_nonblock(int fd)3480afa8e06SEd Maste set_nonblock(int fd)
3490afa8e06SEd Maste {
3500afa8e06SEd Maste int flags;
3510afa8e06SEd Maste
3520afa8e06SEd Maste if ((flags = fcntl(fd, F_GETFL)) == -1) {
3530afa8e06SEd Maste fido_log_error(errno, "%s: fcntl F_GETFL", __func__);
3540afa8e06SEd Maste return (-1);
3550afa8e06SEd Maste }
3560afa8e06SEd Maste
3570afa8e06SEd Maste if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
3580afa8e06SEd Maste fido_log_error(errno, "%s: fcntl F_SETFL", __func__);
3590afa8e06SEd Maste return (-1);
3600afa8e06SEd Maste }
3610afa8e06SEd Maste
3620afa8e06SEd Maste return (0);
3630afa8e06SEd Maste }
3640afa8e06SEd Maste
3650afa8e06SEd Maste static int
disable_sigpipe(int fd)3660afa8e06SEd Maste disable_sigpipe(int fd)
3670afa8e06SEd Maste {
3680afa8e06SEd Maste int disabled = 1;
3690afa8e06SEd Maste
3700afa8e06SEd Maste if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) {
3710afa8e06SEd Maste fido_log_error(errno, "%s: fcntl F_SETNOSIGPIPE", __func__);
3720afa8e06SEd Maste return (-1);
3730afa8e06SEd Maste }
3740afa8e06SEd Maste
3750afa8e06SEd Maste return (0);
3760afa8e06SEd Maste }
3770afa8e06SEd Maste
3783e696dfbSEd Maste static io_registry_entry_t
get_ioreg_entry(const char * path)3793e696dfbSEd Maste get_ioreg_entry(const char *path)
3803e696dfbSEd Maste {
3813e696dfbSEd Maste uint64_t id;
3823e696dfbSEd Maste
3833e696dfbSEd Maste if (strncmp(path, IOREG, strlen(IOREG)) != 0)
3843e696dfbSEd Maste return (IORegistryEntryFromPath(kIOMainPortDefault, path));
3853e696dfbSEd Maste
386*2ccfa855SEd Maste if (fido_to_uint64(path + strlen(IOREG), 10, &id) == -1) {
387*2ccfa855SEd Maste fido_log_debug("%s: fido_to_uint64", __func__);
3883e696dfbSEd Maste return (MACH_PORT_NULL);
3893e696dfbSEd Maste }
3903e696dfbSEd Maste
3913e696dfbSEd Maste return (IOServiceGetMatchingService(kIOMainPortDefault,
3923e696dfbSEd Maste IORegistryEntryIDMatching(id)));
3933e696dfbSEd Maste }
3943e696dfbSEd Maste
3950afa8e06SEd Maste void *
fido_hid_open(const char * path)3960afa8e06SEd Maste fido_hid_open(const char *path)
3970afa8e06SEd Maste {
3980afa8e06SEd Maste struct hid_osx *ctx;
3990afa8e06SEd Maste io_registry_entry_t entry = MACH_PORT_NULL;
4000afa8e06SEd Maste char loop_id[32];
4010afa8e06SEd Maste int ok = -1;
4020afa8e06SEd Maste int r;
4030afa8e06SEd Maste
4040afa8e06SEd Maste if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
4050afa8e06SEd Maste fido_log_debug("%s: calloc", __func__);
4060afa8e06SEd Maste goto fail;
4070afa8e06SEd Maste }
4080afa8e06SEd Maste
4090afa8e06SEd Maste ctx->report_pipe[0] = -1;
4100afa8e06SEd Maste ctx->report_pipe[1] = -1;
4110afa8e06SEd Maste
4120afa8e06SEd Maste if (pipe(ctx->report_pipe) == -1) {
4130afa8e06SEd Maste fido_log_error(errno, "%s: pipe", __func__);
4140afa8e06SEd Maste goto fail;
4150afa8e06SEd Maste }
4160afa8e06SEd Maste
4170afa8e06SEd Maste if (set_nonblock(ctx->report_pipe[0]) < 0 ||
4180afa8e06SEd Maste set_nonblock(ctx->report_pipe[1]) < 0) {
4190afa8e06SEd Maste fido_log_debug("%s: set_nonblock", __func__);
4200afa8e06SEd Maste goto fail;
4210afa8e06SEd Maste }
4220afa8e06SEd Maste
4230afa8e06SEd Maste if (disable_sigpipe(ctx->report_pipe[1]) < 0) {
4240afa8e06SEd Maste fido_log_debug("%s: disable_sigpipe", __func__);
4250afa8e06SEd Maste goto fail;
4260afa8e06SEd Maste }
4270afa8e06SEd Maste
4283e696dfbSEd Maste if ((entry = get_ioreg_entry(path)) == MACH_PORT_NULL) {
4293e696dfbSEd Maste fido_log_debug("%s: get_ioreg_entry: %s", __func__, path);
4300afa8e06SEd Maste goto fail;
4310afa8e06SEd Maste }
4320afa8e06SEd Maste
4330afa8e06SEd Maste if ((ctx->ref = IOHIDDeviceCreate(kCFAllocatorDefault,
4340afa8e06SEd Maste entry)) == NULL) {
4350afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceCreate", __func__);
4360afa8e06SEd Maste goto fail;
4370afa8e06SEd Maste }
4380afa8e06SEd Maste
4390afa8e06SEd Maste if (get_report_len(ctx->ref, 0, &ctx->report_in_len) < 0 ||
4400afa8e06SEd Maste get_report_len(ctx->ref, 1, &ctx->report_out_len) < 0) {
4410afa8e06SEd Maste fido_log_debug("%s: get_report_len", __func__);
4420afa8e06SEd Maste goto fail;
4430afa8e06SEd Maste }
4440afa8e06SEd Maste
4450afa8e06SEd Maste if (ctx->report_in_len > sizeof(ctx->report)) {
4460afa8e06SEd Maste fido_log_debug("%s: report_in_len=%zu", __func__,
4470afa8e06SEd Maste ctx->report_in_len);
4480afa8e06SEd Maste goto fail;
4490afa8e06SEd Maste }
4500afa8e06SEd Maste
4510afa8e06SEd Maste if (IOHIDDeviceOpen(ctx->ref,
4520afa8e06SEd Maste kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
4530afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceOpen", __func__);
4540afa8e06SEd Maste goto fail;
4550afa8e06SEd Maste }
4560afa8e06SEd Maste
4570afa8e06SEd Maste if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p",
4580afa8e06SEd Maste (void *)ctx->ref)) < 0 || (size_t)r >= sizeof(loop_id)) {
4590afa8e06SEd Maste fido_log_debug("%s: snprintf", __func__);
4600afa8e06SEd Maste goto fail;
4610afa8e06SEd Maste }
4620afa8e06SEd Maste
4630afa8e06SEd Maste if ((ctx->loop_id = CFStringCreateWithCString(NULL, loop_id,
4640afa8e06SEd Maste kCFStringEncodingASCII)) == NULL) {
4650afa8e06SEd Maste fido_log_debug("%s: CFStringCreateWithCString", __func__);
4660afa8e06SEd Maste goto fail;
4670afa8e06SEd Maste }
4680afa8e06SEd Maste
4690afa8e06SEd Maste IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
4700afa8e06SEd Maste (long)ctx->report_in_len, &report_callback, ctx);
4710afa8e06SEd Maste IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx);
4720afa8e06SEd Maste
4730afa8e06SEd Maste ok = 0;
4740afa8e06SEd Maste fail:
4750afa8e06SEd Maste if (entry != MACH_PORT_NULL)
4760afa8e06SEd Maste IOObjectRelease(entry);
4770afa8e06SEd Maste
4780afa8e06SEd Maste if (ok < 0 && ctx != NULL) {
4790afa8e06SEd Maste if (ctx->ref != NULL)
4800afa8e06SEd Maste CFRelease(ctx->ref);
4810afa8e06SEd Maste if (ctx->loop_id != NULL)
4820afa8e06SEd Maste CFRelease(ctx->loop_id);
4830afa8e06SEd Maste if (ctx->report_pipe[0] != -1)
4840afa8e06SEd Maste close(ctx->report_pipe[0]);
4850afa8e06SEd Maste if (ctx->report_pipe[1] != -1)
4860afa8e06SEd Maste close(ctx->report_pipe[1]);
4870afa8e06SEd Maste free(ctx);
4880afa8e06SEd Maste ctx = NULL;
4890afa8e06SEd Maste }
4900afa8e06SEd Maste
4910afa8e06SEd Maste return (ctx);
4920afa8e06SEd Maste }
4930afa8e06SEd Maste
4940afa8e06SEd Maste void
fido_hid_close(void * handle)4950afa8e06SEd Maste fido_hid_close(void *handle)
4960afa8e06SEd Maste {
4970afa8e06SEd Maste struct hid_osx *ctx = handle;
4980afa8e06SEd Maste
4990afa8e06SEd Maste IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
5000afa8e06SEd Maste (long)ctx->report_in_len, NULL, ctx);
5010afa8e06SEd Maste IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, ctx);
5020afa8e06SEd Maste
5030afa8e06SEd Maste if (IOHIDDeviceClose(ctx->ref,
5040afa8e06SEd Maste kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess)
5050afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceClose", __func__);
5060afa8e06SEd Maste
5070afa8e06SEd Maste CFRelease(ctx->ref);
5080afa8e06SEd Maste CFRelease(ctx->loop_id);
5090afa8e06SEd Maste
5100afa8e06SEd Maste explicit_bzero(ctx->report, sizeof(ctx->report));
5110afa8e06SEd Maste close(ctx->report_pipe[0]);
5120afa8e06SEd Maste close(ctx->report_pipe[1]);
5130afa8e06SEd Maste
5140afa8e06SEd Maste free(ctx);
5150afa8e06SEd Maste }
5160afa8e06SEd Maste
5170afa8e06SEd Maste int
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)5180afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
5190afa8e06SEd Maste {
5200afa8e06SEd Maste (void)handle;
5210afa8e06SEd Maste (void)sigmask;
5220afa8e06SEd Maste
5230afa8e06SEd Maste return (FIDO_ERR_INTERNAL);
5240afa8e06SEd Maste }
5250afa8e06SEd Maste
5260afa8e06SEd Maste int
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)5270afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
5280afa8e06SEd Maste {
5290afa8e06SEd Maste struct hid_osx *ctx = handle;
5300afa8e06SEd Maste ssize_t r;
5310afa8e06SEd Maste
5320afa8e06SEd Maste explicit_bzero(buf, len);
5330afa8e06SEd Maste explicit_bzero(ctx->report, sizeof(ctx->report));
5340afa8e06SEd Maste
5350afa8e06SEd Maste if (len != ctx->report_in_len || len > sizeof(ctx->report)) {
5360afa8e06SEd Maste fido_log_debug("%s: len %zu", __func__, len);
5370afa8e06SEd Maste return (-1);
5380afa8e06SEd Maste }
5390afa8e06SEd Maste
5400afa8e06SEd Maste IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetCurrent(),
5410afa8e06SEd Maste ctx->loop_id);
5420afa8e06SEd Maste
5430afa8e06SEd Maste if (ms == -1)
5440afa8e06SEd Maste ms = 5000; /* wait 5 seconds by default */
5450afa8e06SEd Maste
5460afa8e06SEd Maste CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);
5470afa8e06SEd Maste
5480afa8e06SEd Maste IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetCurrent(),
5490afa8e06SEd Maste ctx->loop_id);
5500afa8e06SEd Maste
5510afa8e06SEd Maste if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
5520afa8e06SEd Maste fido_log_error(errno, "%s: read", __func__);
5530afa8e06SEd Maste return (-1);
5540afa8e06SEd Maste }
5550afa8e06SEd Maste
5560afa8e06SEd Maste if (r < 0 || (size_t)r != len) {
5570afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, len);
5580afa8e06SEd Maste return (-1);
5590afa8e06SEd Maste }
5600afa8e06SEd Maste
5610afa8e06SEd Maste return ((int)len);
5620afa8e06SEd Maste }
5630afa8e06SEd Maste
5640afa8e06SEd Maste int
fido_hid_write(void * handle,const unsigned char * buf,size_t len)5650afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len)
5660afa8e06SEd Maste {
5670afa8e06SEd Maste struct hid_osx *ctx = handle;
5680afa8e06SEd Maste
5690afa8e06SEd Maste if (len != ctx->report_out_len + 1 || len > LONG_MAX) {
5700afa8e06SEd Maste fido_log_debug("%s: len %zu", __func__, len);
5710afa8e06SEd Maste return (-1);
5720afa8e06SEd Maste }
5730afa8e06SEd Maste
5740afa8e06SEd Maste if (IOHIDDeviceSetReport(ctx->ref, kIOHIDReportTypeOutput, 0, buf + 1,
5750afa8e06SEd Maste (long)(len - 1)) != kIOReturnSuccess) {
5760afa8e06SEd Maste fido_log_debug("%s: IOHIDDeviceSetReport", __func__);
5770afa8e06SEd Maste return (-1);
5780afa8e06SEd Maste }
5790afa8e06SEd Maste
5800afa8e06SEd Maste return ((int)len);
5810afa8e06SEd Maste }
5820afa8e06SEd Maste
5830afa8e06SEd Maste size_t
fido_hid_report_in_len(void * handle)5840afa8e06SEd Maste fido_hid_report_in_len(void *handle)
5850afa8e06SEd Maste {
5860afa8e06SEd Maste struct hid_osx *ctx = handle;
5870afa8e06SEd Maste
5880afa8e06SEd Maste return (ctx->report_in_len);
5890afa8e06SEd Maste }
5900afa8e06SEd Maste
5910afa8e06SEd Maste size_t
fido_hid_report_out_len(void * handle)5920afa8e06SEd Maste fido_hid_report_out_len(void *handle)
5930afa8e06SEd Maste {
5940afa8e06SEd Maste struct hid_osx *ctx = handle;
5950afa8e06SEd Maste
5960afa8e06SEd Maste return (ctx->report_out_len);
5970afa8e06SEd Maste }
598