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