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_info
function. - It allocates a keyring using the
keyring_alloc
function and stores it in them_epk_keyring
global 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_update
function and stores it in them_epk_main_key
global variable. The key is created with the type “asymmetric”, a description specified by theCONFIG_EPK_KEY_DESCRIPTION
macro, and data specified by theEPK_X509_CERTIFICATE_DATA
andEPK_X509_CERTIFICATE_LEN
macros. - 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_info
function. - It releases the reference to the
m_epk_main_key
key using thekey_ref_put
function. - It releases the reference to the
m_epk_keyring
keyring using thekey_put
function.
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 []