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

Reply via email to