Подробный гайд по программной блокировке Рутокенов в Linux - Часть 2

Гайд по программной блокировке Рутокенов в Linux. Разбираем связку PKCS#11, udev и sysfs для идентификации и отключения нескольких USB-токенов.

2026.11.09                  


Подробный гайд по программной блокировке Рутокенов в Linux - Часть 2Подробный гайд по программной блокировке Рутокенов в Linux - Часть 2

5. Почему возникает проблема при нескольких токенах

Если токен один, схема простая:

  1. Появилось событие udev о подключении Рутокена.
  2. В системе появился ровно один новый токен в PKCS#11.
  3. Мы читаем его серийный номер.
  4. Если он запрещен — блокируем текущее USB-устройство.

Если токены подключаются последовательно, это тоже часто можно разобрать по времени:

  • событие USB add;
  • появление нового слота/токена;
  • совмещаем по моменту времени.


Но если:

  • несколько токенов уже подключены до загрузки ОС;
  • несколько токенов подключили одновременно;
  • несколько токенов появились до старта вашего сервиса;

то прямой однозначной привязки «вот это событие USB → вот этот слот PKCS#11» уже нет.


Особенно если все они имеют одинаковый USB iSerial, например:

ID_SERIAL=Aktiv_Rutoken_ECP

В этом случае на уровне USB-дескриптора устройства одинаковы. Различаются они только внутренним серийным номером токена, который виден уже через смарткарточный интерфейс / PKCS#11 / SDK.


6. Можно ли заблокировать Рутокен через PKCS#11?

Стандартно — нет

PKCS#11 не предоставляет механизм отключения устройства.

Можно:

C_CloseAllSessions(...);
C_Logout(...);

Но это не запретит другим процессам открыть токен заново.

Можно попытаться испортить состояние токена, например, заблокировать PIN, но это:

  • деструктивно;
  • нестабильно;
  • требует знания PIN;
  • может сделать токен временно или постоянно неработоспособным;
  • не является штатным механизмом администрирования.

Поэтому для блокировки нужно использовать уровень ОС:

  • sysfs;
  • udev;
  • USBGuard;
  • аппаратное управление питанием портов, если оно есть.

Возможное исключение

У вендора могут быть собственные нестандартные функции, например:

  • административные библиотеки;
  • утилиты управления Рутокенами;
  • расширенные функции в librtpkcs11ecp.so;
  • SDK Aktiv, где можно получить больше данных об устройстве.

Если в документации Aktiv есть функция вида:

  • получить список токенов;
  • получить серийный номер;
  • получить USB bus/port;
  • отключить токен;
  • извлечь токен;

то лучше использовать её. Но если опираться только на стандартный PKCS#11, то универсального способа нет.


7. Рабочий универсальный способ: сопоставление через временное отключение

Идея простая:

  1. Получаем список всех подключенных Рутокенов как USB-устройств.
  2. Получаем список серийных номеров токенов через PKCS#11.
  3. Временно отключаем одно USB-устройство.
  4. Снова читаем список токенов через PKCS#11.
  5. Смотрим, какой серийный номер исчез.
  6. Если исчез разрешенный токен — включаем устройство обратно.
  7. Если запрещенный — оставляем отключенным.

Это позволяет установить соответствие:

PKCS#11 serialNumber <-> USB sysfs path

Даже если:

  • токены подключены до загрузки;
  • токены подключены одновременно;
  • у всех одинаковый USB iSerial.

8. Подробный алгоритм

8.1. Подготовка

Нужно выполнять от root.

Также важно:

  • не выполнять это одновременно с активными пользовательскими сеансами, если токены уже используются;
  • остановить или не запускать приложения, которые держат открытые сессии с токенами;
  • если используется pcscd, он должен быть запущен и корректно обрабатывать hotplug;
  • библиотека PKCS#11 должна уметь перечитывать токены после смены состояния устройств.

8.2. Получить текущие серийные номера токенов

Например:

before=$(list_pkcs11_serials)

или через собственный код:

C_Initialize(NULL);
C_GetSlotList(CK_TRUE, slots, &slotCount);

for each slot:
    C_GetTokenInfo(slot, &tokenInfo);
    serial = trim(tokenInfo.serialNumber);

8.3. Отключить одно устройство

Например:

DEV="/sys/bus/usb/devices/1-2"

echo 0 > "$DEV/authorized"
udevadm settle --timeout=5
sleep 1

8.4. Снова получить список токенов

after=$(list_pkcs11_serials)

8.5. Найти исчезнувший серийный номер

gone=$(comm -23 <(echo "$before") <(echo "$after"))

Если gone непустой, значит отключенное USB-устройство соответствовало исчезнувшему токену.

Например:

gone = 1234567890ABCDEF

Тогда:

/sys/bus/usb/devices/1-2 <-> 1234567890ABCDEF


8.6. Принять решение

Если серийный номер разрешен:

echo 1 > "$DEV/authorized"
udevadm settle --timeout=5

Если запрещен:

# Оставить:
echo 0 > "$DEV/authorized"

или, если хотите жестче:

echo 1 > "$DEV/remove"

Но remove сложнее возвращать, поэтому обычно лучше authorized=0.


9. Пример тестового скрипта

Для боевого использования лучше писать полноценный демон на C/Python/Go и использовать нативный PKCS#11 API, но для проверки идеи можно начать с такого.

#!/usr/bin/env bash
set -u

PKCS11_MODULE="/usr/lib/librtpkcs11ecp.so"
ALLOWED_FILE="/etc/rutoken/allowed_serials"

mkdir -p /etc/rutoken
touch "$ALLOWED_FILE"

list_rutoken_usb_devices() {
    local d base v

    for d in /sys/bus/usb/devices/*; do
        base="${d##*/}"

        [[ "$base" == usb* ]] && continue
        [[ "$base" == *:* ]] && continue

        [[ -f "$d/idVendor" ]] || continue
        [[ -f "$d/idProduct" ]] || continue

        v=$(cat "$d/idVendor")

        # Проверьте реальный idVendor/ID_PRODUCT ваших устройств через lsusb.
        if [[ "$v" == "0a89" ]]; then
            echo "$d"
        fi
    done
}

list_pkcs11_serials() {
    pkcs11-tool --module "$PKCS11_MODULE" --list-slots 2>/dev/null |
        sed -n 's/.*Serial Number:[[:space:]]*//p' |
        sed 's/[[:space:]]*$//' |
        sort -u
}

is_allowed_serial() {
    local serial="$1"
    grep -qxF "$serial" "$ALLOWED_FILE"
}

all_allowed() {
    local s
    for s in $1; do
        if ! is_allowed_serial "$s"; then
            return 1
        fi
    done
    return 0
}

wait_pkcs11_change() {
    local old="$1"
    local i new

    for i in {1..20}; do
        new=$(list_pkcs11_serials)
        if [[ "$new" != "$old" ]]; then
            echo "$new"
            return 0
        fi
        sleep 0.3
    done

    echo "$old"
    return 1
}

disable_device() {
    local dev="$1"
    echo "[-] Disable: $dev" >&2
    echo 0 > "$dev/authorized" 2>/dev/null || true
    udevadm settle --timeout=5 || true
    sleep 1
}

enable_device() {
    local dev="$1"
    echo "[+] Enable: $dev" >&2
    echo 1 > "$dev/authorized" 2>/dev/null || true
    udevadm settle --timeout=5 || true
    sleep 1
}

echo "=== Rutoken devices ==="
list_rutoken_usb_devices

echo
echo "=== Current PKCS#11 serials ==="
list_pkcs11_serials

before=$(list_pkcs11_serials)

for dev in $(list_rutoken_usb_devices); do
    echo
    echo "Probing device: $dev"

    disable_device "$dev"

    now=$(wait_pkcs11_change "$before")

    gone=$(comm -23 <(printf '%s\n' "$before") <(printf '%s\n' "$now") | grep -v '^$' || true)

    if [[ -n "$gone" ]]; then
        echo "Device $dev corresponds to token serial(s):"
        echo "$gone"

        if all_allowed "$gone"; then
            echo "Allowed token(s), re-enabling device."
            enable_device "$dev"
        else
            echo "Forbidden token(s), leaving device disabled."
            # Оставляем authorized=0
        fi
    else
        echo "No PKCS#11 serial disappeared for $dev."
        echo "Если политика строгая, можно оставить устройство заблокированным."
        echo "В тестовом режиме включаем обратно."
        enable_device "$dev"
    fi

    before=$(list_pkcs11_serials)
done

echo
echo "=== Final PKCS#11 serials ==="
list_pkcs11_serials

Запуск:

sudo ./rutoken-probe.sh

Файл разрешенных серийных номеров:

sudo mkdir -p /etc/rutoken
sudo nano /etc/rutoken/allowed_serials

Формат:



1234567890ABCDEF
FEDCBA0987654321

Информация предоставлена в ознакомительных целях. Применение описанных настроек в системах, должно осуществляться только после согласования с ответственными за информационную безопасность и в соответствии с требованиями ФСТЭК, ФСБ и иных уполномоченных органов.


Статью подготовил: Аверко Денис Сергеевич @Nymexis г. Омск (специалист по ЗИ)

Комментарии

Загрузка...
Если комментарии не загружаются, можете попробовать отключить блокировщик рекламы для этого сайта