Hi folks, here's a patch which allows changing process' privileges via procfs. Suggestions and testing appreciated. cu -- --------------------------------------------------------------------- Enrico Weigelt == metux IT service - http://www.metux.de/ --------------------------------------------------------------------- Please visit the OpenSource QM Taskforce: http://wiki.metux.de/public/OpenSource_QM_Taskforce Patches / Fixes for a lot dozens of packages in dozens of versions: http://patches.metux.de/ ---------------------------------------------------------------------
diff -ruN VM.orig/fs/proc/array.c VM/fs/proc/array.c --- VM.orig/fs/proc/array.c 2008-05-02 02:48:17.000000000 +0200 +++ VM/fs/proc/array.c 2008-05-06 20:54:19.000000000 +0200 @@ -82,6 +82,8 @@ #include <asm/processor.h> #include "internal.h" +#include <linux/key.h> + /* Gcc optimizes away "strlen(x)" for constant x */ #define ADDBUF(buffer, string) \ do { memcpy(buffer, string, strlen(string)); \ @@ -291,6 +293,39 @@ cap_t(p->cap_effective)); } +#ifdef CONFIG_PROC_PRIVSET + +#define _TASK_LV_HANDLER(FIELD) \ + int proc_pid_##FIELD (struct task_struct *task, long* value, int rw) \ + { \ + switch (rw) \ + { \ + case PROC_TASK_LV_READ: \ + *value = task->FIELD; \ + return 1; \ + \ + case PROC_TASK_LV_WRITE: \ + task->FIELD = *value; \ + key_fsuid_changed(task); \ + return 1; \ + \ + default: \ + return -EINVAL; \ + } \ + } + +_TASK_LV_HANDLER(uid) +_TASK_LV_HANDLER(euid) +_TASK_LV_HANDLER(suid) +_TASK_LV_HANDLER(fsuid) + +_TASK_LV_HANDLER(gid) +_TASK_LV_HANDLER(egid) +_TASK_LV_HANDLER(sgid) +_TASK_LV_HANDLER(fsgid) + +#endif + int proc_pid_status(struct task_struct *task, char * buffer) { char * orig = buffer; diff -ruN VM.orig/fs/proc/base.c VM/fs/proc/base.c --- VM.orig/fs/proc/base.c 2008-05-02 02:48:17.000000000 +0200 +++ VM/fs/proc/base.c 2008-05-06 20:55:31.000000000 +0200 @@ -123,6 +123,13 @@ NULL, &proc_info_file_operations, \ { .proc_read = &proc_##OTYPE } ) +// This is for long values of an task attribute +// These may be r/w and are represented as decimal printout +#define TASK_LV(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + NULL, &proc_tasklv_file_operations, \ + { .proc_task_longval = &proc_##OTYPE } ) + static struct fs_struct *get_fs_struct(struct task_struct *task) { struct fs_struct *fs; @@ -501,10 +508,88 @@ return length; } +static ssize_t proc_tasklv_read(struct file * file, char __user * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode = file->f_path.dentry->d_inode; + unsigned long page; + ssize_t length; + struct task_struct *task = get_proc_task(inode); + long value = 0; + + length = -ESRCH; + if (!task) + goto out_no_task; + + if (count > PROC_BLOCK_SIZE) + count = PROC_BLOCK_SIZE; + + length = -ENOMEM; + if (!(page = __get_free_page(GFP_KERNEL))) + goto out; + + length = PROC_I(inode)->op.proc_task_longval(task, &value, PROC_TASK_LV_READ); + + if (length >= 0) + { + // hmm. is the extra page really required or might an stack buffer be enough ? + sprintf((char*)page,"%ld", value); // should always be short enough to fit in + length = simple_read_from_buffer(buf, count, ppos, (char *)page, strlen((char*)page)); + } + free_page(page); +out: + put_task_struct(task); +out_no_task: + return length; +} + +static ssize_t proc_tasklv_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + // catch the task struct + struct inode * inode = file->f_path.dentry->d_inode; + struct task_struct *task = get_proc_task(inode); + char buffer[PROC_NUMBUF], *end; + long value; + int ret; + + // jump away if task doesnt exist + if (!task) + return -EINVAL; + + // copy from userland to local buffer + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) + return -EFAULT; + + // parse the longint value + value = simple_strtol(buffer, &end, 0); + + // now call the write handler + ret = PROC_I(inode)->op.proc_task_longval(task, &value, PROC_TASK_LV_WRITE); + + // release the task struct + put_task_struct(task); + + // ret > 0 = write succeed + // ret < 0 = the (negative) error code + if (ret>0) + return count; + else + return ret; +} + static const struct file_operations proc_info_file_operations = { .read = proc_info_read, }; +static const struct file_operations proc_tasklv_file_operations = { + .read = proc_tasklv_read, + .write = proc_tasklv_write +}; + static int mem_open(struct inode* inode, struct file* file) { file->private_data = (void*)((long)current->self_exec_id); @@ -1837,6 +1922,13 @@ INF("cmdline", S_IRUGO, pid_cmdline), INF("stat", S_IRUGO, tgid_stat), INF("statm", S_IRUGO, pid_statm), +#ifdef CONFIG_PROC_PRIVSET + TASK_LV("uid", S_IRUGO, pid_uid), + TASK_LV("euid", S_IRUGO, pid_euid), + TASK_LV("suid", S_IRUGO, pid_suid), + TASK_LV("fsuid", S_IRUGO, pid_fsuid), + TASK_LV("gid", S_IRUGO, pid_gid), +#endif REG("maps", S_IRUGO, maps), #ifdef CONFIG_NUMA REG("numa_maps", S_IRUGO, numa_maps), diff -ruN VM.orig/fs/proc/internal.h VM/fs/proc/internal.h --- VM.orig/fs/proc/internal.h 2008-05-02 02:48:16.000000000 +0200 +++ VM/fs/proc/internal.h 2008-05-06 20:56:34.000000000 +0200 @@ -44,6 +44,19 @@ extern int proc_pid_status(struct task_struct *, char *); extern int proc_pid_statm(struct task_struct *, char *); +#define PROC_TASK_LV_READ 0 +#define PROC_TASK_LV_WRITE 1 + +#ifdef CONFIG_PROC_PRIVSET + +extern int proc_pid_uid (struct task_struct*, long* value, int rw); +extern int proc_pid_euid (struct task_struct*, long* value, int rw); +extern int proc_pid_suid (struct task_struct*, long* value, int rw); +extern int proc_pid_fsuid (struct task_struct*, long* value, int rw); +extern int proc_pid_gid (struct task_struct*, long* value, int rw); + +#endif // CONFIG_PROC_PRIVSET + extern const struct file_operations proc_maps_operations; extern const struct file_operations proc_numa_maps_operations; extern const struct file_operations proc_smaps_operations; diff -ruN VM.orig/fs/Kconfig VM/fs/Kconfig --- VM.orig/fs/Kconfig 2008-05-02 02:48:08.000000000 +0200 +++ VM/fs/Kconfig 2008-05-06 20:51:50.000000000 +0200 @@ -944,6 +944,19 @@ building a kernel for install/rescue disks or your system is very limited in memory. +config PROC_PRIVSET + bool "Process privilege setting via /proc" + depends on PROC_FS + default n + ---help--- + Adds a new interface for changing another process' privileges + (uid, gid, etc) via procfs. This allows authentication agents + similar to Plan9's factotum. + + You'll find some new entries in /proc/<pid>/ for uid, euid, etc, + Reading from these files gives the current value (decimal), + writing to them sets a new one. + config SYSFS bool "sysfs file system support" if EMBEDDED default y