xref: /freebsd-src/contrib/libfido2/src/hid_osx.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
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