Commit 2bed9c56 authored by Anton Blanchard's avatar Anton Blanchard

Merge samba.org:/home/anton/ppc64/linux-2.5

into samba.org:/home/anton/ppc64/for-linus-ppc64_tmp
parents 20f026f0 eb0c5161
VERSION = 2 VERSION = 2
PATCHLEVEL = 5 PATCHLEVEL = 5
SUBLEVEL = 26 SUBLEVEL = 27
EXTRAVERSION = EXTRAVERSION =
# *DOCUMENTATION* # *DOCUMENTATION*
...@@ -157,7 +157,8 @@ objtree := $(TOPDIR) ...@@ -157,7 +157,8 @@ objtree := $(TOPDIR)
export srctree objtree export srctree objtree
SUBDIRS := init kernel mm fs ipc lib drivers sound net SUBDIRS := init kernel mm fs ipc lib drivers sound net security
noconfig_targets := xconfig menuconfig config oldconfig randconfig \ noconfig_targets := xconfig menuconfig config oldconfig randconfig \
defconfig allyesconfig allnoconfig allmodconfig \ defconfig allyesconfig allnoconfig allmodconfig \
...@@ -223,7 +224,7 @@ endif ...@@ -223,7 +224,7 @@ endif
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
INIT := init/init.o INIT := init/init.o
CORE_FILES := kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o CORE_FILES := kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o security/built-in.o
LIBS := lib/lib.a LIBS := lib/lib.a
DRIVERS := drivers/built-in.o sound/sound.o DRIVERS := drivers/built-in.o sound/sound.o
NETWORKS := net/network.o NETWORKS := net/network.o
......
...@@ -38,7 +38,7 @@ static ctl_table ctl_isa_vars[4] = { ...@@ -38,7 +38,7 @@ static ctl_table ctl_isa_vars[4] = {
static struct ctl_table_header *isa_sysctl_header; static struct ctl_table_header *isa_sysctl_header;
static ctl_table ctl_isa[2] = {{BUS_ISA, "isa", NULL, 0, 0555, ctl_isa_vars}, static ctl_table ctl_isa[2] = {{CTL_BUS_ISA, "isa", NULL, 0, 0555, ctl_isa_vars},
{0}}; {0}};
static ctl_table ctl_bus[2] = {{CTL_BUS, "bus", NULL, 0, 0555, ctl_isa}, static ctl_table ctl_bus[2] = {{CTL_BUS, "bus", NULL, 0, 0555, ctl_isa},
{0}}; {0}};
......
...@@ -423,4 +423,5 @@ fi ...@@ -423,4 +423,5 @@ fi
endmenu endmenu
source security/Config.in
source lib/Config.in source lib/Config.in
...@@ -744,7 +744,7 @@ ENTRY(sys_call_table) ...@@ -744,7 +744,7 @@ ENTRY(sys_call_table)
.long sys_getdents64 /* 220 */ .long sys_getdents64 /* 220 */
.long sys_fcntl64 .long sys_fcntl64
.long sys_ni_syscall /* reserved for TUX */ .long sys_ni_syscall /* reserved for TUX */
.long sys_ni_syscall /* reserved for Security */ .long sys_security /* reserved for Security */
.long sys_gettid .long sys_gettid
.long sys_readahead /* 225 */ .long sys_readahead /* 225 */
.long sys_setxattr .long sys_setxattr
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/user.h> #include <linux/user.h>
#include <linux/security.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -159,6 +160,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ...@@ -159,6 +160,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
/* are we already being traced? */ /* are we already being traced? */
if (current->ptrace & PT_PTRACED) if (current->ptrace & PT_PTRACED)
goto out; goto out;
ret = security_ops->ptrace(current->parent, current);
if (ret)
goto out;
/* set the ptrace bit in the process flags. */ /* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED; current->ptrace |= PT_PTRACED;
ret = 0; ret = 0;
......
...@@ -2078,7 +2078,7 @@ int generic_cont_expand(struct inode *inode, loff_t size) ...@@ -2078,7 +2078,7 @@ int generic_cont_expand(struct inode *inode, loff_t size)
*/ */
int cont_prepare_write(struct page *page, unsigned offset, int cont_prepare_write(struct page *page, unsigned offset,
unsigned to, get_block_t *get_block, unsigned long *bytes) unsigned to, get_block_t *get_block, loff_t *bytes)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
......
...@@ -623,6 +623,7 @@ int prepare_binprm(struct linux_binprm *bprm) ...@@ -623,6 +623,7 @@ int prepare_binprm(struct linux_binprm *bprm)
{ {
int mode; int mode;
struct inode * inode = bprm->file->f_dentry->d_inode; struct inode * inode = bprm->file->f_dentry->d_inode;
int retval;
mode = inode->i_mode; mode = inode->i_mode;
/* /*
...@@ -652,27 +653,10 @@ int prepare_binprm(struct linux_binprm *bprm) ...@@ -652,27 +653,10 @@ int prepare_binprm(struct linux_binprm *bprm)
bprm->e_gid = inode->i_gid; bprm->e_gid = inode->i_gid;
} }
/* We don't have VFS support for capabilities yet */ /* fill in binprm security blob */
cap_clear(bprm->cap_inheritable); retval = security_ops->bprm_set_security(bprm);
cap_clear(bprm->cap_permitted); if (retval)
cap_clear(bprm->cap_effective); return retval;
/* To support inheritance of root-permissions and suid-root
* executables under compatibility mode, we raise all three
* capability sets for the file.
*
* If only the real uid is 0, we only raise the inheritable
* and permitted sets of the executable file.
*/
if (!issecure(SECURE_NOROOT)) {
if (bprm->e_uid == 0 || current->uid == 0) {
cap_set_full(bprm->cap_inheritable);
cap_set_full(bprm->cap_permitted);
}
if (bprm->e_uid == 0)
cap_set_full(bprm->cap_effective);
}
memset(bprm->buf,0,BINPRM_BUF_SIZE); memset(bprm->buf,0,BINPRM_BUF_SIZE);
return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE); return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
...@@ -695,16 +679,9 @@ int prepare_binprm(struct linux_binprm *bprm) ...@@ -695,16 +679,9 @@ int prepare_binprm(struct linux_binprm *bprm)
void compute_creds(struct linux_binprm *bprm) void compute_creds(struct linux_binprm *bprm)
{ {
kernel_cap_t new_permitted, working;
int do_unlock = 0; int do_unlock = 0;
new_permitted = cap_intersect(bprm->cap_permitted, cap_bset); if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
working = cap_intersect(bprm->cap_inheritable,
current->cap_inheritable);
new_permitted = cap_combine(new_permitted, working);
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
!cap_issubset(new_permitted, current->cap_permitted)) {
current->mm->dumpable = 0; current->mm->dumpable = 0;
lock_kernel(); lock_kernel();
...@@ -716,32 +693,17 @@ void compute_creds(struct linux_binprm *bprm) ...@@ -716,32 +693,17 @@ void compute_creds(struct linux_binprm *bprm)
bprm->e_uid = current->uid; bprm->e_uid = current->uid;
bprm->e_gid = current->gid; bprm->e_gid = current->gid;
} }
if(!capable(CAP_SETPCAP)) {
new_permitted = cap_intersect(new_permitted,
current->cap_permitted);
}
} }
do_unlock = 1; do_unlock = 1;
} }
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
if (current->pid != 1) {
current->cap_permitted = new_permitted;
current->cap_effective =
cap_intersect(new_permitted, bprm->cap_effective);
}
/* AUD: Audit candidate if current->cap_effective is set */
current->suid = current->euid = current->fsuid = bprm->e_uid; current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid; current->sgid = current->egid = current->fsgid = bprm->e_gid;
if(do_unlock) if(do_unlock)
unlock_kernel(); unlock_kernel();
current->keep_capabilities = 0;
security_ops->bprm_compute_creds(bprm);
} }
...@@ -811,6 +773,10 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) ...@@ -811,6 +773,10 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
} }
} }
#endif #endif
retval = security_ops->bprm_check_security(bprm);
if (retval)
return retval;
/* kernel module loader fixup */ /* kernel module loader fixup */
/* so we don't try to load run modprobe in kernel space. */ /* so we don't try to load run modprobe in kernel space. */
set_fs(USER_DS); set_fs(USER_DS);
...@@ -887,7 +853,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs ...@@ -887,7 +853,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
bprm.sh_bang = 0; bprm.sh_bang = 0;
bprm.loader = 0; bprm.loader = 0;
bprm.exec = 0; bprm.exec = 0;
bprm.security = NULL;
bprm.mm = mm_alloc(); bprm.mm = mm_alloc();
retval = -ENOMEM; retval = -ENOMEM;
if (!bprm.mm) if (!bprm.mm)
...@@ -905,6 +871,10 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs ...@@ -905,6 +871,10 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
if ((retval = bprm.envc) < 0) if ((retval = bprm.envc) < 0)
goto out_mm; goto out_mm;
retval = security_ops->bprm_alloc_security(&bprm);
if (retval)
goto out;
retval = prepare_binprm(&bprm); retval = prepare_binprm(&bprm);
if (retval < 0) if (retval < 0)
goto out; goto out;
...@@ -923,9 +893,11 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs ...@@ -923,9 +893,11 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
goto out; goto out;
retval = search_binary_handler(&bprm,regs); retval = search_binary_handler(&bprm,regs);
if (retval >= 0) if (retval >= 0) {
/* execve success */ /* execve success */
security_ops->bprm_free_security(&bprm);
return retval; return retval;
}
out: out:
/* Something went wrong, return the inode and free the argument pages*/ /* Something went wrong, return the inode and free the argument pages*/
...@@ -935,6 +907,9 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs ...@@ -935,6 +907,9 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
__free_page(page); __free_page(page);
} }
if (bprm.security)
security_ops->bprm_free_security(&bprm);
out_mm: out_mm:
mmdrop(bprm.mm); mmdrop(bprm.mm);
......
...@@ -54,7 +54,7 @@ int fat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_r ...@@ -54,7 +54,7 @@ int fat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_r
} }
if (!create) if (!create)
return 0; return 0;
if (iblock << sb->s_blocksize_bits != MSDOS_I(inode)->mmu_private) { if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
BUG(); BUG();
return -EIO; return -EIO;
} }
......
...@@ -417,7 +417,7 @@ static int fat_read_root(struct inode *inode) ...@@ -417,7 +417,7 @@ static int fat_read_root(struct inode *inode)
} }
inode->i_blksize = 1 << sbi->cluster_bits; inode->i_blksize = 1 << sbi->cluster_bits;
inode->i_blocks = ((inode->i_size + inode->i_blksize - 1) inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
& ~(inode->i_blksize - 1)) >> 9; & ~((loff_t)inode->i_blksize - 1)) >> 9;
MSDOS_I(inode)->i_logstart = 0; MSDOS_I(inode)->i_logstart = 0;
MSDOS_I(inode)->mmu_private = inode->i_size; MSDOS_I(inode)->mmu_private = inode->i_size;
...@@ -775,6 +775,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, ...@@ -775,6 +775,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
sbi->fat_length = CF_LE_L(b->fat32_length); sbi->fat_length = CF_LE_L(b->fat32_length);
sbi->root_cluster = CF_LE_L(b->root_cluster); sbi->root_cluster = CF_LE_L(b->root_cluster);
sb->s_maxbytes = 0xffffffff;
/* MC - if info_sector is 0, don't multiply by 0 */ /* MC - if info_sector is 0, don't multiply by 0 */
sbi->fsinfo_sector = CF_LE_W(b->info_sector); sbi->fsinfo_sector = CF_LE_W(b->info_sector);
if (sbi->fsinfo_sector == 0) if (sbi->fsinfo_sector == 0)
...@@ -1063,7 +1065,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) ...@@ -1063,7 +1065,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
/* this is as close to the truth as we can get ... */ /* this is as close to the truth as we can get ... */
inode->i_blksize = 1 << sbi->cluster_bits; inode->i_blksize = 1 << sbi->cluster_bits;
inode->i_blocks = ((inode->i_size + inode->i_blksize - 1) inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
& ~(inode->i_blksize - 1)) >> 9; & ~((loff_t)inode->i_blksize - 1)) >> 9;
inode->i_mtime = inode->i_atime = inode->i_mtime = inode->i_atime =
date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date)); date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
inode->i_ctime = inode->i_ctime =
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* adfs file system inode data in memory * adfs file system inode data in memory
*/ */
struct adfs_inode_info { struct adfs_inode_info {
unsigned long mmu_private; loff_t mmu_private;
unsigned long parent_id; /* object id of parent */ unsigned long parent_id; /* object id of parent */
__u32 loadaddr; /* RISC OS load address */ __u32 loadaddr; /* RISC OS load address */
__u32 execaddr; /* RISC OS exec address */ __u32 execaddr; /* RISC OS exec address */
......
...@@ -35,7 +35,7 @@ struct affs_inode_info { ...@@ -35,7 +35,7 @@ struct affs_inode_info {
struct affs_ext_key *i_ac; /* associative cache of extended blocks */ struct affs_ext_key *i_ac; /* associative cache of extended blocks */
u32 i_ext_last; /* last accessed extended block */ u32 i_ext_last; /* last accessed extended block */
struct buffer_head *i_ext_bh; /* bh of last extended block */ struct buffer_head *i_ext_bh; /* bh of last extended block */
unsigned long mmu_private; loff_t mmu_private;
u32 i_protect; /* unused attribute bits */ u32 i_protect; /* unused attribute bits */
u32 i_lastalloc; /* last allocated block */ u32 i_lastalloc; /* last allocated block */
int i_pa_cnt; /* number of preallocated blocks */ int i_pa_cnt; /* number of preallocated blocks */
......
...@@ -28,6 +28,7 @@ struct linux_binprm{ ...@@ -28,6 +28,7 @@ struct linux_binprm{
struct file * file; struct file * file;
int e_uid, e_gid; int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective; kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
void *security;
int argc, envc; int argc, envc;
char * filename; /* Name of binary */ char * filename; /* Name of binary */
unsigned long loader, exec; unsigned long loader, exec;
......
...@@ -178,7 +178,7 @@ int block_write_full_page(struct page*, get_block_t*); ...@@ -178,7 +178,7 @@ int block_write_full_page(struct page*, get_block_t*);
int block_read_full_page(struct page*, get_block_t*); int block_read_full_page(struct page*, get_block_t*);
int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*); int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*, int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
unsigned long *); loff_t *);
int generic_cont_expand(struct inode *inode, loff_t size) ; int generic_cont_expand(struct inode *inode, loff_t size) ;
int block_commit_write(struct page *page, unsigned from, unsigned to); int block_commit_write(struct page *page, unsigned from, unsigned to);
int block_sync_page(struct page *); int block_sync_page(struct page *);
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
struct hfs_inode_info { struct hfs_inode_info {
int magic; /* A magic number */ int magic; /* A magic number */
unsigned long mmu_private; loff_t mmu_private;
struct hfs_cat_entry *entry; struct hfs_cat_entry *entry;
/* For a regular or header file */ /* For a regular or header file */
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define _HPFS_FS_I #define _HPFS_FS_I
struct hpfs_inode_info { struct hpfs_inode_info {
unsigned long mmu_private; loff_t mmu_private;
ino_t i_parent_dir; /* (directories) gives fnode of parent dir */ ino_t i_parent_dir; /* (directories) gives fnode of parent dir */
unsigned i_dno; /* (directories) root dnode */ unsigned i_dno; /* (directories) root dnode */
unsigned i_dpos; /* (directories) temp for readdir */ unsigned i_dpos; /* (directories) temp for readdir */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
*/ */
struct msdos_inode_info { struct msdos_inode_info {
unsigned long mmu_private; loff_t mmu_private;
int i_start; /* first cluster or 0 */ int i_start; /* first cluster or 0 */
int i_logstart; /* logical first cluster */ int i_logstart; /* logical first cluster */
int i_attrs; /* unused attribute bits */ int i_attrs; /* unused attribute bits */
......
...@@ -106,7 +106,7 @@ struct qnx4_sb_info { ...@@ -106,7 +106,7 @@ struct qnx4_sb_info {
struct qnx4_inode_info { struct qnx4_inode_info {
struct qnx4_inode_entry raw; struct qnx4_inode_entry raw;
unsigned long mmu_private; loff_t mmu_private;
struct inode vfs_inode; struct inode vfs_inode;
}; };
......
...@@ -354,6 +354,8 @@ struct task_struct { ...@@ -354,6 +354,8 @@ struct task_struct {
void *notifier_data; void *notifier_data;
sigset_t *notifier_mask; sigset_t *notifier_mask;
void *security;
/* Thread group tracking */ /* Thread group tracking */
u32 parent_exec_id; u32 parent_exec_id;
u32 self_exec_id; u32 self_exec_id;
...@@ -587,10 +589,9 @@ extern int request_irq(unsigned int, ...@@ -587,10 +589,9 @@ extern int request_irq(unsigned int,
unsigned long, const char *, void *); unsigned long, const char *, void *);
extern void free_irq(unsigned int, void *); extern void free_irq(unsigned int, void *);
/* /* capable prototype and code moved to security.[hc] */
* capable() checks for a particular capability. #include <linux/security.h>
* See include/linux/capability.h for defined capabilities. #if 0
*/
static inline int capable(int cap) static inline int capable(int cap)
{ {
if (cap_raised(current->cap_effective, cap)) { if (cap_raised(current->cap_effective, cap)) {
...@@ -599,6 +600,7 @@ static inline int capable(int cap) ...@@ -599,6 +600,7 @@ static inline int capable(int cap)
} }
return 0; return 0;
} }
#endif /* if 0 */
/* /*
* Routines for handling mm_structs * Routines for handling mm_structs
......
This diff is collapsed.
...@@ -72,7 +72,7 @@ enum ...@@ -72,7 +72,7 @@ enum
/* CTL_BUS names: */ /* CTL_BUS names: */
enum enum
{ {
BUS_ISA=1 /* ISA */ CTL_BUS_ISA=1 /* ISA */
}; };
/* CTL_KERN names: */ /* CTL_KERN names: */
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/security.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/bugs.h> #include <asm/bugs.h>
...@@ -390,6 +391,7 @@ asmlinkage void __init start_kernel(void) ...@@ -390,6 +391,7 @@ asmlinkage void __init start_kernel(void)
fork_init(mempages); fork_init(mempages);
proc_caches_init(); proc_caches_init();
security_scaffolding_startup();
buffer_init(); buffer_init();
vfs_caches_init(mempages); vfs_caches_init(mempages);
radix_tree_init(); radix_tree_init();
......
...@@ -63,6 +63,7 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) ...@@ -63,6 +63,7 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
data.permitted = cap_t(target->cap_permitted); data.permitted = cap_t(target->cap_permitted);
data.inheritable = cap_t(target->cap_inheritable); data.inheritable = cap_t(target->cap_inheritable);
data.effective = cap_t(target->cap_effective); data.effective = cap_t(target->cap_effective);
ret = security_ops->capget(target, &data.effective, &data.inheritable, &data.permitted);
out: out:
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
...@@ -87,9 +88,7 @@ static inline void cap_set_pg(int pgrp, kernel_cap_t *effective, ...@@ -87,9 +88,7 @@ static inline void cap_set_pg(int pgrp, kernel_cap_t *effective,
for_each_task(target) { for_each_task(target) {
if (target->pgrp != pgrp) if (target->pgrp != pgrp)
continue; continue;
target->cap_effective = *effective; security_ops->capset_set(target, effective, inheritable, permitted);
target->cap_inheritable = *inheritable;
target->cap_permitted = *permitted;
} }
} }
...@@ -106,9 +105,7 @@ static inline void cap_set_all(kernel_cap_t *effective, ...@@ -106,9 +105,7 @@ static inline void cap_set_all(kernel_cap_t *effective,
for_each_task(target) { for_each_task(target) {
if (target == current || target->pid == 1) if (target == current || target->pid == 1)
continue; continue;
target->cap_effective = *effective; security_ops->capset_set(target, effective, inheritable, permitted);
target->cap_inheritable = *inheritable;
target->cap_permitted = *permitted;
} }
} }
...@@ -166,7 +163,9 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) ...@@ -166,7 +163,9 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
ret = -EPERM; ret = -EPERM;
/* verify restrictions on target's new Inheritable set */ if (security_ops->capset_check(target, &effective, &inheritable, &permitted))
goto out;
if (!cap_issubset(inheritable, cap_combine(target->cap_inheritable, if (!cap_issubset(inheritable, cap_combine(target->cap_inheritable,
current->cap_permitted))) current->cap_permitted)))
goto out; goto out;
...@@ -182,6 +181,8 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) ...@@ -182,6 +181,8 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
ret = 0; ret = 0;
/* having verified that the proposed changes are legal,
we now put them into effect. */
if (pid < 0) { if (pid < 0) {
if (pid == -1) /* all procs other than current and init */ if (pid == -1) /* all procs other than current and init */
cap_set_all(&effective, &inheritable, &permitted); cap_set_all(&effective, &inheritable, &permitted);
...@@ -189,9 +190,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) ...@@ -189,9 +190,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
else /* all procs in process group */ else /* all procs in process group */
cap_set_pg(-pid, &effective, &inheritable, &permitted); cap_set_pg(-pid, &effective, &inheritable, &permitted);
} else { } else {
target->cap_effective = effective; security_ops->capset_set(target, &effective, &inheritable, &permitted);
target->cap_inheritable = inheritable;
target->cap_permitted = permitted;
} }
out: out:
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/namespace.h> #include <linux/namespace.h>
#include <linux/security.h>
#include <linux/acct.h> #include <linux/acct.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/binfmts.h> #include <linux/binfmts.h>
...@@ -61,6 +62,7 @@ static void release_task(struct task_struct * p) ...@@ -61,6 +62,7 @@ static void release_task(struct task_struct * p)
wait_task_inactive(p); wait_task_inactive(p);
#endif #endif
atomic_dec(&p->user->processes); atomic_dec(&p->user->processes);
security_ops->task_free_security(p);
free_uid(p->user); free_uid(p->user);
unhash_process(p); unhash_process(p);
...@@ -187,10 +189,7 @@ void reparent_to_init(void) ...@@ -187,10 +189,7 @@ void reparent_to_init(void)
/* cpus_allowed? */ /* cpus_allowed? */
/* rt_priority? */ /* rt_priority? */
/* signals? */ /* signals? */
current->cap_effective = CAP_INIT_EFF_SET; security_ops->task_reparent_to_init(current);
current->cap_inheritable = CAP_INIT_INH_SET;
current->cap_permitted = CAP_FULL_SET;
current->keep_capabilities = 0;
memcpy(current->rlim, init_task.rlim, sizeof(*(current->rlim))); memcpy(current->rlim, init_task.rlim, sizeof(*(current->rlim)));
current->user = INIT_USER; current->user = INIT_USER;
...@@ -625,6 +624,10 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -625,6 +624,10 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0)) if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
&& !(options & __WALL)) && !(options & __WALL))
continue; continue;
if (security_ops->task_wait(p))
continue;
flag = 1; flag = 1;
switch (p->state) { switch (p->state) {
case TASK_STOPPED: case TASK_STOPPED:
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mm.h> #include <linux/security.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -618,6 +618,10 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -618,6 +618,10 @@ struct task_struct *do_fork(unsigned long clone_flags,
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
retval = security_ops->task_create(clone_flags);
if (retval)
goto fork_out;
retval = -ENOMEM; retval = -ENOMEM;
p = dup_task_struct(current); p = dup_task_struct(current);
if (!p) if (!p)
...@@ -697,13 +701,16 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -697,13 +701,16 @@ struct task_struct *do_fork(unsigned long clone_flags,
p->array = NULL; p->array = NULL;
p->lock_depth = -1; /* -1 = no lock */ p->lock_depth = -1; /* -1 = no lock */
p->start_time = jiffies; p->start_time = jiffies;
p->security = NULL;
INIT_LIST_HEAD(&p->local_pages); INIT_LIST_HEAD(&p->local_pages);
retval = -ENOMEM; retval = -ENOMEM;
if (security_ops->task_alloc_security(p))
goto bad_fork_cleanup;
/* copy all the process information */ /* copy all the process information */
if (copy_semundo(clone_flags, p)) if (copy_semundo(clone_flags, p))
goto bad_fork_cleanup; goto bad_fork_cleanup_security;
if (copy_files(clone_flags, p)) if (copy_files(clone_flags, p))
goto bad_fork_cleanup_semundo; goto bad_fork_cleanup_semundo;
if (copy_fs(clone_flags, p)) if (copy_fs(clone_flags, p))
...@@ -812,6 +819,8 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -812,6 +819,8 @@ struct task_struct *do_fork(unsigned long clone_flags,
exit_files(p); /* blocking */ exit_files(p); /* blocking */
bad_fork_cleanup_semundo: bad_fork_cleanup_semundo:
exit_semundo(p); exit_semundo(p);
bad_fork_cleanup_security:
security_ops->task_free_security(p);
bad_fork_cleanup: bad_fork_cleanup:
put_exec_domain(p->thread_info->exec_domain); put_exec_domain(p->thread_info->exec_domain);
if (p->binfmt && p->binfmt->module) if (p->binfmt && p->binfmt->module)
......
...@@ -134,7 +134,7 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) ...@@ -134,7 +134,7 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
/* Give kmod all effective privileges.. */ /* Give kmod all effective privileges.. */
curtask->euid = curtask->fsuid = 0; curtask->euid = curtask->fsuid = 0;
curtask->egid = curtask->fsgid = 0; curtask->egid = curtask->fsgid = 0;
cap_set_full(curtask->cap_effective); security_ops->task_kmod_set_label();
/* Allow execve args to be in kernel space. */ /* Allow execve args to be in kernel space. */
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
......
...@@ -41,7 +41,9 @@ int ptrace_check_attach(struct task_struct *child, int kill) ...@@ -41,7 +41,9 @@ int ptrace_check_attach(struct task_struct *child, int kill)
int ptrace_attach(struct task_struct *task) int ptrace_attach(struct task_struct *task)
{ {
int retval;
task_lock(task); task_lock(task);
retval = -EPERM;
if (task->pid <= 1) if (task->pid <= 1)
goto bad; goto bad;
if (task == current) if (task == current)
...@@ -53,7 +55,6 @@ int ptrace_attach(struct task_struct *task) ...@@ -53,7 +55,6 @@ int ptrace_attach(struct task_struct *task)
(current->uid != task->uid) || (current->uid != task->uid) ||
(current->gid != task->egid) || (current->gid != task->egid) ||
(current->gid != task->sgid) || (current->gid != task->sgid) ||
(!cap_issubset(task->cap_permitted, current->cap_permitted)) ||
(current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
goto bad; goto bad;
rmb(); rmb();
...@@ -62,6 +63,9 @@ int ptrace_attach(struct task_struct *task) ...@@ -62,6 +63,9 @@ int ptrace_attach(struct task_struct *task)
/* the same process cannot be attached many times */ /* the same process cannot be attached many times */
if (task->ptrace & PT_PTRACED) if (task->ptrace & PT_PTRACED)
goto bad; goto bad;
retval = security_ops->ptrace(current, task);
if (retval)
goto bad;
/* Go */ /* Go */
task->ptrace |= PT_PTRACED; task->ptrace |= PT_PTRACED;
...@@ -82,7 +86,7 @@ int ptrace_attach(struct task_struct *task) ...@@ -82,7 +86,7 @@ int ptrace_attach(struct task_struct *task)
bad: bad:
task_unlock(task); task_unlock(task);
return -EPERM; return retval;
} }
int ptrace_detach(struct task_struct *child, unsigned int data) int ptrace_detach(struct task_struct *child, unsigned int data)
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/security.h>
/* /*
* Convert user-nice values [ -20 ... 0 ... 19 ] * Convert user-nice values [ -20 ... 0 ... 19 ]
...@@ -1123,6 +1124,7 @@ void set_user_nice(task_t *p, long nice) ...@@ -1123,6 +1124,7 @@ void set_user_nice(task_t *p, long nice)
asmlinkage long sys_nice(int increment) asmlinkage long sys_nice(int increment)
{ {
int retval;
long nice; long nice;
/* /*
...@@ -1144,6 +1146,11 @@ asmlinkage long sys_nice(int increment) ...@@ -1144,6 +1146,11 @@ asmlinkage long sys_nice(int increment)
nice = -20; nice = -20;
if (nice > 19) if (nice > 19)
nice = 19; nice = 19;
retval = security_ops->task_setnice(current, nice);
if (retval)
return retval;
set_user_nice(current, nice); set_user_nice(current, nice);
return 0; return 0;
} }
...@@ -1236,6 +1243,10 @@ static int setscheduler(pid_t pid, int policy, struct sched_param *param) ...@@ -1236,6 +1243,10 @@ static int setscheduler(pid_t pid, int policy, struct sched_param *param)
!capable(CAP_SYS_NICE)) !capable(CAP_SYS_NICE))
goto out_unlock; goto out_unlock;
retval = security_ops->task_setscheduler(p, policy, &lp);
if (retval)
goto out_unlock;
array = p->array; array = p->array;
if (array) if (array)
deactivate_task(p, task_rq(p)); deactivate_task(p, task_rq(p));
...@@ -1280,8 +1291,11 @@ asmlinkage long sys_sched_getscheduler(pid_t pid) ...@@ -1280,8 +1291,11 @@ asmlinkage long sys_sched_getscheduler(pid_t pid)
retval = -ESRCH; retval = -ESRCH;
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
p = find_process_by_pid(pid); p = find_process_by_pid(pid);
if (p) if (p) {
retval = p->policy; retval = security_ops->task_getscheduler(p);
if (!retval)
retval = p->policy;
}
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
out_nounlock: out_nounlock:
...@@ -1302,6 +1316,11 @@ asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param) ...@@ -1302,6 +1316,11 @@ asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param)
retval = -ESRCH; retval = -ESRCH;
if (!p) if (!p)
goto out_unlock; goto out_unlock;
retval = security_ops->task_getscheduler(p);
if (retval)
goto out_unlock;
lp.sched_priority = p->rt_priority; lp.sched_priority = p->rt_priority;
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
...@@ -1509,14 +1528,22 @@ asmlinkage long sys_sched_rr_get_interval(pid_t pid, struct timespec *interval) ...@@ -1509,14 +1528,22 @@ asmlinkage long sys_sched_rr_get_interval(pid_t pid, struct timespec *interval)
retval = -ESRCH; retval = -ESRCH;
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
p = find_process_by_pid(pid); p = find_process_by_pid(pid);
if (p) if (!p)
jiffies_to_timespec(p->policy & SCHED_FIFO ? goto out_unlock;
0 : TASK_TIMESLICE(p), &t);
retval = security_ops->task_getscheduler(p);
if (retval)
goto out_unlock;
jiffies_to_timespec(p->policy & SCHED_FIFO ?
0 : TASK_TIMESLICE(p), &t);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
if (p) retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0;
retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0;
out_nounlock: out_nounlock:
return retval; return retval;
out_unlock:
read_unlock(&tasklist_lock);
return retval;
} }
static void show_task(task_t * p) static void show_task(task_t * p)
......
...@@ -548,6 +548,9 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig); ...@@ -548,6 +548,9 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
ret = -EPERM; ret = -EPERM;
if (bad_signal(sig, info, t)) if (bad_signal(sig, info, t))
goto out_nolock; goto out_nolock;
ret = security_ops->task_kill(t, info, sig);
if (ret)
goto out_nolock;
/* The null signal is a permissions and process existence probe. /* The null signal is a permissions and process existence probe.
No signal is actually delivered. Same goes for zombies. */ No signal is actually delivered. Same goes for zombies. */
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/tqueue.h> #include <linux/tqueue.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/times.h> #include <linux/times.h>
#include <linux/security.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -234,6 +235,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) ...@@ -234,6 +235,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
for_each_task(p) { for_each_task(p) {
int no_nice;
if (!proc_sel(p, which, who)) if (!proc_sel(p, which, who))
continue; continue;
if (p->uid != current->euid && if (p->uid != current->euid &&
...@@ -243,10 +245,17 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) ...@@ -243,10 +245,17 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
} }
if (error == -ESRCH) if (error == -ESRCH)
error = 0; error = 0;
if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) {
error = -EACCES; error = -EACCES;
else continue;
set_user_nice(p, niceval); }
no_nice = security_ops->task_setnice(p, niceval);
if (no_nice) {
error = no_nice;
continue;
}
set_user_nice(p, niceval);
} }
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
...@@ -416,6 +425,11 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) ...@@ -416,6 +425,11 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
int old_egid = current->egid; int old_egid = current->egid;
int new_rgid = old_rgid; int new_rgid = old_rgid;
int new_egid = old_egid; int new_egid = old_egid;
int retval;
retval = security_ops->task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
if (retval)
return retval;
if (rgid != (gid_t) -1) { if (rgid != (gid_t) -1) {
if ((old_rgid == rgid) || if ((old_rgid == rgid) ||
...@@ -457,6 +471,11 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) ...@@ -457,6 +471,11 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
asmlinkage long sys_setgid(gid_t gid) asmlinkage long sys_setgid(gid_t gid)
{ {
int old_egid = current->egid; int old_egid = current->egid;
int retval;
retval = security_ops->task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
if (retval)
return retval;
if (capable(CAP_SETGID)) if (capable(CAP_SETGID))
{ {
...@@ -481,52 +500,6 @@ asmlinkage long sys_setgid(gid_t gid) ...@@ -481,52 +500,6 @@ asmlinkage long sys_setgid(gid_t gid)
return 0; return 0;
} }
/*
* cap_emulate_setxuid() fixes the effective / permitted capabilities of
* a process after a call to setuid, setreuid, or setresuid.
*
* 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of
* {r,e,s}uid != 0, the permitted and effective capabilities are
* cleared.
*
* 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective
* capabilities of the process are cleared.
*
* 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
* capabilities are set to the permitted capabilities.
*
* fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
* never happen.
*
* -astor
*
* cevans - New behaviour, Oct '99
* A process may, via prctl(), elect to keep its capabilities when it
* calls setuid() and switches away from uid==0. Both permitted and
* effective sets will be retained.
* Without this change, it was impossible for a daemon to drop only some
* of its privilege. The call to setuid(!=0) would drop all privileges!
* Keeping uid 0 is not an option because uid 0 owns too many vital
* files..
* Thanks to Olaf Kirch and Peter Benie for spotting this.
*/
static inline void cap_emulate_setxuid(int old_ruid, int old_euid,
int old_suid)
{
if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
(current->uid != 0 && current->euid != 0 && current->suid != 0) &&
!current->keep_capabilities) {
cap_clear(current->cap_permitted);
cap_clear(current->cap_effective);
}
if (old_euid == 0 && current->euid != 0) {
cap_clear(current->cap_effective);
}
if (old_euid != 0 && current->euid == 0) {
current->cap_effective = current->cap_permitted;
}
}
static int set_user(uid_t new_ruid, int dumpclear) static int set_user(uid_t new_ruid, int dumpclear)
{ {
struct user_struct *new_user, *old_user; struct user_struct *new_user, *old_user;
...@@ -572,6 +545,11 @@ static int set_user(uid_t new_ruid, int dumpclear) ...@@ -572,6 +545,11 @@ static int set_user(uid_t new_ruid, int dumpclear)
asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
{ {
int old_ruid, old_euid, old_suid, new_ruid, new_euid; int old_ruid, old_euid, old_suid, new_ruid, new_euid;
int retval;
retval = security_ops->task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
if (retval)
return retval;
new_ruid = old_ruid = current->uid; new_ruid = old_ruid = current->uid;
new_euid = old_euid = current->euid; new_euid = old_euid = current->euid;
...@@ -608,11 +586,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) ...@@ -608,11 +586,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
current->suid = current->euid; current->suid = current->euid;
current->fsuid = current->euid; current->fsuid = current->euid;
if (!issecure(SECURE_NO_SETUID_FIXUP)) { return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
cap_emulate_setxuid(old_ruid, old_euid, old_suid);
}
return 0;
} }
...@@ -632,6 +606,11 @@ asmlinkage long sys_setuid(uid_t uid) ...@@ -632,6 +606,11 @@ asmlinkage long sys_setuid(uid_t uid)
{ {
int old_euid = current->euid; int old_euid = current->euid;
int old_ruid, old_suid, new_ruid, new_suid; int old_ruid, old_suid, new_ruid, new_suid;
int retval;
retval = security_ops->task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
if (retval)
return retval;
old_ruid = new_ruid = current->uid; old_ruid = new_ruid = current->uid;
old_suid = current->suid; old_suid = current->suid;
...@@ -652,11 +631,7 @@ asmlinkage long sys_setuid(uid_t uid) ...@@ -652,11 +631,7 @@ asmlinkage long sys_setuid(uid_t uid)
current->fsuid = current->euid = uid; current->fsuid = current->euid = uid;
current->suid = new_suid; current->suid = new_suid;
if (!issecure(SECURE_NO_SETUID_FIXUP)) { return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
cap_emulate_setxuid(old_ruid, old_euid, old_suid);
}
return 0;
} }
...@@ -669,6 +644,11 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) ...@@ -669,6 +644,11 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
int old_ruid = current->uid; int old_ruid = current->uid;
int old_euid = current->euid; int old_euid = current->euid;
int old_suid = current->suid; int old_suid = current->suid;
int retval;
retval = security_ops->task_setuid(ruid, euid, suid, LSM_SETID_RES);
if (retval)
return retval;
if (!capable(CAP_SETUID)) { if (!capable(CAP_SETUID)) {
if ((ruid != (uid_t) -1) && (ruid != current->uid) && if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
...@@ -697,11 +677,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) ...@@ -697,11 +677,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
if (suid != (uid_t) -1) if (suid != (uid_t) -1)
current->suid = suid; current->suid = suid;
if (!issecure(SECURE_NO_SETUID_FIXUP)) { return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
cap_emulate_setxuid(old_ruid, old_euid, old_suid);
}
return 0;
} }
asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
...@@ -720,6 +696,12 @@ asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) ...@@ -720,6 +696,12 @@ asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
*/ */
asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
{ {
int retval;
retval = security_ops->task_setgid(rgid, egid, sgid, LSM_SETID_RES);
if (retval)
return retval;
if (!capable(CAP_SETGID)) { if (!capable(CAP_SETGID)) {
if ((rgid != (gid_t) -1) && (rgid != current->gid) && if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
(rgid != current->egid) && (rgid != current->sgid)) (rgid != current->egid) && (rgid != current->sgid))
...@@ -768,6 +750,11 @@ asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) ...@@ -768,6 +750,11 @@ asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid)
asmlinkage long sys_setfsuid(uid_t uid) asmlinkage long sys_setfsuid(uid_t uid)
{ {
int old_fsuid; int old_fsuid;
int retval;
retval = security_ops->task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
if (retval)
return retval;
old_fsuid = current->fsuid; old_fsuid = current->fsuid;
if (uid == current->uid || uid == current->euid || if (uid == current->uid || uid == current->euid ||
...@@ -782,24 +769,9 @@ asmlinkage long sys_setfsuid(uid_t uid) ...@@ -782,24 +769,9 @@ asmlinkage long sys_setfsuid(uid_t uid)
current->fsuid = uid; current->fsuid = uid;
} }
/* We emulate fsuid by essentially doing a scaled-down version retval = security_ops->task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
* of what we did in setresuid and friends. However, we only if (retval)
* operate on the fs-specific bits of the process' effective return retval;
* capabilities
*
* FIXME - is fsuser used for all CAP_FS_MASK capabilities?
* if not, we might be a bit too harsh here.
*/
if (!issecure(SECURE_NO_SETUID_FIXUP)) {
if (old_fsuid == 0 && current->fsuid != 0) {
cap_t(current->cap_effective) &= ~CAP_FS_MASK;
}
if (old_fsuid != 0 && current->fsuid == 0) {
cap_t(current->cap_effective) |=
(cap_t(current->cap_permitted) & CAP_FS_MASK);
}
}
return old_fsuid; return old_fsuid;
} }
...@@ -810,6 +782,11 @@ asmlinkage long sys_setfsuid(uid_t uid) ...@@ -810,6 +782,11 @@ asmlinkage long sys_setfsuid(uid_t uid)
asmlinkage long sys_setfsgid(gid_t gid) asmlinkage long sys_setfsgid(gid_t gid)
{ {
int old_fsgid; int old_fsgid;
int retval;
retval = security_ops->task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS);
if (retval)
return retval;
old_fsgid = current->fsgid; old_fsgid = current->fsgid;
if (gid == current->gid || gid == current->egid || if (gid == current->gid || gid == current->egid ||
...@@ -904,6 +881,10 @@ asmlinkage long sys_setpgid(pid_t pid, pid_t pgid) ...@@ -904,6 +881,10 @@ asmlinkage long sys_setpgid(pid_t pid, pid_t pgid)
} }
ok_pgid: ok_pgid:
err = security_ops->task_setpgid(p, pgid);
if (err)
goto out;
p->pgrp = pgid; p->pgrp = pgid;
err = 0; err = 0;
out: out:
...@@ -924,8 +905,11 @@ asmlinkage long sys_getpgid(pid_t pid) ...@@ -924,8 +905,11 @@ asmlinkage long sys_getpgid(pid_t pid)
p = find_task_by_pid(pid); p = find_task_by_pid(pid);
retval = -ESRCH; retval = -ESRCH;
if (p) if (p) {
retval = p->pgrp; retval = security_ops->task_getpgid(p);
if (!retval)
retval = p->pgrp;
}
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
return retval; return retval;
} }
...@@ -949,8 +933,11 @@ asmlinkage long sys_getsid(pid_t pid) ...@@ -949,8 +933,11 @@ asmlinkage long sys_getsid(pid_t pid)
p = find_task_by_pid(pid); p = find_task_by_pid(pid);
retval = -ESRCH; retval = -ESRCH;
if(p) if(p) {
retval = p->session; retval = security_ops->task_getsid(p);
if (!retval)
retval = p->session;
}
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
return retval; return retval;
} }
...@@ -1008,12 +995,19 @@ asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist) ...@@ -1008,12 +995,19 @@ asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist)
asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist) asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist)
{ {
gid_t groups[NGROUPS];
int retval;
if (!capable(CAP_SETGID)) if (!capable(CAP_SETGID))
return -EPERM; return -EPERM;
if ((unsigned) gidsetsize > NGROUPS) if ((unsigned) gidsetsize > NGROUPS)
return -EINVAL; return -EINVAL;
if(copy_from_user(current->groups, grouplist, gidsetsize * sizeof(gid_t))) if(copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t)))
return -EFAULT; return -EFAULT;
retval = security_ops->task_setgroups(gidsetsize, groups);
if (retval)
return retval;
memcpy(current->groups, groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize; current->ngroups = gidsetsize;
return 0; return 0;
} }
...@@ -1158,6 +1152,7 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim) ...@@ -1158,6 +1152,7 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim)
asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim) asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim)
{ {
struct rlimit new_rlim, *old_rlim; struct rlimit new_rlim, *old_rlim;
int retval;
if (resource >= RLIM_NLIMITS) if (resource >= RLIM_NLIMITS)
return -EINVAL; return -EINVAL;
...@@ -1172,6 +1167,11 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim) ...@@ -1172,6 +1167,11 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim)
if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN) if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN)
return -EPERM; return -EPERM;
} }
retval = security_ops->task_setrlimit(resource, &new_rlim);
if (retval)
return retval;
*old_rlim = new_rlim; *old_rlim = new_rlim;
return 0; return 0;
} }
...@@ -1243,6 +1243,10 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, ...@@ -1243,6 +1243,10 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
int error = 0; int error = 0;
int sig; int sig;
error = security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
if (error)
return error;
switch (option) { switch (option) {
case PR_SET_PDEATHSIG: case PR_SET_PDEATHSIG:
sig = arg2; sig = arg2;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/prctl.h> #include <linux/prctl.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/highuid.h> #include <linux/highuid.h>
#include <linux/security.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -128,6 +129,7 @@ asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t *grouplist) ...@@ -128,6 +129,7 @@ asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t *grouplist)
asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t *grouplist) asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t *grouplist)
{ {
old_gid_t groups[NGROUPS]; old_gid_t groups[NGROUPS];
gid_t new_groups[NGROUPS];
int i; int i;
if (!capable(CAP_SETGID)) if (!capable(CAP_SETGID))
...@@ -137,7 +139,11 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t *grouplist) ...@@ -137,7 +139,11 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t *grouplist)
if (copy_from_user(groups, grouplist, gidsetsize * sizeof(old_gid_t))) if (copy_from_user(groups, grouplist, gidsetsize * sizeof(old_gid_t)))
return -EFAULT; return -EFAULT;
for (i = 0 ; i < gidsetsize ; i++) for (i = 0 ; i < gidsetsize ; i++)
current->groups[i] = (gid_t)groups[i]; new_groups[i] = (gid_t)groups[i];
i = security_ops->task_setgroups(gidsetsize, new_groups);
if (i)
return i;
memcpy(current->groups, new_groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize; current->ngroups = gidsetsize;
return 0; return 0;
} }
......
CONFIG_SECURITY_CAPABILITIES
This enables the "default" Linux capabilities functionality.
If you are unsure how to answer this question, answer Y.
#
# Security configuration
#
mainmenu_option next_comment
comment 'Security options'
define_bool CONFIG_SECURITY_CAPABILITIES y
endmenu
#
# Makefile for the kernel security code
#
# Objects that export symbols
export-objs := security.o
# Object file lists
obj-y := security.o dummy.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += capability.o
include $(TOPDIR)/Rules.make
This diff is collapsed.
/*
* Stub functions for the default security function pointers in case no
* security model is loaded.
*
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
{
return 0;
}
static int dummy_capget (struct task_struct *target, kernel_cap_t * effective,
kernel_cap_t * inheritable, kernel_cap_t * permitted)
{
return 0;
}
static int dummy_capset_check (struct task_struct *target,
kernel_cap_t * effective,
kernel_cap_t * inheritable,
kernel_cap_t * permitted)
{
return 0;
}
static void dummy_capset_set (struct task_struct *target,
kernel_cap_t * effective,
kernel_cap_t * inheritable,
kernel_cap_t * permitted)
{
return;
}
static int dummy_capable (struct task_struct *tsk, int cap)
{
if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0)
/* capability granted */
return 0;
/* capability denied */
return -EPERM;
}
static int dummy_sys_security (unsigned int id, unsigned int call,
unsigned long *args)
{
return -ENOSYS;
}
static int dummy_bprm_alloc_security (struct linux_binprm *bprm)
{
return 0;
}
static void dummy_bprm_free_security (struct linux_binprm *bprm)
{
return;
}
static void dummy_bprm_compute_creds (struct linux_binprm *bprm)
{
return;
}
static int dummy_bprm_set_security (struct linux_binprm *bprm)
{
return 0;
}
static int dummy_bprm_check_security (struct linux_binprm *bprm)
{
return 0;
}
static int dummy_task_create (unsigned long clone_flags)
{
return 0;
}
static int dummy_task_alloc_security (struct task_struct *p)
{
return 0;
}
static void dummy_task_free_security (struct task_struct *p)
{
return;
}
static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
return 0;
}
static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
return 0;
}
static int dummy_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags)
{
return 0;
}
static int dummy_task_setpgid (struct task_struct *p, pid_t pgid)
{
return 0;
}
static int dummy_task_getpgid (struct task_struct *p)
{
return 0;
}
static int dummy_task_getsid (struct task_struct *p)
{
return 0;
}
static int dummy_task_setgroups (int gidsetsize, gid_t * grouplist)
{
return 0;
}
static int dummy_task_setnice (struct task_struct *p, int nice)
{
return 0;
}
static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim)
{
return 0;
}
static int dummy_task_setscheduler (struct task_struct *p, int policy,
struct sched_param *lp)
{
return 0;
}
static int dummy_task_getscheduler (struct task_struct *p)
{
return 0;
}
static int dummy_task_wait (struct task_struct *p)
{
return 0;
}
static int dummy_task_kill (struct task_struct *p, struct siginfo *info,
int sig)
{
return 0;
}
static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
return 0;
}
static void dummy_task_kmod_set_label (void)
{
return;
}
static void dummy_task_reparent_to_init (struct task_struct *p)
{
p->euid = p->fsuid = 0;
return;
}
static int dummy_register (const char *name, struct security_operations *ops)
{
return -EINVAL;
}
static int dummy_unregister (const char *name, struct security_operations *ops)
{
return -EINVAL;
}
struct security_operations dummy_security_ops = {
ptrace: dummy_ptrace,
capget: dummy_capget,
capset_check: dummy_capset_check,
capset_set: dummy_capset_set,
capable: dummy_capable,
sys_security: dummy_sys_security,
bprm_alloc_security: dummy_bprm_alloc_security,
bprm_free_security: dummy_bprm_free_security,
bprm_compute_creds: dummy_bprm_compute_creds,
bprm_set_security: dummy_bprm_set_security,
bprm_check_security: dummy_bprm_check_security,
task_create: dummy_task_create,
task_alloc_security: dummy_task_alloc_security,
task_free_security: dummy_task_free_security,
task_setuid: dummy_task_setuid,
task_post_setuid: dummy_task_post_setuid,
task_setgid: dummy_task_setgid,
task_setpgid: dummy_task_setpgid,
task_getpgid: dummy_task_getpgid,
task_getsid: dummy_task_getsid,
task_setgroups: dummy_task_setgroups,
task_setnice: dummy_task_setnice,
task_setrlimit: dummy_task_setrlimit,
task_setscheduler: dummy_task_setscheduler,
task_getscheduler: dummy_task_getscheduler,
task_wait: dummy_task_wait,
task_kill: dummy_task_kill,
task_prctl: dummy_task_prctl,
task_kmod_set_label: dummy_task_kmod_set_label,
task_reparent_to_init: dummy_task_reparent_to_init,
register_security: dummy_register,
unregister_security: dummy_unregister,
};
/*
* Security plug functions
*
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/security.h>
#define SECURITY_SCAFFOLD_VERSION "1.0.0"
extern struct security_operations dummy_security_ops; /* lives in dummy.c */
struct security_operations *security_ops; /* Initialized to NULL */
/* This macro checks that all pointers in a struct are non-NULL. It
* can be fooled by struct padding for object tile alignment and when
* pointers to data and pointers to functions aren't the same size.
* Yes it's ugly, we'll replace it if it becomes a problem.
*/
#define VERIFY_STRUCT(struct_type, s, e) \
do { \
unsigned long * __start = (unsigned long *)(s); \
unsigned long * __end = __start + \
sizeof(struct_type)/sizeof(unsigned long *); \
while (__start != __end) { \
if (!*__start) { \
printk(KERN_INFO "%s is missing something\n",\
#struct_type); \
e++; \
break; \
} \
__start++; \
} \
} while (0)
static int inline verify (struct security_operations *ops)
{
int err;
/* verify the security_operations structure exists */
if (!ops) {
printk (KERN_INFO "Passed a NULL security_operations "
"pointer, " __FUNCTION__ " failed.\n");
return -EINVAL;
}
/* Perform a little sanity checking on our inputs */
err = 0;
/* This first check scans the whole security_ops struct for
* missing structs or functions.
*
* (There is no further check now, but will leave as is until
* the lazy registration stuff is done -- JM).
*/
VERIFY_STRUCT(struct security_operations, ops, err);
if (err) {
printk (KERN_INFO "Not enough functions specified in the "
"security_operation structure, " __FUNCTION__
" failed.\n");
return -EINVAL;
}
return 0;
}
/**
* security_scaffolding_startup - initialzes the security scaffolding framework
*
* This should be called early in the kernel initialization sequence.
*/
int security_scaffolding_startup (void)
{
printk (KERN_INFO "Security Scaffold v" SECURITY_SCAFFOLD_VERSION
" initialized\n");
security_ops = &dummy_security_ops;
return 0;
}
/**
* register_security - registers a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
* This function is to allow a security module to register itself with the
* kernel security subsystem. Some rudimentary checking is done on the @ops
* value passed to this function. A call to unregister_security() should be
* done to remove this security_options structure from the kernel.
*
* If the @ops structure does not contain function pointers for all hooks in
* the structure, or there is already a security module registered with the
* kernel, an error will be returned. Otherwise 0 is returned on success.
*/
int register_security (struct security_operations *ops)
{
if (verify (ops)) {
printk (KERN_INFO __FUNCTION__ " could not verify "
"security_operations structure.\n");
return -EINVAL;
}
if (security_ops != &dummy_security_ops) {
printk (KERN_INFO "There is already a security "
"framework initialized, " __FUNCTION__ " failed.\n");
return -EINVAL;
}
security_ops = ops;
return 0;
}
/**
* unregister_security - unregisters a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
* This function removes a struct security_operations variable that had
* previously been registered with a successful call to register_security().
*
* If @ops does not match the valued previously passed to register_security()
* an error is returned. Otherwise the default security options is set to the
* the dummy_security_ops structure, and 0 is returned.
*/
int unregister_security (struct security_operations *ops)
{
if (ops != security_ops) {
printk (KERN_INFO __FUNCTION__ ": trying to unregister "
"a security_opts structure that is not "
"registered, failing.\n");
return -EINVAL;
}
security_ops = &dummy_security_ops;
return 0;
}
/**
* mod_reg_security - allows security modules to be "stacked"
* @name: a pointer to a string with the name of the security_options to be registered
* @ops: a pointer to the struct security_options that is to be registered
*
* This function allows security modules to be stacked if the currently loaded
* security module allows this to happen. It passes the @name and @ops to the
* register_security function of the currently loaded security module.
*
* The return value depends on the currently loaded security module, with 0 as
* success.
*/
int mod_reg_security (const char *name, struct security_operations *ops)
{
if (verify (ops)) {
printk (KERN_INFO __FUNCTION__ " could not verify "
"security operations.\n");
return -EINVAL;
}
if (ops == security_ops) {
printk (KERN_INFO __FUNCTION__ " security operations "
"already registered.\n");
return -EINVAL;
}
return security_ops->register_security (name, ops);
}
/**
* mod_unreg_security - allows a security module registered with mod_reg_security() to be unloaded
* @name: a pointer to a string with the name of the security_options to be removed
* @ops: a pointer to the struct security_options that is to be removed
*
* This function allows security modules that have been successfully registered
* with a call to mod_reg_security() to be unloaded from the system.
* This calls the currently loaded security module's unregister_security() call
* with the @name and @ops variables.
*
* The return value depends on the currently loaded security module, with 0 as
* success.
*/
int mod_unreg_security (const char *name, struct security_operations *ops)
{
if (ops == security_ops) {
printk (KERN_INFO __FUNCTION__ " invalid attempt to unregister "
" primary security ops.\n");
return -EINVAL;
}
return security_ops->unregister_security (name, ops);
}
/**
* capable - calls the currently loaded security module's capable() function with the specified capability
* @cap: the requested capability level.
*
* This function calls the currently loaded security module's cabable()
* function with a pointer to the current task and the specified @cap value.
*
* This allows the security module to implement the capable function call
* however it chooses to.
*/
int capable (int cap)
{
if (security_ops->capable (current, cap)) {
/* capability denied */
return 0;
}
/* capability granted */
current->flags |= PF_SUPERPRIV;
return 1;
}
/**
* sys_security - security syscall multiplexor.
* @id: module id
* @call: call identifier
* @args: arg list for call
*
* Similar to sys_socketcall. Can use id to help identify which module user
* app is talking to. The recommended convention for creating the
* hexadecimal id value is:
* 'echo "Name_of_module" | md5sum | cut -c -8'.
* By following this convention, there's no need for a central registry.
*/
asmlinkage long sys_security (unsigned int id, unsigned int call,
unsigned long *args)
{
return security_ops->sys_security (id, call, args);
}
EXPORT_SYMBOL (register_security);
EXPORT_SYMBOL (unregister_security);
EXPORT_SYMBOL (mod_reg_security);
EXPORT_SYMBOL (mod_unreg_security);
EXPORT_SYMBOL (capable);
EXPORT_SYMBOL (security_ops);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment