Skip to content

Commit 0c2da16

Browse files
committed
Add new blog post: Kernel Key API Test
Signed-off-by: Elmurod Talipov <elmurod.talipov@gmail.com>
1 parent 30a8a77 commit 0c2da16

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
layout: post
3+
title: "Kernel Key API Test: Load custom key"
4+
date: 2022-12-18
5+
description: This blog post provides instructions and code for installing and implementing a kernel module on a Debian machine. It includes a Makefile for building and cleaning the module, and a C file for the actual code of the module. The code involves creating and manipulating a keyring and key in the Linux kernel. The module can be tested by inserting and removing it from the kernel. The post also includes a description of the variables and functions used in the code.
6+
---
7+
8+
### Install Kernel Headers
9+
10+
To build kernel module you need to install kernel headers package. On debian machines you may install kernel headers with following command.
11+
12+
```sh
13+
sudo apt install linux-headers-$(uname -r)
14+
```
15+
16+
### Implement sample module
17+
18+
Here is `Makefile` for building sample module.
19+
20+
```make
21+
PROJECT := epk
22+
MODULE_DIR := $(PWD)
23+
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
24+
MOD_EXISTS := $(shell lsmod | grep -o $(PROJECT))
25+
obj-m += $(PROJECT).o
26+
27+
all:
28+
@make -C $(KERNEL_DIR) M=$(MODULE_DIR) modules
29+
30+
clean:
31+
@make -C $(KERNEL_DIR) M=$(MODULE_DIR) clean
32+
33+
test:
34+
@if [ ! -z "$(MOD_EXISTS)" ]; then \
35+
sudo rmmod $(PROJECT); \
36+
fi
37+
@sudo insmod $(PROJECT).ko
38+
```
39+
40+
This Makefile is used to build and clean a Linux kernel module with the name `epk`. The `PROJECT` variable is set to `epk`, which is the name of the kernel module. The `MODULE_DIR` variable is set to the current working directory (`PWD`), which is the directory where the Makefile and kernel module source files are located. The `KERNEL_DIR` variable is set to the directory of the Linux kernel source code, which is typically located at `/lib/modules/[kernel version]/build`.
41+
42+
The `obj-m` line specifies that the `epk` kernel module should be built as a loadable module.
43+
44+
The `all` target is used to build the kernel module. It runs the `make` command with the `-C` option, which specifies the directory to run make in (in this case, the Linux kernel source code directory). The `M` option specifies the directory of the kernel module source files (the `MODULE_DIR`). The `modules` target builds the kernel module.
45+
46+
The `clean` target is used to clean up the files created during the build process. It runs the `make` command in the same way as the `all` target, but with the clean target instead of `modules`.
47+
48+
The `test` target is used to test the kernel module. It first checks if the kernel module is already loaded in the kernel using the `lsmod` command and the `grep` command. If the module is loaded, it removes the module from the kernel using the `rmmod` command. It then inserts the module into the kernel using the `insmod` command.
49+
50+
Now we implement actual code in `epk.c` file.
51+
52+
```c
53+
#include <linux/init.h>
54+
#include <linux/module.h>
55+
#include <linux/kernel.h>
56+
#include <linux/vmalloc.h>
57+
#include <linux/key.h>
58+
#include <linux/cred.h>
59+
60+
#define CONFIG_EPK_KEYRING_NAME ".epk_custom"
61+
#define CONFIG_EPK_KEY_DESCRIPTION "epk-verification-key"
62+
63+
static struct key *m_epk_keyring;
64+
static key_ref_t m_epk_main_key;
65+
66+
static unsigned char EPK_X509_CERTIFICATE_DATA[] = { /* x509 certificate data */ };
67+
static unsigned int EPK_X509_CERTIFICATE_LEN = 0; /* size of above array */
68+
69+
static int __init epk_init(void)
70+
{
71+
const struct cred *cred = current_cred();
72+
key_perm_t perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
73+
unsigned long flags = KEY_ALLOC_NOT_IN_QUOTA;
74+
75+
pr_info("epk: Initialize module\n");
76+
77+
m_epk_keyring = keyring_alloc(CONFIG_EPK_KEYRING_NAME, KUIDT_INIT(0), KGIDT_INIT(0),
78+
cred, perm, flags, NULL, NULL);
79+
if (IS_ERR(m_epk_keyring))
80+
{
81+
pr_err("epk: Failed to allocate keyring (%ld)\n", PTR_ERR(m_epk_keyring));
82+
return PTR_ERR(m_epk_keyring);
83+
}
84+
85+
flags |= KEY_ALLOC_BUILT_IN;
86+
flags |= KEY_ALLOC_BYPASS_RESTRICTION;
87+
m_epk_main_key = key_create_or_update(make_key_ref(m_epk_keyring, 1), "asymmetric",
88+
CONFIG_EPK_KEY_DESCRIPTION,
89+
EPK_X509_CERTIFICATE_DATA, EPK_X509_CERTIFICATE_LEN,
90+
perm, flags);
91+
if (IS_ERR(m_epk_main_key))
92+
{
93+
pr_err("epk: Failed to load X.509 certificate (%ld)\n", PTR_ERR(m_epk_main_key));
94+
key_put(m_epk_keyring);
95+
return PTR_ERR(m_epk_main_key);
96+
}
97+
98+
return 0;
99+
}
100+
101+
static void __exit epk_exit(void)
102+
{
103+
pr_info("epk: Deinitialize module\n");
104+
105+
if (!IS_ERR(m_epk_main_key))
106+
key_ref_put(m_epk_main_key);
107+
108+
if (!IS_ERR(m_epk_keyring))
109+
key_put(m_epk_keyring);
110+
}
111+
112+
module_init(epk_init);
113+
module_exit(epk_exit);
114+
115+
MODULE_LICENSE("GPL");
116+
MODULE_AUTHOR("Elmurod A. Talipov");
117+
MODULE_DESCRIPTION("Kernel Key API Test Module.");
118+
MODULE_VERSION("0.1");
119+
```
120+
121+
122+
This above defines the initialization and exit functions for a Linux kernel module.
123+
124+
The `__init` and `__exit` functions are special functions in the Linux kernel that are executed when the kernel module is loaded into the kernel and unloaded from the kernel, respectively. The `__init` function is executed when the module is loaded, and the `__exit` function is executed when the module is unloaded.
125+
126+
The `epk_init` function is the initialization function for the kernel module. It performs the following tasks:
127+
128+
- It prints a message to the kernel log using the `pr_info` function.
129+
- It allocates a keyring using the `keyring_alloc` function and stores it in the `m_epk_keyring` global variable. A keyring is a special kind of key that can store other keys.
130+
- It creates or updates a key in the keyring using the `key_create_or_update` function and stores it in the `m_epk_main_key` global variable. The key is created with the type "asymmetric", a description specified by the `CONFIG_EPK_KEY_DESCRIPTION` macro, and data specified by the `EPK_X509_CERTIFICATE_DATA` and `EPK_X509_CERTIFICATE_LEN` macros.
131+
- It returns 0 to indicate that the initialization was successful.
132+
133+
The `epk_exit` function is the exit function for the kernel module. It performs the following tasks:
134+
135+
- It prints a message to the kernel log using the `pr_info` function.
136+
- It releases the reference to the `m_epk_main_key` key using the `key_ref_put` function.
137+
- It releases the reference to the `m_epk_keyring` keyring using the `key_put` function.
138+
139+
The `module_init` and `module_exit` macros are used to specify the initialization and exit functions for the kernel module. When the module is loaded into the kernel, the `epk_init` function is executed, and when the module is unloaded from the kernel, the `epk_exit` function is executed.
140+
141+
142+
You can generate x509 certificate data using [this script](https://github.com/smartnode/epk/blob/linux-5.4/data/genkey.sh).
143+
144+
### Building Module
145+
146+
To install module you need to have root access. Run make command to build and verify output.
147+
148+
```sh
149+
[elmurod@smartnode:epk{main}]$ make
150+
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-100-generic'
151+
Building modules, stage 2.
152+
MODPOST 1 modules
153+
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-100-generic'pk/epk.mod.o
154+
LD [M] /home/elmurod/tizen/github/e-talipov/epk/epk.ko
155+
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-100-generic'
156+
```
157+
158+
To install module run `make test`
159+
160+
### Verify Loaded Key
161+
162+
Check the key in key list with following command:
163+
164+
```sh
165+
sudo cat /proc/keys | grep epk
166+
```
167+
168+
Output should be something like below
169+
170+
```sh
171+
22685c40 I------ 1 perm 1f030000 0 0 keyring .epk_custom: 1
172+
38b5aca8 I------ 2 perm 1f030000 0 0 asymmetri epk-verification-key: X509.rsa []
173+
```

0 commit comments

Comments
 (0)