Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Link keyrings in initramfs using syscall()

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

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

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

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

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);

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

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
    }
}

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
like image 756
Gerhardh Avatar asked Mar 29 '19 14:03

Gerhardh


2 Answers

I'm not entirely clear on the actual steps you have taken to populate the initramfs... try examining the contents: (assuming gzip compression, can also try xz/lz4/none)

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

My first guess is that you're going to find /etc/keys/x509-blahblah don't exist in the initramfs img. Especially since they're "not found" at boot time but work after login. It may also be missing necessary binaries/libs or kernel modules.

If it's just the keys themselves, adding them to whatever initramfs-generating tool's configuration should be fairly straightforward, provided that's allowed. If keyctl requires a symlink to the same physical storage, you'll need to arrange for initramfs to run your tasks after mounting real_root, but cpio can get one in there. If it needs you to add crypto modules by hand you might be in for a very fun time.

The kernel (since 2.something?) can also accept multiple initramfs files, which it will merge. Last time I looked most desktop-targeted bootloaders did have some way of doing this, complete documentation silence on the matter notwithstanding... it may be sufficient to build a seperate initramfs image with just your keys in it and pass both. The following should work to create one.

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'

The way I've written it the keys will get dumped into initramfs's root directory at boot time, which in my experience is simpler than trying to get directories to merge, but change that if you like.

EDIT: I assume your system is not configured to union mount the rootfs after pivot_root or some similar embedded-person thing?

like image 141
l.k Avatar answered Nov 11 '22 07:11

l.k


After a bit more reading and digging into configuration it turned out that an additional configuration flag is required.

I had to enable CONFIG_PERSISTENT_KEYRINGS=y and with that flag I can now load the key within initramfs and use it later after login.

No more "no _ima keyring" complaints.

like image 24
Gerhardh Avatar answered Nov 11 '22 06:11

Gerhardh