request_key(2)

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

LIBRARY
Linux Key Management Utilities (libkeyutils, -lkeyutils)

СИНТАКСИС
#include <keyutils.h>

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

ОПИСАНИЕ
Системный вызов 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. В этом случае выполняются следующие шаги:

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

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

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

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

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

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

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

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

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

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

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

(3) Ядро создаёт процесс, который запускает службу пользовательского пространства, такую как 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, которые выдаются в виде шестнадцатеричных значений).

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

    •   Принимает полномочия на создание ключа 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).

If a key is created, then—regardless of whether it is a valid key or a negatively instantiated key—it will displace any other key with the same type and description from the keyring specified in dest_keyring.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
On success, request_key() returns the serial number of the key it found or caused to be created. On error, -1 is returned and errno is set to indicate the error.

ОШИБКИ

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
    The type argument started with a period ('.').

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

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

ПРИМЕРЫ
В программе, представленной ниже, показано использование 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 <keyutils.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.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("Key ID is %jx\n", (uintmax_t) 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).

ПЕРЕВОД
Русский перевод этой страницы руководства был сделан aereiae <aereiae@gmail.com>, Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitriy S. Seregin <dseregin@59.ru>, Katrin Kutepova <blackkatelv@gmail.com>, Lockal <lockalsash@gmail.com>, Yuri Kozlov <yuray@komyakino.ru>, Баринов Владимир и Иван Павлов <pavia00@gmail.com>

Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 (https://www.gnu.org/licenses/gpl-3.0.html) или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.