REQUEST_KEY - Linux手册页
Section: Linux Key Management Calls (2)
更新日期: 2020-06-09
名称
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()尝试查找具有与指定描述匹配的描述(名称)的给定类型的键。如果找不到这样的密钥,则可以选择创建该密钥。如果找到或创建了密钥,则request_key()会将其附加到在dest_keyring中指定了ID的密钥环上,并返回密钥的序列号。
request_key()首先在附加到调用过程的所有密钥环中递归搜索匹配的密钥。按以下顺序搜索密钥环:特定于线程的密钥环,特定于进程的密钥环,然后是会话密钥环。
如果从request_key()代表其他进程调用的程序中调用request_key()以生成密钥,则接下来将使用该另一个进程的用户ID,组ID,补充组ID搜索该另一个进程的密钥环。 ,并根据安全上下文确定访问权限。
密钥环树的搜索是广度优先的:在将任何子密钥环递归到其中之前,先检查每个搜索到的密钥环中的密钥是否匹配。仅找到呼叫者具有搜索许可的密钥,并且仅搜索呼叫者具有搜索许可的密钥环。
如果未找到键,且标注为NULL,则调用失败,错误为ENOKEY。
如果未找到密钥,并且标注不为NULL,则内核将尝试调用用户空间程序以实例化密钥。详细情况如下。
dest_keyring序列号可以是调用者对其具有写许可权的有效密钥环的序列号,也可以是以下特殊密钥环ID之一:
- 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))。
- *
- 进程用户ID的会话密钥环(KEY_REQKEY_DEFL_USER_SESSION_KEYRING;请参阅user-session-keyring(7))。预计此密钥环将始终存在。
- *
- 特定于UID的密钥环(KEY_REQKEY_DEFL_USER_KEYRING;参见user-keyring(7))。该密钥环也有望始终存在。
如果keyctl(2)KEYCTL_SET_REQKEY_KEYRING操作指定KEY_REQKEY_DEFL_DEFAULT(或者不执行KEYCTL_SET_REQKEY_KEYRING操作),则内核从列表的开头开始寻找密钥环。
Requesting user-space instantiation of a key
如果内核找不到与类型和描述匹配的键,并且标注不为NULL,则内核会尝试调用用户空间程序来实例化具有给定类型和描述的键。在这种情况下,执行以下步骤:
- a)
- 内核使用请求的类型和描述创建一个未实例化的密钥U。
- b)
- The kernel creates an authorization key, V,
that refers to the key U and records the facts that the caller of
request_key()is:
- (1)
- 实例化和保护密钥U的上下文,以及
- (2)
- 可以满足关联密钥请求的上下文。
- The authorization key is constructed as follows:
- *
- 密钥类型为.request_key_auth。
- *
- 密钥的UID和GID与请求进程的相应文件系统ID相同。
- *
- 密钥向密钥所有者授予查看,读取和搜索权限,以及密钥用户的查看权限。
- *
- 密钥的描述(名称)是一个十六进制字符串,代表要在请求程序中实例化的密钥的ID。
- *
- 密钥的有效载荷来自callout_info中指定的数据。
- *
- 在内部,内核还记录称为request_key()的进程的PID。
- c)
- 内核创建一个进程,该进程执行带有新会话密钥环的用户空间服务,例如请求密钥(8),该会话密钥环包含指向授权密钥V的链接。
- This program is supplied with the following command-line arguments:
- [0]
- 字符串/ sbin / request-key。
- [1]
- 字符串create(指示要创建密钥)。
- [2]
- 要实例化的密钥的ID。
- [3]
- request_key()的调用者的文件系统UID。
- [4]
- request_key()的调用者的文件系统GID。
- [5]
- request_key()的调用者的线程密钥环的ID。如果尚未创建该密钥环,则该值为零。
- [6]
- request_key()的调用者的进程密钥环的ID。如果尚未创建该密钥环,则该值为零。
- [7]
- request_key()的调用者的会话密钥环的ID。
- 注意:每个作为键ID的命令行参数都以十进制编码(与/ proc / keys中显示的键ID不同,它们以十六进制值显示)。
- d)
- The program spawned in the previous step:
- *
- 假定使用keyctl(2)KEYCTL_ASSUME_AUTHORITY操作(通常通过keyctl_assume_authority(3)函数)实例化密钥U的权限。
- *
- 从授权密钥V的有效负载中获取标注数据(使用keyctl(2)KEYCTL_READ操作(或更常见的是使用keyctl_read(3)函数),其密钥ID值为KEY_SPEC_REQKEY_AUTH_KEY)。
- *
- 实例化密钥(或执行另一个执行该任务的程序),指定有效负载和目标密钥环。 (可以使用特殊密钥ID KEY_SPEC_REQUESTOR_KEYRING访问请求者在调用request_key()时指定的目标密钥环。)使用keyctl(2)KEYCTL_INSTANTIATE操作(或更常见的是keyctl_instantiate(3)函数)来执行实例化。至此,request_key()调用完成,发出请求的程序可以继续执行。
如果这些步骤不成功,则将ENOKEY错误返回给request_key()的调用方,并且将在dest_keyring指定的密钥环中安装一个临时的,负实例化的密钥。这将在几秒钟后过期,但会导致随后对request_key()的调用失败,直到失败为止。此负实例化密钥的目的是防止(可能不同的)进程针对无法(当前)正实例化的密钥进行重复请求(需要昂贵的request-key(8)上调)。
实例化密钥后,将撤消授权密钥(KEY_SPEC_REQKEY_AUTH_KEY),并且不再可以从request-key(8)程序访问目标密钥环(KEY_SPEC_REQUESTOR_KEYRING)。
如果创建了密钥,则-(无论它是有效密钥还是负实例化的密钥)-都会从dest_keyring中指定的密钥环中替换具有相同类型和描述的任何其他密钥。
返回值
成功后,request_key()返回找到或导致创建的密钥的序列号。发生错误时,将返回-1并将errno设置为指示错误原因。
错误说明
- EACCES
- 用户无法修改密钥环。
- EDQUOT
- 通过创建此密钥或将其链接到密钥环,将超出此用户的密钥配额。
- EFAULT
- 类型,描述或callout_info之一指向进程的可访问地址空间之外。
- EINTR
- 该请求被信号中断;参见signal(7)。
- EINVAL
- 在类型或描述中指定的字符串大小(包括终止的空字节)超过了限制(分别为32个字节和4096个字节)。
- EINVAL
- 在callout_info中指定的字符串(包括终止空字节)的大小超过了系统页面大小。
- EKEYEXPIRED
- 找到了过期的密钥,但是无法获得替换密钥。
- EKEYREJECTED
- 生成新密钥的尝试被拒绝。
- EKEYREVOKED
- 找到了已吊销的钥匙,但是无法获得替代钥匙。
- ENOKEY
- 找不到匹配的密钥。
- ENOMEM
- 内存不足,无法创建密钥。
- EPERM
- 类型参数以句点(aq.aq)开头。
版本
该系统调用首次出现在Linux 2.6.10中。 Linux 2.6.13中添加了根据请求实例化密钥的功能。
遵循规范
此系统调用是非标准的Linux扩展。
备注
glibc中没有提供此系统调用的包装器。 libkeyutils软件包中提供了包装器。在该库中使用包装器时,请与-lkeyutils链接。
示例
下面的程序演示了request_key()的用法。系统调用的类型,描述和callout_info参数取自命令行参数中提供的值。该呼叫将会话密钥环指定为目标密钥环。
为了演示该程序,我们首先在文件/etc/request-key.conf中创建一个合适的条目。
$ sudo sh # echo 'create user mtk:* * /bin/keyctl instantiate %k %c %S' \ > /etc/request-key.conf # exit
此项指定当必须实例化带有前缀" mtk:"的新"用户"键时,应通过keyctl(1)命令的实例化操作来执行该任务。提供给实例化操作的参数是:未实例化的键的ID(%k);提供给request_key()调用的标注数据(%c);以及请求者(即request_key()的调用者)的会话密钥环(%S)。有关这些%说明符的详细信息,请参见request-key.conf(5)。
然后,我们运行程序并检查/ proc / keys的内容,以验证请求的密钥已被实例化:
$ ./t_request_key user mtk:key1 "Payload data" $ grep aq2dddaf50aq /proc/keys 2dddaf50 I--Q--- 1 perm 3f010000 1000 1000 user mtk:key1: 12
有关使用此程序的另一个示例,请参见keyctl(2)。
Program source
/* 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, "Usage: %s type description 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 %lx\n", (long) key); exit(EXIT_SUCCESS); }
另外参见
keyctl(1),add_key(2),keyctl(2),keyctl(3),功能(7),keyrings(7),keyutils(7),persistent-keyring(7),process-keyring(7),会话-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中) 。文本)。
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。