Beispiel #1
0
def _parse_mode_string(ctx, param, mode):
    try:
        mode_int = int(mode)
        return Mode.from_code(mode_int)
    except IndexError:
        ctx.fail(f"Invalid mode: {mode_int}")
    except ValueError:
        pass  # Not a numeric mode, parse string

    try:
        if mode[0] in ["+", "-"]:
            info = ctx.obj["info"]
            usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
            interfaces = USB_INTERFACE.for_capabilities(usb_enabled)
            for mod in re.findall(r"[+-][A-Z]+", mode.upper()):
                interface = _parse_interface_string(mod[1:])
                if mod.startswith("+"):
                    interfaces |= interface
                else:
                    interfaces ^= interface
        else:
            interfaces = USB_INTERFACE(0)
            for t in re.split(r"[+]+", mode.upper()):
                if t:
                    interfaces |= _parse_interface_string(t)
    except ValueError:
        ctx.fail(f"Invalid mode string: {mode}")

    return Mode(interfaces)
Beispiel #2
0
def mode(ctx, mode, touch_eject, autoeject_timeout, chalresp_timeout, force):
    """
    Manage connection modes (USB Interfaces).

    This command is generaly used with YubiKeys prior to the 5 series.
    Use "ykman config usb" for more granular control on YubiKey 5 and later.

    Get the current connection mode of the YubiKey, or set it to MODE.

    MODE can be a string, such as "OTP+FIDO+CCID", or a shortened form: "o+f+c".
    It can also be a mode number.

    Examples:

    \b
      Set the OTP and FIDO mode:
      $ ykman config mode OTP+FIDO

    \b
      Set the CCID only mode and use touch to eject the smart card:
      $ ykman config mode CCID --touch-eject
    """
    info = ctx.obj["info"]
    mgmt = ctx.obj["controller"]
    usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
    my_mode = Mode(USB_INTERFACE.for_capabilities(usb_enabled))
    usb_supported = info.supported_capabilities[TRANSPORT.USB]
    interfaces_supported = USB_INTERFACE.for_capabilities(usb_supported)
    pid = ctx.obj["pid"]
    if pid:
        key_type = pid.get_type()
    else:
        key_type = None

    if autoeject_timeout:  # autoeject implies touch eject
        touch_eject = True
    autoeject = autoeject_timeout if touch_eject else None

    if mode.interfaces != USB_INTERFACE.CCID:
        if touch_eject:
            ctx.fail(
                "--touch-eject can only be used when setting CCID-only mode")

    if not force:
        if mode == my_mode:
            cli_fail(f"Mode is already {mode}, nothing to do...", 0)
        elif key_type in (YUBIKEY.YKS, YUBIKEY.YKP):
            cli_fail("Mode switching is not supported on this YubiKey!\n"
                     "Use --force to attempt to set it anyway.")
        elif mode.interfaces not in interfaces_supported:
            cli_fail(f"Mode {mode} is not supported on this YubiKey!\n" +
                     "Use --force to attempt to set it anyway.")
        force or click.confirm(
            f"Set mode of YubiKey to {mode}?", abort=True, err=True)

    try:
        mgmt.set_mode(mode, chalresp_timeout, autoeject)
        click.echo("Mode set! You must remove and re-insert your YubiKey "
                   "for this change to take effect.")
    except Exception as e:
        logger.debug("Failed to switch mode", exc_info=e)
        click.echo("Failed to switch mode on the YubiKey. Make sure your "
                   "YubiKey does not have an access code set.")
Beispiel #3
0
def usb(
    ctx,
    enable,
    disable,
    list_enabled,
    enable_all,
    touch_eject,
    no_touch_eject,
    autoeject_timeout,
    chalresp_timeout,
    lock_code,
    force,
):
    """
    Enable or disable applications over USB.
    """
    _require_config(ctx)

    def ensure_not_all_disabled(ctx, usb_enabled):
        for app in CAPABILITY:
            if app & usb_enabled:
                return
        ctx.fail("Can not disable all applications over USB.")

    if not (list_enabled or enable_all or enable or disable or touch_eject
            or no_touch_eject or autoeject_timeout or chalresp_timeout):
        ctx.fail("No configuration options chosen.")

    info = ctx.obj["info"]
    usb_supported = info.supported_capabilities[TRANSPORT.USB]
    usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
    usb_interfaces = USB_INTERFACE.for_capabilities(usb_enabled)
    flags = info.config.device_flags

    if enable_all:
        enable = [c for c in CAPABILITY if c in usb_supported]

    _ensure_not_invalid_options(ctx, enable, disable)

    if touch_eject and no_touch_eject:
        ctx.fail("Invalid options.")

    if not usb_supported:
        cli_fail("USB not supported on this YubiKey.")

    if list_enabled:
        _list_apps(ctx, usb_enabled)

    if touch_eject:
        flags |= DEVICE_FLAG.EJECT
    if no_touch_eject:
        flags &= ~DEVICE_FLAG.EJECT

    for app in enable:
        if app & usb_supported:
            usb_enabled |= app
        else:
            cli_fail(f"{app.name} not supported over USB on this YubiKey.")
    for app in disable:
        if app & usb_supported:
            usb_enabled &= ~app
        else:
            cli_fail(f"{app.name} not supported over USB on this YubiKey.")

    ensure_not_all_disabled(ctx, usb_enabled)

    reboot = usb_interfaces != USB_INTERFACE.for_capabilities(usb_enabled)

    f_confirm = ""
    if enable:
        f_confirm += f"Enable {', '.join(str(app) for app in enable)}.\n"
    if disable:
        f_confirm += f"Disable {', '.join(str(app) for app in disable)}.\n"
    if touch_eject:
        f_confirm += "Set touch eject.\n"
    elif no_touch_eject:
        f_confirm += "Disable touch eject.\n"
    if autoeject_timeout:
        f_confirm += f"Set autoeject timeout to {autoeject_timeout}.\n"
    if chalresp_timeout:
        f_confirm += f"Set challenge-response timeout to {chalresp_timeout}.\n"
    if reboot:
        f_confirm += "This will cause the YubiKey to reboot.\n"
    f_confirm += "Configure USB?"

    is_locked = info.is_locked

    if force and is_locked and not lock_code:
        cli_fail(
            "Configuration is locked - please supply the --lock-code option.")
    if lock_code and not is_locked:
        cli_fail(
            "Configuration is not locked - please remove the --lock-code option."
        )

    force or click.confirm(f_confirm, abort=True, err=True)

    if is_locked and not lock_code:
        lock_code = prompt_lock_code()

    if lock_code:
        lock_code = _parse_lock_code(ctx, lock_code)

    app = ctx.obj["controller"]
    try:
        app.write_device_config(
            DeviceConfig(
                {TRANSPORT.USB: usb_enabled},
                autoeject_timeout,
                chalresp_timeout,
                flags,
            ),
            reboot,
            lock_code,
        )
    except Exception as e:
        logger.error("Failed to write config", exc_info=e)
        cli_fail("Failed to configure USB applications.")