요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
===================
Key Request Service
===================
The key request service is part of the key retention service (refer to
Documentation/security/keys/core.rst). This document explains more fully how
the requesting algorithm works.
The process starts by either the kernel requesting a service by calling
``request_key*()``::
struct key *request_key(const struct key_type *type,
const char *description,
const char *callout_info);
or::
struct key *request_key_tag(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag,
const char *callout_info);
or::
struct key *request_key_with_auxdata(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag,
const char *callout_info,
size_t callout_len,
void *aux);
or::
struct key *request_key_rcu(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag);
Or by userspace invoking the request_key system call::
key_serial_t request_key(const char *type,
const char *description,
const char *callout_info,
key_serial_t dest_keyring);
The main difference between the access points is that the in-kernel interface
does not need to link the key to a keyring to prevent it from being immediately
destroyed. The kernel interface returns a pointer directly to the key, and
it's up to the caller to destroy the key.
The request_key_tag() call is like the in-kernel request_key(), except that it
also takes a domain tag that allows keys to be separated by namespace and
killed off as a group.
The request_key_with_auxdata() calls is like the request_key_tag() call, except
that they permit auxiliary data to be passed to the upcaller (the default is
NULL). This is only useful for those key types that define their own upcall
mechanism rather than using /sbin/request-key.
The request_key_rcu() call is like the request_key_tag() call, except that it
doesn't check for keys that are under construction and doesn't attempt to
construct missing keys.
The userspace interface links the key to a keyring associated with the process
to prevent the key from going away, and returns the serial number of the key to
the caller.
The following example assumes that the key types involved don't define their
own upcall mechanisms. If they do, then those should be substituted for the
forking and execution of /sbin/request-key.
The Process
===========
A request proceeds in the following manner:
1) Process A calls request_key() [the userspace syscall calls the kernel
interface].
2) request_key() searches the process's subscribed keyrings to see if there's
a suitable key there. If there is, it returns the key. If there isn't,
and callout_info is not set, an error is returned. Otherwise the process
proceeds to the next step.
3) request_key() sees that A doesn't have the desired key yet, so it creates
two things:
a) An uninstantiated key U of requested type and description.
b) An authorisation key V that refers to key U and notes that process A
is the context in which key U should be instantiated and secured, and
from which associated key requests may be satisfied.
4) request_key() then forks and executes /sbin/request-key with a new session
keyring that contains a link to auth key V.
5) /sbin/request-key assumes the authority associated with key U.
6) /sbin/request-key execs an appropriate program to perform the actual
instantiation.
7) The program may want to access another key from A's context (say a
Kerberos TGT key). It just requests the appropriate key, and the keyring
search notes that the session keyring has auth key V in its bottom level.
This will permit it to then search the keyrings of process A with the
UID, GID, groups and security info of process A as if it was process A,
and come up with key W.
8) The program then does what it must to get the data with which to
instantiate key U, using key W as a reference (perhaps it contacts a
Kerberos server using the TGT) and then instantiates key U.
9) Upon instantiating key U, auth key V is automatically revoked so that it
may not be used again.
10) The program then exits 0 and request_key() deletes key V and returns key
U to the caller.
This also extends further. If key W (step 7 above) didn't exist, key W would
be created uninstantiated, another auth key (X) would be created (as per step
3) and another copy of /sbin/request-key spawned (as per step 4); but the
context specified by auth key X will still be process A, as it was in auth key
V.
This is because process A's keyrings can't simply be attached to
/sbin/request-key at the appropriate places because (a) execve will discard two
of them, and (b) it requires the same UID/GID/Groups all the way through.
Negative Instantiation And Rejection
====================================
Rather than instantiating a key, it is possible for the possessor of an
authorisation key to negatively instantiate a key that's under construction.
This is a short duration placeholder that causes any attempt at re-requesting
the key while it exists to fail with error ENOKEY if negated or the specified
error if rejected.
This is provided to prevent excessive repeated spawning of /sbin/request-key
processes for a key that will never be obtainable.
Should the /sbin/request-key process exit anything other than 0 or die on a
signal, the key under construction will be automatically negatively
instantiated for a short amount of time.
The Search Algorithm
====================
A search of any particular keyring proceeds in the following fashion:
1) When the key management code searches for a key (keyring_search_rcu) it
firstly calls key_permission(SEARCH) on the keyring it's starting with,
if this denies permission, it doesn't search further.
2) It considers all the non-keyring keys within that keyring and, if any key
matches the criteria specified, calls key_permission(SEARCH) on it to see
if the key is allowed to be found. If it is, that key is returned; if
not, the search continues, and the error code is retained if of higher
priority than the one currently set.
3) It then considers all the keyring-type keys in the keyring it's currently
searching. It calls key_permission(SEARCH) on each keyring, and if this
grants permission, it recurses, executing steps (2) and (3) on that
keyring.
The process stops immediately a valid key is found with permission granted to
use it. Any error from a previous match attempt is discarded and the key is
returned.
When request_key() is invoked, if CONFIG_KEYS_REQUEST_CACHE=y, a per-task
one-key cache is first checked for a match.
When search_process_keyrings() is invoked, it performs the following searches
until one succeeds:
1) If extant, the process's thread keyring is searched.
2) If extant, the process's process keyring is searched.
3) The process's session keyring is searched.
4) If the process has assumed the authority associated with a request_key()
authorisation key then:
a) If extant, the calling process's thread keyring is searched.
b) If extant, the calling process's process keyring is searched.
c) The calling process's session keyring is searched.
The moment one succeeds, all pending errors are discarded and the found key is
returned. If CONFIG_KEYS_REQUEST_CACHE=y, then that key is placed in the
per-task cache, displacing the previous key. The cache is cleared on exit or
just prior to resumption of userspace.
Only if all these fail does the whole thing fail with the highest priority
error. Note that several errors may have come from LSM.
The error priority is::
EKEYREVOKED > EKEYEXPIRED > ENOKEY
EACCES/EPERM are only returned on a direct search of a specific keyring where
the basal keyring does not grant Search permission.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
커널·사용자 공간 요청 진입점
1-72key request service는 `Documentation/security/keys/core.rst`의 key retention service 일부이며 요청 알고리즘을 자세히 설명한다. 커널은 `request_key()`, domain namespace와 group teardown을 지원하는 `request_key_tag()`, type 자체 upcall에 aux를 넘기는 `request_key_with_auxdata()`, construction 중 key를 보지 않고 누락 key를 만들지도 않는 `request_key_rcu()`로 시작할 수 있다. 사용자 공간은 `request_key` system call에 type·description·callout 정보·destination keyring을 전달한다.
커널 인터페이스는 key 포인터를 직접 반환하므로 즉시 파괴되지 않게 keyring에 붙일 필요가 없고 caller가 나중에 reference를 해제한다. 사용자 공간 인터페이스는 process 관련 keyring에 link하고 serial을 반환한다. 아래 절차는 type이 자체 upcall을 정의하지 않아 `/sbin/request-key`를 fork·exec하는 경우를 가정한다.
진입점마다 domain·aux·construction 동작이 다르다.
===================
Key Request Service
===================
The key request service is part of the key retention service (refer to
Documentation/security/keys/core.rst). This document explains more fully how
the requesting algorithm works.
The process starts by either the kernel requesting a service by calling
``request_key*()``::
struct key *request_key(const struct key_type *type,
const char *description,
const char *callout_info);
or::
struct key *request_key_tag(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag,
const char *callout_info);
or::
struct key *request_key_with_auxdata(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag,
const char *callout_info,
size_t callout_len,
void *aux);
or::
struct key *request_key_rcu(const struct key_type *type,
const char *description,
const struct key_tag *domain_tag);
Or by userspace invoking the request_key system call::
key_serial_t request_key(const char *type,
const char *description,
const char *callout_info,
key_serial_t dest_keyring);
The main difference between the access points is that the in-kernel interface
does not need to link the key to a keyring to prevent it from being immediately
destroyed. The kernel interface returns a pointer directly to the key, and
it's up to the caller to destroy the key.
The request_key_tag() call is like the in-kernel request_key(), except that it
also takes a domain tag that allows keys to be separated by namespace and
killed off as a group.
The request_key_with_auxdata() calls is like the request_key_tag() call, except
that they permit auxiliary data to be passed to the upcaller (the default is
NULL). This is only useful for those key types that define their own upcall
mechanism rather than using /sbin/request-key.
The request_key_rcu() call is like the request_key_tag() call, except that it
doesn't check for keys that are under construction and doesn't attempt to
construct missing keys.
The userspace interface links the key to a keyring associated with the process
to prevent the key from going away, and returns the serial number of the key to
the caller.
The following example assumes that the key types involved don't define their
own upcall mechanisms. If they do, then those should be substituted for the
forking and execution of /sbin/request-key.
U·V·W·X key를 통한 construction
73-131Process A가 `request_key()`를 호출하면 subscribed keyring을 검색한다. 일치 key가 있으면 즉시 반환하고, 없으며 `callout_info`도 없으면 오류다. 만들 수 있다면 requested type·description의 uninstantiated key U와, U를 가리키고 process A의 UID·GID·groups·security context를 기록한 authorization key V를 만든다.
그 뒤 V로 향하는 링크가 든 새 session keyring으로 `/sbin/request-key`를 실행한다. helper는 U에 대한 authority를 취하고 실제 instantiate 프로그램을 실행한다. 프로그램이 A의 context에서 Kerberos TGT 같은 다른 key W를 요청하면 session ring 아래 V가 발견되어 process A인 것처럼 A의 keyring을 검색할 수 있다. W를 이용해 자료를 얻고 U를 instantiate하면 V가 자동 revoke된다. 프로그램이 0으로 끝나면 request_key는 V를 삭제하고 U를 caller에 반환한다.
W도 없으면 W와 authorization key X를 만들고 request-key helper를 중첩 실행한다. X가 기록하는 context도 V와 마찬가지로 원래 process A다. A의 keyring을 helper에 직접 붙일 수 없는 이유는 execve가 thread·process keyring을 버리고 전체 과정에서 동일 UID·GID·groups가 필요하기 때문이다.
authorization key가 원래 requester context를 중첩 upcall까지 전달한다.
The Process
===========
A request proceeds in the following manner:
1) Process A calls request_key() [the userspace syscall calls the kernel
interface].
2) request_key() searches the process's subscribed keyrings to see if there's
a suitable key there. If there is, it returns the key. If there isn't,
and callout_info is not set, an error is returned. Otherwise the process
proceeds to the next step.
3) request_key() sees that A doesn't have the desired key yet, so it creates
two things:
a) An uninstantiated key U of requested type and description.
b) An authorisation key V that refers to key U and notes that process A
is the context in which key U should be instantiated and secured, and
from which associated key requests may be satisfied.
4) request_key() then forks and executes /sbin/request-key with a new session
keyring that contains a link to auth key V.
5) /sbin/request-key assumes the authority associated with key U.
6) /sbin/request-key execs an appropriate program to perform the actual
instantiation.
7) The program may want to access another key from A's context (say a
Kerberos TGT key). It just requests the appropriate key, and the keyring
search notes that the session keyring has auth key V in its bottom level.
This will permit it to then search the keyrings of process A with the
UID, GID, groups and security info of process A as if it was process A,
and come up with key W.
8) The program then does what it must to get the data with which to
instantiate key U, using key W as a reference (perhaps it contacts a
Kerberos server using the TGT) and then instantiates key U.
9) Upon instantiating key U, auth key V is automatically revoked so that it
may not be used again.
10) The program then exits 0 and request_key() deletes key V and returns key
U to the caller.
This also extends further. If key W (step 7 above) didn't exist, key W would
be created uninstantiated, another auth key (X) would be created (as per step
3) and another copy of /sbin/request-key spawned (as per step 4); but the
context specified by auth key X will still be process A, as it was in auth key
V.
This is because process A's keyrings can't simply be attached to
/sbin/request-key at the appropriate places because (a) execve will discard two
of them, and (b) it requires the same UID/GID/Groups all the way through.
Negative instantiate와 reject
132-148authorization key possessor는 construction 중 key를 positive로 만들지 못할 때 negative instantiate할 수 있다. negate는 짧은 placeholder 수명 동안 재요청을 `ENOKEY`, reject는 지정 오류로 실패시켜 얻을 수 없는 key 때문에 `/sbin/request-key`가 반복 생성되는 일을 막는다.
`/sbin/request-key`가 0 이외 상태로 종료하거나 signal로 죽으면 construction 중 key는 자동으로 짧은 시간 negative instantiate된다.
Negative Instantiation And Rejection
====================================
Rather than instantiating a key, it is possible for the possessor of an
authorisation key to negatively instantiate a key that's under construction.
This is a short duration placeholder that causes any attempt at re-requesting
the key while it exists to fail with error ENOKEY if negated or the specified
error if rejected.
This is provided to prevent excessive repeated spawning of /sbin/request-key
processes for a key that will never be obtainable.
Should the /sbin/request-key process exit anything other than 0 or die on a
signal, the key under construction will be automatically negatively
instantiated for a short amount of time.
단일 keyring의 재귀 검색
149-172`keyring_search_rcu`는 시작 keyring에 `key_permission(SEARCH)`를 먼저 적용하고 거부되면 중단한다. 허용되면 현재 ring의 non-keyring key를 criterion과 비교하고, match마다 Search 권한을 검사해 허용된 첫 key를 반환한다. 거부된 match 오류는 현재 오류보다 우선순위가 높을 때 보존한다.
이후 현재 ring의 keyring-type key마다 Search 권한을 검사하고 허용된 child에 같은 non-key·child 단계로 재귀한다. 유효하고 사용 권한이 있는 key를 찾는 순간 이전 오류를 버리고 즉시 반환한다.
현재 ring의 일반 key를 먼저 본 뒤 허용된 child ring으로 내려간다.
The Search Algorithm
====================
A search of any particular keyring proceeds in the following fashion:
1) When the key management code searches for a key (keyring_search_rcu) it
firstly calls key_permission(SEARCH) on the keyring it's starting with,
if this denies permission, it doesn't search further.
2) It considers all the non-keyring keys within that keyring and, if any key
matches the criteria specified, calls key_permission(SEARCH) on it to see
if the key is allowed to be found. If it is, that key is returned; if
not, the search continues, and the error code is retained if of higher
priority than the one currently set.
3) It then considers all the keyring-type keys in the keyring it's currently
searching. It calls key_permission(SEARCH) on each keyring, and if this
grants permission, it recurses, executing steps (2) and (3) on that
keyring.
The process stops immediately a valid key is found with permission granted to
use it. Any error from a previous match attempt is discarded and the key is
returned.
프로세스 keyring 순서와 오류 우선순위
173-207`request_key()`는 `CONFIG_KEYS_REQUEST_CACHE=y`이면 먼저 task별 one-key cache를 검사한다. `search_process_keyrings()`는 존재하는 thread ring, 존재하는 process ring, session ring 순서로 검색한다. request authorization authority를 취한 상태면 이어서 원래 requester의 thread·process·session ring도 같은 순서로 검색한다.
하나라도 성공하면 pending error를 모두 버리고 key를 반환한다. request cache 설정이 있으면 이전 항목을 밀어내고 찾은 key를 cache하며, cache는 task exit 또는 사용자 공간 복귀 직전에 비운다. 모두 실패하면 가장 높은 우선순위 오류를 반환한다. LSM에서 여러 오류가 올 수 있고 우선순위는 `EKEYREVOKED > EKEYEXPIRED > ENOKEY`다. `EACCES`·`EPERM`은 특정 basal keyring을 직접 검색했는데 그 ring이 Search를 허용하지 않을 때만 반환한다.
현재 process와 authorization requester의 keyring을 차례로 탐색한다.
When request_key() is invoked, if CONFIG_KEYS_REQUEST_CACHE=y, a per-task
one-key cache is first checked for a match.
When search_process_keyrings() is invoked, it performs the following searches
until one succeeds:
1) If extant, the process's thread keyring is searched.
2) If extant, the process's process keyring is searched.
3) The process's session keyring is searched.
4) If the process has assumed the authority associated with a request_key()
authorisation key then:
a) If extant, the calling process's thread keyring is searched.
b) If extant, the calling process's process keyring is searched.
c) The calling process's session keyring is searched.
The moment one succeeds, all pending errors are discarded and the found key is
returned. If CONFIG_KEYS_REQUEST_CACHE=y, then that key is placed in the
per-task cache, displacing the previous key. The cache is cleared on exit or
just prior to resumption of userspace.
Only if all these fail does the whole thing fail with the highest priority
error. Note that several errors may have come from LSM.
The error priority is::
EKEYREVOKED > EKEYEXPIRED > ENOKEY
EACCES/EPERM are only returned on a direct search of a specific keyring where
the basal keyring does not grant Search permission.
요약·해설
request-key.rst:1-207request_key 진입점, authorization key 기반 upcall, negative cache와 keyring 검색·오류 우선순위를 설명합니다.