diff -urNp -U4 linux-2.6.24.7/arch/alpha/kernel/ptrace.c linux-2.6.24.7-new/arch/alpha/kernel/ptrace.c --- linux-2.6.24.7/arch/alpha/kernel/ptrace.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/alpha/kernel/ptrace.c 2008-05-06 17:49:03.000000000 -0400 @@ -14,8 +14,9 @@ #include #include #include #include +#include #include #include #include @@ -265,8 +266,11 @@ long arch_ptrace(struct task_struct *chi unsigned long tmp; size_t copied; long ret; + if (gr_handle_ptrace(child, request)) + return -EPERM; + switch (request) { /* When I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: diff -urNp -U4 linux-2.6.24.7/arch/ia64/kernel/ptrace.c linux-2.6.24.7-new/arch/ia64/kernel/ptrace.c --- linux-2.6.24.7/arch/ia64/kernel/ptrace.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/ia64/kernel/ptrace.c 2008-05-06 17:49:03.000000000 -0400 @@ -16,8 +16,9 @@ #include #include #include #include +#include #include #include #include @@ -1450,8 +1451,11 @@ sys_ptrace (long request, pid_t pid, uns ret = -EPERM; if (pid == 1) /* no messing around with init! */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } diff -urNp -U4 linux-2.6.24.7/arch/sparc/kernel/ptrace.c linux-2.6.24.7-new/arch/sparc/kernel/ptrace.c --- linux-2.6.24.7/arch/sparc/kernel/ptrace.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/sparc/kernel/ptrace.c 2008-05-06 17:49:04.000000000 -0400 @@ -18,8 +18,9 @@ #include #include #include #include +#include #include #include #include @@ -302,8 +303,13 @@ asmlinkage void do_ptrace(struct pt_regs pt_error_return(regs, -ret); goto out; } + if (gr_handle_ptrace(child, request)) { + pt_error_return(regs, EPERM); + goto out_tsk; + } + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { if (ptrace_attach(child)) { pt_error_return(regs, EPERM); diff -urNp -U4 linux-2.6.24.7/arch/sparc/Makefile linux-2.6.24.7-new/arch/sparc/Makefile --- linux-2.6.24.7/arch/sparc/Makefile 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/sparc/Makefile 2008-05-06 17:49:04.000000000 -0400 @@ -35,9 +35,9 @@ drivers-$(CONFIG_OPROFILE) += arch/sparc # Export what is needed by arch/sparc/boot/Makefile # Renaming is done to avoid confusing pattern matching rules in 2.5.45 (multy-) INIT_Y := $(patsubst %/, %/built-in.o, $(init-y)) CORE_Y := $(core-y) -CORE_Y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ +CORE_Y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ grsecurity/ CORE_Y := $(patsubst %/, %/built-in.o, $(CORE_Y)) DRIVERS_Y := $(patsubst %/, %/built-in.o, $(drivers-y)) NET_Y := $(patsubst %/, %/built-in.o, $(net-y)) LIBS_Y1 := $(patsubst %/, %/lib.a, $(libs-y)) diff -urNp -U4 linux-2.6.24.7/arch/sparc64/kernel/ptrace.c linux-2.6.24.7-new/arch/sparc64/kernel/ptrace.c --- linux-2.6.24.7/arch/sparc64/kernel/ptrace.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/sparc64/kernel/ptrace.c 2008-05-06 17:49:04.000000000 -0400 @@ -21,8 +21,9 @@ #include #include #include #include +#include #include #include #include @@ -219,8 +220,13 @@ asmlinkage void do_ptrace(struct pt_regs pt_error_return(regs, -ret); goto out; } + if (gr_handle_ptrace(child, (long)request)) { + pt_error_return(regs, EPERM); + goto out_tsk; + } + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { if (ptrace_attach(child)) { pt_error_return(regs, EPERM); diff -urNp -U4 linux-2.6.24.7/arch/x86/Kconfig linux-2.6.24.7-new/arch/x86/Kconfig --- linux-2.6.24.7/arch/x86/Kconfig 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/x86/Kconfig 2008-05-10 09:28:15.000000000 -0400 @@ -1189,10 +1189,10 @@ config HOTPLUG_CPU suspend. config COMPAT_VDSO bool "Compat VDSO support" - default y - depends on X86_32 + default n + depends on X86_32 && !PAX_NOEXEC help Map the VDSO to the predictable old-style address too. ---help--- Say N here if you are running a sufficiently recent glibc diff -urNp -U4 linux-2.6.24.7/arch/x86/kernel/ioport_32.c linux-2.6.24.7-new/arch/x86/kernel/ioport_32.c --- linux-2.6.24.7/arch/x86/kernel/ioport_32.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/x86/kernel/ioport_32.c 2008-05-10 09:28:15.000000000 -0400 @@ -13,8 +13,9 @@ #include #include #include #include +#include /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int extent, int new_value) { @@ -61,11 +62,18 @@ asmlinkage long sys_ioperm(unsigned long unsigned long *bitmap; if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) return -EINVAL; +#ifdef CONFIG_GRKERNSEC_IO + if (turn_on) { + gr_handle_ioperm(); +#else if (turn_on && !capable(CAP_SYS_RAWIO)) +#endif return -EPERM; - +#ifdef CONFIG_GRKERNSEC_IO + } +#endif /* * If it's the first ioperm() call in this thread's lifetime, set the * IO bitmap up. ioperm() is much less timing critical than clone(), * this is why we delay this operation until now: @@ -140,10 +148,15 @@ asmlinkage long sys_iopl(unsigned long u if (level > 3) return -EINVAL; /* Trying to gain more privileges? */ if (level > old) { +#ifdef CONFIG_GRKERNSEC_IO + gr_handle_iopl(); + return -EPERM; +#else if (!capable(CAP_SYS_RAWIO)) return -EPERM; +#endif } t->iopl = level << 12; regs->eflags = (regs->eflags & ~X86_EFLAGS_IOPL) | t->iopl; set_iopl_mask(t->iopl); diff -urNp -U4 linux-2.6.24.7/arch/x86/kernel/ioport_64.c linux-2.6.24.7-new/arch/x86/kernel/ioport_64.c --- linux-2.6.24.7/arch/x86/kernel/ioport_64.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/x86/kernel/ioport_64.c 2008-05-10 09:28:15.000000000 -0400 @@ -13,8 +13,9 @@ #include #include #include #include +#include /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int extent, int new_value) { @@ -38,10 +39,19 @@ asmlinkage long sys_ioperm(unsigned long unsigned long *bitmap; if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) return -EINVAL; + +#ifdef CONFIG_GRKERNSEC_IO + if (turn_on) { + gr_handle_ioperm(); +#else if (turn_on && !capable(CAP_SYS_RAWIO)) +#endif return -EPERM; +#ifdef CONFIG_GRKERNSEC_IO + } +#endif /* * If it's the first ioperm() call in this thread's lifetime, set the * IO bitmap up. ioperm() is much less timing critical than clone(), @@ -108,10 +118,15 @@ asmlinkage long sys_iopl(unsigned int le if (level > 3) return -EINVAL; /* Trying to gain more privileges? */ if (level > old) { +#ifdef CONFIG_GRKERNSEC_IO + gr_handle_iopl(); + return -EPERM; +#else if (!capable(CAP_SYS_RAWIO)) return -EPERM; +#endif } regs->eflags = (regs->eflags &~ X86_EFLAGS_IOPL) | (level << 12); return 0; } diff -urNp -U4 linux-2.6.24.7/arch/x86/kernel/ptrace_32.c linux-2.6.24.7-new/arch/x86/kernel/ptrace_32.c --- linux-2.6.24.7/arch/x86/kernel/ptrace_32.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/arch/x86/kernel/ptrace_32.c 2008-05-10 09:28:15.000000000 -0400 @@ -418,9 +418,19 @@ long arch_ptrace(struct task_struct *chi if(addr == (long) &dummy->u_debugreg[4]) break; if(addr == (long) &dummy->u_debugreg[5]) break; if(addr < (long) &dummy->u_debugreg[4] && ((unsigned long) data) >= TASK_SIZE-3) break; - + +#ifdef CONFIG_GRKERNSEC + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[3]) { + long reg = (addr - (long) &dummy->u_debugreg[0]) >> 2; + long type = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 4*reg)) & 3; + long align = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 2 + 4*reg)) & 3; + if ((type & 1) && (data & align)) + break; + } +#endif /* Sanity-check data. Take one half-byte at once with * check = (val >> (16 + 4*i)) & 0xf. It contains the * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits * 2 and 3 are LENi. Given a list of invalid values, diff -urNp -U4 linux-2.6.24.7/drivers/char/keyboard.c linux-2.6.24.7-new/drivers/char/keyboard.c --- linux-2.6.24.7/drivers/char/keyboard.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/char/keyboard.c 2008-05-10 09:28:16.000000000 -0400 @@ -630,8 +630,18 @@ static void k_spec(struct vc_data *vc, u if ((kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) && value != KVAL(K_SAK)) return; /* SAK is allowed even in raw mode */ + +#if defined(CONFIG_GRKERNSEC_PROC) || defined(CONFIG_GRKERNSEC_PROC_MEMMAP) + { + void *func = fn_handler[value]; + if (func == fn_show_state || func == fn_show_ptregs || + func == fn_show_mem) + return; + } +#endif + fn_handler[value](vc); } static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag) diff -urNp -U4 linux-2.6.24.7/drivers/char/mem.c linux-2.6.24.7-new/drivers/char/mem.c --- linux-2.6.24.7/drivers/char/mem.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/char/mem.c 2008-05-06 17:49:06.000000000 -0400 @@ -25,16 +25,21 @@ #include #include #include #include +#include #include #include #ifdef CONFIG_IA64 # include #endif +#ifdef CONFIG_GRKERNSEC +extern struct file_operations grsec_fops; +#endif + /* * Architectures vary in how they handle caching for addresses * outside of main memory. * @@ -179,8 +184,13 @@ static ssize_t write_mem(struct file * f if (!valid_phys_addr_range(p, count)) return -EFAULT; +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_mem_write(); + return -EPERM; +#endif + written = 0; #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED /* we don't have page 0 mapped on sparc and m68k.. */ @@ -280,8 +290,13 @@ static int mmap_mem(struct file * file, if (!private_mapping_ok(vma)) return -ENOSYS; +#ifdef CONFIG_GRKERNSEC_KMEM + if (gr_handle_mem_mmap(vma->vm_pgoff << PAGE_SHIFT, vma)) + return -EPERM; +#endif + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, size, vma->vm_page_prot); @@ -511,8 +526,13 @@ static ssize_t write_kmem(struct file * ssize_t virtr = 0; ssize_t written; char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_kmem_write(); + return -EPERM; +#endif + if (p < (unsigned long) high_memory) { wrote = count; if (count > (unsigned long) high_memory - p) @@ -713,16 +733,25 @@ static loff_t memory_lseek(struct file * } static int open_port(struct inode * inode, struct file * filp) { +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_open_port(); + return -EPERM; +#endif + + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static int open_mem(struct inode * inode, struct file * filp) +{ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } #define zero_lseek null_lseek #define full_lseek null_lseek #define write_zero write_null #define read_full read_zero -#define open_mem open_port #define open_kmem open_mem #define open_oldmem open_mem static const struct file_operations mem_fops = { @@ -853,8 +882,13 @@ static int memory_open(struct inode * in case 12: filp->f_op = &oldmem_fops; break; #endif +#ifdef CONFIG_GRKERNSEC + case 13: + filp->f_op = &grsec_fops; + break; +#endif default: return -ENXIO; } if (filp->f_op && filp->f_op->open) @@ -885,8 +919,11 @@ static const struct { {11,"kmsg", S_IRUGO | S_IWUSR, &kmsg_fops}, #ifdef CONFIG_CRASH_DUMP {12,"oldmem", S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops}, #endif +#ifdef CONFIG_GRKERNSEC + {13,"grsec", S_IRUSR | S_IWUGO, &grsec_fops}, +#endif }; static struct class *mem_class; diff -urNp -U4 linux-2.6.24.7/drivers/char/random.c linux-2.6.24.7-new/drivers/char/random.c --- linux-2.6.24.7/drivers/char/random.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/char/random.c 2008-05-10 09:28:16.000000000 -0400 @@ -247,10 +247,15 @@ /* * Configuration information */ +#ifdef CONFIG_GRKERNSEC_RANDNET +#define INPUT_POOL_WORDS 512 +#define OUTPUT_POOL_WORDS 128 +#else #define INPUT_POOL_WORDS 128 #define OUTPUT_POOL_WORDS 32 +#endif #define SEC_XFER_SIZE 512 /* * The minimum number of bits of entropy before we wake up a read on @@ -285,12 +290,19 @@ static DEFINE_PER_CPU(int, trickle_count static struct poolinfo { int poolwords; int tap1, tap2, tap3, tap4, tap5; } poolinfo_table[] = { +#ifdef CONFIG_GRKERNSEC_RANDNET + /* x^512 + x^411 + x^308 + x^208 +x^104 + x + 1 -- 225 */ + { 512, 411, 308, 208, 104, 1 }, + /* x^128 + x^103 + x^76 + x^51 + x^25 + x + 1 -- 105 */ + { 128, 103, 76, 51, 25, 1 }, +#else /* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */ { 128, 103, 76, 51, 25, 1 }, /* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */ { 32, 26, 20, 14, 7, 1 }, +#endif #if 0 /* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1 -- 115 */ { 2048, 1638, 1231, 819, 411, 1 }, diff -urNp -U4 linux-2.6.24.7/drivers/char/vt_ioctl.c linux-2.6.24.7-new/drivers/char/vt_ioctl.c --- linux-2.6.24.7/drivers/char/vt_ioctl.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/char/vt_ioctl.c 2008-05-06 17:49:06.000000000 -0400 @@ -95,8 +95,14 @@ do_kdsk_ioctl(int cmd, struct kbentry __ return put_user(val, &user_kbe->kb_value); case KDSKBENT: if (!perm) return -EPERM; + +#ifdef CONFIG_GRKERNSEC + if (!capable(CAP_SYS_TTY_CONFIG)) + return -EPERM; +#endif + if (!i && v == K_NOSUCHMAP) { /* deallocate map */ key_map = key_maps[s]; if (s && key_map) { @@ -235,8 +241,15 @@ do_kdgkb_ioctl(int cmd, struct kbsentry ret = -EPERM; goto reterr; } +#ifdef CONFIG_GRKERNSEC + if (!capable(CAP_SYS_TTY_CONFIG)) { + ret = -EPERM; + goto reterr; + } +#endif + q = func_table[i]; first_free = funcbufptr + (funcbufsize - funcbufleft); for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ; diff -urNp -U4 linux-2.6.24.7/drivers/mtd/devices/doc2001.c linux-2.6.24.7-new/drivers/mtd/devices/doc2001.c --- linux-2.6.24.7/drivers/mtd/devices/doc2001.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/mtd/devices/doc2001.c 2008-05-06 17:49:06.000000000 -0400 @@ -397,8 +397,10 @@ static int doc_read (struct mtd_info *mt /* Don't allow read past end of device */ if (from >= this->totlen) return -EINVAL; + if (!len) + return -EINVAL; /* Don't allow a single read to cross a 512-byte block boundary */ if (from + len > ((from | 0x1ff) + 1)) len = ((from | 0x1ff) + 1) - from; diff -urNp -U4 linux-2.6.24.7/drivers/pci/proc.c linux-2.6.24.7-new/drivers/pci/proc.c --- linux-2.6.24.7/drivers/pci/proc.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/pci/proc.c 2008-05-06 17:49:07.000000000 -0400 @@ -466,9 +466,17 @@ static const struct file_operations proc static int __init pci_proc_init(void) { struct proc_dir_entry *entry; struct pci_dev *dev = NULL; +#ifdef CONFIG_GRKERNSEC_PROC_ADD +#ifdef CONFIG_GRKERNSEC_PROC_USER + proc_bus_pci_dir = proc_mkdir_mode("pci", S_IRUSR | S_IXUSR, proc_bus); +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + proc_bus_pci_dir = proc_mkdir_mode("pci", S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP, proc_bus); +#endif +#else proc_bus_pci_dir = proc_mkdir("pci", proc_bus); +#endif entry = create_proc_entry("devices", 0, proc_bus_pci_dir); if (entry) entry->proc_fops = &proc_bus_pci_dev_operations; proc_initialized = 1; diff -urNp -U4 linux-2.6.24.7/drivers/video/i810/i810_main.c linux-2.6.24.7-new/drivers/video/i810/i810_main.c --- linux-2.6.24.7/drivers/video/i810/i810_main.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/drivers/video/i810/i810_main.c 2008-05-10 09:28:16.000000000 -0400 @@ -1508,9 +1508,9 @@ static int i810fb_cursor(struct fb_info if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { int size = ((cursor->image.width + 7) >> 3) * cursor->image.height; int i; - u8 *data = kmalloc(64 * 8, GFP_ATOMIC); + u8 *data = kmalloc(64 * 8, GFP_KERNEL); if (data == NULL) return -ENOMEM; diff -urNp -U4 linux-2.6.24.7/fs/binfmt_aout.c linux-2.6.24.7-new/fs/binfmt_aout.c --- linux-2.6.24.7/fs/binfmt_aout.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/binfmt_aout.c 2008-05-10 09:28:16.000000000 -0400 @@ -23,8 +23,9 @@ #include #include #include #include +#include #include #include #include @@ -122,20 +123,24 @@ static int aout_core_dump(long signr, st /* If the size of the dump file exceeds the rlimit, then see what would happen if we wrote the stack, but not the data area. */ #ifdef __sparc__ + gr_learn_resource(current, RLIMIT_CORE, dump.u_dsize + dump.u_ssize, 1); if ((dump.u_dsize + dump.u_ssize) > limit) dump.u_dsize = 0; #else + gr_learn_resource(current, RLIMIT_CORE, (dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE, 1); if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit) dump.u_dsize = 0; #endif /* Make sure we have enough room to write the stack and data areas. */ #ifdef __sparc__ + gr_learn_resource(current, RLIMIT_CORE, dump.u_ssize, 1); if (dump.u_ssize > limit) dump.u_ssize = 0; #else + gr_learn_resource(current, RLIMIT_CORE, (dump.u_ssize + 1) * PAGE_SIZE, 1); if ((dump.u_ssize + 1) * PAGE_SIZE > limit) dump.u_ssize = 0; #endif @@ -289,8 +294,10 @@ static int load_aout_binary(struct linux */ rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) rlim = ~0; + + gr_learn_resource(current, RLIMIT_DATA, ex.a_data + ex.a_bss, 1); if (ex.a_data + ex.a_bss > rlim) return -ENOMEM; /* Flush all traces of the currently running executable */ diff -urNp -U4 linux-2.6.24.7/fs/binfmt_elf.c linux-2.6.24.7-new/fs/binfmt_elf.c --- linux-2.6.24.7/fs/binfmt_elf.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/binfmt_elf.c 2008-05-10 09:28:16.000000000 -0400 @@ -38,8 +38,10 @@ #include #include #include #include +#include + #include #include #include @@ -1318,10 +1320,13 @@ static int writenote(struct memelfnote * } #undef DUMP_WRITE #define DUMP_WRITE(addr, nr) \ + do { \ + gr_learn_resource(current, RLIMIT_CORE, size + (nr), 1); \ if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \ - goto end_coredump; + goto end_coredump; \ + } while (0); #define DUMP_SEEK(off) \ if (!dump_seek(file, (off))) \ goto end_coredump; @@ -1772,8 +1777,9 @@ static int elf_core_dump(long signr, str void *kaddr; flush_cache_page(vma, addr, page_to_pfn(page)); kaddr = kmap(page); + gr_learn_resource(current, RLIMIT_CORE, size + PAGE_SIZE, 1); if ((size += PAGE_SIZE) > limit || !dump_write(file, kaddr, PAGE_SIZE)) { kunmap(page); diff -urNp -U4 linux-2.6.24.7/fs/buffer.c linux-2.6.24.7-new/fs/buffer.c --- linux-2.6.24.7/fs/buffer.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/buffer.c 2008-05-06 17:49:07.000000000 -0400 @@ -40,8 +40,9 @@ #include #include #include #include +#include static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); #define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers) @@ -2169,8 +2170,9 @@ int generic_cont_expand_simple(struct in int err; err = -EFBIG; limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; + gr_learn_resource(current, RLIMIT_FSIZE, (unsigned long) size, 1); if (limit != RLIM_INFINITY && size > (loff_t)limit) { send_sig(SIGXFSZ, current, 0); goto out; } diff -urNp -U4 linux-2.6.24.7/fs/compat.c linux-2.6.24.7-new/fs/compat.c --- linux-2.6.24.7/fs/compat.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/compat.c 2008-05-10 09:28:17.000000000 -0400 @@ -49,8 +49,9 @@ #include #include #include #include +#include #include #include #include @@ -1352,8 +1353,13 @@ int compat_do_execve(char * filename, compat_uptr_t __user *argv, compat_uptr_t __user *envp, struct pt_regs * regs) { +#ifdef CONFIG_GRKERNSEC + struct file *old_exec_file; + struct acl_subject_label *old_acl; + struct rlimit old_rlim[RLIM_NLIMITS]; +#endif struct linux_binprm *bprm; struct file *file; int retval; @@ -1372,8 +1378,16 @@ int compat_do_execve(char * filename, bprm->file = file; bprm->filename = filename; bprm->interp = filename; + gr_learn_resource(current, RLIMIT_NPROC, atomic_read(¤t->user->processes), 1); + retval = -EAGAIN; + if (gr_handle_nproc()) + goto out_file; + retval = -EACCES; + if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) + goto out_file; + retval = bprm_mm_init(bprm); if (retval) goto out_file; @@ -1405,17 +1419,52 @@ int compat_do_execve(char * filename, retval = compat_copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out; + if (!gr_tpe_allow(file)) { + retval = -EACCES; + goto out; + } + + if (gr_check_crash_exec(file)) { + retval = -EACCES; + goto out; + } + + gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt); + + gr_handle_exec_args(bprm, (char __user * __user *)argv); + +#ifdef CONFIG_GRKERNSEC + old_acl = current->acl; + memcpy(old_rlim, current->signal->rlim, sizeof(old_rlim)); + old_exec_file = current->exec_file; + get_file(file); + current->exec_file = file; +#endif + + gr_set_proc_label(file->f_dentry, file->f_vfsmnt); + retval = search_binary_handler(bprm, regs); if (retval >= 0) { +#ifdef CONFIG_GRKERNSEC + if (old_exec_file) + fput(old_exec_file); +#endif /* execve success */ security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); return retval; } +#ifdef CONFIG_GRKERNSEC + current->acl = old_acl; + memcpy(current->signal->rlim, old_rlim, sizeof(old_rlim)); + fput(current->exec_file); + current->exec_file = old_exec_file; +#endif + out: if (bprm->security) security_bprm_free(bprm); diff -urNp -U4 linux-2.6.24.7/fs/exec.c linux-2.6.24.7-new/fs/exec.c --- linux-2.6.24.7/fs/exec.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/exec.c 2008-05-10 09:29:27.000000000 -0400 @@ -50,8 +50,10 @@ #include #include #include #include +#include +#include #include #include #include @@ -1292,8 +1294,13 @@ int do_execve(char * filename, char __user *__user *argv, char __user *__user *envp, struct pt_regs * regs) { +#ifdef CONFIG_GRKERNSEC + struct file *old_exec_file; + struct acl_subject_label *old_acl; + struct rlimit old_rlim[RLIM_NLIMITS]; +#endif struct linux_binprm *bprm; struct file *file; unsigned long env_p; int retval; @@ -1307,8 +1314,22 @@ int do_execve(char * filename, retval = PTR_ERR(file); if (IS_ERR(file)) goto out_kfree; + gr_learn_resource(current, RLIMIT_NPROC, atomic_read(¤t->user->processes), 1); + + if (gr_handle_nproc()) { + allow_write_access(file); + fput(file); + return -EAGAIN; + } + + if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) { + allow_write_access(file); + fput(file); + return -EACCES; + } + sched_exec(); bprm->file = file; bprm->filename = filename; @@ -1348,18 +1369,56 @@ int do_execve(char * filename, if (retval < 0) goto out; bprm->argv_len = env_p - bprm->p; + if (!gr_tpe_allow(file)) { + retval = -EACCES; + goto out; + } + + if (gr_check_crash_exec(file)) { + retval = -EACCES; + goto out; + } + + gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt); + + gr_handle_exec_args(bprm, argv); + +#ifdef CONFIG_GRKERNSEC + old_acl = current->acl; + memcpy(old_rlim, current->signal->rlim, sizeof(old_rlim)); + old_exec_file = current->exec_file; + get_file(file); + current->exec_file = file; +#endif + + retval = gr_set_proc_label(file->f_dentry, file->f_vfsmnt); + if (retval < 0) + goto out_fail; + retval = search_binary_handler(bprm,regs); if (retval >= 0) { +#ifdef CONFIG_GRKERNSEC + if (old_exec_file) + fput(old_exec_file); +#endif /* execve success */ free_arg_pages(bprm); security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); return retval; } +out_fail: +#ifdef CONFIG_GRKERNSEC + current->acl = old_acl; + memcpy(current->signal->rlim, old_rlim, sizeof(old_rlim)); + fput(current->exec_file); + current->exec_file = old_exec_file; +#endif + out: free_arg_pages(bprm); if (bprm->security) security_bprm_free(bprm); @@ -1719,8 +1778,12 @@ int do_coredump(long signr, int exit_cod * be seen by the filesystem code called to write the core file. */ clear_thread_flag(TIF_SIGPENDING); + if (signr == SIGKILL || signr == SIGILL) + gr_handle_brute_attach(current); + gr_learn_resource(current, RLIMIT_CORE, binfmt->min_coredump, 1); + /* * lock_kernel() because format_corename() is controlled by sysctl, which * uses lock_kernel() */ diff -urNp -U4 linux-2.6.24.7/fs/ext2/balloc.c linux-2.6.24.7-new/fs/ext2/balloc.c --- linux-2.6.24.7/fs/ext2/balloc.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/ext2/balloc.c 2008-05-06 17:49:08.000000000 -0400 @@ -1126,9 +1126,9 @@ static int ext2_has_free_blocks(struct e ext2_fsblk_t free_blocks, root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); - if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) && + if (free_blocks < root_blocks + 1 && !capable_nolog(CAP_SYS_RESOURCE) && sbi->s_resuid != current->fsuid && (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { return 0; } diff -urNp -U4 linux-2.6.24.7/fs/ext3/balloc.c linux-2.6.24.7-new/fs/ext3/balloc.c --- linux-2.6.24.7/fs/ext3/balloc.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/ext3/balloc.c 2008-05-06 17:49:08.000000000 -0400 @@ -1358,9 +1358,9 @@ static int ext3_has_free_blocks(struct e ext3_fsblk_t free_blocks, root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); - if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) && + if (free_blocks < root_blocks + 1 && !capable_nolog(CAP_SYS_RESOURCE) && sbi->s_resuid != current->fsuid && (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { return 0; } diff -urNp -U4 linux-2.6.24.7/fs/ext4/balloc.c linux-2.6.24.7-new/fs/ext4/balloc.c --- linux-2.6.24.7/fs/ext4/balloc.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/ext4/balloc.c 2008-05-06 17:49:08.000000000 -0400 @@ -1478,9 +1478,9 @@ static int ext4_has_free_blocks(struct e ext4_fsblk_t free_blocks, root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = ext4_r_blocks_count(sbi->s_es); - if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) && + if (free_blocks < root_blocks + 1 && !capable_nolog(CAP_SYS_RESOURCE) && sbi->s_resuid != current->fsuid && (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { return 0; } diff -urNp -U4 linux-2.6.24.7/fs/fcntl.c linux-2.6.24.7-new/fs/fcntl.c --- linux-2.6.24.7/fs/fcntl.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/fcntl.c 2008-05-06 17:49:08.000000000 -0400 @@ -18,8 +18,9 @@ #include #include #include #include +#include #include #include #include @@ -63,8 +64,9 @@ static int locate_fd(struct files_struct int error; struct fdtable *fdt; error = -EINVAL; + gr_learn_resource(current, RLIMIT_NOFILE, orig_start, 0); if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out; repeat: @@ -82,8 +84,9 @@ repeat: newfd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds, start); error = -EMFILE; + gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0); if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out; error = expand_files(files, newfd); @@ -143,8 +146,10 @@ asmlinkage long sys_dup2(unsigned int ol struct file * file, *tofree; struct files_struct * files = current->files; struct fdtable *fdt; + gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0); + spin_lock(&files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; err = newfd; @@ -462,9 +467,10 @@ static inline int sigio_perm(struct task { return (((fown->euid == 0) || (fown->euid == p->suid) || (fown->euid == p->uid) || (fown->uid == p->suid) || (fown->uid == p->uid)) && - !security_file_send_sigiotask(p, fown, sig)); + !security_file_send_sigiotask(p, fown, sig) && + !gr_check_protected_task(p) && !gr_pid_is_chrooted(p)); } static void send_sigio_to_task(struct task_struct *p, struct fown_struct *fown, diff -urNp -U4 linux-2.6.24.7/fs/Kconfig linux-2.6.24.7-new/fs/Kconfig --- linux-2.6.24.7/fs/Kconfig 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/Kconfig 2008-05-06 17:49:08.000000000 -0400 @@ -936,9 +936,9 @@ config PROC_FS programs depend on this, so everyone should say Y here. config PROC_KCORE bool "/proc/kcore support" if !ARM - depends on PROC_FS && MMU + depends on PROC_FS && MMU && !GRKERNSEC_PROC_ADD config PROC_VMCORE bool "/proc/vmcore support (EXPERIMENTAL)" depends on PROC_FS && EXPERIMENTAL && CRASH_DUMP diff -urNp -U4 linux-2.6.24.7/fs/namei.c linux-2.6.24.7-new/fs/namei.c --- linux-2.6.24.7/fs/namei.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/namei.c 2008-05-10 09:28:17.000000000 -0400 @@ -29,8 +29,9 @@ #include #include #include #include +#include #include #include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -652,8 +653,15 @@ static inline int do_follow_link(struct cond_resched(); err = security_inode_follow_link(path->dentry, nd); if (err) goto loop; + + if (gr_handle_follow_link(path->dentry->d_parent->d_inode, + path->dentry->d_inode, path->dentry, nd->mnt)) { + err = -EACCES; + goto loop; + } + current->link_count++; current->total_link_count++; nd->depth++; err = __do_follow_link(path, nd); @@ -997,13 +1005,20 @@ return_reval: if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd)) break; } return_base: + if (!gr_acl_handle_hidden_file(nd->dentry, nd->mnt)) { + path_release(nd); + return -ENOENT; + } return 0; out_dput: dput_path(&next, nd); break; } + if (!gr_acl_handle_hidden_file(nd->dentry, nd->mnt)) + err = -ENOENT; + path_release(nd); return_err: return err; } @@ -1679,11 +1694,19 @@ static int open_namei_create(struct name { int error; struct dentry *dir = nd->dentry; + if (!gr_acl_handle_creat(path->dentry, nd->dentry, nd->mnt, flag, mode)) { + error = -EACCES; + goto out_unlock_dput; + } + if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; error = vfs_create(dir->d_inode, path->dentry, mode, nd); + if (!error) + gr_handle_create(path->dentry, nd->mnt); +out_unlock_dput: mutex_unlock(&dir->d_inode->i_mutex); dput(nd->dentry); nd->dentry = path->dentry; if (error) @@ -1732,8 +1755,19 @@ int open_namei(int dfd, const char *path error = path_lookup_open(dfd, pathname, lookup_flags(flag), nd, flag); if (error) return error; + + if (gr_handle_rawio(nd->dentry->d_inode)) { + error = -EPERM; + goto exit; + } + + if (!gr_acl_handle_open(nd->dentry, nd->mnt, flag)) { + error = -EACCES; + goto exit; + } + goto ok; } /* @@ -1781,8 +1815,25 @@ do_last: /* * It already exists. */ + + if (gr_handle_rawio(path.dentry->d_inode)) { + mutex_unlock(&dir->d_inode->i_mutex); + error = -EPERM; + goto exit_dput; + } + if (!gr_acl_handle_open(path.dentry, nd->mnt, flag)) { + mutex_unlock(&dir->d_inode->i_mutex); + error = -EACCES; + goto exit_dput; + } + if (gr_handle_fifo(path.dentry, nd->mnt, dir, flag, acc_mode)) { + mutex_unlock(&dir->d_inode->i_mutex); + error = -EACCES; + goto exit_dput; + } + mutex_unlock(&dir->d_inode->i_mutex); audit_inode(pathname, path.dentry); error = -EEXIST; @@ -1836,8 +1887,15 @@ do_link: nd->flags |= LOOKUP_PARENT; error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; + + if (gr_handle_follow_link(path.dentry->d_parent->d_inode, path.dentry->d_inode, + path.dentry, nd->mnt)) { + error = -EACCES; + goto exit_dput; + } + error = __do_follow_link(&path, nd); if (error) { /* Does someone understand code flow here? Or it is only * me so stupid? Anathema to whoever designed this non-sense @@ -1964,8 +2022,24 @@ asmlinkage long sys_mknodat(int dfd, con if (!IS_POSIXACL(nd.dentry->d_inode)) mode &= ~current->fs->umask; if (!IS_ERR(dentry)) { + if (gr_handle_chroot_mknod(dentry, nd.mnt, mode)) { + error = -EPERM; + dput(dentry); + mutex_unlock(&nd.dentry->d_inode->i_mutex); + path_release(&nd); + goto out; + } + + if (!gr_acl_handle_mknod(dentry, nd.dentry, nd.mnt, mode)) { + error = -EACCES; + dput(dentry); + mutex_unlock(&nd.dentry->d_inode->i_mutex); + path_release(&nd); + goto out; + } + switch (mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd); break; @@ -1981,8 +2055,12 @@ asmlinkage long sys_mknodat(int dfd, con break; default: error = -EINVAL; } + + if (!error) + gr_handle_create(dentry, nd.mnt); + dput(dentry); } mutex_unlock(&nd.dentry->d_inode->i_mutex); path_release(&nd); @@ -2038,11 +2116,20 @@ asmlinkage long sys_mkdirat(int dfd, con error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; + if (!gr_acl_handle_mkdir(dentry, nd.dentry, nd.mnt)) { + error = -EACCES; + goto out_unlock_dput; + } + if (!IS_POSIXACL(nd.dentry->d_inode)) mode &= ~current->fs->umask; error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); + + if (!error) + gr_handle_create(dentry, nd.mnt); +out_unlock_dput: dput(dentry); out_unlock: mutex_unlock(&nd.dentry->d_inode->i_mutex); path_release(&nd); @@ -2122,8 +2209,10 @@ static long do_rmdir(int dfd, const char int error = 0; char * name; struct dentry *dentry; struct nameidata nd; + ino_t saved_ino = 0; + dev_t saved_dev = 0; name = getname(pathname); if(IS_ERR(name)) return PTR_ERR(name); @@ -2147,9 +2236,24 @@ static long do_rmdir(int dfd, const char dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit2; + + if (dentry->d_inode != NULL) { + if (dentry->d_inode->i_nlink <= 1) { + saved_ino = dentry->d_inode->i_ino; + saved_dev = dentry->d_inode->i_sb->s_dev; + } + + if (!gr_acl_handle_rmdir(dentry, nd.mnt)) { + error = -EACCES; + goto dput_exit2; + } + } error = vfs_rmdir(nd.dentry->d_inode, dentry); + if (!error && (saved_dev || saved_ino)) + gr_handle_delete(saved_ino, saved_dev); +dput_exit2: dput(dentry); exit2: mutex_unlock(&nd.dentry->d_inode->i_mutex); exit1: @@ -2206,8 +2310,10 @@ static long do_unlinkat(int dfd, const c char * name; struct dentry *dentry; struct nameidata nd; struct inode *inode = NULL; + ino_t saved_ino = 0; + dev_t saved_dev = 0; name = getname(pathname); if(IS_ERR(name)) return PTR_ERR(name); @@ -2221,15 +2327,28 @@ static long do_unlinkat(int dfd, const c mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { + error = 0; /* Why not before? Because we want correct error value */ if (nd.last.name[nd.last.len]) goto slashes; inode = dentry->d_inode; - if (inode) + if (inode) { + if (inode->i_nlink <= 1) { + saved_ino = inode->i_ino; + saved_dev = inode->i_sb->s_dev; + } + + if (!gr_acl_handle_unlink(dentry, nd.mnt)) + error = -EACCES; + atomic_inc(&inode->i_count); - error = vfs_unlink(nd.dentry->d_inode, dentry); + } + if (!error) + error = vfs_unlink(nd.dentry->d_inode, dentry); + if (!error && (saved_ino || saved_dev)) + gr_handle_delete(saved_ino, saved_dev); exit2: dput(dentry); } mutex_unlock(&nd.dentry->d_inode->i_mutex); @@ -2308,9 +2427,18 @@ asmlinkage long sys_symlinkat(const char error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; + if (!gr_acl_handle_symlink(dentry, nd.dentry, nd.mnt, from)) { + error = -EACCES; + goto out_dput_unlock; + } + error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO); + + if (!error) + gr_handle_create(dentry, nd.mnt); +out_dput_unlock: dput(dentry); out_unlock: mutex_unlock(&nd.dentry->d_inode->i_mutex); path_release(&nd); @@ -2403,9 +2531,27 @@ asmlinkage long sys_linkat(int olddfd, c new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto out_unlock; + + if (gr_handle_hardlink(old_nd.dentry, old_nd.mnt, + old_nd.dentry->d_inode, + old_nd.dentry->d_inode->i_mode, to)) { + error = -EACCES; + goto out_unlock_dput; + } + + if (!gr_acl_handle_link(new_dentry, nd.dentry, nd.mnt, + old_nd.dentry, old_nd.mnt, to)) { + error = -EACCES; + goto out_unlock_dput; + } + error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); + + if (!error) + gr_handle_create(new_dentry, nd.mnt); +out_unlock_dput: dput(new_dentry); out_unlock: mutex_unlock(&nd.dentry->d_inode->i_mutex); out_release: @@ -2629,10 +2775,18 @@ static int do_rename(int olddfd, const c error = -ENOTEMPTY; if (new_dentry == trap) goto exit5; - error = vfs_rename(old_dir->d_inode, old_dentry, + error = gr_acl_handle_rename(new_dentry, newnd.dentry, newnd.mnt, + old_dentry, old_dir->d_inode, oldnd.mnt, + newname); + + if (!error) + error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); + if (!error) + gr_handle_rename(old_dir->d_inode, newnd.dentry->d_inode, old_dentry, + new_dentry, oldnd.mnt, new_dentry->d_inode ? 1 : 0); exit5: dput(new_dentry); exit4: dput(old_dentry); diff -urNp -U4 linux-2.6.24.7/fs/namespace.c linux-2.6.24.7-new/fs/namespace.c --- linux-2.6.24.7/fs/namespace.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/namespace.c 2008-05-06 17:49:08.000000000 -0400 @@ -24,8 +24,9 @@ #include #include #include #include +#include #include #include #include "pnode.h" #include "internal.h" @@ -596,8 +597,10 @@ static int do_umount(struct vfsmount *mn lock_kernel(); DQUOT_OFF(sb); retval = do_remount_sb(sb, MS_RDONLY, NULL, 0); unlock_kernel(); + + gr_log_remount(mnt->mnt_devname, retval); } up_write(&sb->s_umount); return retval; } @@ -616,8 +619,11 @@ static int do_umount(struct vfsmount *mn if (retval) security_sb_umount_busy(mnt); up_write(&namespace_sem); release_mounts(&umount_list); + + gr_log_unmount(mnt->mnt_devname, retval); + return retval; } /* @@ -1441,8 +1447,13 @@ long do_mount(char *dev_name, char *dir_ retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page); if (retval) goto dput_out; + if (gr_handle_chroot_mount(nd.dentry, nd.mnt, dev_name)) { + retval = -EPERM; + goto dput_out; + } + if (flags & MS_REMOUNT) retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) @@ -1455,8 +1466,11 @@ long do_mount(char *dev_name, char *dir_ retval = do_new_mount(&nd, type_page, flags, mnt_flags, dev_name, data_page); dput_out: path_release(&nd); + + gr_log_mount(dev_name, dir_name, retval); + return retval; } /* @@ -1692,8 +1706,11 @@ asmlinkage long sys_pivot_root(const cha if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (gr_handle_chroot_pivot()) + return -EPERM; + lock_kernel(); error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new_nd); diff -urNp -U4 linux-2.6.24.7/fs/open.c linux-2.6.24.7-new/fs/open.c --- linux-2.6.24.7/fs/open.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/open.c 2008-05-06 17:49:08.000000000 -0400 @@ -26,8 +26,9 @@ #include #include #include #include +#include int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { int retval = -ENODEV; @@ -203,8 +204,11 @@ int do_truncate(struct dentry *dentry, l /* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */ if (length < 0) return -EINVAL; + if (filp && !gr_acl_handle_truncate(dentry, filp->f_vfsmnt)) + return -EACCES; + newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | time_attrs; if (filp) { newattrs.ia_file = filp; @@ -460,8 +464,11 @@ asmlinkage long sys_faccessat(int dfd, c if(IS_RDONLY(nd.dentry->d_inode)) res = -EROFS; + if (!res && !gr_acl_handle_access(nd.dentry, nd.mnt, mode)) + res = -EACCES; + out_path_release: path_release(&nd); out: current->fsuid = old_fsuid; @@ -489,8 +496,10 @@ asmlinkage long sys_chdir(const char __u error = vfs_permission(&nd, MAY_EXEC); if (error) goto dput_and_out; + gr_log_chdir(nd.dentry, nd.mnt); + set_fs_pwd(current->fs, nd.mnt, nd.dentry); dput_and_out: path_release(&nd); @@ -519,8 +528,15 @@ asmlinkage long sys_fchdir(unsigned int if (!S_ISDIR(inode->i_mode)) goto out_putf; error = file_permission(file, MAY_EXEC); + + if (!error && !gr_chroot_fchdir(dentry, mnt)) + error = -EPERM; + + if (!error) + gr_log_chdir(dentry, mnt); + if (!error) set_fs_pwd(current->fs, mnt, dentry); out_putf: fput(file); @@ -544,10 +560,18 @@ asmlinkage long sys_chroot(const char __ error = -EPERM; if (!capable(CAP_SYS_CHROOT)) goto dput_and_out; + if (gr_handle_chroot_chroot(nd.dentry, nd.mnt)) + goto dput_and_out; + set_fs_root(current->fs, nd.mnt, nd.dentry); set_fs_altroot(); + + gr_handle_chroot_caps(current); + + gr_handle_chroot_chdir(nd.dentry, nd.mnt); + error = 0; dput_and_out: path_release(&nd); out: @@ -576,11 +600,24 @@ asmlinkage long sys_fchmod(unsigned int goto out_putf; err = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_putf; + + if (!gr_acl_handle_fchmod(dentry, file->f_vfsmnt, mode)) { + err = -EACCES; + goto out_putf; + } + mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) mode = inode->i_mode; + + if (gr_handle_chroot_chmod(dentry, file->f_vfsmnt, mode)) { + err = -EPERM; + mutex_unlock(&inode->i_mutex); + goto out_putf; + } + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; err = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); @@ -611,11 +648,23 @@ asmlinkage long sys_fchmodat(int dfd, co error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; + if (!gr_acl_handle_chmod(nd.dentry, nd.mnt, mode)) { + error = -EACCES; + goto dput_and_out; + }; + mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) mode = inode->i_mode; + + if (gr_handle_chroot_chmod(nd.dentry, nd.mnt, mode)) { + error = -EACCES; + mutex_unlock(&inode->i_mutex); + goto dput_and_out; + } + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; error = notify_change(nd.dentry, &newattrs); mutex_unlock(&inode->i_mutex); @@ -630,9 +679,9 @@ asmlinkage long sys_chmod(const char __u { return sys_fchmodat(AT_FDCWD, filename, mode); } -static int chown_common(struct dentry * dentry, uid_t user, gid_t group) +static int chown_common(struct dentry * dentry, uid_t user, gid_t group, struct vfsmount *mnt) { struct inode * inode; int error; struct iattr newattrs; @@ -647,8 +696,14 @@ static int chown_common(struct dentry * goto out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; + + if (!gr_acl_handle_chown(dentry, mnt)) { + error = -EACCES; + goto out; + } + newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { newattrs.ia_valid |= ATTR_UID; newattrs.ia_uid = user; @@ -674,9 +729,9 @@ asmlinkage long sys_chown(const char __u error = user_path_walk(filename, &nd); if (error) goto out; - error = chown_common(nd.dentry, user, group); + error = chown_common(nd.dentry, user, group, nd.mnt); path_release(&nd); out: return error; } @@ -694,9 +749,9 @@ asmlinkage long sys_fchownat(int dfd, co follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; error = __user_walk_fd(dfd, filename, follow, &nd); if (error) goto out; - error = chown_common(nd.dentry, user, group); + error = chown_common(nd.dentry, user, group, nd.mnt); path_release(&nd); out: return error; } @@ -708,9 +763,9 @@ asmlinkage long sys_lchown(const char __ error = user_path_walk_link(filename, &nd); if (error) goto out; - error = chown_common(nd.dentry, user, group); + error = chown_common(nd.dentry, user, group, nd.mnt); path_release(&nd); out: return error; } @@ -727,9 +782,9 @@ asmlinkage long sys_fchown(unsigned int goto out; dentry = file->f_path.dentry; audit_inode(NULL, dentry); - error = chown_common(dentry, user, group); + error = chown_common(dentry, user, group, file->f_vfsmnt); fput(file); out: return error; } @@ -938,8 +993,9 @@ repeat: /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. */ + gr_learn_resource(current, RLIMIT_NOFILE, fd, 0); if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out; /* Do we need to expand the fd array or fd set? */ diff -urNp -U4 linux-2.6.24.7/fs/pipe.c linux-2.6.24.7-new/fs/pipe.c --- linux-2.6.24.7/fs/pipe.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/pipe.c 2008-05-06 17:49:08.000000000 -0400 @@ -886,9 +886,9 @@ void free_pipe_info(struct inode *inode) __free_pipe_info(inode->i_pipe); inode->i_pipe = NULL; } -static struct vfsmount *pipe_mnt __read_mostly; +struct vfsmount *pipe_mnt __read_mostly; static int pipefs_delete_dentry(struct dentry *dentry) { /* * At creation time, we pretended this dentry was hashed diff -urNp -U4 linux-2.6.24.7/fs/proc/array.c linux-2.6.24.7-new/fs/proc/array.c --- linux-2.6.24.7/fs/proc/array.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/array.c 2008-05-10 09:28:17.000000000 -0400 @@ -385,8 +385,14 @@ static cputime_t task_gtime(struct task_ { return p->gtime; } +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP +#define PAX_RAND_FLAGS(_mm) (_mm != NULL && _mm != current->mm && \ + (_mm->pax_flags & MF_PAX_RANDMMAP || \ + _mm->pax_flags & MF_PAX_SEGMEXEC)) +#endif + static int do_task_stat(struct task_struct *task, char *buffer, int whole) { unsigned long vsize, eip, esp, wchan = ~0UL; long priority, nice; @@ -480,8 +486,21 @@ static int do_task_stat(struct task_stru stime = task_stime(task); gtime = task_gtime(task); } +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP + if (PAX_RAND_FLAGS(mm)) { + eip = 0; + esp = 0; + wchan = 0; + } +#endif +#ifdef CONFIG_GRKERNSEC_HIDESYM + wchan = 0; + eip =0; + esp =0; +#endif + /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ priority = task_prio(task); nice = task_nice(task); @@ -520,11 +539,17 @@ static int do_task_stat(struct task_stru start_time, vsize, mm ? get_mm_rss(mm) : 0, rsslim, +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP + PAX_RAND_FLAGS(mm) ? 1 : (mm ? mm->start_code : 0), + PAX_RAND_FLAGS(mm) ? 1 : (mm ? mm->end_code : 0), + PAX_RAND_FLAGS(mm) ? 0 : (mm ? mm->start_stack : 0), +#else mm ? mm->start_code : 0, mm ? mm->end_code : 0, mm ? mm->start_stack : 0, +#endif esp, eip, /* The signal information here is obsolete. * It must be decimal for Linux 2.0 compatibility. @@ -571,4 +596,15 @@ int proc_pid_statm(struct task_struct *t return sprintf(buffer, "%d %d %d %d %d %d %d\n", size, resident, shared, text, lib, data, 0); } + +#ifdef CONFIG_GRKERNSEC_PROC_IPADDR +int proc_pid_ipaddr(struct task_struct *task, char * buffer) +{ + int len; + + len = sprintf(buffer, "%u.%u.%u.%u\n", NIPQUAD(task->signal->curr_ip)); + return len; +} +#endif + diff -urNp -U4 linux-2.6.24.7/fs/proc/base.c linux-2.6.24.7-new/fs/proc/base.c --- linux-2.6.24.7/fs/proc/base.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/base.c 2008-05-10 09:28:17.000000000 -0400 @@ -75,8 +75,10 @@ #include #include #include #include +#include + #include "internal.h" /* NOTE: * Implementing inode permission operations in /proc is almost @@ -199,9 +201,9 @@ static int proc_root_link(struct inode * (task == current || \ (task->parent == current && \ (task->ptrace & PT_PTRACED) && \ (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ - security_ptrace(current,task) == 0)) + security_ptrace(current,task) == 0 && !gr_handle_proc_ptrace(task))) struct mm_struct *mm_for_maps(struct task_struct *task) { struct mm_struct *mm = get_task_mm(task); @@ -608,9 +610,9 @@ static ssize_t mem_read(struct file * fi if (!task) goto out_no_task; - if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) + if (!MAY_PTRACE(task) || !ptrace_may_attach(task) || gr_acl_handle_procpidmem(task)) goto out; ret = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); @@ -678,9 +680,9 @@ static ssize_t mem_write(struct file * f copied = -ESRCH; if (!task) goto out_no_task; - if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) + if (!MAY_PTRACE(task) || !ptrace_may_attach(task) || gr_acl_handle_procpidmem(task)) goto out; copied = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); @@ -1201,9 +1203,13 @@ static struct inode *proc_pid_make_inode inode->i_uid = 0; inode->i_gid = 0; if (task_dumpable(task)) { inode->i_uid = task->euid; +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + inode->i_gid = CONFIG_GRKERNSEC_PROC_GID; +#else inode->i_gid = task->egid; +#endif } security_task_to_inode(task, inode); out: @@ -1217,19 +1223,47 @@ out_unlock: static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = dentry->d_inode; struct task_struct *task; +#if defined(CONFIG_GRKERNSEC_PROC_USER) || defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + struct task_struct *tmp = current; +#endif + generic_fillattr(inode, stat); rcu_read_lock(); stat->uid = 0; stat->gid = 0; task = pid_task(proc_pid(inode), PIDTYPE_PID); - if (task) { + + if (task && (gr_pid_is_chrooted(task) || gr_check_hidden_task(task))) { + rcu_read_unlock(); + return -ENOENT; + } + + + if (task +#if defined(CONFIG_GRKERNSEC_PROC_USER) || defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + && (!tmp->uid || (tmp->uid == task->uid) +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + || in_group_p(CONFIG_GRKERNSEC_PROC_GID) +#endif + ) +#endif + ) { if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || +#ifdef CONFIG_GRKERNSEC_PROC_USER + (inode->i_mode == (S_IFDIR|S_IRUSR|S_IXUSR)) || +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + (inode->i_mode == (S_IFDIR|S_IRUSR|S_IRGRP|S_IXUSR|S_IXGRP)) || +#endif task_dumpable(task)) { stat->uid = task->euid; +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + stat->gid = CONFIG_GRKERNSEC_PROC_GID; +#else stat->gid = task->egid; +#endif } } rcu_read_unlock(); return 0; @@ -1255,13 +1289,23 @@ static int pid_getattr(struct vfsmount * static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; struct task_struct *task = get_proc_task(inode); + if (task) { if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || +#ifdef CONFIG_GRKERNSEC_PROC_USER + (inode->i_mode == (S_IFDIR|S_IRUSR|S_IXUSR)) || +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + (inode->i_mode == (S_IFDIR|S_IRUSR|S_IRGRP|S_IXUSR|S_IXGRP)) || +#endif task_dumpable(task)) { inode->i_uid = task->euid; +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + inode->i_gid = CONFIG_GRKERNSEC_PROC_GID; +#else inode->i_gid = task->egid; +#endif } else { inode->i_uid = 0; inode->i_gid = 0; } @@ -1632,14 +1676,24 @@ static const struct file_operations proc static int proc_fd_permission(struct inode *inode, int mask, struct nameidata *nd) { int rv; + struct task_struct *task; rv = generic_permission(inode, mask, NULL); - if (rv == 0) - return 0; + if (task_pid(current) == proc_pid(inode)) rv = 0; + + task = get_proc_task(inode); + if (task == NULL) + return rv; + + if (gr_acl_handle_procpidmem(task)) + rv = -EACCES; + + put_task_struct(task); + return rv; } /* @@ -1748,8 +1802,11 @@ static struct dentry *proc_pident_lookup if (!task) goto out_no_task; + if (gr_pid_is_chrooted(task) || gr_check_hidden_task(task)) + goto out; + /* * Yes, it does not scale. And it should not. Don't add * new entries into /proc// without very good reasons. */ @@ -1792,8 +1849,11 @@ static int proc_pident_readdir(struct fi ret = -ENOENT; if (!task) goto out_no_task; + if (gr_pid_is_chrooted(task) || gr_check_hidden_task(task)) + goto out; + ret = 0; i = filp->f_pos; switch (i) { case 0: @@ -2146,8 +2206,11 @@ static struct dentry *proc_base_lookup(s } if (p > last) goto out; + if (gr_pid_is_chrooted(task) || gr_check_hidden_task(task)) + goto out; + error = proc_base_instantiate(dir, dentry, task, p); out: put_task_struct(task); @@ -2249,8 +2312,11 @@ static const struct pid_entry tgid_base_ #endif #ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, pid_io_accounting), #endif +#ifdef CONFIG_GRKERNSEC_PROC_IPADDR + INF("ipaddr", S_IRUSR, pid_ipaddr), +#endif }; static int proc_tgid_base_readdir(struct file * filp, void * dirent, filldir_t filldir) @@ -2377,9 +2443,16 @@ static struct dentry *proc_pid_instantia inode = proc_pid_make_inode(dir->i_sb, task); if (!inode) goto out; +#ifdef CONFIG_GRKERNSEC_PROC_USER + inode->i_mode = S_IFDIR|S_IRUSR|S_IXUSR; +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + inode->i_gid = CONFIG_GRKERNSEC_PROC_GID; + inode->i_mode = S_IFDIR|S_IRUSR|S_IRGRP|S_IXUSR|S_IXGRP; +#else inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; +#endif inode->i_op = &proc_tgid_base_inode_operations; inode->i_fop = &proc_tgid_base_operations; inode->i_flags|=S_IMMUTABLE; inode->i_nlink = 5; @@ -2420,9 +2493,13 @@ struct dentry *proc_pid_lookup(struct in rcu_read_unlock(); if (!task) goto out; + if (gr_check_hidden_task(task)) + goto out_put_task; + result = proc_pid_instantiate(dir, dentry, task, NULL); +out_put_task: put_task_struct(task); out: return result; } @@ -2485,8 +2562,11 @@ static int proc_pid_fill_cache(struct fi int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode); +#if defined(CONFIG_GRKERNSEC_PROC_USER) || defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + struct task_struct *tmp = current; +#endif struct tgid_iter iter; struct pid_namespace *ns; if (!reaper) @@ -2503,8 +2583,19 @@ int proc_pid_readdir(struct file * filp, iter.tgid = filp->f_pos - TGID_OFFSET; for (iter = next_tgid(ns, iter); iter.task; iter.tgid += 1, iter = next_tgid(ns, iter)) { + if (gr_pid_is_chrooted(iter.task) || gr_check_hidden_task(iter.task) +#if defined(CONFIG_GRKERNSEC_PROC_USER) || defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + || (tmp->uid && (iter.task->uid != tmp->uid) +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + && !in_group_p(CONFIG_GRKERNSEC_PROC_GID) +#endif + ) +#endif + ) + continue; + filp->f_pos = iter.tgid + TGID_OFFSET; if (proc_pid_fill_cache(filp, dirent, filldir, iter) < 0) { put_task_struct(iter.task); goto out; diff -urNp -U4 linux-2.6.24.7/fs/proc/inode.c linux-2.6.24.7-new/fs/proc/inode.c --- linux-2.6.24.7/fs/proc/inode.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/inode.c 2008-05-06 17:49:08.000000000 -0400 @@ -410,9 +410,13 @@ struct inode *proc_get_inode(struct supe if (de) { if (de->mode) { inode->i_mode = de->mode; inode->i_uid = de->uid; +#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP + inode->i_gid = CONFIG_GRKERNSEC_PROC_GID; +#else inode->i_gid = de->gid; +#endif } if (de->size) inode->i_size = de->size; if (de->nlink) diff -urNp -U4 linux-2.6.24.7/fs/proc/internal.h linux-2.6.24.7-new/fs/proc/internal.h --- linux-2.6.24.7/fs/proc/internal.h 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/internal.h 2008-05-06 17:49:08.000000000 -0400 @@ -51,8 +51,11 @@ extern int proc_exe_link(struct inode *, extern int proc_tid_stat(struct task_struct *, char *); extern int proc_tgid_stat(struct task_struct *, char *); extern int proc_pid_status(struct task_struct *, char *); extern int proc_pid_statm(struct task_struct *, char *); +#ifdef CONFIG_GRKERNSEC_PROC_IPADDR +extern int proc_pid_ipaddr(struct task_struct*,char*); +#endif 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 -urNp -U4 linux-2.6.24.7/fs/proc/proc_misc.c linux-2.6.24.7-new/fs/proc/proc_misc.c --- linux-2.6.24.7/fs/proc/proc_misc.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/proc_misc.c 2008-05-06 17:49:09.000000000 -0400 @@ -686,8 +686,10 @@ void create_seq_entry(char *name, mode_t } void __init proc_misc_init(void) { + int gr_mode = 0; + static struct { char *name; int (*read_proc)(char*,char**,off_t,int,int*,void*); } *p, simple_ones[] = { @@ -701,15 +703,26 @@ void __init proc_misc_init(void) #ifdef CONFIG_STRAM_PROC {"stram", stram_read_proc}, #endif {"filesystems", filesystems_read_proc}, +#ifndef CONFIG_GRKERNSEC_PROC_ADD {"cmdline", cmdline_read_proc}, +#endif {"execdomains", execdomains_read_proc}, {NULL,} }; for (p = simple_ones; p->name; p++) create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL); +#ifdef CONFIG_GRKERNSEC_PROC_USER + gr_mode = S_IRUSR; +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + gr_mode = S_IRUSR | S_IRGRP; +#endif +#ifdef CONFIG_GRKERNSEC_PROC_ADD + create_proc_read_entry("cmdline", gr_mode, NULL, &cmdline_read_proc, NULL); +#endif + proc_symlink("mounts", NULL, "self/mounts"); /* And now for trickier ones */ #ifdef CONFIG_PRINTK @@ -720,17 +733,25 @@ void __init proc_misc_init(void) entry->proc_fops = &proc_kmsg_operations; } #endif create_seq_entry("locks", 0, &proc_locks_operations); +#ifdef CONFIG_GRKERNSEC_PROC_ADD + create_seq_entry("devices", gr_mode, &proc_devinfo_operations); +#else create_seq_entry("devices", 0, &proc_devinfo_operations); +#endif create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); #ifdef CONFIG_BLOCK create_seq_entry("partitions", 0, &proc_partitions_operations); #endif create_seq_entry("stat", 0, &proc_stat_operations); create_seq_entry("interrupts", 0, &proc_interrupts_operations); #ifdef CONFIG_SLABINFO +#ifdef CONFIG_GRKRENSEC_PROC_ADD + create_seq_entry("slabinfo",S_IWUSR|gr_mode,&proc_slabinfo_operations); +#else create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations); +#endif #ifdef CONFIG_DEBUG_SLAB_LEAK create_seq_entry("slab_allocators", 0 ,&proc_slabstats_operations); #endif #endif @@ -746,9 +767,9 @@ void __init proc_misc_init(void) #endif #ifdef CONFIG_SCHEDSTATS create_seq_entry("schedstat", 0, &proc_schedstat_operations); #endif -#ifdef CONFIG_PROC_KCORE +#if defined(CONFIG_PROC_KCORE) && !defined(CONFIG_GRKERNSEC_PROC_ADD) proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL); if (proc_root_kcore) { proc_root_kcore->proc_fops = &proc_kcore_operations; proc_root_kcore->size = diff -urNp -U4 linux-2.6.24.7/fs/proc/proc_net.c linux-2.6.24.7-new/fs/proc/proc_net.c --- linux-2.6.24.7/fs/proc/proc_net.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/proc_net.c 2008-05-06 17:49:09.000000000 -0400 @@ -68,9 +68,15 @@ static __net_init int proc_net_ns_init(s if (!root) goto out; err = -EEXIST; +#ifdef CONFIG_GRKERNSEC_PROC_USER + netd = proc_mkdir_mode("net", S_IRUSR | S_IXUSR, root); +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + netd = proc_mkdir_mode("net", S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP, root); +#else netd = proc_mkdir("net", root); +#endif if (!netd) goto free_root; err = -EEXIST; diff -urNp -U4 linux-2.6.24.7/fs/proc/proc_sysctl.c linux-2.6.24.7-new/fs/proc/proc_sysctl.c --- linux-2.6.24.7/fs/proc/proc_sysctl.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/proc_sysctl.c 2008-05-06 17:49:09.000000000 -0400 @@ -6,8 +6,10 @@ #include #include #include "internal.h" +extern __u32 gr_handle_sysctl(const struct ctl_table *table, const int op); + static struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; static struct inode_operations proc_sys_inode_operations; @@ -150,8 +152,11 @@ static struct dentry *proc_sys_lookup(st table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); if (!table) goto out; + if (gr_handle_sysctl(table, 001)) + goto out; + err = ERR_PTR(-ENOMEM); inode = proc_sys_make_inode(dir, table); if (!inode) goto out; @@ -359,8 +364,11 @@ static int proc_sys_readdir(struct file if (pos < filp->f_pos) continue; + if (gr_handle_sysctl(table, 0)) + continue; + if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0) goto out; filp->f_pos = pos + 1; } @@ -421,8 +429,32 @@ out: sysctl_head_finish(head); return error; } +/* Eric Biederman is to blame */ +static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + int error = 0; + struct ctl_table_header *head; + struct ctl_table *table; + + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); + /* Has the sysctl entry disappeared on us? */ + if (!table) + goto out; + + if (gr_handle_sysctl(table, 001)) { + error = -ENOENT; + goto out; + } + +out: + sysctl_head_finish(head); + + generic_fillattr(dentry->d_inode, stat); + + return error; +} static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int error; @@ -449,8 +481,9 @@ static const struct file_operations proc static struct inode_operations proc_sys_inode_operations = { .lookup = proc_sys_lookup, .permission = proc_sys_permission, .setattr = proc_sys_setattr, + .getattr = proc_sys_getattr, }; static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) { diff -urNp -U4 linux-2.6.24.7/fs/proc/root.c linux-2.6.24.7-new/fs/proc/root.c --- linux-2.6.24.7/fs/proc/root.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/root.c 2008-05-06 17:49:09.000000000 -0400 @@ -136,9 +136,17 @@ void __init proc_root_init(void) proc_tty_init(); #ifdef CONFIG_PROC_DEVICETREE proc_device_tree_init(); #endif +#ifdef CONFIG_GRKERNSEC_PROC_ADD +#ifdef CONFIG_GRKERNSEC_PROC_USER + proc_bus = proc_mkdir_mode("bus", S_IRUSR | S_IXUSR, NULL); +#elif defined(CONFIG_GRKERNSEC_PROC_USERGROUP) + proc_bus = proc_mkdir_mode("bus", S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP, NULL); +#endif +#else proc_bus = proc_mkdir("bus", NULL); +#endif proc_sys_init(); } static int proc_root_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat diff -urNp -U4 linux-2.6.24.7/fs/proc/task_mmu.c linux-2.6.24.7-new/fs/proc/task_mmu.c --- linux-2.6.24.7/fs/proc/task_mmu.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/proc/task_mmu.c 2008-05-10 09:28:17.000000000 -0400 @@ -130,8 +130,14 @@ struct pmd_walker { void (*action)(struct vm_area_struct *, pmd_t *, unsigned long, unsigned long, void *); }; +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP +#define PAX_RAND_FLAGS(_mm) (_mm != NULL && _mm != current->mm && \ + (_mm->pax_flags & MF_PAX_RANDMMAP || \ + _mm->pax_flags & MF_PAX_SEGMEXEC)) +#endif + static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss) { struct proc_maps_private *priv = m->private; struct task_struct *task = priv->task; @@ -152,15 +158,24 @@ static int show_map_internal(struct seq_ ino = inode->i_ino; } seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n", +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP + PAX_RAND_FLAGS(mm) ? 0UL : vma->vm_start, + PAX_RAND_FLAGS(mm) ? 0UL : vma->vm_end, +#else vma->vm_start, vma->vm_end, +#endif flags & VM_READ ? 'r' : '-', flags & VM_WRITE ? 'w' : '-', flags & VM_EXEC ? 'x' : '-', flags & VM_MAYSHARE ? 's' : 'p', +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP + PAX_RAND_FLAGS(mm) ? 0UL : vma->vm_pgoff << PAGE_SHIFT, +#else vma->vm_pgoff << PAGE_SHIFT, +#endif MAJOR(dev), MINOR(dev), ino, &len); /* * Print the dentry name for named mappings, and a @@ -190,9 +205,29 @@ static int show_map_internal(struct seq_ } } seq_putc(m, '\n'); - if (mss) + + if (mss) { +#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP + if (PAX_RAND_FLAGS(mm)) + seq_printf(m, + "Size: %8lu kB\n" + "Rss: %8lu kB\n" + "Shared_Clean: %8lu kB\n" + "Shared_Dirty: %8lu kB\n" + "Private_Clean: %8lu kB\n" + "Private_Dirty: %8lu kB\n", + "Referenced: %8lu kB\n", + 0UL, + 0UL, + 0UL, + 0UL, + 0UL, + 0UL, + 0UL); + else +#endif seq_printf(m, "Size: %8lu kB\n" "Rss: %8lu kB\n" "Shared_Clean: %8lu kB\n" @@ -206,8 +241,9 @@ static int show_map_internal(struct seq_ mss->shared_dirty >> 10, mss->private_clean >> 10, mss->private_dirty >> 10, mss->referenced >> 10); + } if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task))? vma->vm_start: 0; return 0; diff -urNp -U4 linux-2.6.24.7/fs/readdir.c linux-2.6.24.7-new/fs/readdir.c --- linux-2.6.24.7/fs/readdir.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/readdir.c 2008-05-06 17:49:09.000000000 -0400 @@ -15,8 +15,10 @@ #include #include #include #include +#include +#include #include int vfs_readdir(struct file *file, filldir_t filler, void *buf) @@ -63,8 +65,9 @@ struct old_linux_dirent { }; struct readdir_callback { struct old_linux_dirent __user * dirent; + struct file * file; int result; }; static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, @@ -78,8 +81,12 @@ static int fillonedir(void * __buf, cons return -EINVAL; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) return -EOVERFLOW; + + if (!gr_acl_handle_filldir(buf->file, name, namlen, ino)) + return 0; + buf->result++; dirent = buf->dirent; if (!access_ok(VERIFY_WRITE, dirent, (unsigned long)(dirent->d_name + namlen + 1) - @@ -109,8 +116,9 @@ asmlinkage long old_readdir(unsigned int goto out; buf.result = 0; buf.dirent = dirent; + buf.file = file; error = vfs_readdir(file, fillonedir, &buf); if (error >= 0) error = buf.result; @@ -135,8 +143,9 @@ struct linux_dirent { struct getdents_callback { struct linux_dirent __user * current_dir; struct linux_dirent __user * previous; + struct file * file; int count; int error; }; @@ -153,8 +162,12 @@ static int filldir(void * __buf, const c return -EINVAL; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) return -EOVERFLOW; + + if (!gr_acl_handle_filldir(buf->file, name, namlen, ino)) + return 0; + dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) goto efault; @@ -199,8 +212,9 @@ asmlinkage long sys_getdents(unsigned in buf.current_dir = dirent; buf.previous = NULL; buf.count = count; buf.error = 0; + buf.file = file; error = vfs_readdir(file, filldir, &buf); if (error < 0) goto out_putf; @@ -221,8 +235,9 @@ out: struct getdents_callback64 { struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; + struct file *file; int count; int error; }; @@ -235,8 +250,12 @@ static int filldir64(void * __buf, const buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; + + if (!gr_acl_handle_filldir(buf->file, name, namlen, ino)) + return 0; + dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) goto efault; @@ -281,8 +300,9 @@ asmlinkage long sys_getdents64(unsigned goto out; buf.current_dir = dirent; buf.previous = NULL; + buf.file = file; buf.count = count; buf.error = 0; error = vfs_readdir(file, filldir64, &buf); diff -urNp -U4 linux-2.6.24.7/fs/utimes.c linux-2.6.24.7-new/fs/utimes.c --- linux-2.6.24.7/fs/utimes.c 2008-05-01 17:50:00.000000000 -0400 +++ linux-2.6.24.7-new/fs/utimes.c 2008-05-06 17:49:09.000000000 -0400 @@ -5,8 +5,9 @@ #include #include #include #include +#include #include #include #ifdef __ARCH_WANT_SYS_UTIME @@ -54,8 +55,9 @@ long do_utimes(int dfd, char __user *fil { int error; struct nameidata nd; struct dentry *dentry; + struct vfsmount *mnt; struct inode *inode; struct iattr newattrs; struct file *f = NULL; @@ -77,14 +79,16 @@ long do_utimes(int dfd, char __user *fil f = fget(dfd); if (!f) goto out; dentry = f->f_path.dentry; + mnt = f->f_path.mnt; } else { error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); if (error) goto out; dentry = nd.dentry; + mnt = nd.mnt; } inode = dentry->d_inode; @@ -129,8 +133,14 @@ long do_utimes(int dfd, char __user *fil goto dput_and_out; } } } + + if (!gr_acl_handle_utime(dentry, mnt)) { + error = -EACCES; + goto dput_and_out; + } + mutex_lock(&inode->i_mutex); error = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); dput_and_out: diff -urNp -U4 linux-2.6.24.7/grsecurity/gracl_alloc.c linux-2.6.24.7-new/grsecurity/gracl_alloc.c --- linux-2.6.24.7/grsecurity/gracl_alloc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.24.7-new/grsecurity/gracl_alloc.c 2008-05-06 17:49:09.000000000 -0400 @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include + +static unsigned long alloc_stack_next = 1; +static unsigned long alloc_stack_size = 1; +static void **alloc_stack; + +static __inline__ int +alloc_pop(void) +{ + if (alloc_stack_next == 1) + return 0; + + kfree(alloc_stack[alloc_stack_next - 2]); + + alloc_stack_next--; + + return 1; +} + +static __inline__ void +alloc_push(void *buf) +{ + if (alloc_stack_next >= alloc_stack_size) + BUG(); + + alloc_stack[alloc_stack_next - 1] = buf; + + alloc_stack_next++; + + return; +} + +void * +acl_alloc(unsigned long len) +{ + void *ret; + + if (len > PAGE_SIZE) + BUG(); + + ret = kmalloc(len, GFP_KERNEL); + + if (ret) + alloc_push(ret); + + return ret; +} + +void +acl_free_all(void) +{ + if (gr_acl_is_enabled() || !alloc_stack) + return; + + while (alloc_pop()) ; + + if (alloc_stack) { + if ((alloc_stack_size * sizeof (void *)) <= PAGE_SIZE) + kfree(alloc_stack); + else + vfree(alloc_stack); + } + + alloc_stack = NULL; + alloc_stack_size = 1; + alloc_stack_next = 1; + + return; +} + +int +acl_alloc_stack_init(unsigned long size) +{ + if ((size * sizeof (void *)) <= PAGE_SIZE) + alloc_stack = + (void **) kmalloc(size * sizeof (void *), GFP_KERNEL); + else + alloc_stack = (void **) vmalloc(size * sizeof (void *)); + + alloc_stack_size = size; + + if (!alloc_stack) + return 0; + else + return 1; +} diff -urNp -U4 linux-2.6.24.7/grsecurity/gracl.c linux-2.6.24.7-new/grsecurity/gracl.c --- linux-2.6.24.7/grsecurity/gracl.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.24.7-new/grsecurity/gracl.c 2008-05-06 17:49:09.000000000 -0400 @@ -0,0 +1,3722 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct acl_role_db acl_role_set; +static struct name_db name_set; +static struct inodev_db inodev_set; + +/* for keeping track of userspace pointers used for subjects, so we + can share references in the kernel as well +*/ + +static struct dentry *real_root; +static struct vfsmount *real_root_mnt; + +static struct acl_subj_map_db subj_map_set; + +static struct acl_role_label *default_role; + +static u16 acl_sp_role_value; + +extern char *gr_shared_page[4]; +static DECLARE_MUTEX(gr_dev_sem); +rwlock_t gr_inode_lock = RW_LOCK_UNLOCKED; + +struct gr_arg *gr_usermode; + +static unsigned int gr_status = GR_STATUS_INIT; + +extern int chkpw(struct gr_arg *entry, unsigned char *salt, unsigned char *sum); +extern void gr_clear_learn_entries(void); + +#ifdef CONFIG_GRKERNSEC_RESLOG +extern void gr_log_resource(const struct task_struct *task, + const int res, const unsigned long wanted, const int gt); +#endif + +unsigned char *gr_system_salt; +unsigned char *gr_system_sum; + +static struct sprole_pw **acl_special_roles = NULL; +static __u16 num_sprole_pws = 0; + +static struct acl_role_label *kernel_role = NULL; + +static unsigned int gr_auth_attempts = 0; +static unsigned long gr_auth_expires = 0UL; + +extern struct vfsmount *sock_mnt; +extern struct vfsmount *pipe_mnt; +extern struct vfsmount *shm_mnt; +static struct acl_object_label *fakefs_obj; + +extern int gr_init_uidset(void); +extern void gr_free_uidset(void); +extern void gr_remove_uid(uid_t uid); +extern int gr_find_uid(uid_t uid); + +__inline__ int +gr_acl_is_enabled(void) +{ + return (gr_status & GR_READY); +} + +char gr_roletype_to_char(void) +{ + switch (current->role->roletype & + (GR_ROLE_DEFAULT | GR_ROLE_USER | GR_ROLE_GROUP | + GR_ROLE_SPECIAL)) { + case GR_ROLE_DEFAULT: + return 'D'; + case GR_ROLE_USER: + return 'U'; + case GR_ROLE_GROUP: + return 'G'; + case GR_ROLE_SPECIAL: + return 'S'; + } + + return 'X'; +} + +__inline__ int +gr_acl_tpe_check(void) +{ + if (unlikely(!(gr_status & GR_READY))) + return 0; + if (current->role->roletype & GR_ROLE_TPE) + return 1; + else + return 0; +} + +int +gr_handle_rawio(const struct inode *inode) +{ +#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS + if (inode && S_ISBLK(inode->i_mode) && + grsec_enable_chroot_caps && proc_is_chrooted(current) && + !capable(CAP_SYS_RAWIO)) + return 1; +#endif + return 0; +} + +static int +gr_streq(const char *a, const char *b, const unsigned int lena, const unsigned int lenb) +{ + int i; + unsigned long *l1; + unsigned long *l2; + unsigned char *c1; + unsigned char *c2; + int num_longs; + + if (likely(lena != lenb)) + return 0; + + l1 = (unsigned long *)a; + l2 = (unsigned long *)b; + + num_longs = lena / sizeof(unsigned long); + + for (i = num_longs; i--; l1++, l2++) { + if (unlikely(*l1 != *l2)) + return 0; + } + + c1 = (unsigned char *) l1; + c2 = (unsigned char *) l2; + + i = lena - (num_longs * sizeof(unsigned long)); + + for (; i--; c1++, c2++) { + if (unlikely(*c1 != *c2)) + return 0; + } + + return 1; +} + +static char * __our_d_path(struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, + char *buffer, int buflen) +{ + char * end = buffer+buflen; + char * retval; + int namelen; + + *--end = '\0'; + buflen--; + + if (buflen < 1) + goto Elong; + /* Get '/' right */ + retval = end-1; + *retval = '/'; + + for (;;) { + struct dentry * parent; + + if (dentry == root && vfsmnt == rootmnt) + break; + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + /* Global root? */ + spin_lock(&vfsmount_lock); + if (vfsmnt->mnt_parent == vfsmnt) { + spin_unlock(&vfsmount_lock); + goto global_root; + } + dentry = vfsmnt->mnt_mountpoint; + vfsmnt = vfsmnt->mnt_parent; + spin_unlock(&vfsmount_lock); + continue; + } + parent = dentry->d_parent; + prefetch(parent); + namelen = dentry->d_name.len; + buflen -= namelen + 1; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, dentry->d_name.name, namelen); + *--end = '/'; + retval = end; + dentry = parent; + } + + return retval; + +global_root: + namelen = dentry->d_name.len; + buflen -= namelen; + if (buflen < 0) + goto Elong; + retval -= namelen-1; /* hit the slash */ + memcpy(retval, dentry->d_name.name, namelen); + return retval; +Elong: + return ERR_PTR(-ENAMETOOLONG); +} + +static char * +gen_full_path(struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, char *buf, int buflen) +{ + char *retval; + + retval = __our_d_path(dentry, vfsmnt, root, rootmnt, buf, buflen); + if (unlikely(IS_ERR(retval))) + retval = strcpy(buf, ""); + else if (unlikely(retval[1] == '/' && retval[2] == '\0')) + retval[1] = '\0'; + + return retval; +} + +static char * +__d_real_path(const struct dentry *dentry, const struct vfsmount *vfsmnt, + char *buf, int buflen) +{ + char *res; + + /* we can use real_root, real_root_mnt, because this is only called + by the RBAC system */ + res = gen_full_path((struct dentry *)dentry, (struct vfsmount *)vfsmnt, real_root, real_root_mnt, buf, buflen); + + return res; +} + +static char * +d_real_path(const struct dentry *dentry, const struct vfsmount *vfsmnt, + char *buf, int buflen) +{ + char *res; + struct dentry *root; + struct vfsmount *rootmnt; + struct task_struct *reaper = current->nsproxy->pid_ns->child_reaper; + + /* we can't use real_root, real_root_mnt, because they belong only to the RBAC system */ + read_lock(&reaper->fs->lock); + root = dget(reaper->fs->root); + rootmnt = mntget(reaper->fs->rootmnt); + read_unlock(&reaper->fs->lock); + + spin_lock(&dcache_lock); + res = gen_full_path((struct dentry *)dentry, (struct vfsmount *)vfsmnt, root, rootmnt, buf, buflen); + spin_unlock(&dcache_lock); + + dput(root); + mntput(rootmnt); + return res; +} + +static char * +gr_to_filename_rbac(const struct dentry *dentry, const struct vfsmount *mnt) +{ + char *ret; + spin_lock(&dcache_lock); + ret = __d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[0],smp_processor_id()), + PAGE_SIZE); + spin_unlock(&dcache_lock); + return ret; +} + +char * +gr_to_filename_nolock(const struct dentry *dentry, const struct vfsmount *mnt) +{ + return __d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[0],smp_processor_id()), + PAGE_SIZE); +} + +char * +gr_to_filename(const struct dentry *dentry, const struct vfsmount *mnt) +{ + return d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[0], smp_processor_id()), + PAGE_SIZE); +} + +char * +gr_to_filename1(const struct dentry *dentry, const struct vfsmount *mnt) +{ + return d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[1], smp_processor_id()), + PAGE_SIZE); +} + +char * +gr_to_filename2(const struct dentry *dentry, const struct vfsmount *mnt) +{ + return d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[2], smp_processor_id()), + PAGE_SIZE); +} + +char * +gr_to_filename3(const struct dentry *dentry, const struct vfsmount *mnt) +{ + return d_real_path(dentry, mnt, per_cpu_ptr(gr_shared_page[3], smp_processor_id()), + PAGE_SIZE); +} + +__inline__ __u32 +to_gr_audit(const __u32 reqmode) +{ + /* masks off auditable permission flags, then shifts them to create + auditing flags, and adds the special case of append auditing if + we're requesting write */ + return (((reqmode & ~GR_AUDITS) << 10) | ((reqmode & GR_WRITE) ? GR_AUDIT_APPEND : 0)); +} + +struct acl_subject_label * +lookup_subject_map(const struct acl_subject_label *userp) +{ + unsigned int index = shash(userp, subj_map_set.s_size); + struct subject_map *match; + + match = subj_map_set.s_hash[index]; + + while (match && match->user != userp) + match = match->next; + + if (match != NULL) + return match->kernel; + else + return NULL; +} + +static void +insert_subj_map_entry(struct subject_map *subjmap) +{ + unsigned int index = shash(subjmap->user, subj_map_set.s_size); + struct subject_map **curr; + + subjmap->prev = NULL; + + curr = &subj_map_set.s_hash[index]; + if (*curr != NULL) + (*curr)->prev = subjmap; + + subjmap->next = *curr; + *curr = subjmap; + + return; +} + +static struct acl_role_label * +lookup_acl_role_label(const struct task_struct *task, const uid_t uid, + const gid_t gid) +{ + unsigned int index = rhash(uid, GR_ROLE_USER, acl_role_set.r_size); + struct acl_role_label *match; + struct role_allowed_ip *ipp; + unsigned int x; + + match = acl_role_set.r_hash[index]; + + while (match) { + if ((match->roletype & (GR_ROLE_DOMAIN | GR_ROLE_USER)) == (GR_ROLE_DOMAIN | GR_ROLE_USER)) { + for (x = 0; x < match->domain_child_num; x++) { + if (match->domain_children[x] == uid) + goto found; + } + } else if (match->uidgid == uid && match->roletype & GR_ROLE_USER) + break; + match = match->next; + } +found: + if (match == NULL) { + try_group: + index = rhash(gid, GR_ROLE_GROUP, acl_role_set.r_size); + match = acl_role_set.r_hash[index]; + + while (match) { + if ((match->roletype & (GR_ROLE_DOMAIN | GR_ROLE_GROUP)) == (GR_ROLE_DOMAIN | GR_ROLE_GROUP)) { + for (x = 0; x < match->domain_child_num; x++) { + if (match->domain_children[x] == gid) + goto found2; + } + } else if (match->uidgid == gid && match->roletype & GR_ROLE_GROUP) + break; + match = match->next; + } +found2: + if (match == NULL) + match = default_role; + if (match->allowed_ips == NULL) + return match; + else { + for (ipp = match->allowed_ips; ipp; ipp = ipp->next) { + if (likely + ((ntohl(task->signal->curr_ip) & ipp->netmask) == + (ntohl(ipp->addr) & ipp->netmask))) + return match; + } + match = default_role; + } + } else if (match->allowed_ips == NULL) { + return match; + } else { + for (ipp = match->allowed_ips; ipp; ipp = ipp->next) { + if (likely + ((ntohl(task->signal->curr_ip) & ipp->netmask) == + (ntohl(ipp->addr) & ipp->netmask))) + return match; + } + goto try_group; + } + + return match; +} + +struct acl_subject_label * +lookup_acl_subj_label(const ino_t ino, const dev_t dev, + const struct acl_role_label *role) +{ + unsigned int index = fhash(ino, dev, role->subj_hash_size); + struct acl_subject_label *match; + + match = role->subj_hash[index]; + + while (match && (match->inode != ino || match->device != dev || + (match->mode & GR_DELETED))) { + match = match->next; + } + + if (match && !(match->mode & GR_DELETED)) + return match; + else + return NULL; +} + +static struct acl_object_label * +lookup_acl_obj_label(const ino_t ino, const dev_t dev, + const struct acl_subject_label *subj) +{ + unsigned int index = fhash(ino, dev, subj->obj_hash_size); + struct acl_object_label *match; + + match = subj->obj_hash[index]; + + while (match && (match->inode != ino || match->device != dev || + (match->mode & GR_DELETED))) { + match = match->next; + } + + if (match && !(match->mode & GR_DELETED)) + return match; + else + return NULL; +} + +static struct acl_object_label * +lookup_acl_obj_label_create(const ino_t ino, const dev_t dev, + const struct acl_subject_label *subj) +{ + unsigned int index = fhash(ino, dev, subj->obj_hash_size); + struct acl_object_label *match; + + match = subj->obj_hash[index]; + + while (match && (match->inode != ino || match->device != dev || + !(match->mode & GR_DELETED))) { + match = match->next; + } + + if (match && (match->mode & GR_DELETED)) + return match; + + match = subj->obj_hash[index]; + + while (match && (match->inode != ino || match->device != dev || + (match->mode & GR_DELETED))) { + match = match->next; + } + + if (match && !(match->mode & GR_DELETED)) + return match; + else + return NULL; +} + +static struct name_entry * +lookup_name_entry(const char *name) +{ + unsigned int len = strlen(name); + unsigned int key = full_name_hash(name, len); + unsigned int index = key % name_set.n_size; + struct name_entry *match; + + match = name_set.n_hash[index]; + + while (match && (match->key != key || !gr_streq(match->name, name, match->len, len))) + match = match->next; + + return match; +} + +static struct name_entry * +lookup_name_entry_create(const char *name) +{ + unsigned int len = strlen(name); + unsigned int key = full_name_hash(name, len); + unsigned int index = key % name_set.n_size; + struct name_entry *match; + + match = name_set.n_hash[index]; + + while (match && (match->key != key || !gr_streq(match->name, name, match->len, len) || + !match->deleted)) + match = match->next; + + if (match && match->deleted) + return match; + + match = name_set.n_hash[index]; + + while (match && (match->key != key || !gr_streq(match->name, name, match->len, len) || + match->deleted)) + match = match->next; + + if (match && !match->deleted) + return match; + else + return NULL; +} + +static struct inodev_entry * +lookup_inodev_entry(const ino_t ino, const dev_t dev) +{ + unsigned int index = fhash(ino, dev, inodev_set.i_size); + struct inodev_entry *match; + + match = inodev_set.i_hash[index]; + + while (match && (match->nentry->inode != ino || match->nentry->device != dev)) + match = match->next; + + return match; +} + +static void +insert_inodev_entry(struct inodev_entry *entry) +{ + unsigned int index = fhash(entry->nentry->inode, entry->nentry->device, + inodev_set.i_size); + struct inodev_entry **curr; + + entry->prev = NULL; + + curr = &inodev_set.i_hash[index]; + if (*curr != NULL) + (*curr)->prev = entry; + + entry->next = *curr; + *curr = entry; + + return; +} + +static void +__insert_acl_role_label(struct acl_role_label *role, uid_t uidgid) +{ + unsigned int index = + rhash(uidgid, role->roletype & (GR_ROLE_USER | GR_ROLE_GROUP), acl_role_set.r_size); + struct acl_role_label **curr; + + role->prev = NULL; + + curr = &acl_role_set.r_hash[index]; + if (*curr != NULL) + (*curr)->prev = role; + + role->next = *curr; + *curr = role; + + return; +} + +static void +insert_acl_role_label(struct acl_role_label *role) +{ + int i; + + if (role->roletype & GR_ROLE_DOMAIN) { + for (i = 0; i < role->domain_child_num; i++) + __insert_acl_role_label(role, role->domain_children[i]); + } else + __insert_acl_role_label(role, role->uidgid); +} + +static int +insert_name_entry(char *name, const ino_t inode, const dev_t device, __u8 deleted) +{ + struct name_entry **curr, *nentry; + struct inodev_entry *ientry; + unsigned int len = strlen(name); + unsigned int key = full_name_hash(name, len); + unsigned int index = key % name_set.n_size; + + curr = &name_set.n_hash[index]; + + while (*curr && ((*curr)->key != key || !gr_streq((*curr)->name, name, (*curr)->len, len))) + curr = &((*curr)->next); + + if (*curr != NULL) + return 1; + + nentry = acl_alloc(sizeof (struct name_entry)); + if (nentry == NULL) + return 0; + ientry = acl_alloc(sizeof (struct inodev_entry)); + if (ientry == NULL) + return 0; + ientry->nentry = nentry; + + nentry->key = key; + nentry->name = name; + nentry->inode = inode; + nentry->device = device; + nentry->len = len; + nentry->deleted = deleted; + + nentry->prev = NULL; + curr = &name_set.n_hash[index]; + if (*curr != NULL) + (*curr)->prev = nentry; + nentry->next = *curr; + *curr = nentry; + + /* insert us into the table searchable by inode/dev */ + insert_inodev_entry(ientry); + + return 1; +} + +static void +insert_acl_obj_label(struct acl_object_label *obj, + struct acl_subject_label *subj) +{ + unsigned int index = + fhash(obj->inode, obj->device, subj->obj_hash_size); + struct acl_object_label **curr; + + + obj->prev = NULL; + + curr = &subj->obj_hash[index]; + if (*curr != NULL) + (*curr)->prev = obj; + + obj->next = *curr; + *curr = obj; + + return; +} + +static void +insert_acl_subj_label(struct acl_subject_label *obj, + struct acl_role_label *role) +{ + unsigned int index = fhash(obj->inode, obj->device, role->subj_hash_size); + struct acl_subject_label **curr; + + obj->prev = NULL; + + curr = &role->subj_hash[index]; + if (*curr != NULL) + (*curr)->prev = obj; + + obj->next = *curr; + *curr = obj; + + return; +} + +/* allocating chained hash tables, so optimal size is where lambda ~ 1 */ + +static void * +create_table(__u32 * len, int elementsize) +{ + unsigned int table_sizes[] = { + 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, + 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, + 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, + 268435399, 536870909, 1073741789, 2147483647 + }; + void *newtable = NULL; + unsigned int pwr = 0; + + while ((pwr < ((sizeof (table_sizes) / sizeof (table_sizes[0])) - 1)) && + table_sizes[pwr] <= *len) + pwr++; + + if (table_sizes[pwr] <= *len) + return newtable; + + if ((table_sizes[pwr] * elementsize) <= PAGE_SIZE) + newtable = + kmalloc(table_sizes[pwr] * elementsize, GFP_KERNEL); + else + newtable = vmalloc(table_sizes[pwr] * elementsize); + + *len = table_sizes[pwr]; + + return newtable; +} + +static int +init_variables(const struct gr_arg *arg) +{ + struct task_struct *reaper = current->nsproxy->pid_ns->child_reaper; + unsigned int stacksize; + + subj_map_set.s_size = arg->role_db.num_subjects; + acl_role_set.r_size = arg->role_db.num_roles + arg->role_db.num_domain_children; + name_set.n_size = arg->role_db.num_objects; + inodev_set.i_size = arg->role_db.num_objects; + + if (!subj_map_set.s_size || !acl_role_set.r_size || + !name_set.n_size || !inodev_set.i_size) + return 1; + + if (!gr_init_uidset()) + return 1; + + /* set up the stack that holds allocation info */ + + stacksize = arg->role_db.num_pointers + 5; + + if (!acl_alloc_stack_init(stacksize)) + return 1; + + /* grab reference for the real root dentry and vfsmount */ + read_lock(&reaper->fs->lock); + real_root_mnt = mntget(reaper->fs->rootmnt); + real_root = dget(reaper->fs->root); + read_unlock(&reaper->fs->lock); + + fakefs_obj = acl_alloc(sizeof(struct acl_object_label)); + if (fakefs_obj == NULL) + return 1; + fakefs_obj->mode = GR_FIND | GR_READ | GR_WRITE | GR_EXEC; + + subj_map_set.s_hash = + (struct subject_map **) create_table(&subj_map_set.s_size, sizeof(void *)); + acl_role_set.r_hash = + (struct acl_role_label **) create_table(&acl_role_set.r_size, sizeof(void *)); + name_set.n_hash = (struct name_entry **) create_table(&name_set.n_size, sizeof(void *)); + inodev_set.i_hash = + (struct inodev_entry **) create_table(&inodev_set.i_size, sizeof(void *)); + + if (!subj_map_set.s_hash || !acl_role_set.r_hash || + !name_set.n_hash || !inodev_set.i_hash) + return 1; + + memset(subj_map_set.s_hash, 0, + sizeof(struct subject_map *) * subj_map_set.s_size); + memset(acl_role_set.r_hash, 0, + sizeof (struct acl_role_label *) * acl_role_set.r_size); + memset(name_set.n_hash, 0, + sizeof (struct name_entry *) * name_set.n_size); + memset(inodev_set.i_hash, 0, + sizeof (struct inodev_entry *) * inodev_set.i_size); + + return 0; +} + +/* free information not needed after startup + currently contains user->kernel pointer mappings for subjects +*/ + +static void +free_init_variables(void) +{ + __u32 i; + + if (subj_map_set.s_hash) { + for (i = 0; i < subj_map_set.s_size; i++) { + if (subj_map_set.s_hash[i]) { + kfree(subj_map_set.s_hash[i]); + subj_map_set.s_hash[i] = NULL; + } + } + + if ((subj_map_set.s_size * sizeof (struct subject_map *)) <= + PAGE_SIZE) + kfree(subj_map_set.s_hash); + else + vfree(subj_map_set.s_hash); + } + + return; +} + +static void +free_variables(void) +{ + struct acl_subject_label *s; + struct acl_role_label *r; + struct task_struct *task, *task2; + unsigned int i, x; + + gr_clear_learn_entries(); + + read_lock(&tasklist_lock); + do_each_thread(task2, task) { + task->acl_sp_role = 0; + task->acl_role_id = 0; + task->acl = NULL; + task->role = NULL; + } while_each_thread(task2, task); + read_unlock(&tasklist_lock); + + /* release the reference to the real root dentry and vfsmount */ + if (real_root) + dput(real_root); + real_root = NULL; + if (real_root_mnt) + mntput(real_root_mnt); + real_root_mnt = NULL; + + /* free all object hash tables */ + + FOR_EACH_ROLE_START(r, i) + if (r->subj_hash == NULL) + break; + FOR_EACH_SUBJECT_START(r, s, x) + if (s->obj_hash == NULL) + break; + if ((s->obj_hash_size * sizeof (struct acl_object_label *)) <= PAGE_SIZE) + kfree(s->obj_hash); + else + vfree(s->obj_hash); + FOR_EACH_SUBJECT_END(s, x) + FOR_EACH_NESTED_SUBJECT_START(r, s) + if (s->obj_hash == NULL) + break; + if ((s->obj_hash_size * sizeof (struct acl_object_label *)) <= PAGE_SIZE) + kfree(s->obj_hash); + else + vfree(s->obj_hash); + FOR_EACH_NESTED_SUBJECT_END(s) + if ((r->subj_hash_size * sizeof (struct acl_subject_label *)) <= PAGE_SIZE) + kfree(r->subj_hash); + else + vfree(r->subj_hash); + r->subj_hash = NULL; + FOR_EACH_ROLE_END(r,i) + + acl_free_all(); + + if (acl_role_set.r_hash) { + if ((acl_role_set.r_size * sizeof (struct acl_role_label *)) <= + PAGE_SIZE) + kfree(acl_role_set.r_hash); + else + vfree(acl_role_set.r_hash); + } + if (name_set.n_hash) { + if ((name_set.n_size * sizeof (struct name_entry *)) <= + PAGE_SIZE) + kfree(name_set.n_hash); + else + vfree(name_set.n_hash); + } + + if (inodev_set.i_hash) { + if ((inodev_set.i_size * sizeof (struct inodev_entry *)) <= + PAGE_SIZE) + kfree(inodev_set.i_hash); + else + vfree(inodev_set.i_hash); + } + + gr_free_uidset(); + + memset(&name_set, 0, sizeof (struct name_db)); + memset(&inodev_set, 0, sizeof (struct inodev_db)); + memset(&acl_role_set, 0, sizeof (struct acl_role_db)); + memset(&subj_map_set, 0, sizeof (struct acl_subj_map_db)); + + default_role = NULL; + + return; +} + +static __u32 +count_user_objs(struct acl_object_label *userp) +{ + struct acl_object_label o_tmp; + __u32 num = 0; + + while (userp) { + if (copy_from_user(&o_tmp, userp, + sizeof (struct acl_object_label))) + break; + + userp = o_tmp.prev; + num++; + } + + return num; +} + +static struct acl_subject_label * +do_copy_user_subj(struct acl_subject_label *userp, struct acl_role_label *role); + +static int +copy_user_glob(struct acl_object_label *obj) +{ + struct acl_object_label *g_tmp, **guser; + unsigned int len; + char *tmp; + + if (obj->globbed == NULL) + return 0; + + guser = &obj->globbed; + while (*guser) { + g_tmp = (struct acl_object_label *) + acl_alloc(sizeof (struct acl_object_label)); + if (g_tmp == NULL) + return -ENOMEM; + + if (copy_from_user(g_tmp, *guser, + sizeof (struct acl_object_label))) + return -EFAULT; + + len = strnlen_user(g_tmp->filename, PATH_MAX); + + if (!len || len >= PATH_MAX) + return -EINVAL; + + if ((tmp = (char *) acl_alloc(len)) == NULL) + return -ENOMEM; + + if (copy_from_user(tmp, g_tmp->filename, len)) + return -EFAULT; + + g_tmp->filename = tmp; + + *guser = g_tmp; + guser = &(g_tmp->next); + } + + return 0; +} + +static int +copy_user_objs(struct acl_object_label *userp, struct acl_subject_label *subj, + struct acl_role_label *role) +{ + struct acl_object_label *o_tmp; + unsigned int len; + int ret; + char *tmp; + + while (userp) { + if ((o_tmp = (struct acl_object_label *) + acl_alloc(sizeof (struct acl_object_label))) == NULL) + return -ENOMEM; + + if (copy_from_user(o_tmp, userp, + sizeof (struct acl_object_label))) + return -EFAULT; + + userp = o_tmp->prev; + + len = strnlen_user(o_tmp->filename, PATH_MAX); + + if (!len || len >= PATH_MAX) + return -EINVAL; + + if ((tmp = (char *) acl_alloc(len)) == NULL) + return -ENOMEM; + + if (copy_from_user(tmp, o_tmp->filename, len)) + return -EFAULT; + + o_tmp->filename = tmp; + + insert_acl_obj_label(o_tmp, subj); + if (!insert_name_entry(o_tmp->filename, o_tmp->inode, + o_tmp->device, (o_tmp->mode & GR_DELETED) ? 1 : 0)) + return -ENOMEM; + + ret = copy_user_glob(o_tmp); + if (ret) + return ret; + + if (o_tmp->nested) { + o_tmp->nested = do_copy_user_subj(o_tmp->nested, role); + if (IS_ERR(o_tmp->nested)) + return PTR_ERR(o_tmp->nested); + + /* insert into nested subject list */ + o_tmp->nested->next = role->hash->first; + role->hash->first = o_tmp->nested; + } + } + + return 0; +} + +static __u32 +count_user_subjs(struct acl_subject_label *userp) +{ + struct acl_subject_label s_tmp; + __u32 num = 0; + + while (userp) { + if (copy_from_user(&s_tmp, userp, + sizeof (struct acl_subject_label))) + break; + + userp = s_tmp.prev; + /* do not count nested subjects against this count, since + they are not included in the hash table, but are + attached to objects. We have already counted + the subjects in userspace for the allocation + stack + */ + if (!(s_tmp.mode & GR_NESTED)) + num++; + } + + return num; +} + +static int +copy_user_allowedips(struct acl_role_label *rolep) +{ + struct role_allowed_ip *ruserip, *rtmp = NULL, *rlast; + + ruserip = rolep->allowed_ips; + + while (ruserip) { + rlast = rtmp; + + if ((rtmp = (struct role_allowed_ip *) + acl_alloc(sizeof (struct role_allowed_ip))) == NULL) + return -ENOMEM; + + if (copy_from_user(rtmp, ruserip, + sizeof (struct role_allowed_ip))) + return -EFAULT; + + ruserip = rtmp->prev; + + if (!rlast) { + rtmp->prev = NULL; + rolep->allowed_ips = rtmp; + } else { + rlast->next = rtmp; + rtmp->prev = rlast; + } + + if (!ruserip) + rtmp->next = NULL; + } + + return 0; +} + +static int +copy_user_transitions(struct acl_role_label *rolep) +{ + struct role_transition *rusertp, *rtmp = NULL, *rlast; + + unsigned int len; + char *tmp; + + rusertp = rolep->transitions; + + while (rusertp) { + rlast = rtmp; + + if ((rtmp = (struct role_transition *) + acl_alloc(sizeof (struct role_transition))) == NULL) + return -ENOMEM; + + if (copy_from_user(rtmp, rusertp, + sizeof (struct role_transition))) + return -EFAULT; + + rusertp = rtmp->prev; + + len = strnlen_user(rtmp->rolename, GR_SPROLE_LEN); + + if (!len || len >= GR_SPROLE_LEN) + return -EINVAL; + + if ((tmp = (char *) acl_alloc(len)) == NULL) + return -ENOMEM; + + if (copy_from_user(tmp, rtmp->rolename, len)) + return -EFAULT; + + rtmp->rolename = tmp; + + if (!rlast) { + rtmp->prev = NULL; + rolep->transitions = rtmp; + } else { + rlast->next = rtmp; + rtmp->prev = rlast; + } + + if (!rusertp) + rtmp->next = NULL; + } + + return 0; +} + +static struct acl_subject_label * +do_copy_user_subj(struct acl_subject_label *userp, struct acl_role_label *role) +{ + struct acl_subject_label *s_tmp = NULL, *s_tmp2; + unsigned int len; + char *tmp; + __u32 num_objs; + struct acl_ip_label **i_tmp, *i_utmp2; + struct gr_hash_struct ghash; + struct subject_map *subjmap; + unsigned int i_num; + int err; + + s_tmp = lookup_subject_map(userp); + + /* we've already copied this subject into the kernel, just return + the reference to it, and don't copy it over again + */ + if (s_tmp) + return(s_tmp); + + if ((s_tmp = (struct acl_subject_label *) + acl_alloc(sizeof (struct acl_subject_label))) == NULL) + return ERR_PTR(-ENOMEM); + + subjmap = (struct subject_map *)kmalloc(sizeof (struct subject_map), GFP_KERNEL); + if (subjmap == NULL) + return ERR_PTR(-ENOMEM); + + subjmap->user = userp; + subjmap->kernel = s_tmp; + insert_subj_map_entry(subjmap); + + if (copy_from_user(s_tmp, userp, + sizeof (struct acl_subject_label))) + return ERR_PTR(-EFAULT); + + len = strnlen_user(s_tmp->filename, PATH_MAX); + + if (!len || len >= PATH_MAX) + return ERR_PTR(-EINVAL); + + if ((tmp = (char *) acl_alloc(len)) == NULL) + return ERR_PTR(-ENOMEM); + + if (copy_from_user(tmp, s_tmp->filename, len)) + return ERR_PTR(-EFAULT); + + s_tmp->filename = tmp; + + if (!strcmp(s_tmp->filename, "/")) + role->root_label = s_tmp; + + if (copy_from_user(&ghash, s_tmp->hash, sizeof(struct gr_hash_struct))) + return ERR_PTR(-EFAULT); + + /* copy user and group transition tables */ + + if (s_tmp->user_trans_num) { + uid_t *uidlist; + + uidlist = (uid_t *)acl_alloc(s_tmp->user_trans_num * sizeof(uid_t)); + if (uidlist == NULL) + return ERR_PTR(-ENOMEM); + if (copy_from_user(uidlist, s_tmp->user_transitions, s_tmp->user_trans_num * sizeof(uid_t))) + return ERR_PTR(-EFAULT); + + s_tmp->user_transitions = uidlist; + } + + if (s_tmp->group_trans_num) { + gid_t *gidlist; + + gidlist = (gid_t *)acl_alloc(s_tmp->group_trans_num * sizeof(gid_t)); + if (gidlist == NULL) + return ERR_PTR(-ENOMEM); + if (copy_from_user(gidlist, s_tmp->group_transitions, s_tmp->group_trans_num * sizeof(gid_t))) + return ERR_PTR(-EFAULT); + + s_tmp->group_transitions = gidlist; + } + + /* set up object hash table */ + num_objs = count_user_objs(ghash.first); + + s_tmp->obj_hash_size = num_objs; + s_tmp->obj_hash = + (struct acl_object_label **) + create_table(&(s_tmp->obj_hash_size), sizeof(void *)); + + if (!s_tmp->obj_hash) + return ERR_PTR(-ENOMEM); + + memset(s_tmp->obj_hash, 0, + s_tmp->obj_hash_size * + sizeof (struct acl_object_label *)); + + /* add in objects */ + err = copy_user_objs(ghash.first, s_tmp, role); + + if (err) + return ERR_PTR(err); + + /* set pointer for parent subject */ + if (s_tmp->parent_subject) { + s_tmp2 = do_copy_user_subj(s_tmp->parent_subject, role); + + if (IS_ERR(s_tmp2)) + return s_tmp2; + + s_tmp->parent_subject = s_tmp2; + } + + /* add in ip acls */ + + if (!s_tmp->ip_num) { + s_tmp->ips = NULL; + goto insert; + } + + i_tmp = + (struct acl_ip_label **) acl_alloc(s_tmp->ip_num * + sizeof (struct + acl_ip_label *)); + + if (!i_tmp) + return ERR_PTR(-ENOMEM); + + for (i_num = 0; i_num < s_tmp->ip_num; i_num++) { + *(i_tmp + i_num) = + (struct acl_ip_label *) + acl_alloc(sizeof (struct acl_ip_label)); + if (!*(i_tmp + i_num)) + return ERR_PTR(-ENOMEM); + + if (copy_from_user + (&i_utmp2, s_tmp->ips + i_num, + sizeof (struct acl_ip_label *))) + return ERR_PTR(-EFAULT); + + if (copy_from_user + (*(i_tmp + i_num), i_utmp2, + sizeof (struct acl_ip_label))) + return ERR_PTR(-EFAULT); + + if ((*(i_tmp + i_num))->iface == NULL) + continue; + + len = strnlen_user((*(i_tmp + i_num))->iface, IFNAMSIZ); + if (!len || len >= IFNAMSIZ) + return ERR_PTR(-EINVAL); + tmp = acl_alloc(len); + if (tmp == NULL) + return ERR_PTR(-ENOMEM); + if (copy_from_user(tmp, (*(i_tmp + i_num))->iface, len)) + return ERR_PTR(-EFAULT); + (*(i_tmp + i_num))->iface = tmp; + } + + s_tmp->ips = i_tmp; + +insert: + if (!insert_name_entry(s_tmp->filename, s_tmp->inode, + s_tmp->device, (s_tmp->mode & GR_DELETED) ? 1 : 0)) + return ERR_PTR(-ENOMEM); + + return s_tmp; +} + +static int +copy_user_subjs(struct acl_subject_label *userp, struct acl_role_label *role) +{ + struct acl_subject_label s_pre; + struct acl_subject_label * ret; + int err; + + while (userp) { + if (copy_from_user(&s_pre, userp, + sizeof (struct acl_subject_label))) + return -EFAULT; + + /* do not add nested subjects here, add + while parsing objects + */ + + if (s_pre.mode & GR_NESTED) { + userp = s_pre.prev; + continue; + } + + ret = do_copy_user_subj(userp, role); + + err = PTR_ERR(ret); + if (IS_ERR(ret)) + return err; + + insert_acl_subj_label(ret, role); + + userp = s_pre.prev; + } + + return 0; +} + +static int +copy_user_acl(struct gr_arg *arg) +{ + struct acl_role_label *r_tmp = NULL, **r_utmp, *r_utmp2; + struct sprole_pw *sptmp; + struct gr_hash_struct *ghash; + uid_t *domainlist; + unsigned int r_num; + unsigned int len; + char *tmp; + int err = 0; + __u16 i; + __u32 num_subjs; + + /* we need a default and kernel role */ + if (arg->role_db.num_roles < 2) + return -EINVAL; + + /* copy special role authentication info from userspace */ + + num_sprole_pws = arg->num_sprole_pws; + acl_special_roles = (struct sprole_pw **) acl_alloc(num_sprole_pws * sizeof(struct sprole_pw *)); + + if (!acl_special_roles) { + err = -ENOMEM; + goto cleanup; + } + + for (i = 0; i < num_sprole_pws; i++) { + sptmp = (struct sprole_pw *) acl_alloc(sizeof(struct sprole_pw)); + if (!sptmp) { + err = -ENOMEM; + goto cleanup; + } + if (copy_from_user(sptmp, arg->sprole_pws + i, + sizeof (struct sprole_pw))) { + err = -EFAULT; + goto cleanup; + } + + len = + strnlen_user(sptmp->rolename, GR_SPROLE_LEN); + + if (!len || len >= GR_SPROLE_LEN) { + err = -EINVAL; + goto cleanup; + } + + if ((tmp = (char *) acl_alloc(len)) == NULL) { + err = -ENOMEM; + goto cleanup; + } + + if (copy_from_user(tmp, sptmp->rolename, len)) { + err = -EFAULT; + goto cleanup; + } + +#ifdef CONFIG_GRKERNSEC_ACL_DEBUG + printk(KERN_ALERT "Copying special role %s\n", tmp); +#endif + sptmp->rolename = tmp; + acl_special_roles[i] = sptmp; + } + + r_utmp = (struct acl_role_label **) arg->role_db.r_table; + + for (r_num = 0; r_num < arg->role_db.num_roles; r_num++) { + r_tmp = acl_alloc(sizeof (struct acl_role_label)); + + if (!r_tmp) { + err = -ENOMEM; + goto cleanup; + } + + if (copy_from_user(&r_utmp2, r_utmp + r_num, + sizeof (struct acl_role_label *))) { + err = -EFAULT; + goto cleanup; + } + + if (copy_from_user(r_tmp, r_utmp2, + sizeof (struct acl_role_label))) { + err = -EFAULT; + goto cleanup; + } + + len = strnlen_user(r_tmp->rolename, GR_SPROLE_LEN); + + if (!len || len >= PATH_MAX) { + err = -EINVAL; + goto cleanup; + } + + if ((tmp = (char *) acl_alloc(len)) == NULL) { + err = -ENOMEM; + goto cleanup; + } + if (copy_from_user(tmp, r_tmp->rolename, len)) { + err = -EFAULT; + goto cleanup; + } + r_tmp->rolename = tmp; + + if (!strcmp(r_tmp->rolename, "default") + && (r_tmp->roletype & GR_ROLE_DEFAULT)) { + default_role = r_tmp; + } else if (!strcmp(r_tmp->rolename, ":::kernel:::")) { + kernel_role = r_tmp; + } + + if ((ghash = (struct gr_hash_struct *) acl_alloc(sizeof(struct gr_hash_struct))) == NULL) { + err = -ENOMEM; + goto cleanup; + } + if (copy_from_user(ghash, r_tmp->hash, sizeof(struct gr_hash_struct))) { + err = -EFAULT; + goto cleanup; + } + + r_tmp->hash = ghash; + + num_subjs = count_user_subjs(r_tmp->hash->first); + + r_tmp->subj_hash_size = num_subjs; + r_tmp->subj_hash = + (struct acl_subject_label **) + create_table(&(r_tmp->subj_hash_size), sizeof(void *)); + + if (!r_tmp->subj_hash) { + err = -ENOMEM; + goto cleanup; + } + + err = copy_user_allowedips(r_tmp); + if (err) + goto cleanup; + + /* copy domain info */ + if (r_tmp->domain_children != NULL) { + domainlist = acl_alloc(r_tmp->domain_child_num * sizeof(uid_t)); + if (domainlist == NULL) { + err = -ENOMEM; + goto cleanup; + } + if (copy_from_user(domainlist, r_tmp->domain_children, r_tmp->domain_child_num * sizeof(uid_t))) { + err = -EFAULT; + goto cleanup; + } + r_tmp->domain_children = domainlist; + } + + err = copy_user_transitions(r_tmp); + if (err) + goto cleanup; + + memset(r_tmp->subj_hash, 0, + r_tmp->subj_hash_size * + sizeof (struct acl_subject_label *)); + + err = copy_user_subjs(r_tmp->hash->first, r_tmp); + + if (err) + goto cleanup; + + /* set nested subject list to null */ + r_tmp->hash->first = NULL; + + insert_acl_role_label(r_tmp); + } + + goto return_err; + cleanup: + free_variables(); + return_err: + return err; + +} + +static int +gracl_init(struct gr_arg *args) +{ + int error = 0; + + memcpy(gr_system_salt, args->salt, GR_SALT_LEN); + memcpy(gr_system_sum, args->sum, GR_SHA_LEN); + + if (init_variables(args)) { + gr_log_str(GR_DONT_AUDIT_GOOD, GR_INITF_ACL_MSG, GR_VERSION); + error = -ENOMEM; + free_variables(); + goto out; + } + + error = copy_user_acl(args); + free_init_variables(); + if (error) { + free_variables(); + goto out; + } + + if ((error = gr_set_acls(0))) { + free_variables(); + goto out; + } + + gr_status |= GR_READY; + out: + return error; +} + +/* derived from glibc fnmatch() 0: match, 1: no match*/ + +static int +glob_match(const char *p, const char *n) +{ + char c; + + while ((c = *p++) != '\0') { + switch (c) { + case '?': + if (*n == '\0') + return 1; + else if (*n == '/') + return 1; + break; + case '\\': + if (*n != c) + return 1; + break; + case '*': + for (c = *p++; c == '?' || c == '*'; c = *p++) { + if (*n == '/') + return 1; + else if (c == '?') { + if (*n == '\0') + return 1; + else + ++n; + } + } + if (c == '\0') { + return 0; + } else { + const char *endp; + + if ((endp = strchr(n, '/')) == NULL) + endp = n + strlen(n); + + if (c == '[') { + for (--p; n < endp; ++n) + if (!glob_match(p, n)) + return 0; + } else if (c == '/') { + while (*n != '\0' && *n != '/') + ++n; + if (*n == '/' && !glob_match(p, n + 1)) + return 0; + } else { + for (--p; n < endp; ++n) + if (*n == c && !glob_match(p, n)) + return 0; + } + + return 1; + } + case '[': + { + int not; + char cold; + + if (*n == '\0' || *n == '/') + return 1; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) { + unsigned char fn = (unsigned char)*n; + + if (c == '\0') + return 1; + else { + if (c == fn) + goto matched; + cold = c; + c = *p++; + + if (c == '-' && *p != ']') { + unsigned char cend = *p++; + + if (cend == '\0') + return 1; + + if (cold <= fn && fn <= cend) + goto matched; + + c = *p++; + } + } + + if (c == ']') + break; + } + if (!not) + return 1; + break; + matched: + while (c != ']') { + if (c == '\0') + return 1; + + c = *p++; + } + if (not) + return 1; + } + break; + default: + if (c != *n) + return 1; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if (*n == '/') + return 0; + + return 1; +} + +static struct acl_object_label * +chk_glob_label(struct acl_object_label *globbed, + struct dentry *dentry, struct vfsmount *mnt, char **path) +{ + struct acl_object_label *tmp; + + if (*path == NULL) + *path = gr_to_filename_nolock(dentry, mnt); + + tmp = globbed; + + while (tmp) { + if (!glob_match(tmp->filename, *path)) + return tmp; + tmp = tmp->next; + } + + return NULL; +} + +static struct acl_object_label * +__full_lookup(const struct dentry *orig_dentry, const struct vfsmount *orig_mnt, + const ino_t curr_ino, const dev_t curr_dev, + const struct acl_subject_label *subj, char **path) +{ + struct acl_subject_label *tmpsubj; + struct acl_object_label *retval; + struct acl_object_label *retval2; + + tmpsubj = (struct acl_subject_label *) subj; + read_lock(&gr_inode_lock); + do { + retval = lookup_acl_obj_label(curr_ino, curr_dev, tmpsubj); + if (retval) { + if (retval->globbed) { + retval2 = chk_glob_label(retval->globbed, (struct dentry *)orig_dentry, + (struct vfsmount *)orig_mnt, path); + if (retval2) + retval = retval2; + } + break; + } + } while ((tmpsubj = tmpsubj->parent_subject)); + read_unlock(&gr_inode_lock); + + return retval; +} + +static __inline__ struct acl_object_label * +full_lookup(const struct dentry *orig_dentry, const struct vfsmount *orig_mnt, + const struct dentry *curr_dentry, + const struct acl_subject_label *subj, char **path) +{ + return __full_lookup(orig_dentry, orig_mnt, + curr_dentry->d_inode->i_ino, + curr_dentry->d_inode->i_sb->s_dev, subj, path); +} + +static struct acl_object_label * +__chk_obj_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt, + const struct acl_subject_label *subj, char *path) +{ + struct dentry *dentry = (struct dentry *) l_dentry; + struct vfsmount *mnt = (struct vfsmount *) l_mnt; + struct acl_object_label *retval; + + spin_lock(&dcache_lock); + + if (unlikely(mnt == shm_mnt || mnt == pipe_mnt || mnt == sock_mnt || + /* ignore Eric Biederman */ + IS_PRIVATE(l_dentry->d_inode))) { + retval = fakefs_obj; + goto out; + } + + for (;;) { + if (dentry == real_root && mnt == real_root_mnt) + break; + + if (dentry == mnt->mnt_root || IS_ROOT(dentry)) { + if (mnt->mnt_parent == mnt) + break; + + retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path); + if (retval != NULL) + goto out; + + dentry = mnt->mnt_mountpoint; + mnt = mnt->mnt_parent; + continue; + } + + retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path); + if (retval != NULL) + goto out; + + dentry = dentry->d_parent; + } + + retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path); + + if (retval == NULL) + retval = full_lookup(l_dentry, l_mnt, real_root, subj, &path); +out: + spin_unlock(&dcache_lock); + return retval; +} + +static __inline__ struct acl_object_label * +chk_obj_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt, + const struct acl_subject_label *subj) +{ + char *path = NULL; + return __chk_obj_label(l_dentry, l_mnt, subj, path); +} + +static __inline__ struct acl_object_label * +chk_obj_create_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt, + const struct acl_subject_label *subj, char *path) +{ + return __chk_obj_label(l_dentry, l_mnt, subj, path); +} + +static struct acl_subject_label * +chk_subj_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt, + const struct acl_role_label *role) +{ + struct dentry *dentry = (struct dentry *) l_dentry; + struct vfsmount *mnt = (struct vfsmount *) l_mnt; + struct acl_subject_label *retval; + + spin_lock(&dcache_lock); + + for (;;) { + if (dentry == real_root && mnt == real_root_mnt) + break; + if (dentry == mnt->mnt_root || IS_ROOT(dentry)) { + if (mnt->mnt_parent == mnt) + break; + + read_lock(&gr_inode_lock); + retval = + lookup_acl_subj_label(dentry->d_inode->i_ino, + dentry->d_inode->i_sb->s_dev, role); + read_unlock(&gr_inode_lock); + if (retval != NULL) + goto out; + + dentry = mnt->mnt_mountpoint; + mnt = mnt->mnt_parent; + continue; + } + + read_lock(&gr_inode_lock); + retval = lookup_acl_subj_label(dentry->d_inode->i_ino, + dentry->d_inode->i_sb->s_dev, role); + read_unlock(&gr_inode_lock); + if (retval != NULL) + goto out; + + dentry = dentry->d_parent; + } + + read_lock(&gr_inode_lock); + retval = lookup_acl_subj_label(dentry->d_inode->i_ino, + dentry->d_inode->i_sb->s_dev, role); + read_unlock(&gr_inode_lock); + + if (unlikely(retval == NULL)) { + read_lock(&gr_inode_lock); + retval = lookup_acl_subj_label(real_root->d_inode->i_ino, + real_root->d_inode->i_sb->s_dev, role); + read_unlock(&gr_inode_lock); + } +out: + spin_unlock(&dcache_lock); + + return retval; +} + +static void +gr_log_learn(const struct task_struct *task, const struct dentry *dentry, const struct vfsmount *mnt, const __u32 mode) +{ + security_learn(GR_LEARN_AUDIT_MSG, task->role->rolename, task->role->roletype, + task->uid, task->gid, task->exec_file ? gr_to_filename1(task->exec_file->f_dentry, + task->exec_file->f_vfsmnt) : task->acl->filename, task->acl->filename, + 1, 1, gr_to_filename(dentry, mnt), (unsigned long) mode, NIPQUAD(task->signal->curr_ip)); + + return; +} + +static void +gr_log_learn_sysctl(const struct task_struct *task, const char *path, const __u32 mode) +{ + security_learn(GR_LEARN_AUDIT_MSG, task->role->rolename, task->role->roletype, + task->uid, task->gid, task->exec_file ? gr_to_filename1(task->exec_file->f_dentry, + task->exec_file->f_vfsmnt) : task->acl->filename, task->acl->filename, + 1, 1, path, (unsigned long) mode, NIPQUAD(task->signal->curr_ip)); + + return; +} + +static void +gr_log_learn_id_change(const struct task_struct *task, const char type, const unsigned int real, + const unsigned int effective, const unsigned int fs) +{ + security_learn(GR_ID_LEARN_MSG, task->role->rolename, task->role->roletype, + task->uid, task->gid, task->exec_file ? gr_to_filename1(task->exec_file->f_dentry, + task->exec_file->f_vfsmnt) : task->acl->filename, task->acl->filename, + type, real, effective, fs, NIPQUAD(task->signal->curr_ip)); + + return; +} + +__u32 +gr_check_link(const struct dentry * new_dentry, + const struct dentry * parent_dentry, + const struct vfsmount * parent_mnt, + const struct dentry * old_dentry, const struct vfsmount * old_mnt) +{ + struct acl_object_label *obj; + __u32 oldmode, newmode; + __u32 needmode; + + if (unlikely(!(gr_status & GR_READY))) + return (GR_CREATE | GR_LINK); + + obj = chk_obj_label(old_dentry, old_mnt, current->acl); + oldmode = obj->mode; + + if (current->acl->mode & (GR_LEARN | GR_INHERITLEARN)) + oldmode |= (GR_CREATE | GR_LINK); + + needmode = GR_CREATE | GR_AUDIT_CREATE | GR_SUPPRESS; + if (old_dentry->d_inode->i_mode & (S_ISUID | S_ISGID)) + needmode |= GR_SETID | GR_AUDIT_SETID; + + newmode = + gr_check_create(new_dentry, parent_dentry, parent_mnt, + oldmode | needmode); + + needmode = newmode & (GR_FIND | GR_APPEND | GR_WRITE | GR_EXEC | + GR_SETID | GR_READ | GR_FIND | GR_DELETE | + GR_INHERIT | GR_AUDIT_INHERIT); + + if (old_dentry->d_inode->i_mode & (S_ISUID | S_ISGID) && !(newmode & GR_SETID)) + goto bad; + + if ((oldmode & needmode) != needmode) + goto bad; + + needmode = oldmode & (GR_NOPTRACE | GR_PTRACERD | GR_INHERIT | GR_AUDITS); + if ((newmode & needmode) != needmode) + goto bad; + + if ((newmode & (GR_CREATE | GR_LINK)) == (GR_CREATE | GR_LINK)) + return newmode; +bad: + needmode = oldmode; + if (old_dentry->d_inode->i_mode & (S_ISUID | S_ISGID)) + needmode |= GR_SETID; + + if (current->acl->mode & (GR_LEARN | GR_INHERITLEARN)) { + gr_log_learn(current, old_dentry, old_mnt, needmode); + return (GR_CREATE | GR_LINK); + } else if (newmode & GR_SUPPRESS) + return GR_SUPPRESS; + else + return 0; +} + +__u32 +gr_search_file(const struct dentry * dentry, const __u32 mode, + const struct vfsmount * mnt) +{ + __u32 retval = mode; + struct acl_subject_label *curracl; + struct acl_object_label *currobj; + + if (unlikely(!(gr_status & GR_READY))) + return (mode & ~GR_AUDITS); + + curracl = current->acl; + + currobj = chk_obj_label(dentry, mnt, curracl); + retval = currobj->mode & mode; + + if (unlikely + ((curracl->mode & (GR_LEARN | GR_INHERITLEARN)) && !(mode & GR_NOPTRACE) + && (retval != (mode & ~(GR_AUDITS | GR_SUPPRESS))))) { + __u32 new_mode = mode; + + new_mode &= ~(GR_AUDITS | GR_SUPPRESS); + + retval = new_mode; + + if (new_mode & GR_EXEC && curracl->mode & GR_INHERITLEARN) + new_mode |= GR_INHERIT; + + if (!(mode & GR_NOLEARN)) + gr_log_learn(current, dentry, mnt, new_mode); + } + + return retval; +} + +__u32 +gr_check_create(const struct dentry * new_dentry, const struct dentry * parent, + const struct vfsmount * mnt, const __u32 mode) +{ + struct name_entry *match; + struct acl_object_label *matchpo; + struct acl_subject_label *curracl; + char *path; + __u32 retval; + + if (unlikely(!(gr_status & GR_READY))) + return (mode & ~GR_AUDITS); + + preempt_disable(); + path = gr_to_filename_rbac(new_dentry, mnt); + match = lookup_name_entry_create(path); + + if (!match) + goto check_parent; + + curracl = current->acl; + + read_lock(&gr_inode_lock); + matchpo = lookup_acl_obj_label_create(match->inode, match->device, curracl); + read_unlock(&gr_inode_lock); + + if (matchpo) { + if ((matchpo->mode & mode) != + (mode & ~(GR_AUDITS | GR_SUPPRESS)) + && curracl->mode & (GR_LEARN | GR_INHERITLEARN)) { + __u32 new_mode = mode; + + new_mode &= ~(GR_AUDITS | GR_SUPPRESS); + + gr_log_learn(current, new_dentry, mnt, new_mode); + + preempt_enable(); + return new_mode; + } + preempt_enable(); + return (matchpo->mode & mode); + } + + check_parent: + curracl = current->acl; + + matchpo = chk_obj_create_label(parent, mnt, curracl, path); + retval = matchpo->mode & mode; + + if ((retval != (mode & ~(GR_AUDITS | GR_SUPPRESS))) + && (curracl->mode & (GR_LEARN | GR_INHERITLEARN))) { + __u32 new_mode = mode; + + new_mode &= ~(GR_AUDITS | GR_SUPPRESS); + + gr_log_learn(current, new_dentry, mnt, new_mode); + preempt_enable(); + return new_mode; + } + + preempt_enable(); + return retval; +} + +int +gr_check_hidden_task(const struct task_struct *task) +{ + if (unlikely(!(gr_status & GR_READY))) + return 0; + + if (!(task->acl->mode & GR_PROCFIND) && !(current->acl->mode & GR_VIEW)) + return 1; + + return 0; +} + +int +gr_check_protected_task(const struct task_struct *task) +{ + if (unlikely(!(gr_status & GR_READY) || !task)) + return 0; + + if ((task->acl->mode & GR_PROTECTED) && !(current->acl->mode & GR_KILL) && + task->acl != current->acl) + return 1; + + return 0; +} + +void +gr_copy_label(struct task_struct *tsk) +{ + tsk->signal->used_accept = 0; + tsk->acl_sp_role = 0; + tsk->acl_role_id = current->acl_role_id; + tsk->acl = current->acl; + tsk->role = current->role; + tsk->signal->curr_ip = current->signal->curr_ip; + if (current->exec_file) + get_file(current->exec_file); + tsk->exec_file = current->exec_file; + tsk->is_writable = current->is_writable; + if (unlikely(current->signal->used_accept)) + current->signal->curr_ip = 0; + + return; +} + +static void +gr_set_proc_res(struct task_struct *task) +{ + struct acl_subject_label *proc; + unsigned short i; + + proc = task->acl; + + if (proc->mode & (GR_LEARN | GR_INHERITLEARN)) + return; + + for (i = 0; i < (GR_NLIMITS - 1); i++) { + if (!(proc->resmask & (1 << i))) + continue; + + task->signal->rlim[i].rlim_cur = proc->res[i].rlim_cur; + task->signal->rlim[i].rlim_max = proc->res[i].rlim_max; + } + + return; +} + +int +gr_check_user_change(int real, int effective, int fs) +{ + unsigned int i; + __u16 num; + uid_t *uidlist; + int curuid; + int realok = 0; + int effectiveok = 0; + int fsok = 0; + + if (unlikely(!(gr_status & GR_READY))) + return 0; + + if (current->acl->mode & (GR_LEARN | GR_INHERITLEARN)) + gr_log_learn_id_change(current, 'u', real, effective, fs); + + num = current->acl->user_trans_num; + uidlist = current->acl->user_transitions; + + if (uidlist == NULL) + return 0; + + if (real == -1) + realok = 1; + if (effective == -1) + effectiveok = 1; + if (fs == -1) + fsok = 1; + + if (current->acl->user_trans_type & GR_ID_ALLOW) { + for (i = 0; i < num; i++) { + curuid = (int)uidlist[i]; + if (real == curuid) + realok = 1; + if (effective == curuid) + effectiveok = 1; + if (fs == curuid) + fsok = 1; + } + } else if (current->acl->user_trans_type & GR_ID_DENY) { + for (i = 0; i < num; i++) { + curuid = (int)uidlist[i]; + if (real == curuid) + break; + if (effective == curuid) + break; + if (fs == curuid) + break; + } + /* not in deny list */ + if (i == num) { + realok = 1; + effectiveok = 1; + fsok = 1; + } + } + + if (realok && effectiveok && fsok) + return 0; + else { + gr_log_int(GR_DONT_AUDIT, GR_USRCHANGE_ACL_MSG, realok ? (effectiveok ? (fsok ? 0 : fs) : effective) : real); + return 1; + } +} + +int +gr_check_group_change(int real, int effective, int fs) +{ + unsigned int i; + __u16 num; + gid_t *gidlist; + int curgid; + int realok = 0; + int effectiveok = 0; + int fsok = 0; + + if (unlikely(!(gr_status & GR_READY))) + return 0; + + if (current->acl->mode & (GR_LEARN | GR_INHERITLEARN)) + gr_log_learn_id_change(current, 'g', real, effective, fs); + + num = current->acl->group_trans_num; + gidlist = current->acl->group_transitions; + + if (gidlist == NULL) + return 0; + + if (real == -1) + realok = 1; + if (effective == -1) + effectiveok = 1; + if (fs == -1) + fsok = 1; + + if (current->acl->group_trans_type & GR_ID_ALLOW) { + for (i = 0; i < num; i++) { + curgid = (int)gidlist[i]; + if (real == curgid) + realok = 1; + if (effective == curgid) + effectiveok = 1; + if (fs == curgid) + fsok = 1; + } + } else if (current->acl->group_trans_type & GR_ID_DENY) { + for (i = 0; i < num; i++) { + curgid = (int)gidlist[i]; + if (real == curgid) + break; + if (effective == curgid) + break; + if (fs == curgid) + break; + } + /* not in deny list */ + if (i == num) { + realok = 1; + effectiveok = 1; + fsok = 1; + } + } + + if (realok && effectiveok && fsok) + return 0; + else { + gr_log_int(GR_DONT_AUDIT, GR_GRPCHANGE_ACL_MSG, realok ? (effectiveok ? (fsok ? 0 : fs) : effective) : real); + return 1; + } +} + +void +gr_set_role_label(struct task_struct *task, const uid_t uid, const uid_t gid) +{ + struct acl_role_label *role = task->role; + struct acl_subject_label *subj = NULL; + struct acl_object_label *obj; + struct file *filp; + + if (unlikely(!(gr_status & GR_READY))) + return; + + filp = task->exec_file; + + /* kernel process, we'll give them the kernel role */ + if (unlikely(!filp)) { + task->role = kernel_role; + task->acl = kernel_role->root_label; + return; + } else if (!task->role || !(task->role->roletype & GR_ROLE_SPECIAL)) + role = lookup_acl_role_label(task, uid, gid); + + /* perform subject lookup in possibly new role + we can use this result below in the case where role == task->role + */ + subj = chk_subj_label(filp->f_dentry, filp->f_vfsmnt, role); + + /* if we changed uid/gid, but result in the same role + and are using inheritance, don't lose the inherited subject + if current subject is other than what normal lookup + would result in, we arrived via inheritance, don't + lose subject + */ + if (role != task->role || (!(task->acl->mode & GR_INHERITLEARN) && + (subj == task->acl))) + task->acl = subj; + + task->role = role; + + task->is_writable = 0; + + /* ignore additional mmap checks for processes that are writable + by the default ACL */ + obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, default_role->root_label); + if (unlikely(obj->mode & GR_WRITE)) + task->is_writable = 1; + obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, task->role->root_label); + if (unlikely(obj->mode & GR_WRITE)) + task->is_writable = 1; + +#ifdef CONFIG_GRKERNSEC_ACL_DEBUG + printk(KERN_ALERT "Set role label for (%s:%d): role:%s, subject:%s\n", task->comm, task->pid, task->role->rolename, task->acl->filename); +#endif + + gr_set_proc_res(task); + + return; +} + +int +gr_set_proc_label(const struct dentry *dentry, const struct vfsmount *mnt) +{ + struct task_struct *task = current; + struct acl_subject_label *newacl; + struct acl_object_label *obj; + __u32 retmode; + + if (unlikely(!(gr_status & GR_READY))) + return 0; + + newacl = chk_subj_label(dentry, mnt, task->role); + + task_lock(task); + if (((task->ptrace & PT_PTRACED) && !(task->acl->mode & + GR_POVERRIDE) && (task->acl != newacl) && + !(task->role->roletype & GR_ROLE_GOD) && + !gr_search_file(dentry, GR_PTRACERD, mnt) && + !(task->acl->mode & (GR_LEARN | GR_INHERITLEARN))) || + (atomic_read(&task->fs->count) > 1 || + atomic_read(&task->files->count) > 1 || + atomic_read(&task->sighand->count) > 1)) { + task_unlock(task); + gr_log_fs_generic(GR_DONT_AUDIT, GR_PTRACE_EXEC_ACL_MSG, dentry, mnt); + return -EACCES; + } + task_unlock(task); + + obj = chk_obj_label(dentry, mnt, task->acl); + retmode = obj->mode & (GR_INHERIT | GR_AUDIT_INHERIT)