Environment
ubuntun 18.04
Linux ubuntu 5.4.0-122-generic #138~18.04.1-Ubuntu SMP Fri Jun 24 14:14:03 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
What I'm trying to do
I'm trying to write a kernel module that hooks sys_read, sys_write and sys_open, using ftrace.
The ftrace code is copied from some blogs, I'm sure ftrace usage is right.
But when i execute insmod my.ko, the vmware workstation shows up cpu disabled by guest operating system, I have no idea about it, the system crashed immediately so that i can't read the dmesg or do something else about debug.
Here is my code (maybe only fh_sys_read and GetPath make sense for this question):
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/unistd.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/version.h>
//#include<linux/err.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/rtc.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/unistd.h>
//#include<linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
//#include<linux/init.h>
#include <linux/dcache.h>
#include <linux/delay.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs_struct.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/limits.h> //for PATH_MAX
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <stddef.h>
// #include <string.h>
MODULE_LICENSE("GPL");
#define F_LEN 256
static char* GetPath(int fd){
char *tmp;
char *pathname;
struct file *file;
struct path path;
struct files_struct *files = NULL;
files = current->files;
file = files->fdt->fd[fd];
if (!file) {
return NULL;
}
path = file->f_path;
path_get(&path);
tmp = (char *)__get_free_page(GFP_KERNEL);
if (!tmp) {
path_put(&path);
return NULL;
}
path_put(&path);
pathname = d_path(&path, tmp, PAGE_SIZE);
if (IS_ERR(pathname)) {
free_page((unsigned long)tmp);
return PTR_ERR(pathname);
}
printk("path:%s\n", pathname);
free_page((unsigned long)tmp);
return NULL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
#include <linux/ftrace.h>
#ifndef CONFIG_X86_64
#error Currently only x86_64 architecture is supported
#endif // config_x86_64
#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0))
#define PTREGS_SYSCALL_STUBS 1
#endif // config PTREGS
#ifdef PTREGS_SYSCALL_STUBS
#define SYSCALL_NAME(name) ("__x64_" name)
#else
#define SYSCALL_NAME(name) (name)
#endif // PTREGS_SYSCALL_STUBS
struct ftrace_hook {
const char *name;
void *function;
void *original;
unsigned long address;
struct ftrace_ops ops;
};
#define HOOK(_name, _function, _original) \
{ \
.name = SYSCALL_NAME(_name), .function = (_function), \
.original = (_original), \
}
static asmlinkage long (*real_sys_read)(unsigned int fd, char *buf, size_t count);
static asmlinkage long (*real_sys_write)(unsigned int fd, const char *buf, size_t count);
static asmlinkage long (*real_sys_open)(const char *filename, int flags, umode_t mode);
static asmlinkage long fh_sys_read(unsigned int fd, char __user *buf, size_t count) {
GetPath(fd);
return (*real_sys_read)(fd, buf, count);
}
static asmlinkage long fh_sys_write(unsigned int fd, const char __user *buf,
size_t count) {
return (*real_sys_write)(fd, buf, count);
}
static asmlinkage long fh_sys_open(const char __user *filename, int flags,
umode_t mode) {
return (*real_sys_open)(filename, flags, mode);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
static unsigned long lookup_name(const char *name) {
struct kprobe kp = {.symbol_name = name};
unsigned long retval;
if (register_kprobe(&kp) < 0) return 0;
retval = (unsigned long)kp.addr;
unregister_kprobe(&kp);
return retval;
}
#else
static unsigned long lookup_name(const char *name) {
return kallsyms_lookup_name(name);
}
#endif // lookup_name
static int fh_resolve_address(struct ftrace_hook *hook) {
pr_debug("start lookup_name");
hook->address = lookup_name(hook->name);
if (!hook->address) {
printk(KERN_ERR "lookup_name error");
printk(KERN_ERR "unresolved symbol: %s\n", hook->name);
return -ENOENT;
}
*((unsigned long *)hook->original) = hook->address;
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
#define ftrace_ops_fl_recursion ftrace_ops_fl_recursion_safe
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
#define ftrace_regs pt_regs
static __always_inline struct pt_regs *ftrace_get_regs(
struct ftrace_regs *fregs) {
return fregs;
}
#endif // versioncode 5.11
static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops,
struct ftrace_regs *regs) {
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
/* Skip the function calls from the current module. */
if (!within_module(parent_ip, THIS_MODULE))
regs->ip = (unsigned long)hook->function;
}
int fh_install_hook(struct ftrace_hook *hook) {
int err = 0;
err = fh_resolve_address(hook);
if (err) {
printk(KERN_ERR "resolve_address error");
return err;
}
hook->ops.func = fh_ftrace_thunk;
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY;
pr_info("start set ip");
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
if (err) {
pr_debug("ftrace_set_filter_ip() failed: %d\n", err);
return err;
}
pr_debug("start register_ftrace_function");
err = register_ftrace_function(&hook->ops);
if (err) {
pr_debug("register_ftrace_function() failed: %d\n", err);
ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
return err;
}
return 0;
}
int fh_install_hooks(struct ftrace_hook *hooks, int count) {
int i = 0;
for (i = 0; i < count; i++) {
fh_install_hook(&hooks[i]);
}
return 0;
}
int fh_remove_hook(struct ftrace_hook *hook) {
int err;
err = unregister_ftrace_function(&hook->ops);
if (err) {
pr_debug("unregister_ftrace_function() failed: %d\n", err);
}
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
if (err) {
pr_debug("ftrace_set_filter_ip() failed: %d\n", err);
}
return 0;
}
int fh_remove_hooks(struct ftrace_hook *hooks, int count) {
int i = 0;
for (i = 0; i < count; i++) {
fh_remove_hook(&hooks[i]);
}
return 0;
}
struct ftrace_hook hooks[] = {HOOK("sys_read", fh_sys_read, &real_sys_read),
HOOK("sys_open", fh_sys_open, &real_sys_open),
HOOK("sys_write", fh_sys_write, &real_sys_write)};
#endif // if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
static int __init kprobe_test_init(void) {
fh_install_hooks(hooks, 3);
return 0;
}
static void __exit kprobe_test_exit(void) {
fh_remove_hooks(hooks, 3);
}
module_init(kprobe_test_init);
module_exit(kprobe_test_exit);
My thoughts
I think the GetPath function caused the crash, and when I commment the line that calls GetPath, it works well, but it doesn't make sense.
I had tried to comment each line in GetPath, this GetPath wouldn't cause the crash, but it make no sense too.
static char* GetPath(int fd){
char *tmp;
char *pathname;
struct file *file;
struct path path;
struct files_struct *files = NULL;
files = current->files;
file = files->fdt->fd[fd];
if (!file) {
return NULL;
}
path = file->f_path;
// path_get(&path);
tmp = (char *)__get_free_page(GFP_KERNEL);
if (!tmp) {
path_put(&path);
return NULL;
}
return NULL;
// FIXME:
/*
path_put(&path);
pathname = d_path(&path, tmp, PAGE_SIZE);
if (IS_ERR(pathname)) {
free_page((unsigned long)tmp);
return PTR_ERR(pathname);
}
printk("path:%s\n", pathname);
free_page((unsigned long)tmp);
return NULL;
*/
}
Question
What is the problem, the GetPath function is just everal line and it looks all right.
How to debug kernel module that will cause system crashed? Like gdb, I can make some breakpoint and debug them step by step, but kernel module I don't know how.
Feel very sorry for my poor english.
Thanks for viewing.
Related
So I have this function in my driver for network NIC and this function appears in proc/kallsyms[https://stackoverflow.com/a/67766463/4808760] file with base address this is the function
static int rtl8169_poll(struct napi_struct *napi, int budget)
{
struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);
struct net_device *dev = tp->dev;
int work_done;
rtl_tx(dev, tp, budget);
work_done = rtl_rx(dev, tp, budget);
if (work_done < budget && napi_complete_done(napi, work_done))
rtl_irq_enable(tp);
return work_done;
}
appears as
ffffffffc02d2210 t rtl8169_poll [r8169]
and this is my ebpf program
SEC("kprobe/rtl8169_poll")
int bpf_prog2(struct pt_regs *ctx)
{
int sc_nr = (int)PT_REGS_PARM1(ctx);
char *fmt="HELLO from FWDALI %d %d";
bpf_trace_printk(fmt,1,sc_nr);
bpf_trace_printk(fmt ,2,sc_nr);
/* dispatch into next BPF program depending on syscall number */
//bpf_tail_call(ctx, &progs, sc_nr);
/* fall through -> unknown syscall */
//if (sc_nr >= __NR_getuid && sc_nr <= __NR_getsid) {
// char fmt[] = "-----FWD-------------------------syscall=%d (one of get/set uid/pid/gid)\n";
// bpf_trace_printk(fmt, sizeof(fmt), sc_nr);
//}
return 0;
}
And this is my simple userspace code
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include <fcntl.h>
#ifdef __mips__
#define MAX_ENTRIES 6000 /* MIPS n64 syscalls start at 5000 */
#else
#define MAX_ENTRIES 1024
#endif
/* install fake seccomp program to enable seccomp code path inside the kernel,
* so that our kprobe attached to seccomp_phase1() can be triggered
*/
void read_trace_pipe(void)
{
int trace_fd;
//printf("-%s-\n",DEBUGFS);
trace_fd = open( "/sys/kernel/debug/tracing/trace_pipe", O_RDONLY, 0);
if (trace_fd < 0)
return;
while (1) {
static char buf[4096];
ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz > 0) {
buf[sz] = 0;
puts(buf);
}
}
}
static void install_accept_all_seccomp(void)
{
struct sock_filter filter[] = {
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_SECCOMP, 2, &prog))
perror("prctl");
}
int main(int ac, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
int key, fd, progs_fd;
const char *section;
char filename[256];
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[1]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog2");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
progs_fd = bpf_object__find_map_fd_by_name(obj, "progs");
if (progs_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
section = bpf_program__section_name(prog);
/* register only syscalls to PROG_ARRAY */
if (sscanf(section, "kprobe/%d", &key) != 1)
continue;
fd = bpf_program__fd(prog);
bpf_map_update_elem(progs_fd, &key, &fd, BPF_ANY);
}
install_accept_all_seccomp();
f = popen("dd if=/dev/zero of=/dev/null count=5", "r");
(void) f;
read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
SO i like if some take a look at above and explain what exactly I need to add to my ebpf program for kprobe and also what I need to do in my userspace loader program..
I am still having tough time with getting to loads of stuff that tells its simple to implement to use this magical line SEC("kprobe/rtl8169_poll") or something with just loading the program from userspace and its done, But I havent started thinking much of ebpf since ebpf is kind of failed in this simple function hook
this link gave me the idea that I can hook to this function https://stackoverflow.com/a/67766463/4808760
I am a total beginner in kernel module programming. I wanted to create a module which reads input from the proc filesystem. With this you should be able to perform operations in the program. I did that with threading. However my machine crashes after inserting the module. Probably I did something wrong. Here is the source code:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/string.h>
#define PROCFS_MAX_SIZE 1024
#define PROCFS_NAME "test"
MODULE_LICENSE("GPL");
int pid = 1;
static struct proc_dir_entry *Our_Proc_File;
static char procfs_buffer[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0;
struct task_struct *task;
static ssize_t procfile_read(struct file *file, char *buffer, size_t buffer_length, loff_t *offset) {
static int flag = 0;
if(flag) {
printk(KERN_INFO "read : END\n");
flag = 0;
return 0;
}
printk(KERN_INFO "read (/proc/%s) : called\n",PROCFS_NAME);
flag = 1;
return snprintf(buffer, buffer_lengh, procfs_buffer);
}
static ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) {
procfs_buffer_size = count;
if (copy_from_user(procfs_buffer, buffer, procfs_buffer_size)) {
return -EFAULT;
}
return procfs_buffer_size;
}
static int main_prog(void *data) {
int end = 0;
while(!end) {
while(memcmp(procfs_buffer,"\x00",1)==0);
if(memcmp(procfs_buffer,"test",4)) {
printk(KERN_INFO "Test triggered!\n");
}
if(memcmp(procfs_buffer,"exit",4)) {
end++;
}
memset(procfs_buffer, 0, 1024);
}
kthread_stop(task);
}
static struct file_operations fops_struct = {
.read = procfile_read,
.write = procfile_write,
};
int init_module() {
Our_Proc_File = proc_create(PROCFS_NAME, 0666, NULL, &fops_struct);
if (Our_Proc_File == NULL) {
remove_proc_entry(PROCFS_NAME, NULL);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", PROCFS_NAME);
return -ENOMEM;
}
task = kthread_run(&main_prog, NULL, "thread");
wake_up_process(task);
printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);
return 0;
}
void cleanup_module() {
remove_proc_entry(PROCFS_NAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}
Can someone help me?
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include<linux/sched.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
char *msg;
ssize_t write_proc(struct file *filp,const char *buf,size_t count,loff_t *offp)
{
copy_from_user(msg,buf,count);
printk(KERN_INFO "%s",msg);
return count;
}
struct file_operations proc_fops = {
write: write_proc
};
int proc_init (void) {
proc_create("write",0,NULL,&proc_fops);
return 0;
}
void proc_cleanup(void) {
remove_proc_entry("write",NULL);
}
MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);
When I used the command echo 'hello' > /proc/write Nothing show up on terminal . Can you help me to find mistakes in the code ? The string that I writed on it should have show up on terminal.
Example :
$ echo 'hello' > /proc/write
hello
Here are some simple modifications on your code:
#define MSG_SIZE (512)
static char *msg;
#define ourmin(a,b) (((a)<(b)) ? (a) : (b))
ssize_t write_proc(struct file *filp,const char *buf,size_t count,loff_t *offp)
{
unsigned long actual_len = ourmin(count, MSG_SIZE-1);
memset(msg, 0, MSG_SIZE);
copy_from_user(msg, buf, actual_len);
printk(KERN_DEBUG "Got: %s",msg);
return count;
}
int proc_init (void) {
// Allocate space for msg
if ((msg = kmalloc(MSG_SIZE, GFP_KERNEL)) == NULL)
return -ENOMEM;
// Should check the output of this too
proc_create("write",0,NULL,&proc_fops);
return 0;
}
void proc_cleanup(void) {
remove_proc_entry("write",NULL);
kfree(msg);
}
I could retrieve the output in the kernel log (dmesg for instance).
Im working on little kernel module. Im trying to use IOCTL (in ioctl_add), but I get ENOTTY when I call it, which is checked in switch, on the bottom of main. The code is below. Has anyone got any idea what am I doing wrong?
user.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
void cleanup()
{
if(f>=0) {
close(f);
}
}
int ioctl_add(int f)
{
int ret;
ret = ioctl(f, IOCTL_ADD);
printf("Add \n");
return ret;
}
int main(int argc, char * argv[])
{
int fd;
int *ptr;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("error");
}
posix_memalign((void **)&ptr, 4096, 4096);
* ptr = atoi(argv[2]);
write(fd, ptr, 4096);
ioctl_add(fd);
printf("data is %d\n", *ptr);
close(fd);
switch(errno){
case EBADF:
printf("errno: EBADF \n");
break;
case EFAULT:
printf("errno: EFAULT \n");
break;
case EINVAL:
printf("errno: EINVAL \n");
break;
case ENOTTY:
printf("errno: ENOTTY \n");
break;
default:
printf("errno: none \n");
return 0;
}
return 0;
}
module.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//#include <linux/mm.h>
//#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <linux/ioctl.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
#include <linux/mm.h>
#include <linux/pagemap.h>
#define DEVICE_NAME "acc_priv"
MODULE_LICENSE("GPL v2");
int ress, tmp;
struct page *page;
int *myaddr;
ssize_t acc_read(struct file *filp,
char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Opened\n\r");
return 0;
}
ssize_t acc_write(struct file *filp,
const char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Write\n\r");
printk(KERN_INFO "%s\n", __FUNCTION__);
down_read(¤t->mm->mmap_sem);
ress = get_user_pages(current, current->mm,(unsigned long)buf,1,1,1,&page,NULL);
if (ress) {
printk(KERN_INFO "Got mmaped.\n");
myaddr = kmap(page);
printk(KERN_INFO "%d\n", *myaddr);
tmp = *myaddr;
tmp = tmp * 2;
printk(KERN_INFO "the result of multiplying: %d\n", tmp);
* myaddr = tmp;
page_cache_release(page);
}
up_read(¤t->mm->mmap_sem);
return (0);
}
static int acc_open(struct inode *inode,
struct file *file)
{
printk(KERN_INFO "Opened inode:%p, file:%p\n", inode, file);
return 0;
}
long acc_ioctl(struct file *filp,
unsigned int cmd,unsigned long arg)
{
if(cmd == IOCTL_ADD)
printk(KERN_INFO "Do specified job \n");
return 0;
{
int acc_release(struct inode *inode,
struct file *file)
{
printk (KERN_INFO "device_release(%p,%p)\n", inode, file);
return 0;
}
struct file_operations Fops = {
.owner=THIS_MODULE,
.read=acc_read,
.write=acc_write,
.open=acc_open,
.unlocked_ioctl=acc_ioctl,
.release=acc_release,
};
dev_t my_dev=0;
struct cdev * my_cdev = NULL;
static struct class *class_acc_priv = NULL;
void clean_up(void)
{
if(my_dev && class_acc_priv) {
device_destroy(class_acc_priv,my_dev);
}
if(my_cdev) {
cdev_del(my_cdev);
my_cdev=NULL;
}
if(my_dev) {
unregister_chrdev_region(my_dev, 1);
}
if(class_acc_priv) {
class_destroy(class_acc_priv);
class_acc_priv=NULL;
}
}
int init_acc_priv(void)
{
int res=0;
res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
if(res) {
printk (KERN_ALERT "Alocation of the device number for %s failed\n",
DEVICE_NAME);
return res;
};
class_acc_priv = class_create(THIS_MODULE, "acc_class");
if (IS_ERR(class_acc_priv)) {
printk(KERN_ERR "Error creating rs_class.\n");
res=PTR_ERR(class_acc_priv);
goto err1;
}
my_cdev = cdev_alloc( );
my_cdev->ops = &Fops;
my_cdev->owner = THIS_MODULE;
res=cdev_add(my_cdev, my_dev, 1);
if(res) {
printk (KERN_ALERT "Registration of the device number for %s failed\n",
DEVICE_NAME);
res=-EFAULT;
goto err1;
};
device_create(class_acc_priv,NULL,my_dev,NULL,"acc_priv%d",MINOR(my_dev));
printk (KERN_ALERT "%s The major device number is %d.\n",
"Registeration is a success.",
MAJOR(my_dev));
return res;
err1:
clean_up();
return res;
}
module_init(init_acc_priv);
void cleanup_acc_priv( void )
{
clean_up();
}
module_exit(cleanup_acc_priv);
When 32bit application is run on 64bit OS, it uses compat_ioctl syscall instead of unlocked_ioctl one for perform ioctl command. The reason of special syscall is that size of ioctl argument may differ for 64bit and 32bit applications.
So you need to implement .compat_ioctl file operation.
I tried code(Using Linux Kernel Crypto APIs) for calculating HMAC-SHA512 on a file.
Surprisingly, HMAC calculated from the kernel code and from OpenSSL commands are different.
OpenSSL commands are:
openssl rand -base64 16
plakOhrXlfnBENPVBo91kg==
openssl dgst -sha512 -mac hmac -macopt key:plakOhrXlfnBENPVBo91kg== ../hello6.ko
HMAC-SHA512(../hello6.ko)= 9605f40851630f5b7a18fa30c7e5d6f1e77afb011d319efa515556d12ba6930f459825b3695a0d0d910a714724c0d99b36ccea5c878962b32a8de65dcbcc247d
HMAC-SHA512 from the kernel code is:
84306723b713379aa666fe9aa75af3192a9707d19136c25dd2286c5f6d86dfd8f76ceaa0ce147b53c0e9f7bfab485f38f5139c687c34c840db7f7fa9438bf8b0d8df8e770088ffffc8de8e770088ffffc3f027a0ffffffff672c00000088fffff0de8e770088ffff1900000000000080672c000000000000e8de8e770088ffff
How come these HMACs are different for the same content? Any pointers to get rid of this issue is very helpful.
Base64 encoded key is generated by below command and stored in a file:
openssl rand -base64 -out $Module_name.sha512key 16
HMAC is generated on a file and stored in a file.
openssl dgst -sha512 -mac hmac -macopt key:$HMAC_KEY $HMAC_PATH/$Module_name> temp.txt
Key and File content are read by below code:
static char hmac[HMAC_SHA512_SIZE];
const char *kofilename = "/home/sri/Documents/Hello/hello6.ko";
const char *hmackey_file = "/home/sri/Documents/Hello/hello6.ko.sha512key";
const char *hmac_file = "/home/sri/Documents/Hello/hello6.ko.sha512";
unsigned char *data = NULL;
int readkostatus;
int readkeystatus;
int readhmacstatus;
unsigned char *hmackey = NULL;
unsigned char *stored_hmac = NULL;
readkeystatus = read_file(hmackey_file, &hmackey);
if(readkeystatus < 0)
goto readkeyerr;
printk(KERN_INFO "HMAC Key is :%s", hmackey);
readkostatus = read_kofile(kofilename, &data);
if(readkostatus < 0)
goto readkoerr;
printk(KERN_INFO "File data size is :%ld", strlen(data));
printk(KERN_INFO "File data is :%s", data);
hmac_sha512(hmackey, data, hmac, sizeof(hmac));
printk(KERN_INFO "FINAL HMAC:%s", hmac);
readhmacstatus = read_file(hmac_file, &stored_hmac);
if(readhmacstatus < 0)
goto readhmacerr;
printk(KERN_INFO "Stored HMAC:%s", stored_hmac);
if(!memcmp(stored_hmac, hmac, HMAC_SHA512_SIZE))
printk(KERN_INFO "HMACs match");
else
printk(KERN_INFO "HMACs do not match");
vfree(stored_hmac);
vfree(data);
vfree(hmackey);
return 0;
readkeyerr:
{
printk(KERN_INFO "hmac key read error:%d", readkeystatus);
return readkeystatus;
}
readkoerr:
{
printk(KERN_INFO "ko read error:%d", readkostatus);
return readkostatus;
}
readhmacerr:
{
printk(KERN_INFO "hmac read error:%d", readhmacstatus);
return readhmacstatus;
}
Code for reading the files is given below:
int read_file(const char *filename, unsigned char **data)
{
struct file* filp = NULL;
long filesize;
int ret = 0;
mm_segment_t old_fs = get_fs();
set_fs(get_ds());
filp = filp_open(filename, O_RDONLY, 0);
if(IS_ERR(filp)) {
ret = PTR_ERR(filp);
printk(KERN_INFO "kofile is not opened");
}
else
{
loff_t offset;
offset = 0;
filesize = filp->f_dentry->d_inode->i_size;
if (filesize <= 0 || filesize > 131072)
{
goto fileoperror;
}
*data = (unsigned char *)vmalloc(filesize);
if (*data == NULL)
goto datanull;
ret = vfs_read(filp, *data, filesize, &offset);
if(ret != filesize)
goto read_error;
filp_close(filp, NULL);
}
set_fs(old_fs);
return ret;
fileoperror:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Invalid file operation '%s'\n", filename);
return (-EPERM);
}
datanull:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Data Buffer is not allocated");
return (-EFAULT);
}
read_error:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Failed to read '%s'.\n", filename);
return (-EFBIG);
}
}
int read_kofile(const char* filename, unsigned char **data)
{
return read_file(filename, data);
}
HMAC-SHA-512 is calculated on the file by calling Crypto APIs:
#define HMAC_SHA512_SIZE 64
struct hmac_sha512_result {
struct completion completion;
int err;
};
static void hmac_sha512_complete(struct crypto_async_request *req, int err) {
struct hmac_sha512_result *r=req->data;
if(err==-EINPROGRESS)
return;
r->err=err;
complete(&r->completion);
}
int hmac_sha512(const unsigned char *key, // key
const unsigned char *data_in, // data in
unsigned char *hash_out, size_t outlen) { // hash buffer and length
int rc=0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_sha512_result tresult;
void *hash_buf;
size_t klen = strlen(key);
size_t dlen = strlen(data_in);
int len = HMAC_SHA512_SIZE;
char hash_tmp[HMAC_SHA512_SIZE];
char *hash_res = hash_tmp;
printk(KERN_INFO "hmac_sha512: HMAC key is %s ", key);
/* Set hash output to 0 initially */
memset(hash_out, 0, outlen);
init_completion(&tresult.completion);
tfm=crypto_alloc_ahash("hmac(sha512)",0,0);
if(IS_ERR(tfm)) {
printk(KERN_ERR "hmac_sha512: crypto_alloc_ahash failed.\n");
rc=PTR_ERR(tfm);
goto err_tfm;
}
if(!(req=ahash_request_alloc(tfm,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to allocate request for hmac(sha512)\n");
rc=-ENOMEM;
goto err_req;
}
if(crypto_ahash_digestsize(tfm)>len) {
printk(KERN_ERR "hmac_sha512: tfm size > result buffer.\n");
rc=-EINVAL;
goto err_req;
}
ahash_request_set_callback(req,CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_sha512_complete,&tresult);
if(!(hash_buf=kzalloc(dlen,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to kzalloc hash_buf");
rc=-ENOMEM;
goto err_hash_buf;
}
memcpy(hash_buf,data_in,dlen);
sg_init_one(&sg,hash_buf,dlen);
crypto_ahash_clear_flags(tfm,-0);
if((rc=crypto_ahash_setkey(tfm,key,klen))){
printk(KERN_ERR "hmac_sha512: crypto_ahash_setkey failed\n");
goto err_setkey;
}
ahash_request_set_crypt(req,&sg,hash_res,dlen);
rc=crypto_ahash_digest(req);
switch(rc) {
case 0:
while (len--) {
snprintf(hash_out, outlen, "%02x", (*hash_res++ & 0x0FF));
hash_out += 2;
}
break;
case -EINPROGRESS:
case -EBUSY:
rc=wait_for_completion_interruptible(&tresult.completion);
if(!rc && !(rc=tresult.err)) {
INIT_COMPLETION(tresult.completion);
break;
} else {
printk(KERN_ERR "hmac_sha512: wait_for_completion_interruptible failed\n");
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buf);
err_hash_buf:
ahash_request_free(req);
err_req:
crypto_free_ahash(tfm);
err_tfm:
return rc;
}
Please let me know, if something is missing.
crypto_req_done defined in hash.h
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
#include <linux/msi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <linux/ahci-remap.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <net/sock.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/buffer_head.h>
#include <linux/libata.h>
#include <asm/uaccess.h>
#include <asm/traps.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/hashtable.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/signal_types.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/crypto.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define TESTMGR_POISON_BYTE 0xfe
static int do_ahash_op(int (*op)(struct ahash_request *req), struct ahash_request *req, struct crypto_wait *wait)
{
int err;
err = op(req);
return crypto_wait_req(err, wait);
}
static int check_nonfinal_ahash_op(const char *op, int err){
if(err < 0){
pr_err("alg: ahash: %s() failed with err %d on test vector %s\n", op, err, get_error(err));
return 1;
}
return 0;
}
now hmac_sha256:
int hmac_sha256(unsigned char *key, size_t key_size, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
int rc, key_is_null;
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist pending_sgl;
DECLARE_CRYPTO_WAIT(wait);
if(key == NULL){
key = kzalloc(32, GFP_KERNEL);
key_size = 32;
key_is_null = 1;
}else{key_is_null = 0;}
tfm = crypto_alloc_ahash("hmac(sha256)", 0, 0);
if(IS_ERR(tfm)) {
rc=PTR_ERR(tfm);
printk(KERN_ERR "hmac_sha256: crypto_alloc_ahash failed(%s).\n", get_error(rc));
goto err_tfm;
}
if(!(req = ahash_request_alloc(tfm, GFP_KERNEL))) {
rc =- ENOMEM;
printk(KERN_ERR "hmac_sha256: failed to allocate request for hmac(sha512)(%s).\n", get_error(rc));
}
if(key_size){
if((rc = crypto_ahash_setkey(tfm, key, key_size))){
printk(KERN_ERR "hmac_sha256: crypto_ahash_setkey failed.(%s)\n", get_error(rc));
goto out;
}
}
sg_init_table(&pending_sgl, 1);
memset(req->__ctx, TESTMGR_POISON_BYTE, crypto_ahash_reqsize(tfm));
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, NULL, okm, 0);
rc = do_ahash_op(crypto_ahash_init, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_init", rc);
if (rc)
goto out;
sg_set_buf(&pending_sgl, ikm, ikm_len);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, &pending_sgl, okm, okm_len);
rc = do_ahash_op(crypto_ahash_update, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_update", rc);
if(rc)
goto out;
rc = do_ahash_op(crypto_ahash_final, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_final", rc);
if (rc)
goto out;
dump_memory(okm, "hmac_sha256", okm_len);
out:
ahash_request_free(req);
crypto_free_ahash(tfm);
err_tfm:
if(key_is_null)
kfree(key);
return rc;
}
Break A leg!
int hkdf_extract(unsigned char *salt, size_t salt_len, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
return hmac_sha256(salt, salt_len, ikm, ikm_len, okm, okm_len);
}