Permalink
Browse files

Implement "lock" for tpe sysctl entries

When the lock flag is enabled, you won't be able to change any of the sysctl
entries for this module. Note that the module can just be unloaded/loaded. If
you enable the lock, you will also want to enable this:

/proc/sys/kernel/modules_disabled

If your kernel doesn't have modules_disable (such as the el5 guys), sucks to be
you. Maybe in the future I'll implement one with this module.
  • Loading branch information...
cormander committed Nov 30, 2011
1 parent d9a9b13 commit 12354e74315749dca92fe16f1edf33dd876b8e12
Showing with 70 additions and 9 deletions.
  1. +1 −0 README
  2. +0 −8 core.c
  3. +9 −0 module.h
  4. +51 −1 security.c
  5. +9 −0 sysctl.c
View
1 README
@@ -60,6 +60,7 @@ trusted_gid - gid of "trusted users" who TPE is not enforced. default 1337
admin_gid - files belonging to this group are treated as if they're owned
by root; TPE is not enforced on them. default 0 (off)
dmz_gid - users in this gid can't exec anything at all. default 0 (off)
+lock - when enabled, these sysctl entries can no longer be changed.
extras/ - directory for additional protections that aren't TPE related.
These protections are all off by default, and are as follows:
View
8 core.c
@@ -7,14 +7,6 @@
unsigned long tpe_alert_wtime = 0;
unsigned long tpe_alert_fyet = 0;
-// d_path changed argument types. lame
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-#define tpe_d_path(file, buf, len) d_path(file->f_dentry, file->f_vfsmnt, buf, len);
-#else
-#define tpe_d_path(file, buf, len) d_path(&file->f_path, buf, len);
-#endif
-
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
#define get_inode(file) file->f_dentry->d_inode;
#define get_parent_inode(file) file->f_dentry->d_parent->d_inode;
View
@@ -39,6 +39,14 @@
#define get_task_parent(task) task->real_parent
#endif
+// d_path changed argument types. lame
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+#define tpe_d_path(file, buf, len) d_path(file->f_dentry, file->f_vfsmnt, buf, len);
+#else
+#define tpe_d_path(file, buf, len) d_path(&file->f_path, buf, len);
+#endif
+
struct kernsym {
void *addr; // orig addr
void *end_addr;
@@ -87,6 +95,7 @@ extern int tpe_log;
extern int tpe_log_max;
extern int tpe_log_floodtime;
extern int tpe_log_floodburst;
+extern int tpe_lock;
extern int tpe_dmesg;
extern int tpe_lsmod;
extern int tpe_proc_kallsyms;
View
@@ -15,6 +15,8 @@ struct kernsym sym_m_show;
struct kernsym sym_kallsyms_open;
struct kernsym sym_sys_kill;
struct kernsym sym_pid_getattr;
+struct kernsym sym_security_sysctl;
+struct kernsym sym_do_rw_proc;
// it's possible to mimic execve by loading a binary into memory, mapping pages
// as executable via mmap, thus bypassing TPE protections. This prevents that.
@@ -196,7 +198,7 @@ int tpe_pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *s
int (*run)(struct vfsmount *, struct dentry *, struct kstat *) = sym_pid_getattr.run;
int ret = 0;
- if (tpe_ps && !capable(CAP_SYS_ADMIN) && dentry->d_inode && dentry->d_inode->i_uid != get_task_uid(current) &&
+ if (tpe_ps && !capable(CAP_SYS_ADMIN) && dentry->d_inode && dentry->d_inode->i_uid != get_task_uid(current) &&
(!tpe_ps_gid || (tpe_ps_gid && !in_group_p(tpe_ps_gid))))
return -EPERM;
@@ -205,6 +207,42 @@ int tpe_pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *s
return ret;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+int tpe_security_sysctl(struct ctl_table *table, int op) {
+
+ int (*run)(struct ctl_table *, int) = sym_security_sysctl.run;
+ int ret;
+
+ // every time I have to look that this, I go: o.O
+ // if the tpe_lock is on, and the parent or grandparent ctl_table is "tpe", and they're requesting a write, deny it
+ if (tpe_lock && ((table->parent && table->parent->procname && !strncmp("tpe", table->parent->procname, 3)) ||
+ (table->parent && table->parent->parent && table->parent->parent->procname && !strncmp("tpe", table->parent->parent->procname, 3))) &&
+ (op & MAY_WRITE))
+ return -EPERM;
+
+ ret = run(table, op);
+
+ return ret;
+}
+#else
+static ssize_t tpe_do_rw_proc(int write, struct file * file, char __user * buf,
+ size_t count, loff_t *ppos) {
+
+ char filename[MAX_FILE_LEN], *f;
+ ssize_t (*run)(int, struct file *, char __user *, size_t, loff_t *) = sym_do_rw_proc.run;
+ ssize_t ret;
+
+ f = tpe_d_path(file, filename, MAX_FILE_LEN);
+
+ if (tpe_lock && write && !strncmp("/proc/sys/tpe", f, 13))
+ return -EPERM;
+
+ ret = run(write, file, buf, count, ppos);
+
+ return ret;
+}
+#endif
+
// hijack the needed functions. whenever possible, hijack just the LSM function
void hijack_syscalls(void) {
@@ -287,6 +325,16 @@ void hijack_syscalls(void) {
if (IN_ERR(ret))
printfail("/proc/kallsyms");
+ // sysctl lock
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+ ret = symbol_hijack(&sym_security_sysctl, "security_sysctl", (unsigned long *)tpe_security_sysctl);
+#else
+ ret = symbol_hijack(&sym_do_rw_proc, "do_rw_proc", (unsigned long *)tpe_do_rw_proc);
+#endif
+
+ if (IN_ERR(ret))
+ printfail(MODULE_NAME " sysctl lock");
+
// fetch the kill syscall. don't worry about an error, nothing we can do about it
find_symbol_address(&sym_sys_kill, "sys_kill");
@@ -306,5 +354,7 @@ void undo_hijack_syscalls(void) {
symbol_restore(&sym_m_show);
symbol_restore(&sym_kallsyms_open);
symbol_restore(&sym_pid_getattr);
+ symbol_restore(&sym_security_sysctl);
+ symbol_restore(&sym_do_rw_proc);
}
View
@@ -13,6 +13,7 @@ int tpe_log = 1;
int tpe_log_max = 50;
int tpe_log_floodtime = LOG_FLOODTIME;
int tpe_log_floodburst = LOG_FLOODBURST;
+int tpe_lock = 0;
int tpe_dmesg = 0;
int tpe_lsmod = 0;
int tpe_proc_kallsyms = 0;
@@ -160,6 +161,14 @@ static ctl_table tpe_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "lock",
+ .data = &tpe_lock,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{
.ctl_name = CTL_UNNUMBERED,
.procname = "extras",

0 comments on commit 12354e7

Please sign in to comment.