REQUEST_KEY(2)

ИМЯ
request_key - запрашивает ключ из системы управления ключами ядра

ОБЗОР
#include <sys/types.h>
#include <keyutils.h>

key_serial_t request_key(const char *type, const char *description,
                         const char *callout_info,
                         key_serial_t dest_keyring);

В glibc нет обёрточной функции для данного системного вызова; смотрите ЗАМЕЧАНИЯ.

ОПИСАНИЕ
Системный вызов request_key() пытается найти ключ заданного type с описанием (именем), совпадающим с description. Если ключ найти невозможно, то ключ может быть создан. Если ключ найден или создан, то request_key() присоединяет его к связке ключей, чей идентификатор указан keyring, и возвращает серийный номер ключа.

Сначала request_key() выполняет рекурсивный поиск совпадающего ключа во всех связках ключей, присоединённых к вызвавшему процессу. Связки ключей просматриваются в следующем порядке: связки каждой нити, связки вызвавшего процесса и связки ключей сеанса.

Если request_key() вызван из программы, вызванной request_key() от имени какого-то другого процесс для генерации ключа, то в дальнейшем будет осуществлён поиск по связкам ключей этого другого процесса, используя для определения доступа его идентификатор пользователя, группы и дополнительных групп и контекста безопасности.

Поиск в дереве связок ключей выполняется сначала в ширину: искомые ключи в каждой связке проверяются на совпадение до рекурсивного перехода в дочерние связки ключей. Найдены могут быть только те ключи, которые разрешены вызывающему для поиска, и поиск может осуществляться только в разрешённых для поиска связках ключей.

Если ключ не найден и callout равно NULL, то вызов завершается ошибкой ENOKEY.

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

Серийный номер dest_keyring может задаваться серийным номером допустимой связки ключей, на которую у вызывающего есть права на запись, или же это может быть один из следующих специальных идентификаторов связок ключей:

KEY_SPEC_THREAD_KEYRING
    Связка ключей вызывающей нити (смотрите thread-keyring(7)).

KEY_SPEC_PROCESS_KEYRING
    Связка ключей вызывающего процесса (смотрите process-keyring(7)).

KEY_SPEC_SESSION_KEYRING
    Связка ключей сеанса вызывающего (смотрите session-keyring(7)).

KEY_SPEC_USER_KEYRING
    Связка ключей по UID вызывающего (смотрите user-keyring(7)).

KEY_SPEC_USER_SESSION_KEYRING
    Связка ключей по UID сеанса вызывающего (смотрите user-session-keyring(7)).

Если dest_keyring равно 0 и создание ключа не выполнено, то дополнительных связей не появляется.

В противном случае, если dest_keyring равно 0 и создан новый ключ, то новый ключ будет прицеплен в связку ключей «по умолчанию». Более точно, когда ядро пытается определить в какую связку ключей должен быть прицеплен то только что созданный ключ, оно перебирает связки ключей начиная с со связки, установленной через keyctl(2) с операцией KEYCTL_SET_REQKEY_KEYRING и продолжает в порядке, показанном далее, пока не найдёт существующую связку ключей:

•   Связка ключей запрашивающего (KEY_REQKEY_DEFL_REQUESTOR_KEYRING, начиная с Linux 2.6.29).

•   Связка ключей нити (KEY_REQKEY_DEFL_THREAD_KEYRING; смотрите thread-keyring(7)).

•   Связка ключей процесса (KEY_REQKEY_DEFL_PROCESS_KEYRING; смотрите process-keyring(7)).

•   Связка ключей сеанса (KEY_REQKEY_DEFL_SESSION_KEYRING; смотрите session-keyring(7)).

•   Связка ключей сеанса для идентификатора пользователя процесса (KEY_REQKEY_DEFL_USER_SESSION_KEYRING; смотрите user-session-keyring(7)). Ожидается, что эта связка ключей всегда существует.

•   Связка ключей для идентификатора пользователя процесса (KEY_REQKEY_DEFL_USER_KEYRING; смотрите user-keyring(7)). Ожидается, что эта связка ключей также всегда существует.

Если вызов keyctl(2) с операцией KEYCTL_SET_REQKEY_KEYRING установил KEY_REQKEY_DEFL_DEFAULT (или операция KEYCTL_SET_REQKEY_KEYRING не выполнялась), то ядро ищет связку ключей начиная с начала списка.

  Запрос на создание ключа из пользовательского пространства
Если ядро не может найти ключ с соответствующим type и description, и callout не равно NULL, то ядро пытается вызвать программу из пользовательского пространства для создания ключа с заданными type и description. В этом случае выполняются следующие шаги:

а)  Ядро создаёт неинициализированный ключ U с запрошенными type и description.

б)  Ядро создаёт ключ авторизации V, который ссылается на ключ U и сохраняются данные вызывающего request_key():

    (1) контекст, в котором ключ U должен быть создан и защищён и

    (2) контекст, из которого запросы связанного ключа могут быть проведены.

    Ключ авторизации создаётся со следующими свойствами:

    *   Тип ключа — ".request_key_auth".

    *   UID и GID ключа совпадают с ID в файловой системе для запрашивающего процесса.

    *   Ключ даёт права на просмотр, чтение и поиск на ключ-владелец, а также право просмотра для ключа пользователя.

    *   Описание (имя) ключа представляет собой Шестнадцатеричную строку идентификатора ключа, который будет создан в запрашивающей программе.

    *   Полезные данные ключа берутся из данных callout_info.

    *   Внутри ядра также сохраняется PID процесса, который был вызван request_key().

в)  Ядро создаёт процесс, который запускает службу пользовательского пространства, такую как request-key(8), с новой связкой ключей сеанса с прицепленным ключом авторизации V.

    Данной программе передаются следующие аргументы командной строки:

    [0] Строка "/sbin/request-key".

    [1] Строка "create" (показывающая, что ключ будет создан).

    [2] Идентификатор ключа, который будет создан.

    [3] UID (в файловой системе) вызывающего request_key().

    [4] GID (в файловой системе) вызывающего request_key().

    [5] Идентификатор связки ключей нити вызывающего request_key(). Может быть ноль, если связка ключей не создана.

    [6] Идентификатор связки ключей процесса вызывающего request_key(). Может быть ноль, если связка ключей не создана.

    [7] Идентификатор связки ключей сеанса вызывающего request_key().

    Замечание: каждый аргумент командной строки, обозначающий идентификатор ключа, кодируется десятичным числом (в отличие от идентификаторов ключе, показанных в /proc/keys, которые выдаются в виде шестнадцатеричных значений).

г)  Программа, порождённая на предыдущем шаге:

    *   Принимает полномочия на создание ключа U с помощью keyctl(2) с операцией KEYCTL_ASSUME_AUTHORITY (обычно, с помощью функции keyctl_assume_authority(3)).

    *   Получает исходящие данные (callout data) из полезной нагрузки ключа авторизации V (с помощью keyctl(2) с операцией KEYCTL_READ (или, чаще всего, с помощью функции keyctl_read(3)) с значением идентификатора ключа KEY_SPEC_REQKEY_AUTH_KEY.

    *   Создаёт ключ (или выполняет другую программу, которая делает эту работу), с заданной полезной нагрузкой и связкой ключей назначения (связка ключей назначения та, которую запрашивающий указал при вызове request_key(), и которая быть доступна через специальный идентификатор ключа KEY_SPEC_REQUESTOR_KEYRING). Создание выполняется с помощью keyctl() с операцией KEYCTL_INSTANTIATE (или, чаще всего, с помощью функции keyctl_instantiate(3)). На этот момент вызов request_key() завершается и запрашивающая программа может продолжать выполнение.

Если какой-то из этих шагов завершается ошибкой, то вызвавшему request_key() возвращается ENOKEY и временно в связку ключей dest_keyring будет установлен отрицательно инициализированный ключ. Он устареет через несколько секунд, но пока этого не произойдёт все последующие вызовы request_key() будут завершаться ошибкой. Целью данного отрицательно инициализированного ключа является предотвращение повторяющихся запросов (возможно разными) процессами (они требуют затратных восходящих вызовов request-key(8)) для ключа, который невозможно (в данный момент) положительно инициализировать.

После инициализации ключа, ключ авторизации (KEY_SPEC_REQKEY_AUTH_KEY) отзывается и связка ключей назначения (KEY_SPEC_REQUESTOR_KEYRING) становится недоступной программе request-key(8).

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

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении request_key() возвращается серийный номер найденного или созданного ключа. При ошибке возвращается -1, а в errno содержится код ошибки.

ОШИБКИ

EACCES
    Изменение связки ключей пользователю недоступно.

EDQUOT
    Квота на ключи для данного пользователя была бы превышена, если бы этот ключ создался или был бы прицеплен в связку ключей.

EFAULT
    Значение type, description или callout_info указывают вне доступного адресного пространства процесса.

EINTR
    Запрос был прерван сигналом; смотрите signal(7).

EINVAL
    Размер строки (включая конечный байт null), заданной в type или description, превышает ограничение (32 байта и 4096 байт, соответственно).

EINVAL
    Размер строки (включая конечный байт null), заданной в callout_info, превышает размер системной страницы.

EKEYEXPIRED
    Найден просроченный ключ, и замена не может быть получена.

EKEYREJECTED
    Попытка генерации нового ключа была отвергнута.

EKEYREVOKED
    Найден отозванный ключ, и замена не может быть получена.

ENOKEY
    Искомый ключ не найден.

ENOMEM
    Недостаточно памяти для создания ключа.

EPERM
    Аргумент type начинается с точки ('.').

ВЕРСИИ
Данный системный вызов впервые появился в Linux 2.6.10. Возможность инициализации ключей по запросу была добавлена в Linux 2.6.13.

СООТВЕТСТВИЕ СТАНДАРТАМ
Этот системный вызов является нестандартным расширением Linux.

ЗАМЕЧАНИЯ
В glibc нет обёрточной функции для этого системного вызова. Такая функция предоставляется пакетом libkeyutils. Для работы с функцией подключите библиотеку с помощью -lkeyutils.

ПРИМЕР
В программе, представленной ниже, показано использование request_key(). Аргументы type, description и callout_info для системного вызова берутся из значений, переданных в аргументах командной строки. В качестве связки ключей назначения вызов использует связку ключей сеанса.

Чтобы показать работу программы сначала нужно создать подходящую запись в файле /etc/request-key.conf.

    $ sudo sh
    # echo 'create user mtk:* *   /bin/keyctl instantiate %k %c %S' \
              > /etc/request-key.conf
    # exit

Эта запись говорит о том, что когда должен быть создан новый ключ «user» с префиксом «mtk:», задача должна выполняться посредством команды keyctl(1) с операцией instantiate. Аргументы, передаваемые операции instantiate: идентификатор неинициализированного ключа (%k); исходящие данные, переданные в вызов request_key() (%c); связка ключей сеанса (%S) запрашивающего (т. е., вызывающий request_key()). Описание значений % смотрите в request-key.conf(5).

Теперь запускаем программу и проверяем содержимое /proc/keys, чтобы удостовериться, что запрашиваемый ключ создан:

    $ ./t_request_key user mtk:key1 "Payload data"
    $ grep '2dddaf50' /proc/keys
    2dddaf50 I--Q---  1 perm 3f010000  1000  1000 user  mtk:key1: 12

Другой пример смотрите использования этой программы смотрите в keyctl(2).

  Исходный код программы
    /* t_request_key.c */
    #include <sys/types.h>
    #include <keyutils.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int
    main(int argc, char *argv[])
    {
        key_serial_t key;
        if (argc != 4) {
            fprintf(stderr, "Использование: %s тип описание callout-data\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
        key = request_key(argv[1], argv[2], argv[3],
                          KEY_SPEC_SESSION_KEYRING);
        if (key == -1) {
            perror("request_key");
            exit(EXIT_FAILURE);
        }
        printf("ID ключа = %lx\n", (long) key);
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
keyctl(1), add_key(2), keyctl(2), keyctl(3), capabilities(7), keyrings(7), keyutils(7), persistent-keyring(7), process-keyring(7), session-keyring(7), thread-keyring(7), user-keyring(7), user-session-keyring(7), request-key(8)

Файлы исходного кода ядра Documentation/security/keys/core.rst и Documentation/keys/request-key.rst (или, до Linux 4.13, файлы Documentation/security/keys.txt и Documentation/security/keys-request-key.txt).