Install Kernel Headers
To build kernel module you need to install kernel headers package. On debian machines you may install kernel headers with following command.
sudo apt install linux-headers-$(uname -r)
Implement sample module
Here is Makefile for building sample module.
PROJECT := epk
MODULE_DIR := $(PWD)
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
MOD_EXISTS := $(shell lsmod | grep -o $(PROJECT))
obj-m += $(PROJECT).o
all:
@make -C $(KERNEL_DIR) M=$(MODULE_DIR) modules
clean:
@make -C $(KERNEL_DIR) M=$(MODULE_DIR) clean
test:
@if [ ! -z "$(MOD_EXISTS)" ]; then \
sudo rmmod $(PROJECT); \
fi
@sudo insmod $(PROJECT).ko
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.
The obj-m line specifies that the epk kernel module should be built as a loadable module.
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.
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.
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.
Now we implement actual code in epk.c file.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/key.h>
#include <linux/cred.h>
#define CONFIG_EPK_KEYRING_NAME ".epk_custom"
#define CONFIG_EPK_KEY_DESCRIPTION "epk-verification-key"
static struct key *m_epk_keyring;
static key_ref_t m_epk_main_key;
static unsigned char EPK_X509_CERTIFICATE_DATA[] = { /* x509 certificate data */ };
static unsigned int EPK_X509_CERTIFICATE_LEN = 0; /* size of above array */
static int __init epk_init(void)
{
const struct cred *cred = current_cred();
key_perm_t perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
unsigned long flags = KEY_ALLOC_NOT_IN_QUOTA;
pr_info("epk: Initialize module\n");
m_epk_keyring = keyring_alloc(CONFIG_EPK_KEYRING_NAME, KUIDT_INIT(0), KGIDT_INIT(0),
cred, perm, flags, NULL, NULL);
if (IS_ERR(m_epk_keyring))
{
pr_err("epk: Failed to allocate keyring (%ld)\n", PTR_ERR(m_epk_keyring));
return PTR_ERR(m_epk_keyring);
}
flags |= KEY_ALLOC_BUILT_IN;
flags |= KEY_ALLOC_BYPASS_RESTRICTION;
m_epk_main_key = key_create_or_update(make_key_ref(m_epk_keyring, 1), "asymmetric",
CONFIG_EPK_KEY_DESCRIPTION,
EPK_X509_CERTIFICATE_DATA, EPK_X509_CERTIFICATE_LEN,
perm, flags);
if (IS_ERR(m_epk_main_key))
{
pr_err("epk: Failed to load X.509 certificate (%ld)\n", PTR_ERR(m_epk_main_key));
key_put(m_epk_keyring);
return PTR_ERR(m_epk_main_key);
}
return 0;
}
static void __exit epk_exit(void)
{
pr_info("epk: Deinitialize module\n");
if (!IS_ERR(m_epk_main_key))
key_ref_put(m_epk_main_key);
if (!IS_ERR(m_epk_keyring))
key_put(m_epk_keyring);
}
module_init(epk_init);
module_exit(epk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elmurod A. Talipov");
MODULE_DESCRIPTION("Kernel Key API Test Module.");
MODULE_VERSION("0.1");
This above defines the initialization and exit functions for a Linux kernel module.
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.
The epk_init function is the initialization function for the kernel module. It performs the following tasks:
- It prints a message to the kernel log using the
pr_infofunction. - It allocates a keyring using the
keyring_allocfunction and stores it in them_epk_keyringglobal variable. A keyring is a special kind of key that can store other keys. - It creates or updates a key in the keyring using the
key_create_or_updatefunction and stores it in them_epk_main_keyglobal variable. The key is created with the type “asymmetric”, a description specified by theCONFIG_EPK_KEY_DESCRIPTIONmacro, and data specified by theEPK_X509_CERTIFICATE_DATAandEPK_X509_CERTIFICATE_LENmacros. - It returns 0 to indicate that the initialization was successful.
The epk_exit function is the exit function for the kernel module. It performs the following tasks:
- It prints a message to the kernel log using the
pr_infofunction. - It releases the reference to the
m_epk_main_keykey using thekey_ref_putfunction. - It releases the reference to the
m_epk_keyringkeyring using thekey_putfunction.
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.
You can generate x509 certificate data using this script.
Building Module
To install module you need to have root access. Run make command to build and verify output.
[elmurod@smartnode:epk{main}]$ make
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-100-generic'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-100-generic'pk/epk.mod.o
LD [M] /home/elmurod/tizen/github/e-talipov/epk/epk.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-100-generic'
To install module run make test
Verify Loaded Key
Check the key in key list with following command:
sudo cat /proc/keys | grep epk
Output should be something like below
22685c40 I------ 1 perm 1f030000 0 0 keyring .epk_custom: 1
38b5aca8 I------ 2 perm 1f030000 0 0 asymmetri epk-verification-key: X509.rsa []