Using my earlier authoritative hook patch for reference, I've gone through the current set of LSM hooks and made many of them authoritative. See the attached patch which is relative to the current WireX BitKeeper tree (this patch also includes the capget/capset changes, since those haven't been committed yet to the WireX tree). This patch does not try to address the early short-circuit problem mentioned by Richard - it just tries to make hooks authoritative with respect to the immediately preceding DAC logic. I would like to get feedback on this patch from SGI with regard to how well it meets their needs and what other changes would be necessary, and I would like to get feedback from WireX and Greg K-H about the acceptability of just this patch from their perspective (since there isn't much point in SGI proceeding if even this patch is unacceptable). The following hooks were changed to be authoritative: inode_ops - permission, setattr file_ops - send_sigiotask task_ops - setgroups, setnice, setrlimit, setscheduler, kill module_ops - create_module, init_module, delete_module ipc_ops - permission security_ops - sethostname, setdomainname, reboot, umount, ioperm, iopl, ptrace, capset_check, acct, sysctl Here are some notes both about what the patch does and what is missing from the patch with respect to authoritative hooks: 1) The patch does not try to make the directory operation hooks in inode_ops (e.g. create, link, unlink, mkdir, ...) authoritative. At first glance, it might appear that you would want to save the return value of the may_create or may_delete functions, pass it to these hooks, and let them make the final decision. However, may_create and may_delete contain both functional logic (e.g. dead directory tests, file existence tests, file type tests, root directory tests) and access control logic (e.g. calls to permission, sticky bit on directory test). In some cases, the category to which a particular check should belong is not clear, e.g. the immutable and append-only tests could be viewed as functional logic (they simply enforce the bit set on the inode, which could have been set or cleared by the post_lookup hook) or as access control logic. We don't want the hook to override functional logic. In the may_create case, if we exclude the functional logic, then may_create is just a call to permission, and we already have an authoritative hook for permission in the patch. In the may_delete case, there are the tests for the sticky bit, the append-only flag, and the immutable flag, so a case could be made for needing the unlink/rmdir/rename hooks to be authoritative, but you would still need to separate the functional logic from the access control logic. And making the rename hook authoritative would be especially tricky, since it can involve multiple calls to may_delete (if the target exists) or a may_delete/may_create pair of calls. 2) The patch does not try to make a number of the file_ops hooks authoritative, i.e. file_ops permission, mmap, and mprotect, because my view is that the kernel logic in these cases is functional - it is merely enforcing the bits set in the descriptor flags at open time. Since the inode_ops->permission hook is called at open time, and since this patch makes it authoritative, that should be sufficient for your needs. 3) As I've previously mentioned, it is not possible to make the setattr hook authoritative if the file system type defines its own setattr inode operation, since that inode operation handles both access control and functional processing (e.g. in NFS, where all access control for setattr is handled on the server). So the setattr hook is only authoritative if the file system type does not define its own setattr inode operation. Otherwise the setattr hook is restrictive and called first. 4) A number of hooks are still called prior to any kernel access control logic either because they are used at an earlier point in processing (e.g. fcntl, lock, mount) or because the functional logic is intertwined with the kernel access control logic (e.g. set*uid, set*gid). So those hooks are not authoritative, but that should be ok with SGI - Richard said that SGI would be satisfied if every hook were either placed before the kernel logic or made authoritative. Since I couldn't see a good way to make setpgid cleanly authoritative, I also moved it back to its original position before the DAC logic. 5) In some cases, I moved either the kernel access control logic slightly (e.g. acct, capset, create/init/delete_module, sethostname, setdomainname) or the hook placement slightly (e.g. umount) to allow them to be coupled for authoritative hooks. I tried to make sure that this doesn't introduce any vulnerabilities, but please review the changes. Similarly, for ptrace, I reordered the checks to try to separate the functional checks from the access control checks, with the functional checks still short circuiting. -- Stephen D. Smalley, NAI Labs ssmalleyat_private diff -X /home/sds/dontdiff -ur lsm-wirex/arch/i386/kernel/ioport.c lsm/arch/i386/kernel/ioport.c --- lsm-wirex/arch/i386/kernel/ioport.c Wed Aug 8 08:29:59 2001 +++ lsm/arch/i386/kernel/ioport.c Thu Aug 16 11:59:31 2001 @@ -61,10 +61,11 @@ if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) return -EINVAL; - if (turn_on && !capable(CAP_SYS_RAWIO)) - return -EPERM; - retval = security_ops->ioperm(from, num, turn_on); + retval = 0; + if (turn_on && !capable(CAP_SYS_RAWIO)) + retval = -EPERM; + retval = security_ops->ioperm(from, num, turn_on, retval); if (retval) { return retval; } @@ -116,11 +117,12 @@ if (level > 3) return -EINVAL; /* Trying to gain more privileges? */ + retval = 0; if (level > old) { if (!capable(CAP_SYS_RAWIO)) - return -EPERM; + retval = -EPERM; } - retval = security_ops->iopl(old, level); + retval = security_ops->iopl(old, level, retval); if (retval) { return retval; } diff -X /home/sds/dontdiff -ur lsm-wirex/arch/i386/kernel/ptrace.c lsm/arch/i386/kernel/ptrace.c --- lsm-wirex/arch/i386/kernel/ptrace.c Wed Aug 8 08:29:59 2001 +++ lsm/arch/i386/kernel/ptrace.c Thu Aug 16 12:16:50 2001 @@ -147,7 +147,7 @@ /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; - ret = security_ops->ptrace(current->p_pptr, current); + ret = security_ops->ptrace(current->p_pptr, current, 0); if (ret) goto out; /* set the ptrace bit in the process flags. */ diff -X /home/sds/dontdiff -ur lsm-wirex/arch/ia64/ia32/sys_ia32.c lsm/arch/ia64/ia32/sys_ia32.c --- lsm-wirex/arch/ia64/ia32/sys_ia32.c Mon Aug 13 08:19:47 2001 +++ lsm/arch/ia64/ia32/sys_ia32.c Thu Aug 16 12:01:51 2001 @@ -2592,11 +2592,12 @@ return(-EINVAL); /* Trying to gain more privileges? */ asm volatile ("mov %0=ar.eflag ;;" : "=r"(old)); + retval = 0; if (level > ((old >> 12) & 3)) { if (!capable(CAP_SYS_RAWIO)) - return -EPERM; + retval = -EPERM; } - retval = security_ops->iopl(old,level); + retval = security_ops->iopl(old,level,retval); if (retval) { return retval; } diff -X /home/sds/dontdiff -ur lsm-wirex/arch/ia64/kernel/ptrace.c lsm/arch/ia64/kernel/ptrace.c --- lsm-wirex/arch/ia64/kernel/ptrace.c Mon Aug 13 08:19:47 2001 +++ lsm/arch/ia64/kernel/ptrace.c Thu Aug 16 12:17:06 2001 @@ -806,7 +806,7 @@ /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; - ret = security_ops->ptrace(current->p_pptr, current); + ret = security_ops->ptrace(current->p_pptr, current, 0); if (ret) goto out; current->ptrace |= PT_PTRACED; diff -X /home/sds/dontdiff -ur lsm-wirex/fs/attr.c lsm/fs/attr.c --- lsm-wirex/fs/attr.c Wed Aug 8 08:29:17 2001 +++ lsm/fs/attr.c Thu Aug 16 10:51:51 2001 @@ -122,13 +122,12 @@ lock_kernel(); if (inode->i_op && inode->i_op->setattr) { - error = security_ops->inode_ops->setattr(dentry, attr); + error = security_ops->inode_ops->setattr(dentry, attr, 0); if (!error) error = inode->i_op->setattr(dentry, attr); } else { error = inode_change_ok(inode, attr); - if (!error) - error = security_ops->inode_ops->setattr(dentry, attr); + error = security_ops->inode_ops->setattr(dentry, attr, error); if (!error) inode_setattr(inode, attr); } diff -X /home/sds/dontdiff -ur lsm-wirex/fs/fcntl.c lsm/fs/fcntl.c --- lsm-wirex/fs/fcntl.c Wed Aug 8 08:29:17 2001 +++ lsm/fs/fcntl.c Thu Aug 16 10:56:20 2001 @@ -395,12 +395,14 @@ int fd, int reason) { + int retval = 0; + if ((fown->euid != 0) && (fown->euid ^ p->suid) && (fown->euid ^ p->uid) && (fown->uid ^ p->suid) && (fown->uid ^ p->uid)) - return; + retval = -EPERM; - if (security_ops->file_ops->send_sigiotask(p, fown, fd, reason)) + if (security_ops->file_ops->send_sigiotask(p, fown, fd, reason, retval)) return; switch (fown->signum) { diff -X /home/sds/dontdiff -ur lsm-wirex/fs/namei.c lsm/fs/namei.c --- lsm-wirex/fs/namei.c Mon Aug 13 08:19:49 2001 +++ lsm/fs/namei.c Thu Aug 16 10:46:16 2001 @@ -190,10 +190,7 @@ } else { retval = vfs_permission(inode, submask); } - if (retval) - return retval; - - return security_ops->inode_ops->permission(inode, mask); + return security_ops->inode_ops->permission(inode, mask, retval); } /* diff -X /home/sds/dontdiff -ur lsm-wirex/fs/proc/base.c lsm/fs/proc/base.c --- lsm-wirex/fs/proc/base.c Wed Aug 8 08:29:17 2001 +++ lsm/fs/proc/base.c Thu Aug 16 12:40:22 2001 @@ -310,7 +310,7 @@ }; #define MAY_PTRACE(p) \ -(p==current||(p->p_pptr==current&&(p->ptrace & PT_PTRACED)&&p->state==TASK_STOPPED&&security_ops->ptrace(current,p)==0)) +(p==current||(p->p_pptr==current&&(p->ptrace & PT_PTRACED)&&p->state==TASK_STOPPED&&security_ops->ptrace(current,p,0)==0)) static int mem_open(struct inode* inode, struct file* file) diff -X /home/sds/dontdiff -ur lsm-wirex/fs/super.c lsm/fs/super.c --- lsm-wirex/fs/super.c Mon Aug 13 08:19:49 2001 +++ lsm/fs/super.c Thu Aug 16 11:56:36 2001 @@ -1132,10 +1132,6 @@ struct nameidata parent_nd; int retval; - retval = security_ops->umount(mnt, flags); - if (retval) - return retval; - /* * No sense to grab the lock for this test, but test itself looks * somewhat bogus. Suggestions for better replacement? @@ -1246,8 +1242,11 @@ if (nd.dentry != nd.mnt->mnt_root) goto dput_and_out; - retval = -EPERM; + retval = 0; if (!capable(CAP_SYS_ADMIN) && current->uid!=nd.mnt->mnt_owner) + retval = -EPERM; + retval = security_ops->umount(nd.mnt, flags, retval); + if (retval) goto dput_and_out; down(&mount_sem); diff -X /home/sds/dontdiff -ur lsm-wirex/include/linux/security.h lsm/include/linux/security.h --- lsm-wirex/include/linux/security.h Mon Aug 13 08:19:46 2001 +++ lsm/include/linux/security.h Thu Aug 16 12:35:48 2001 @@ -82,9 +82,9 @@ struct inode *new_dir, struct dentry *new_dentry); int (* readlink) (struct dentry *dentry); int (* follow_link) (struct dentry *dentry, struct nameidata *nd); - int (* permission) (struct inode *inode, int mask); + int (* permission) (struct inode *inode, int mask, int kern_retval); int (* revalidate) (struct dentry *dentry); - int (* setattr) (struct dentry *dentry, struct iattr *attr); + int (* setattr) (struct dentry *dentry, struct iattr *attr, int kern_retval); int (* stat) (struct inode *inode); void (* post_lookup) (struct inode *ino, struct dentry *d); void (* delete) (struct inode *ino); @@ -103,7 +103,7 @@ /* Warning! When the arg parameter represents a user space pointer, it should never be used by the module. */ int (* fcntl) (struct file *file, unsigned int cmd, unsigned long arg); int (* set_fowner) (struct file *file); - int (* send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason); + int (* send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason, int kern_retval); int (* receive) (struct file *file); }; @@ -122,12 +122,12 @@ int (* setpgid) (struct task_struct *p, pid_t pgid); int (* getpgid) (struct task_struct *p); int (* getsid) (struct task_struct *p); - int (* setgroups) (int gidsetsize, gid_t *grouplist); - int (* setnice) (struct task_struct *p, int nice); - int (* setrlimit) (unsigned int resource, struct rlimit *new_rlim); - int (* setscheduler) (struct task_struct *p, int policy, struct sched_param *lp); + int (* setgroups) (int gidsetsize, gid_t *grouplist, int kern_retval); + int (* setnice) (struct task_struct *p, int nice, int kern_retval); + int (* setrlimit) (unsigned int resource, struct rlimit *new_rlim, int kern_retval); + int (* setscheduler) (struct task_struct *p, int policy, struct sched_param *lp, int kern_retval); int (* getscheduler) (struct task_struct *p); - int (* kill) (struct task_struct *p, struct siginfo *info, int sig); + int (* kill) (struct task_struct *p, struct siginfo *info, int sig, int kern_retval); int (* wait) (struct task_struct *p); void (* kmod_set_label) (void); @@ -185,13 +185,13 @@ }; struct module_security_ops { - int (* create_module) (const char *name, size_t size); - int (* init_module) (const char *name, struct module *mod); - int (* delete_module) (const char *name); + int (* create_module) (const char *name, size_t size, int kern_retval); + int (* init_module) (const char *name, struct module *mod, int kern_retval); + int (* delete_module) (const char *name, int kern_retval); }; struct ipc_security_ops { - int (* permission) (struct kern_ipc_perm *ipcp, short flag); + int (* permission) (struct kern_ipc_perm *ipcp, short flag, int kern_retval); int (* getinfo) (int id, int cmd); }; @@ -230,22 +230,24 @@ int version; /* syscalls that are checked for permissions */ - int (* sethostname) (char *hostname); - int (* setdomainname) (char *domainname); - int (* reboot) (unsigned int cmd); + int (* sethostname) (char *hostname, int kern_retval); + int (* setdomainname) (char *domainname, int kern_retval); + int (* reboot) (unsigned int cmd, int kern_retval); int (* mount) (char * dev_name, struct nameidata *nd, char * type, unsigned long flags, void * data); - int (* umount) (struct vfsmount *mnt, int flags); + int (* umount) (struct vfsmount *mnt, int flags, int kern_retval); void (* umount_close) (struct vfsmount *mnt); void (* umount_busy) (struct vfsmount *mnt); void (* post_remount) (struct vfsmount *mnt, unsigned long flags, void *data); - int (* ioperm) (unsigned long from, unsigned long num, int turn_on); - int (* iopl) (unsigned int old, unsigned int level); - int (* ptrace) (struct task_struct *parent, struct task_struct *child); - int (* setcapability) (void); - int (* acct) (struct file *file); - int (* sysctl) (ctl_table * table, int op); + int (* ioperm) (unsigned long from, unsigned long num, int turn_on, int kern_retval); + int (* iopl) (unsigned int old, unsigned int level, int kern_retval); + int (* ptrace) (struct task_struct *parent, struct task_struct *child, int kern_retval); + int (*capget) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); + int (*capset_check) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted, int kern_retval); + void (*capset_set) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); + int (* acct) (struct file *file, int kern_retval); + int (* sysctl) (ctl_table * table, int op, int kern_retval); int (* capable) (struct task_struct *tsk, int cap); void (* post_mountroot) (struct super_block *sb); void (* post_addmount) (struct vfsmount *mnt, struct nameidata *mountpoint_nd); diff -X /home/sds/dontdiff -ur lsm-wirex/ipc/util.c lsm/ipc/util.c --- lsm-wirex/ipc/util.c Wed Aug 8 08:29:29 2001 +++ lsm/ipc/util.c Thu Aug 16 11:51:10 2001 @@ -252,6 +252,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) { /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */ int requested_mode, granted_mode; + int retval; requested_mode = (flag >> 6) | (flag >> 3) | flag; granted_mode = ipcp->mode; @@ -260,11 +261,11 @@ else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) granted_mode >>= 3; /* is there some bit set in requested_mode but not in granted_mode? */ + retval = 0; if ((requested_mode & ~granted_mode & 0007) && !capable(CAP_IPC_OWNER)) - return -1; - - return security_ops->ipc_ops->permission(ipcp, flag); + retval = -EACCES; + return security_ops->ipc_ops->permission(ipcp, flag, retval); } /* diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/acct.c lsm/kernel/acct.c --- lsm-wirex/kernel/acct.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/acct.c Thu Aug 16 12:29:47 2001 @@ -158,9 +158,6 @@ char *tmp; int error; - if (!capable(CAP_SYS_PACCT)) - return -EPERM; - if (name) { tmp = getname(name); error = PTR_ERR(tmp); @@ -182,7 +179,10 @@ goto out_err; } - error = security_ops->acct(file); + error = 0; + if (!capable(CAP_SYS_PACCT)) + error = -EPERM; + error = security_ops->acct(file, error); if (error) goto out_err; diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/capability.c lsm/kernel/capability.c --- lsm-wirex/kernel/capability.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/capability.c Thu Aug 16 12:36:18 2001 @@ -57,9 +57,7 @@ } if (!error) { - data.permitted = cap_t(target->cap_permitted); - data.inheritable = cap_t(target->cap_inheritable); - data.effective = cap_t(target->cap_effective); + error = security_ops->capget(target, &data.effective, &data.inheritable, &data.permitted); } if (target != current) @@ -88,9 +86,7 @@ for_each_task(target) { if (target->pgrp != pgrp) continue; - target->cap_effective = *effective; - target->cap_inheritable = *inheritable; - target->cap_permitted = *permitted; + security_ops->capset_set(target, effective, inheritable, permitted); } read_unlock(&tasklist_lock); } @@ -109,9 +105,7 @@ for_each_task(target) { if (target == current || target->pid == 1) continue; - target->cap_effective = *effective; - target->cap_inheritable = *inheritable; - target->cap_permitted = *permitted; + security_ops->capset_set(target, effective, inheritable, permitted); } read_unlock(&tasklist_lock); } @@ -146,9 +140,6 @@ if (get_user(pid, &header->pid)) return -EFAULT; - if (pid && !capable(CAP_SETPCAP)) - return -EPERM; - if (copy_from_user(&effective, &data->effective, sizeof(effective)) || copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) || copy_from_user(&permitted, &data->permitted, sizeof(permitted))) @@ -168,28 +159,13 @@ target = current; } + error = 0; + if (pid && !capable(CAP_SETPCAP)) + error = -EPERM; + error = security_ops->capset_check(target, &effective, &inheritable, &permitted, error); + if (error) + goto out; - /* verify restrictions on target's new Inheritable set */ - if (!cap_issubset(inheritable, - cap_combine(target->cap_inheritable, - current->cap_permitted))) { - goto out; - } - - /* verify restrictions on target's new Permitted set */ - if (!cap_issubset(permitted, - cap_combine(target->cap_permitted, - current->cap_permitted))) { - goto out; - } - - /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ - if (!cap_issubset(effective, permitted)) { - goto out; - } - - /* having verified that the proposed changes are legal, - we now put them into effect. */ error = 0; if (pid < 0) { @@ -200,10 +176,7 @@ cap_set_pg(-pid, &effective, &inheritable, &permitted); goto spin_out; } else { - /* FIXME: do we need to have a write lock here..? */ - target->cap_effective = effective; - target->cap_inheritable = inheritable; - target->cap_permitted = permitted; + security_ops->capset_set(target, &effective, &inheritable, &permitted); } out: diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/module.c lsm/kernel/module.c --- lsm-wirex/kernel/module.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/module.c Thu Aug 16 11:48:08 2001 @@ -298,8 +298,6 @@ struct module *mod; unsigned long flags; - if (!capable(CAP_SYS_MODULE)) - return -EPERM; lock_kernel(); if ((namelen = get_mod_name(name_user, &name)) < 0) { error = namelen; @@ -315,7 +313,10 @@ } /* check that we have permission to do this */ - error = security_ops->module_ops->create_module(name, size); + error = 0; + if (!capable(CAP_SYS_MODULE)) + error = -EPERM; + error = security_ops->module_ops->create_module(name, size, error); if (error) goto err1; @@ -359,8 +360,6 @@ unsigned long mod_user_size; struct module_ref *dep; - if (!capable(CAP_SYS_MODULE)) - return -EPERM; lock_kernel(); if ((namelen = get_mod_name(name_user, &name)) < 0) { error = namelen; @@ -513,7 +512,10 @@ } /* check that we have permission to do this */ - error = security_ops->module_ops->init_module(name, mod); + error = 0; + if (!capable(CAP_SYS_MODULE)) + error = -EPERM; + error = security_ops->module_ops->init_module(name, mod, error); if (error) goto err3; error = -EINVAL; @@ -619,16 +621,16 @@ long error; int something_changed; - if (!capable(CAP_SYS_MODULE)) - return -EPERM; - lock_kernel(); if (name_user) { if ((error = get_mod_name(name_user, &name)) < 0) goto out; /* check that we have permission to do this */ - error = security_ops->module_ops->delete_module(name); + error = 0; + if (!capable(CAP_SYS_MODULE)) + error = -EPERM; + error = security_ops->module_ops->delete_module(name, error); if (error) { put_mod_name(name); goto out; @@ -654,6 +656,13 @@ spin_unlock(&unload_lock); } goto out; + } else { + error = 0; + if (!capable(CAP_SYS_MODULE)) + error = -EPERM; + error = security_ops->module_ops->delete_module(NULL, error); + if (error) + goto out; } /* Do automatic reaping */ diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/ptrace.c lsm/kernel/ptrace.c --- lsm-wirex/kernel/ptrace.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/ptrace.c Thu Aug 16 12:16:34 2001 @@ -21,26 +21,28 @@ int retval; task_lock(task); retval = -EPERM; - if (task->pid <= 1) - goto bad; if (task == current) goto bad; if (!task->mm) goto bad; - if(((current->uid != task->euid) || + /* the same process cannot be attached many times */ + if (task->ptrace & PT_PTRACED) + goto bad; + retval = 0; + if (task->pid <= 1) + retval = -EPERM; + if(!retval && + ((current->uid != task->euid) || (current->uid != task->suid) || (current->uid != task->uid) || (current->gid != task->egid) || (current->gid != task->sgid) || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) - goto bad; + retval = -EPERM; rmb(); - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) - goto bad; - /* the same process cannot be attached many times */ - if (task->ptrace & PT_PTRACED) - goto bad; - retval = security_ops->ptrace(current, task); + if (!retval && !task->mm->dumpable && !capable(CAP_SYS_PTRACE)) + retval = -EPERM; + retval = security_ops->ptrace(current, task, retval); if (retval) goto bad; diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/sched.c lsm/kernel/sched.c --- lsm-wirex/kernel/sched.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/sched.c Thu Aug 16 11:34:56 2001 @@ -869,7 +869,7 @@ asmlinkage long sys_nice(int increment) { long newprio; - int retval; + int retval = 0; /* * Setpriority might change our priority at the same moment. @@ -878,7 +878,7 @@ */ if (increment < 0) { if (!capable(CAP_SYS_NICE)) - return -EPERM; + retval = -EPERM; if (increment < -40) increment = -40; } @@ -891,7 +891,7 @@ if (newprio > 19) newprio = 19; - retval = security_ops->task_ops->setnice(current, newprio); + retval = security_ops->task_ops->setnice(current, newprio, retval); if (retval) return retval; @@ -956,15 +956,15 @@ if ((policy == SCHED_OTHER) != (lp.sched_priority == 0)) goto out_unlock; - retval = -EPERM; + retval = 0; if ((policy == SCHED_FIFO || policy == SCHED_RR) && !capable(CAP_SYS_NICE)) - goto out_unlock; - if ((current->euid != p->euid) && (current->euid != p->uid) && + retval = -EPERM; + if (!retval && + (current->euid != p->euid) && (current->euid != p->uid) && !capable(CAP_SYS_NICE)) - goto out_unlock; - - retval = security_ops->task_ops->setscheduler(p, policy, &lp); + retval = -EPERM; + retval = security_ops->task_ops->setscheduler(p, policy, &lp, retval); if (retval) goto out_unlock; diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/signal.c lsm/kernel/signal.c --- lsm-wirex/kernel/signal.c Wed Aug 8 08:29:20 2001 +++ lsm/kernel/signal.c Thu Aug 16 11:38:03 2001 @@ -515,10 +515,10 @@ if (sig < 0 || sig > _NSIG) goto out_nolock; /* The somewhat baroque permissions check... */ - ret = -EPERM; + ret = 0; if (bad_signal(sig, info, t)) - goto out_nolock; - ret = security_ops->task_ops->kill(t, info, sig); + ret = -EPERM; + ret = security_ops->task_ops->kill(t, info, sig, ret); if (ret) goto out_nolock; diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/sys.c lsm/kernel/sys.c --- lsm-wirex/kernel/sys.c Mon Aug 13 08:19:49 2001 +++ lsm/kernel/sys.c Thu Aug 16 11:53:42 2001 @@ -211,19 +211,17 @@ read_lock(&tasklist_lock); for_each_task(p) { - int no_nice; + int no_nice = 0; if (!proc_sel(p, which, who)) continue; if (p->uid != current->euid && p->uid != current->uid && !capable(CAP_SYS_NICE)) { - error = -EPERM; - continue; + no_nice = -EPERM; } - if (niceval < p->nice && !capable(CAP_SYS_NICE)) { - error = -EACCES; - continue; + if (!no_nice && niceval < p->nice && !capable(CAP_SYS_NICE)) { + no_nice = -EACCES; } - no_nice = security_ops->task_ops->setnice(p, niceval); + no_nice = security_ops->task_ops->setnice(p, niceval, no_nice); if (no_nice) { error = no_nice; continue; @@ -280,10 +278,10 @@ int retval; /* We only trust the superuser with rebooting the system. */ + retval = 0; if (!capable(CAP_SYS_BOOT)) - return -EPERM; - - retval = security_ops->reboot(cmd); + retval = -EPERM; + retval = security_ops->reboot(cmd, retval); if (retval) { return retval; } @@ -819,6 +817,10 @@ if (!p) goto out; + err = security_ops->task_ops->setpgid(p, pgid); + if (err) + goto out; + if (p->p_pptr == current || p->p_opptr == current) { err = -EPERM; if (p->session != current->session) @@ -842,10 +844,6 @@ } ok_pgid: - err = security_ops->task_ops->setpgid(p, pgid); - if (err) - goto out; - p->pgrp = pgid; err = 0; out: @@ -957,15 +955,15 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist) { gid_t groups[NGROUPS]; - int retval; + int retval = 0; - if (!capable(CAP_SETGID)) - return -EPERM; if ((unsigned) gidsetsize > NGROUPS) return -EINVAL; if(copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t))) return -EFAULT; - retval = security_ops->task_ops->setgroups(gidsetsize, groups); + if (!capable(CAP_SETGID)) + retval = -EPERM; + retval = security_ops->task_ops->setgroups(gidsetsize, groups, retval); if (retval) return retval; memcpy(current->groups, groups, gidsetsize * sizeof(gid_t)); @@ -1026,15 +1024,16 @@ char nodename[__NEW_UTS_LEN+1]; int errno; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL; if (copy_from_user(nodename, name, len)) return -EFAULT; nodename[len] = 0; - errno = security_ops->sethostname(nodename); + errno = 0; + if (!capable(CAP_SYS_ADMIN)) + errno = -EPERM; + errno = security_ops->sethostname(nodename, errno); if (errno) return errno; @@ -1070,15 +1069,16 @@ char domainname[__NEW_UTS_LEN+1]; int errno; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL; if (copy_from_user(domainname, name, len)) return -EFAULT; domainname[len] = 0; - errno = security_ops->setdomainname(domainname); + errno = 0; + if (!capable(CAP_SYS_ADMIN)) + errno = -EPERM; + errno = security_ops->setdomainname(domainname, errno); if (errno) return errno; @@ -1129,16 +1129,17 @@ if(copy_from_user(&new_rlim, rlim, sizeof(*rlim))) return -EFAULT; old_rlim = current->rlim + resource; + + retval = 0; if (((new_rlim.rlim_cur > old_rlim->rlim_max) || (new_rlim.rlim_max > old_rlim->rlim_max)) && !capable(CAP_SYS_RESOURCE)) - return -EPERM; - if (resource == RLIMIT_NOFILE) { + retval = -EPERM; + if (!retval && resource == RLIMIT_NOFILE) { if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN) - return -EPERM; + retval = -EPERM; } - - retval = security_ops->task_ops->setrlimit(resource, &new_rlim); + retval = security_ops->task_ops->setrlimit(resource, &new_rlim, retval); if (retval) return retval; diff -X /home/sds/dontdiff -ur lsm-wirex/kernel/sysctl.c lsm/kernel/sysctl.c --- lsm-wirex/kernel/sysctl.c Mon Aug 13 08:19:49 2001 +++ lsm/kernel/sysctl.c Thu Aug 16 12:32:00 2001 @@ -388,11 +388,8 @@ static inline int ctl_perm(ctl_table *table, int op) { int error; - error = security_ops->sysctl(table, op); - if(error) { - return error; - } - return test_perm(table->mode, op); + error = test_perm(table->mode, op); + return security_ops->sysctl(table, op, error); } static int parse_table(int *name, int nlen, diff -X /home/sds/dontdiff -ur lsm-wirex/security/capability_plug.c lsm/security/capability_plug.c --- lsm-wirex/security/capability_plug.c Thu Aug 16 12:57:06 2001 +++ lsm/security/capability_plug.c Thu Aug 16 12:43:48 2001 @@ -26,16 +26,16 @@ /* flag to keep track of how we were registered */ static int secondary; -static int cap_sethostname (char *hostname) {return 0;} -static int cap_setdomainname (char *domainname) {return 0;} -static int cap_reboot (unsigned int cmd) {return 0;} +static int cap_sethostname (char *hostname, int kern_retval) {return kern_retval;} +static int cap_setdomainname (char *domainname, int kern_retval) {return kern_retval;} +static int cap_reboot (unsigned int cmd, int kern_retval) {return kern_retval;} static int cap_mount (char * dev_name, struct nameidata *nd, char * type, unsigned long flags, void * data) {return 0;} -static int cap_umount (struct vfsmount *mnt, int flags) {return 0;} +static int cap_umount (struct vfsmount *mnt, int flags, int kern_retval) {return kern_retval;} static void cap_umount_close (struct vfsmount *mnt) {return;} static void cap_umount_busy (struct vfsmount *mnt) {return;} static void cap_post_remount (struct vfsmount *mnt, unsigned long flags, void *data) {return;} -static int cap_ioperm (unsigned long from, unsigned long num, int turn_on) {return 0;} -static int cap_iopl (unsigned int old, unsigned int level) {return 0;} +static int cap_ioperm (unsigned long from, unsigned long num, int turn_on, int kern_retval) {return kern_retval;} +static int cap_iopl (unsigned int old, unsigned int level, int kern_retval) {return kern_retval;} static int cap_capable (struct task_struct *tsk, int cap) { @@ -50,8 +50,12 @@ static void cap_post_addmount (struct vfsmount *mnt, struct nameidata *nd) {return;} static int cap_syscall (int call, unsigned long *args) {return 0;} -static int cap_ptrace(struct task_struct *parent, struct task_struct *child) +static int cap_ptrace(struct task_struct *parent, struct task_struct *child, + int kern_retval) { + if (kern_retval) + return kern_retval; + /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ if (!cap_issubset(child->cap_permitted, current->cap_permitted) && !capable(CAP_SYS_PTRACE)) @@ -60,9 +64,52 @@ return 0; } -static int cap_setcapablity (void) {return 0;} -static int cap_acct (struct file *file) {return 0;} -static int cap_sysctl (ctl_table * table, int op) {return 0;} +static int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) +{ + /* Derived from kernel/capability.c:sys_capget. */ + *effective = cap_t(target->cap_effective); + *inheritable = cap_t(target->cap_inheritable); + *permitted = cap_t(target->cap_permitted); + return 0; +} + +static int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted, int kern_retval) +{ + if (kern_retval) + return kern_retval; + + /* Derived from kernel/capability.c:sys_capset. */ + /* verify restrictions on target's new Inheritable set */ + if (!cap_issubset(*inheritable, + cap_combine(target->cap_inheritable, + current->cap_permitted))) { + return -EPERM; + } + + /* verify restrictions on target's new Permitted set */ + if (!cap_issubset(*permitted, + cap_combine(target->cap_permitted, + current->cap_permitted))) { + return -EPERM; + } + + /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ + if (!cap_issubset(*effective, *permitted)) { + return -EPERM; + } + + return 0; +} + +static void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) +{ + target->cap_effective = *effective; + target->cap_inheritable = *inheritable; + target->cap_permitted = *permitted; +} + +static int cap_acct (struct file *file, int kern_retval) {return kern_retval;} +static int cap_sysctl (ctl_table * table, int op, int kern_retval) {return kern_retval;} static int cap_binprm_alloc_security (struct linux_binprm *bprm) {return 0;} @@ -168,9 +215,9 @@ static void cap_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return;} static int cap_inode_readlink (struct dentry *dentry) {return 0;} static int cap_inode_follow_link (struct dentry *dentry, struct nameidata *nameidata) {return 0;} -static int cap_inode_permission (struct inode *inode, int mask) {return 0;} +static int cap_inode_permission (struct inode *inode, int mask, int kern_retval) {return kern_retval;} static int cap_inode_revalidate (struct dentry *inode) {return 0;} -static int cap_inode_setattr (struct dentry *dentry, struct iattr *iattr) {return 0;} +static int cap_inode_setattr (struct dentry *dentry, struct iattr *iattr, int kern_retval) {return kern_retval;} static int cap_inode_stat (struct inode *inode) {return 0;} static void cap_post_lookup (struct inode *ino, struct dentry *d) {return;} static void cap_delete (struct inode *ino) {return;} @@ -186,7 +233,7 @@ static int cap_file_lock (struct file *file, unsigned int cmd) {return 0;} static int cap_file_fcntl (struct file *file, unsigned int cmd, unsigned long arg) {return 0;} static int cap_file_set_fowner (struct file *file) {return 0;} -static int cap_file_send_sigiotask (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason) { return 0; } +static int cap_file_send_sigiotask (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason, int kern_retval) { return kern_retval; } static int cap_file_receive (struct file *file) {return 0;} static int cap_task_create (unsigned long clone_flags) {return 0;} @@ -285,14 +332,14 @@ static int cap_task_setpgid (struct task_struct *p, pid_t pgid) {return 0;} static int cap_task_getpgid (struct task_struct *p) {return 0;} static int cap_task_getsid (struct task_struct *p) {return 0;} -static int cap_task_setgroups (int gidsetsize, gid_t *grouplist) {return 0;} -static int cap_task_setnice (struct task_struct *p, int nice) {return 0;} -static int cap_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) {return 0;} -static int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp) {return 0;} +static int cap_task_setgroups (int gidsetsize, gid_t *grouplist, int kern_retval) {return kern_retval;} +static int cap_task_setnice (struct task_struct *p, int nice, int kern_retval) {return kern_retval;} +static int cap_task_setrlimit (unsigned int resource, struct rlimit *new_rlim, int kern_retval) {return kern_retval;} +static int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp, int kern_retval) {return kern_retval;} static int cap_task_getscheduler(struct task_struct *p) {return 0;} static int cap_task_wait (struct task_struct *p) {return 0;} -static int cap_task_kill (struct task_struct *p, struct siginfo *info, int sig) {return 0;} +static int cap_task_kill (struct task_struct *p, struct siginfo *info, int sig, int kern_retval) {return kern_retval;} static void cap_task_kmod_set_label(void) { @@ -358,11 +405,11 @@ static int cap_socket_shutdown (struct socket *sock, int how) {return 0;} static int cap_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) {return 0;} -static int cap_module_create_module (const char *name_user, size_t size) {return 0;} -static int cap_module_init_module (const char *name_user, struct module *mod_user) {return 0;} -static int cap_module_delete_module (const char *name_user) {return 0;} +static int cap_module_create_module (const char *name, size_t size,int kern_retval) {return kern_retval;} +static int cap_module_init_module (const char *name, struct module *mod_user, int kern_retval) {return kern_retval;} +static int cap_module_delete_module (const char *name, int kern_retval) {return kern_retval;} -static int cap_ipc_permission (struct kern_ipc_perm *ipcp, short flag) {return 0;} +static int cap_ipc_permission (struct kern_ipc_perm *ipcp, short flag, int kern_retval) {return kern_retval;} static int cap_ipc_getinfo (int id, int cmd) {return 0;} static int cap_msg_msg_alloc_security (struct msg_msg *msg) {return 0;} @@ -574,7 +621,9 @@ ioperm: cap_ioperm, iopl: cap_iopl, ptrace: cap_ptrace, - setcapability: cap_setcapablity, + capget: cap_capget, + capset_check: cap_capset_check, + capset_set: cap_capset_set, acct: cap_acct, sysctl: cap_sysctl, capable: cap_capable, diff -X /home/sds/dontdiff -ur lsm-wirex/security/security.c lsm/security/security.c --- lsm-wirex/security/security.c Mon Aug 13 08:19:47 2001 +++ lsm/security/security.c Thu Aug 16 12:33:49 2001 @@ -39,19 +39,21 @@ /* Stub functions for the default security function pointers in case no security model is loaded */ -static int dummy_sethostname (char *hostname) {return 0;} -static int dummy_setdomainname (char *domainname) {return 0;} -static int dummy_reboot (unsigned int cmd) {return 0;} +static int dummy_sethostname (char *hostname, int kern_retval) {return kern_retval;} +static int dummy_setdomainname (char *domainname, int kern_retval) {return kern_retval;} +static int dummy_reboot (unsigned int cmd, int kern_retval) {return kern_retval;} static int dummy_mount (char * dev_name, struct nameidata *nd, char * type, unsigned long flags, void * data) {return 0;} -static int dummy_umount (struct vfsmount *mnt, int flags) {return 0;} +static int dummy_umount (struct vfsmount *mnt, int flags, int kern_retval) {return kern_retval;} static void dummy_umount_close (struct vfsmount *mnt) {return;} static void dummy_umount_busy (struct vfsmount *mnt) {return;} static void dummy_post_remount (struct vfsmount *mnt, unsigned long flags, void *data) {return;} -static int dummy_ioperm (unsigned long from, unsigned long num, int turn_on) {return 0;} -static int dummy_iopl (unsigned int old, unsigned int level) {return 0;} -static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) {return 0;} -static int dummy_setcapablity (void) {return 0;} -static int dummy_acct (struct file *file) {return 0;} +static int dummy_ioperm (unsigned long from, unsigned long num, int turn_on, int kern_retval) {return kern_retval;} +static int dummy_iopl (unsigned int old, unsigned int level, int kern_retval) {return kern_retval;} +static int dummy_ptrace (struct task_struct *parent, struct task_struct *child, int kern_retval) {return kern_retval;} +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, int kern_retval) { return kern_retval; } +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_acct (struct file *file, int kern_retval) {return kern_retval;} static int dummy_capable(struct task_struct *tsk, int cap) { @@ -63,7 +65,7 @@ return -EPERM; } -static int dummy_sysctl (ctl_table * table, int op) {return 0;} +static int dummy_sysctl (ctl_table * table, int op, int kern_retval) {return kern_retval;} static void dummy_post_mountroot (struct super_block *sb) {return;} static void dummy_post_addmount (struct vfsmount *mnt, struct nameidata *nd) {return;} static int dummy_syscall (int call, unsigned long *args) {return 0;} @@ -95,9 +97,9 @@ static void dummy_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return;} static int dummy_inode_readlink (struct dentry *dentry) {return 0;} static int dummy_inode_follow_link (struct dentry *dentry, struct nameidata *nameidata) {return 0;} -static int dummy_inode_permission (struct inode *inode, int mask) { return 0; } +static int dummy_inode_permission (struct inode *inode, int mask, int kern_retval) { return kern_retval; } static int dummy_inode_revalidate (struct dentry *inode) {return 0;} -static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr) {return 0;} +static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr, int kern_retval) {return kern_retval;} static int dummy_inode_stat (struct inode *inode) {return 0;} static void dummy_post_lookup (struct inode *ino, struct dentry *d) {return;} static void dummy_delete (struct inode *ino) {return;} @@ -112,7 +114,7 @@ static int dummy_file_lock (struct file *file, unsigned int cmd) {return 0;} static int dummy_file_fcntl (struct file *file, unsigned int cmd, unsigned long arg) {return 0;} static int dummy_file_set_fowner (struct file *file) {return 0;} -static int dummy_file_send_sigiotask (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason) {return 0;} +static int dummy_file_send_sigiotask (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason, int kern_retval) {return kern_retval;} static int dummy_file_receive (struct file *file) {return 0;} static int dummy_task_create (unsigned long clone_flags) {return 0;} @@ -124,14 +126,14 @@ 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_setgroups (int gidsetsize, gid_t *grouplist, int kern_retval) {return kern_retval;} +static int dummy_task_setnice (struct task_struct *p, int nice, int kern_retval) {return kern_retval;} +static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim, int kern_retval) {return kern_retval;} +static int dummy_task_setscheduler (struct task_struct *p, int policy, struct sched_param *lp, int kern_retval) {return kern_retval;} 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_kill (struct task_struct *p, struct siginfo *info, int sig, int kern_retval) {return kern_retval;} static void dummy_task_kmod_set_label (void) {return;} @@ -194,11 +196,11 @@ static int dummy_socket_shutdown (struct socket *sock, int how) {return 0;} static int dummy_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) {return 0;} -static int dummy_module_create_module (const char *name_user, size_t size) {return 0;} -static int dummy_module_init_module (const char *name_user, struct module *mod_user) {return 0;} -static int dummy_module_delete_module (const char *name_user) {return 0;} +static int dummy_module_create_module (const char *name, size_t size, int kern_retval) {return kern_retval;} +static int dummy_module_init_module (const char *name, struct module *mod_user, int kern_retval) {return kern_retval;} +static int dummy_module_delete_module (const char *name, int kern_retval) {return kern_retval;} -static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag) { return 0; } +static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag, int kern_retval) { return kern_retval; } static int dummy_ipc_getinfo (int id, int cmd) {return 0;} static int dummy_msg_msg_alloc_security (struct msg_msg *msg) {return 0;} @@ -408,7 +410,9 @@ ioperm: dummy_ioperm, iopl: dummy_iopl, ptrace: dummy_ptrace, - setcapability: dummy_setcapablity, + capget: dummy_capget, + capset_check: dummy_capset_check, + capset_set: dummy_capset_set, acct: dummy_acct, capable: dummy_capable, sysctl: dummy_sysctl, @@ -482,7 +486,9 @@ !ops->ioperm || !ops->iopl || !ops->ptrace || - !ops->setcapability || + !ops->capget || + !ops->capset_check || + !ops->capset_set || !ops->acct || !ops->capable || !ops->sysctl || _______________________________________________ linux-security-module mailing list linux-security-moduleat_private http://mail.wirex.com/mailman/listinfo/linux-security-module
This archive was generated by hypermail 2b30 : Thu Aug 16 2001 - 11:19:25 PDT