Question

I am writing kernel module(C in Linux) and I want to change the permission of the other files in it. any solution? since I am in kernel I can't use chmod syscall and ... thanks for your help

This is my Makefile:

> obj-m += ca.o
> 
>     all:
>       make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
>     
>     clean:
>       make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

And this is my Code:

> #include <linux/string.h>
> #include <linux/mm.h>
> /* Snip, tons of includes (all of them :))*/
> #include <linux/delay.h> .... int procfile_write(struct file *file,
> const char *buffer, unsigned long
> count,
>          void *data) { ...    sys_chmod(path, per); ... } ...

When Making it gives a warning:

WARNING: "sys_chmod" [file] undefiened

and when loading the module with "sudo insmod" it gives this error:

Unknown sybol in module

it seems that this error happens especialy in kernel modules. any idea? again thanks!

Was it helpful?

Solution

The syscalls are not exported symbols. You need to do a little bit of hacking if you want them.

you want to get your fingers on sys_call_table. It contains a pointer to every syscall. Look at arch/x86/kernel/syscall_table_32.S or arch/i386/kernel/entry.S on older kernels.

You can grep sys_call_table /usr/src/linux/System.map (or /proc/kallsyms if the symbols are exported) to find the base address of this table. You can have this address as a parameter for your module (converting an hex string to a pointer will be needed).

You'll be able to call the right syscall with the offset defined in arch/x86/include/asm/unistd_32.h (or include/asm-i386/unistd.h on older kernels). You get something like: #define __NR_chmod 15

Macros are helpful:

#define DO_SYSCALL_2(sc, t1, a1, t2, a2)                       \
    (((asmlinkage long (*)(t1, t2)) sys_call_table[__NR_##sc]) (a1, a2));
#define USER_SYSCALL_2(sc, t1, a1, t2, a2)                     \
    static inline asmlinkage long syscall_##sc(t1 a1, t2 a2)   \
    { return DO_SYSCALL_2(sc, t1, a1, t2, a2) }

USER_SYSCALL_2(chmod, const char __user *, filename, mode_t, mode);
int my_code(void) { return syscall_chmod(arg1, arg2); }

Also, if you are passing kernel buffer (for filename for examples) that are supposed to be user buffers, don't forget to change the data segment:

mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS);
ret = syscall_XXX(...);
set_fs(oldfd);

OTHER TIPS

Welcome to stackoverflow! IIRC you want sys_chmod()

From the Linux Kernel Mailing List

On Thu, Feb 20, 2003 at 11:10:27PM +0100, Andrea Arcangeli wrote: On Thu, Feb 20, 2003 at 12:40:43PM -0500, Jeff Garzik wrote:

On Thu, Feb 20, 2003 at 11:04:37PM +0530, Prasad wrote:

Is there a way using which i could invoke a syscall in the kernel space? The syscall is to be run disguised as another process. The actual

Call sys_whatever(). Look at the kernel code for examples.

The kernel already does this in various places. sys_read, sys_write, open_filp, sys_close, and other functions are safe to call from kernel code -- though this is discouraged. init/do_mounts.c is a particularly annoying case, and is a big reason why klibc needs to be merged. syscalls should be made from userspace, not the kernel.

People are starting to worry, as this isn't the kind of thing you might do in the kernel (unless you are use you know what you are doing). If you just want to change permissions on a certain event, do it from userspace with inotify or similar.

Disclaimer aside:

Here is some code I found in another kernel module, which uses the sys_* calls:

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/syscalls.h>
/* Snip */

int openflags = O_WRONLY|O_CREAT;
if (ml != 1)
        openflags |= O_TRUNC;
wfd = sys_open(collected, openflags, mode);

if (wfd >= 0) {
    sys_fchown(wfd, uid, gid);
    sys_fchmod(wfd, mode);
    state = CopyFile;
}

Also found:

asmlinkage long sys_rename(const char __user *oldname, const char __user *newname);
asmlinkage long sys_chmod(const char __user *filename, mode_t mode);
asmlinkage long sys_fchmod(unsigned int fd, mode_t mode);

in include/linux/syscalls.h

Mind you, it has been a while since I did any kernel stuff. Check that this is the appropriate interface for chmod stuff and that you arn't shortcutting any other call that might implement security hooks, for example.

Also, This link contains information on syscalls and their symbols. Also Here is a quick-reference of user-space API system calls and where they are implemented in the kernel.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top