mirror of https://github.com/Yubico/python-fido2
Merge PR #174.
This commit is contained in:
commit
50d0306a2d
|
@ -49,6 +49,8 @@ elif sys.platform.startswith("darwin"):
|
|||
from . import macos as backend
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
from . import freebsd as backend
|
||||
elif sys.platform.startswith("netbsd"):
|
||||
from . import netbsd as backend
|
||||
elif sys.platform.startswith("openbsd"):
|
||||
from . import openbsd as backend
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
# Copyright 2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Implements raw HID interface on NetBSD."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import select
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from ctypes import (
|
||||
Structure,
|
||||
c_char,
|
||||
c_int,
|
||||
c_ubyte,
|
||||
c_uint16,
|
||||
c_uint32,
|
||||
c_uint8,
|
||||
)
|
||||
from typing import Set
|
||||
|
||||
from . import base
|
||||
|
||||
# Don't typecheck this file on Windows
|
||||
assert sys.platform != "win32" # nosec
|
||||
|
||||
from fcntl import ioctl # noqa: E402
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
USB_MAX_DEVNAMELEN = 16
|
||||
USB_MAX_DEVNAMES = 4
|
||||
USB_MAX_STRING_LEN = 128
|
||||
USB_MAX_ENCODED_STRING_LEN = USB_MAX_STRING_LEN * 3
|
||||
|
||||
|
||||
class usb_ctl_report_desc(Structure):
|
||||
_fields_ = [
|
||||
("ucrd_size", c_int),
|
||||
("ucrd_data", c_ubyte * 1024),
|
||||
]
|
||||
|
||||
|
||||
class usb_device_info(Structure):
|
||||
_fields_ = [
|
||||
("udi_bus", c_uint8),
|
||||
("udi_addr", c_uint8),
|
||||
("udi_pad0", c_uint8 * 2),
|
||||
("udi_cookie", c_uint32),
|
||||
("udi_product", c_char * USB_MAX_ENCODED_STRING_LEN),
|
||||
("udi_vendor", c_char * USB_MAX_ENCODED_STRING_LEN),
|
||||
("udi_release", c_char * 8),
|
||||
("udi_serial", c_char * USB_MAX_ENCODED_STRING_LEN),
|
||||
("udi_productNo", c_uint16),
|
||||
("udi_vendorNo", c_uint16),
|
||||
("udi_releaseNo", c_uint16),
|
||||
("udi_class", c_uint8),
|
||||
("udi_subclass", c_uint8),
|
||||
("udi_protocol", c_uint8),
|
||||
("udi_config", c_uint8),
|
||||
("udi_speed", c_uint8),
|
||||
("udi_pad1", c_uint8),
|
||||
("udi_power", c_int),
|
||||
("udi_nports", c_int),
|
||||
("udi_devnames", c_char * USB_MAX_DEVNAMES * USB_MAX_DEVNAMELEN),
|
||||
("udi_ports", c_uint8 * 16),
|
||||
]
|
||||
|
||||
|
||||
USB_GET_DEVICE_INFO = 0x44F45570 # _IOR('U', 112, struct usb_device_info)
|
||||
USB_GET_REPORT_DESC = 0x44045515 # _IOR('U', 21, struct usb_ctl_report_desc)
|
||||
USB_HID_SET_RAW = 0x80046802 # _IOW('h', 2, int)
|
||||
|
||||
|
||||
# Cache for continuously failing devices
|
||||
# XXX not thread-safe
|
||||
_failed_cache: Set[str] = set()
|
||||
|
||||
|
||||
def list_descriptors():
|
||||
stale = set(_failed_cache)
|
||||
descriptors = []
|
||||
|
||||
for i in range(100):
|
||||
path = "/dev/uhid%d" % (i,)
|
||||
stale.discard(path)
|
||||
try:
|
||||
desc = get_descriptor(path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
break
|
||||
if path not in _failed_cache:
|
||||
logger.debug("Failed opening FIDO device %s", path, exc_info=True)
|
||||
_failed_cache.add(path)
|
||||
continue
|
||||
except Exception:
|
||||
if path not in _failed_cache:
|
||||
logger.debug("Failed opening FIDO device %s", path, exc_info=True)
|
||||
_failed_cache.add(path)
|
||||
continue
|
||||
descriptors.append(desc)
|
||||
|
||||
_failed_cache.difference_update(stale)
|
||||
return descriptors
|
||||
|
||||
|
||||
def get_descriptor(path):
|
||||
fd = None
|
||||
try:
|
||||
fd = os.open(path, os.O_RDONLY | os.O_CLOEXEC)
|
||||
devinfo = usb_device_info()
|
||||
ioctl(fd, USB_GET_DEVICE_INFO, devinfo)
|
||||
ucrd = usb_ctl_report_desc()
|
||||
ioctl(fd, USB_GET_REPORT_DESC, ucrd)
|
||||
report_desc = bytearray(ucrd.ucrd_data[: ucrd.ucrd_size])
|
||||
maxin, maxout = base.parse_report_descriptor(report_desc)
|
||||
vid = devinfo.udi_vendorNo
|
||||
pid = devinfo.udi_productNo
|
||||
try:
|
||||
name = devinfo.udi_product.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
name = None
|
||||
try:
|
||||
serial = devinfo.udi_serial.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
serial = None
|
||||
return base.HidDescriptor(path, vid, pid, maxin, maxout, name, serial)
|
||||
finally:
|
||||
if fd is not None:
|
||||
os.close(fd)
|
||||
|
||||
|
||||
def open_connection(descriptor):
|
||||
return NetBSDCtapHidConnection(descriptor)
|
||||
|
||||
|
||||
class NetBSDCtapHidConnection(base.FileCtapHidConnection):
|
||||
def __init__(self, descriptor):
|
||||
# XXX racy -- device can change identity now that it has been
|
||||
# closed
|
||||
super().__init__(descriptor)
|
||||
try:
|
||||
ioctl(self.handle, USB_HID_SET_RAW, struct.pack("@i", 1))
|
||||
ping = bytearray(64)
|
||||
ping[0:7] = bytearray([0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0, 1])
|
||||
for i in range(10):
|
||||
self.write_packet(ping)
|
||||
poll = select.poll()
|
||||
poll.register(self.handle, select.POLLIN)
|
||||
if poll.poll(100):
|
||||
self.read_packet()
|
||||
break
|
||||
else:
|
||||
raise Exception("u2f ping timeout")
|
||||
except Exception:
|
||||
self.close()
|
||||
raise
|
Loading…
Reference in New Issue