Link keyrings in initramfs using syscall()

Ger*_*rdh 6 c linux security system-calls

I want to load certificates for IMA/EVM into Linux keyrings.

The related shell commands are

ima_id=`keyctl newring _ima @u`
evm_id=`keyctl newring _evm @u`
evmctl import  /etc/keys/x509_ima.der $ima_id
evmctl import  /etc/keys/x509_evm.der $evm_id
Run Code Online (Sandbox Code Playgroud)

This nearly works except for a problem with permissions.

# keyctl show @u
Keyring
 272896171 --alswrv      0 65534  keyring: _uid.0
 406281657 --alswrv      0     0   \_ keyring: _ima
keyctl_read: Permission denied
Run Code Online (Sandbox Code Playgroud)

Searching the web I found this: https://github.com/systemd/systemd/issues/5522

And the workaround is to link the keyrings:

keyctl link @us @s
Run Code Online (Sandbox Code Playgroud)

If I enter these commands on a shell after booting, I can see the keys:

# keyctl show @u
Keyring
 272896171 --alswrv      0 65534  keyring: _uid.0
 406281657 --alswrv      0     0   \_ keyring: _ima
 647882074 --als--v      0     0   |   \_ asymmetric: abc: gerhard signing key: 15733607aff5480b5eb8b59b501760f9c5d33965
  19332842 --alswrv      0     0   \_ keyring: _evm
 470827275 --als--v      0     0       \_ asymmetric: abc: gerhard signing key: 7e5959ee64090c7fabb6dd803e7d1f48e83c5970
Run Code Online (Sandbox Code Playgroud)

So far so good...

To be useful I need to put this stuff into initramfs. The system I am dealing with is an embedded Linux where I don't have a shell during initramfs.

Therefore I used syscall to do what needs to be done...

Creating the keyrings and importing the keys works fine. But linking the keyrings does not.

After booting I get the same "permission denied" error as above. I also get error message when I try to execute a file with IMA signature. It says "_ima" keyring was not found.

If I manually enter keyctl link @us @s everything works again.

My assumption is that something related to the keyrings is not yet in place during initramfs but I cannot get hold of it.

The syscall I use for linking is as follows:

ret = syscall(__NR_keyctl, KEYCTL_LINK, KEY_SPEC_USER_SESSION_KEYRING, KEY_SPEC_SESSION_KEYRING, 0, 0);
Run Code Online (Sandbox Code Playgroud)

I don't get any negative result from the call.

UPDATE

I found some hints at this page: https://mjg59.dreamwidth.org/37333.html

According to this, the sequence should be as follows:

$ keyctl add user testkey testdata @s
$ keyctl setperm 678913344 0x3f3f0000
$ keyctl link 678913344 @u
$ keyctl unlink 678913344 @s
Run Code Online (Sandbox Code Playgroud)

This is valid for keys, but as far as I understand it should be valid for keyrings as well.

static void create_ima_keyring(void)
{
    char *name = "_ima";
    char *filename = "/etc/keys/x509_ima.der";

    int ringid = syscall(__NR_add_key, "keyring", name, NULL, 0, KEY_SPEC_SESSION_KEYRING);
    {
        // Set permission for keyring ...
        int ret = syscall(__NR_keyctl, KEYCTL_SETPERM, ringid, 0x3f3f0000, 0, 0);

        // ... and link to @u
        syscall(__NR_keyctl, KEYCTL_LINK, ringid, KEY_SPEC_USER_KEYRING);

        int len;
        unsigned char *pub = file2bin(filename, &len);
        if (pub != NULL)
        {
            int keyid = syscall(__NR_add_key, "asymmetric", NULL, pub, len, ringid);
            if (keyid >= 0)
            {
                int ret = syscall(__NR_keyctl, KEYCTL_SETPERM, keyid, 0x3f3f0000, 0, 0);
            }
            free(pub);
        }

        // TODO: Unlink from @s
    }
}
Run Code Online (Sandbox Code Playgroud)

The error handling was stripped for this example. I do not get any error results.

Now I get the expected result from keyctl show @u but the keyring is still not recognized. Executing a signed file leads to an error message again:

digsig: no _ima keyring: -126
Run Code Online (Sandbox Code Playgroud)

l.k*_*l.k 1

我不完全清楚您填充 initramfs 所采取的实际步骤...尝试检查内容:(假设 gzip 压缩,也可以尝试 xz/lz4/none)

gzip -dkcq -S img '/path/to/initramfs.img' | cpio -t

我的第一个猜测是您会发现/etc/keys/x509-blahblahinitramfs img 中不存在。特别是因为它们在启动时“未找到”,但在登录后可以工作。它还可能缺少必要的二进制文件/库或内核模块。

如果只是密钥本身,那么在允许的情况下将它们添加到任何 initramfs 生成工具的配置中应该相当简单。如果 keyctl 需要指向同一物理存储的符号链接,则您需要安排 initramfs 在安装 real_root 后运行您的任务,但 cpio 可以在那里获取一个。如果它需要您手动添加加密模块,您可能会度过一段非常有趣的时光。

内核(从 2.something 开始?)还可以接受多个 initramfs 文件,并将其合并。上次我看到大多数面向桌面的引导加载程序确实某种方法可以做到这一点,尽管在这个问题上有完整的文档沉默......构建一个单独的 initramfs 映像可能就足够了,其中只包含您的密钥并传递两者。以下应该可以创建一个。

cd /etc/keys
mkdir mkimg
find ./ -maxdepth 1 -type f | 
   sed 's/^[.]\///' |
   cpio -o -H newc --no-absolute-filenames --renumber-inodes |
   gzip -9qc - > ./mkimg/bootkeys.img'
Run Code Online (Sandbox Code Playgroud)

按照我编写的方式,密钥将在启动时转储到 initramfs 的根目录中,根据我的经验,这比尝试合并目录更简单,但如果您愿意,可以更改它。

编辑:我假设您的系统未配置为在pivot_root或一些类似的嵌入式人员之后联合挂载rootfs?