This is SGI's initial LSM patch for C2 (now Controlled Access Protection Profile (CAPP)) auditing. It is not complete. There will be more coming. The work left to do is outlined in the notes below. A Security Audit Trail provides for the accountability requirements of a CAPP system. All access control decisions and changes to the security state of the system are logged, along with sufficient context information to be meaningful. This particular implementation is based on two primary sources; the withdrawn POSIX P1003.1eD17 DRAFT standard and the Irix audit trail implementation. General notes regarding the patch: Any system call which includes a hook that is used for auditing will also require a hook at each return. This is a result of the fact that an audit hook may set aside information to be included in an audit record, and that the record may not be complete until the return status of the system call is known. The return hook count is based on the number of returns present in a stock kernel and may be lower if existing hooks make some of them unnecessary. In some cases capability checks have been moved into the security module. This allows audit to be isolated to the secuirty module. Only the i386 architecture has been addressed. The mechanisms and data structures required to actually implement an audit facility are not included. In particular, the information collected and the way it's initialized, stored, and manipulated are not defined. This proposal assumes kernel-memory-space arguments except in a few, rare cases. This patch fixes earlier work in kernel/capability_plug.c and include/linux/security.h in regard to the msg_queue_security_ops and msg_msg_security_ops structures. It now compiles, but Stephen should probably look it over to check its doing what he wanted. It is not clear in all cases which is a better datum to pass to LSM hooks, a "struct files *" or a file descriptor. Each is useful in some cases, with the fd being better for logging but an "struct files *" better for actually making decisions. In at least one case passing both is proposed. "/tmp/foo" [New file] 324 lines, 10841 characters You have new mail anchovy% cat /tmp/foo General notes regarding the patch: Any system call which includes a hook that is used for auditing will also require a hook at each return. This is a result of the fact that an audit hook may set aside information to be included in an audit record, and that the record may not be complete until the return status of the system call is known. The return hook count is based on the number of returns present in a stock kernel and may be lower if existing hooks make some of them unnecessary. In some cases capability checks have been moved into the security module. This allows audit to be isolated to the secuirty module. Only the i386 architecture has been addressed. The mechanisms and data structures required to actually implement an audit facility are not included. In particular, the information collected and the way it's initialized, stored, and manipulated are not defined. This proposal assumes kernel-memory-space arguments except in a few, rare cases. This patch fixes earlier work in kernel/capability_plug.c and include/linux/security.h in regard to the msg_queue_security_ops and msg_msg_security_ops structures. It now compiles, but Stephen should probably look it over to check its doing what he wanted. It is not clear in all cases which is a better datum to pass to LSM hooks, a "struct files *" or a file descriptor. Each is useful in some cases, with the fd being better for logging but an "struct files *" better for actually making decisions. In at least one case passing both is proposed. System Calls needed: get / set capability mask get/set login user id (luid) get arbitrary process audit mask set audit mask of <process, proc. group, all or uid> CAPS NEEDED: CAP_AUDIT_WRITE, CAP_AUDIT_CONTROL Currently done: ================ ./kernel/acct.c sys_acct stdsec hook added (name), return_status hook ================ ./kernel/capability.c sys_capset stdsec(const int pid, const struct task_struct * target, const __u32 version, const kernel_cap_t *inheritable, const kernel_cap_t * permitted, const kernel_cap_t * effective) 1 return status hook ================ ./arch/i386/kernel/ioport.c: sys_ioperm 1 stdsec hook(from, num, turn_on) sys_iopl 1 stdsec hook(old,level) ======================= ./fs/buffer.c sys_bdflush stdsec hook added, 2 return_status hooks ================ ./fs/attr.c: inode_change_ok stdsec hook added ================ ./kernel/module.c sys_create_module stdsec hook added(name,size), status_return hook sys_init_module stdsec hook(name, mod), status_return hook sys_delete_module 2 stdsec hooks (name/NULL), return_status hook ========= ./arch/i386/kernel/process.c sys_execve use existing hook for for security_ops->execve(filename), add 1 return_status hook ================ ./arch/i386/kernel/sys_i386.c sys_pipe return pipe status sys_old_mmap stdsec hook (fd, prot, flags), return_status sys_mmap2 stdsec hook (fd, prot, flags), return_status ================ ./fs/dquot.c sys_quotactl stdsec hook added, return_status ================ ./fs/filesystems.c sys_nfsservctl stdsec hook added, return_status ================ ./fs/ioctl.c sys_ioctl stdsec hook added, return_status ================ ./fs/namei.c vfs_permission stdhook permission adapted to new vfs_permission hook vfs_create hook moved to catch all post-creates, error passed in open_namei return_status added vfs_mknod hook moved to catch all post-creates, error passed in vfs_mkdir hook moved to catch all post-creates, error passed in vfs_rmdir return_status added vfs_rmdir return_status added vfs_unlink return_status added vfs_symlink hook moved to catch all post-creates, error passed in vfs_link hook moved to catch all post-creates, error passed in vfs_rename_dir hooks moved to catch all post-creates, error passed in, return_status added ================ ./fs/open.c sys_statfs 1 return hook, 1 [m] entry hook (path) sys_fstatfs 1 return hook, 1 [m] entry hook (fd) do_sys_truncate 1 return hook, 1 [m] entry hook (path, length) do_sys_ftruncate 1 return hook, 1 [m] entry hook (fd, length) sys_utime 1 return hook (newattrs, time_isset, filename), [o] entry hook sys_utimes 1 return hook (newattrs, time_isset, filename), [o] entry hook sys_access 2 return hooks, 1 [m] entry hook (filename, mode) sys_chdir 1 return hook, 1 [m] entry hook (filename) sys_fchdir 1 return hook, 1 [m] entry hook (fd) sys_chroot 1 return hook, 1 [m] entry hook (filename) sys_fchmod 1 return hook, 1 [m] entry hook (fd, mode) ================ ./kernel/sys.c sys_setpriority stdsec, return_status sys_reboot stdsec, return_status (8 calls) sys_setregid stdsec, return_status sys_setgid stdsec, return_status sys_setreuid stdsec, return_status sys_setuid stdsec, return_status sys_setresuid stdsec, 2 return_status sys_setresgid stdsec, 2 return_status sys_setfsuid stdsec, 3 return_status sys_setfsgid stdsec, 3 return_status sys_setpgid partially done, return status sys_setsid stdsec, 2 return_status sys_setgroups added 2 return_status sys_sethostname added 2 return_status sys_setdomainname 3 return hooks, entry hook(name, len) sys_setrlimit 6 return hooks (resource, 0 or &new_rlim if resource valid) sys_prctl std_sec, 2 return_status ----------------------------------------------------------------------- Partials/outstanding issues: ================ ./mm/mmap.c The mm/mmap.c call to security_ops->file_ops->mmap() has been commented out. The function (do_mmap_pgoff()) is called from arch/i368/kernel/sys_i386:do_mmap2() and this is alreday hooked. After changing the prototype of the file_ops hooks to take fd's rather than struct file *, this was the only place that couldn't be fixed. ================ ./fs/proc/base.c The prototype to security_ops->ptrace() has changed to allow for passing in the pid and request type, passing PTRACE_PEEKUSR as the request type here, and a pid of 0. =============== ./drivers/char/tty_io.c Calls file_ops->set_fowner() with a dummy fd. ================ .arch/i386/kernel/ptrace.c sys_ptrace stdsec hook added, but still need to move uid and cap checks ================ ./fs/fcntl.c sys_dup2 incomplete, as the do_fcntl call needs to be audited with the F_DUPD cmd. ----------------------------------------------------------------------- ToDo's ================ ./fs/open.c sys_access need stdsec sys_chmod 1 return hook, 1 [m] entry hook (filename, mode) sys_chown 1 return hook, 1 [m] entry hook (filename, user, group) sys_lchown 1 return hook, 1 [m] entry hook (filename, user, group) sys_fchown 1 return hook, 1 [m] entry hook (fd, user, group) sys_open 1 return hook (filename, flags, fd), 1 [m] entry hook sys_close 2 return hooks, 1 entry hook (fd) sys_vhangup 2 return hooks, 1 entry hook ================ ./fs/read_write.c sys_read 1 return hook, 1 entry hook (fd) sys_write 1 return hook, 1 entry hook (fd) sys_readv 1 return hook, 1 entry hook (fd) sys_writev 1 return hook, 1 entry hook (fd) sys_pread 1 return hook, 1 entry hook (fd) sys_pwrite 1 return hook, 1 entry hook (fd) ================ ./fs/smbfs/file.c smb_file_permission 1 status hook on EACCES failure ================ ./fs/stat.c sys_stat 1 return hook, 1 entry hook(filename) sys_newstat 1 return hook, 1 entry hook(filename) ================ ./fs/super.c sys_umount 1 return hook, 1 entry hook(name) sys_mount 1 return hook, 1 entry hook (dev_name, dir_name, flags) sys_pivot_root 2 return hooks, 1 entry hook (new_root, put_old) ================ ./init/main.c init init_hook ================ ./ipc/msg.c sys_msgget 1 return hook, 1 entry hook (key, msgflg) sys_msgctl 9 return hooks, entry hook (msqid, cmd) sys_msgsnd 2 return hooks, entry hook(msqid) sys_msgrcv 2 return hooks, entry hook(msqid) ================ ./ipc/sem.c sys_semget 2 return hooks, entry hook (key, nsems, semflg) sys_semctl 5 return hooks, entry hook (semid, cmd) ================ ./ipc/shm.c sys_shmget 1 return hook (key, size, shmflg), [opt] entry hook sys_shmctl 16 return hooks, entry hook (shmid, cmd, shmid_ds) sys_shmat 4 return hooks, entry hook (shmid, shmflg) sys_shmdt 1 return hook, [opt] entry hook ================ ./kernel/exit.c do_exit hook to cleanup audit (existing hook in release_task would work) sys_exit 1 hook (error_code) ================ ./kernel/printk.c sys_syslog 2 return hooks, 1 [opt] entry hook ================ ./kernel/sched.c sys_nice 2 return hooks, 1 entry hook (increment) setscheduler 1 return hook, entry hook (pid,policy), hook after param copied to lp (lp.sched_priority) sys_sched_setscheduler 1 entry hook sys_sched_setparam 1 entry hook ================ ./kernel/signal.c sig_info 1 return hook needs(ret val of "bad_signal" or -1, t) note that we need to know if capable was used(1), failure(0), and success(2) -- i.e. pass/fail isn't sufficient sys_kill 1 return hook, entry hook(pid, sig) sys_rt_sigqueueinfo 3 return hooks, 1 entry hook(pid,sig) ================ ./kernel/sysctl.c sys_sysctl 2 return hooks (0 or tmp.name), [mand] entry hook test_perm status hook on -EACCES (type of failure, DAC/MAC) sys_stime 3 return hooks, [opt] entry hook sys_settimeofday 3 return hooks, entry hook, 2nd hook after arg copy (tv->tv_sec, tv->tv_usec) sys_adjtimex 3 return hooks, [opt] entry hook ================ ./mm/mlock.c sys_mlock 1 return hook, [opt] entry hook (start, len) sys_mlockall 1 return hook, [opt] entry hook (flags) ================ ./mm/swapfile.c sys_swapoff 2 return hooks, [mand] entry hook (specialfile) sys_swapon 2 return hooks, [mand] entry hook (specialfile, swap_flags) =============== ./net/socket.c sys_socket 2 return hooks, [mand] entry hook (family, type, protocol) ================ ./kernel/fork.c 2 return hooks (-1 or newpid), entry hook, new process init hook (could use existing) =============== ./include/linux/sched.h suser hook to save status of priv used or not fsuser hook to save status of priv used or not capable hook to save status of priv used or not struct task_struct security structure to hold luid and audit bitmask INIT_TASK audit defaults for initial process --------------------------------------------------------- The SGI Trusted Linux Team Includes: ktillat_private - Kevin Till lachlan.mcilroyat_private - Lachlan McIlroy lawat_private - Linda Walsh offerat_private - Richard Offer Special thanks to former members James Buster and Emil Ong. -- Casey Schaufler Manager, Trust Technology, SGI caseyat_private voice: 650.933.1634 casey_pat_private Pager: 888.220.0607 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/arch/i386/kernel/ioport.c ./arch/i386/kernel/ioport.c --- /work/bitkeeper/lsm/arch/i386/kernel/ioport.c Fri Jun 22 13:56:58 2001 +++ ./arch/i386/kernel/ioport.c Mon Jun 25 10:14:38 2001 @@ -61,9 +61,8 @@ if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) return -EINVAL; - if (turn_on && !capable(CAP_SYS_RAWIO)) - return -EPERM; + /* check that we have permission to do this */ retval = security_ops->ioperm(from, num, turn_on); if (retval) { return retval; @@ -115,11 +114,8 @@ if (level > 3) return -EINVAL; - /* Trying to gain more privileges? */ - if (level > old) { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - } + + /* check that we have permission to do this */ retval = security_ops->iopl(old, level); if (retval) { return retval; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/arch/i386/kernel/process.c ./arch/i386/kernel/process.c --- /work/bitkeeper/lsm/arch/i386/kernel/process.c Fri Jun 22 13:56:59 2001 +++ ./arch/i386/kernel/process.c Mon Jun 25 10:14:40 2001 @@ -728,14 +728,21 @@ char * filename; filename = getname((char *) regs.ebx); + error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; + + error = security_ops->execve(filename); + if ( error ) + goto free_out; error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); if (error == 0) current->ptrace &= ~PT_DTRACE; +free_out: putname(filename); out: + security_ops->return_status(error); return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/arch/i386/kernel/ptrace.c ./arch/i386/kernel/ptrace.c --- /work/bitkeeper/lsm/arch/i386/kernel/ptrace.c Fri Jun 22 13:56:59 2001 +++ ./arch/i386/kernel/ptrace.c Mon Jun 25 18:16:27 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(pid,request,current->p_pptr, current); if (ret) goto out; /* set the ptrace bit in the process flags. */ @@ -164,16 +164,17 @@ if (!child) goto out; + ret = security_ops->ptrace(pid,request,current,child); + if (ret) + goto out; ret = -EPERM; - if (pid == 1) /* you may not mess with init */ - goto out_tsk; if (request == PTRACE_ATTACH) { if (child == current) goto out_tsk; - ret = security_ops->ptrace(current, child); - if (ret) - goto out_tsk; + /* LSM-ToDo. + * This uid checking needs to be moved to the std_sec.c file. + */ ret = -EPERM; if(((current->uid != child->euid) || (current->uid != child->suid) || @@ -476,6 +477,7 @@ out_tsk: free_task_struct(child); out: + security_ops->return_status(ret); unlock_kernel(); return ret; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/arch/i386/kernel/sys_i386.c ./arch/i386/kernel/sys_i386.c --- /work/bitkeeper/lsm/arch/i386/kernel/sys_i386.c Fri Jun 22 13:56:59 2001 +++ ./arch/i386/kernel/sys_i386.c Mon Jun 25 17:00:57 2001 @@ -18,6 +18,7 @@ #include <linux/mman.h> #include <linux/file.h> #include <linux/utsname.h> +#include <linux/security.h> #include <asm/uaccess.h> #include <asm/ipc.h> @@ -36,6 +37,7 @@ if (copy_to_user(fildes, fd, 2*sizeof(int))) error = -EFAULT; } + security_ops->return_pipe_status(error,fd); return error; } @@ -69,7 +71,15 @@ unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) { - return do_mmap2(addr, len, prot, flags, fd, pgoff); + int retval = -EPERM; + + if ( security_ops->file_ops->mmap(fd, prot, flags) ) + goto out; + + retval = do_mmap2(addr, len, prot, flags, fd, pgoff); +out: + security_ops->return_status(retval); + return retval; } /* @@ -93,14 +103,22 @@ struct mmap_arg_struct a; int err = -EFAULT; - if (copy_from_user(&a, arg, sizeof(a))) + if (copy_from_user(&a, arg, sizeof(a))) { + err = -EFAULT; goto out; + } + + err=security_ops->file_ops->mmap(a.fd, a.prot, a.flags); + if ( err ) + goto err_out; err = -EINVAL; if (a.offset & ~PAGE_MASK) - goto out; + goto err_out; err = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); +err_out: + security_ops->return_status(err); out: return err; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/drivers/char/tty_io.c ./drivers/char/tty_io.c --- /work/bitkeeper/lsm/drivers/char/tty_io.c Fri Jun 22 13:56:35 2001 +++ ./drivers/char/tty_io.c Tue Jun 26 07:34:38 2001 @@ -1462,7 +1462,7 @@ if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; if (filp->f_owner.pid == 0) { - retval = security_ops->file_ops->set_fowner(filp); + retval = security_ops->file_ops->set_fowner(fd); if (retval) return retval; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/attr.c ./fs/attr.c --- /work/bitkeeper/lsm/fs/attr.c Fri Jun 22 13:56:05 2001 +++ ./fs/attr.c Mon Jun 25 10:14:42 2001 @@ -18,44 +18,12 @@ /* POSIX UID/GID verification for setting inode attributes. */ int inode_change_ok(struct inode *inode, struct iattr *attr) { - int retval = -EPERM; - unsigned int ia_valid = attr->ia_valid; - - /* If force is set do it anyway. */ - if (ia_valid & ATTR_FORCE) - goto fine; - - /* Make sure a caller can chown. */ - if ((ia_valid & ATTR_UID) && - (current->fsuid != inode->i_uid || - attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN)) - goto error; - - /* Make sure caller can chgrp. */ - if ((ia_valid & ATTR_GID) && - (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid) && - !capable(CAP_CHOWN)) - goto error; - - /* Make sure a caller can chmod. */ - if (ia_valid & ATTR_MODE) { - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) - goto error; - /* Also check the setgid bit! */ - if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : - inode->i_gid) && !capable(CAP_FSETID)) - attr->ia_mode &= ~S_ISGID; - } - - /* Check for setting the inode time. */ - if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) - goto error; - } -fine: - retval = 0; -error: - return retval; + /* keep this function in as its exported by the kernel (kernel/ksym.c). + * Until the kernel API is changed, keep the call, but simply + * call the hook. Worst case scenario, an extra function call, worth it + * for improved compatability --- offer. + */ + return security_ops->inode_ops->changeattr(inode, attr); } void inode_setattr(struct inode * inode, struct iattr * attr) @@ -127,9 +95,7 @@ if (inode->i_op && inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else { - error = inode_change_ok(inode, attr); - if (!error) - inode_setattr(inode, attr); + inode_setattr(inode, attr); } unlock_and_out: unlock_kernel(); @@ -138,5 +104,6 @@ if (dn_mask) inode_dir_notify(dentry->d_parent->d_inode, dn_mask); } + security_ops->return_status(error); return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/buffer.c ./fs/buffer.c --- /work/bitkeeper/lsm/fs/buffer.c Fri Jun 22 13:56:05 2001 +++ ./fs/buffer.c Mon Jun 25 10:14:43 2001 @@ -2618,11 +2618,15 @@ asmlinkage long sys_bdflush(int func, long data) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + int error; + + error = security_ops->bdflush(func); + if ( error ) + goto out; if (func == 1) { /* do_exit directly and let kupdate to do its work alone. */ + security_ops->return_status(0); do_exit(0); #if 0 /* left here as it's the only example of lazy-mm-stuff used from a syscall that doesn't care about the current mm context. */ @@ -2646,22 +2650,28 @@ if (func >= 2) { int i = (func-2) >> 1; if (i >= 0 && i < N_PARAM) { - if ((func & 1) == 0) - return put_user(bdf_prm.data[i], (int*)data); + if ((func & 1) == 0) { + error = put_user(bdf_prm.data[i], (int*)data); + goto out; + } if (data >= bdflush_min[i] && data <= bdflush_max[i]) { bdf_prm.data[i] = data; - return 0; + error=0; + goto out; } } - return -EINVAL; + error = -EINVAL; + goto out; } /* Having func 0 used to launch the actual bdflush and then never * return (unless explicitly killed). We return zero here to * remain semi-compatible with present update(8) programs. */ - return 0; +out: + security_ops->return_status(error); + return error; } /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/dnotify.c ./fs/dnotify.c --- /work/bitkeeper/lsm/fs/dnotify.c Fri Jun 22 13:56:05 2001 +++ ./fs/dnotify.c Mon Jun 25 17:04:28 2001 @@ -77,7 +77,7 @@ if (turning_off) goto out; - error = security_ops->file_ops->set_fowner(filp); + error = security_ops->file_ops->set_fowner(fd); if (error) { write_unlock(&dn_lock); return error; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/dquot.c ./fs/dquot.c --- /work/bitkeeper/lsm/fs/dquot.c Fri Jun 22 13:56:06 2001 +++ ./fs/dquot.c Mon Jun 25 10:14:44 2001 @@ -52,6 +52,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/init.h> +#include <linux/security.h> #include <asm/uaccess.h> @@ -1522,21 +1523,9 @@ if (id & ~0xFFFF) goto out; - ret = -EPERM; - switch (cmds) { - case Q_SYNC: - case Q_GETSTATS: - break; - case Q_GETQUOTA: - if (((type == USRQUOTA && current->euid != id) || - (type == GRPQUOTA && !in_egroup_p(id))) && - !capable(CAP_SYS_RESOURCE)) - goto out; - break; - default: - if (!capable(CAP_SYS_RESOURCE)) - goto out; - } + ret = security_ops->quotactl(cmds, type, special, id); + if ( ret ) + goto out; ret = -EINVAL; dev = NODEV; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/fcntl.c ./fs/fcntl.c --- /work/bitkeeper/lsm/fs/fcntl.c Fri Jun 22 13:56:06 2001 +++ ./fs/fcntl.c Tue Jun 26 07:49:23 2001 @@ -174,6 +174,7 @@ filp_close(tofree, files); err = newfd; out: + security_ops->return_dup_status(err,oldfd,newfd); return err; out_unlock: write_unlock(&files->file_lock); @@ -192,6 +193,7 @@ if (file) ret = dupfd(file, 0); + security_ops->return_dup_status(ret < 0 ? ret :0,fildes,ret < 0 ? -1 : ret); return ret; } @@ -238,6 +240,9 @@ get_file(filp); err = dupfd(filp, arg); } + /* LSM-ToDo + * somehow we need to record this event --- offer + */ break; case F_GETFD: err = get_close_on_exec(fd); @@ -274,7 +279,7 @@ case F_SETOWN: lock_kernel(); - err = security_ops->file_ops->set_fowner(filp); + err = security_ops->file_ops->set_fowner(fd); if (err) { unlock_kernel(); break; @@ -328,7 +333,7 @@ if (!filp) goto out; - err = security_ops->file_ops->fcntl(filp, cmd, arg); + err = security_ops->file_ops->fcntl(fd, cmd, arg); if (err) { fput(filp); return err; @@ -352,7 +357,7 @@ if (!filp) goto out; - err = security_ops->file_ops->fcntl64(filp, cmd, arg); + err = security_ops->file_ops->fcntl64(fd, cmd, arg); if (err) { fput(filp); return err; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/filesystems.c ./fs/filesystems.c --- /work/bitkeeper/lsm/fs/filesystems.c Fri Jun 22 13:56:05 2001 +++ ./fs/filesystems.c Mon Jun 25 10:14:45 2001 @@ -12,6 +12,7 @@ #include <linux/smp_lock.h> #include <linux/kmod.h> #include <linux/nfsd/interface.h> +#include <linux/security.h> #if defined(CONFIG_NFSD_MODULE) struct nfsd_linkage *nfsd_linkage = NULL; @@ -23,10 +24,18 @@ lock_kernel(); + ret=security_ops->nfsserv(cmd); + if ( ret ) + goto out; + + ret = -ENOSYS; + if (nfsd_linkage || (request_module ("nfsd") == 0 && nfsd_linkage)) ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp); +out: + security_ops->return_status(ret); unlock_kernel(); return ret; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/ioctl.c ./fs/ioctl.c --- /work/bitkeeper/lsm/fs/ioctl.c Fri Jun 22 13:56:06 2001 +++ ./fs/ioctl.c Tue Jun 26 09:07:33 2001 @@ -26,7 +26,7 @@ /* do we support this mess? */ if (!mapping->a_ops->bmap) return -EINVAL; - if (!capable(CAP_SYS_RAWIO)) + if (!security_ops->capable(current,CAP_SYS_RAWIO)) return -EPERM; if ((error = get_user(block, (int *) arg)) != 0) return error; @@ -59,11 +59,10 @@ error = 0; /* Call the Linux Security Module to perform its checks. */ - error = security_ops->file_ops->ioctl(filp, cmd, arg); - if (error) { - fput(filp); - goto out; - } + error = security_ops->file_ops->ioctl(fd, cmd, arg); + if (error) { + goto free_out; + } lock_kernel(); switch (cmd) { @@ -118,8 +117,10 @@ error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); } unlock_kernel(); +free_out: fput(filp); out: + security_ops->return_status(error); return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/locks.c ./fs/locks.c --- /work/bitkeeper/lsm/fs/locks.c Fri Jun 22 13:56:06 2001 +++ ./fs/locks.c Mon Jun 25 17:07:14 2001 @@ -1270,7 +1270,7 @@ *before = fl; list_add(&fl->fl_link, &file_lock_list); - error = security_ops->file_ops->set_fowner(filp); + error = security_ops->file_ops->set_fowner(fd); if (error) goto out_unlock; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/namei.c ./fs/namei.c --- /work/bitkeeper/lsm/fs/namei.c Fri Jun 22 13:56:06 2001 +++ ./fs/namei.c Mon Jun 25 11:47:02 2001 @@ -147,32 +147,13 @@ * for filesystem access without changing the "normal" uids which * are used for other things.. */ + +/* We need to keep this function pending changling the kernel/module API. + * vfs_permission() is an exported symbol. + */ int vfs_permission(struct inode * inode,int mask) { - int mode = inode->i_mode; - - if ((mask & S_IWOTH) && IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; /* Nobody gets write access to a read-only fs */ - - if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) - return -EACCES; /* Nobody gets write access to an immutable file */ - - if (current->fsuid == inode->i_uid) - mode >>= 6; - else if (in_group_p(inode->i_gid)) - mode >>= 3; - - if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE)) - return 0; - - /* read and search access */ - if ((mask == S_IROTH) || - (S_ISDIR(inode->i_mode) && !(mask & ~(S_IROTH | S_IXOTH)))) - if (capable(CAP_DAC_READ_SEARCH)) - return 0; - - return -EACCES; + return security_ops->inode_ops->vfs_permission(inode,mask); } int permission(struct inode * inode,int mask) @@ -192,6 +173,11 @@ unlock_kernel(); return retval; } + /* we should probably get rid of this function all together + * and simply call the LSM hook directly, but vfs_permission is + * called directly by other parts (and I bet it forms part of the + * kernel/module API --- offer + */ return vfs_permission(inode, mask); } @@ -928,8 +914,8 @@ up(&dir->i_zombie); if (!error) { inode_dir_notify(dir, DN_CREATE); - security_ops->inode_ops->post_create(dir, dentry, mode); } + security_ops->inode_ops->post_create(dir, dentry, mode,error); return error; } @@ -1127,6 +1113,7 @@ dput(dentry); exit: path_release(nd); + security_ops->return_status(error); return error; do_link: @@ -1226,8 +1213,8 @@ up(&dir->i_zombie); if (!error) { inode_dir_notify(dir, DN_CREATE); - security_ops->inode_ops->post_mknod(dir, dentry, mode, dev); } + security_ops->inode_ops->post_mknod(dir, dentry, mode, dev, error); return error; } @@ -1302,8 +1289,8 @@ up(&dir->i_zombie); if (!error) { inode_dir_notify(dir, DN_CREATE); - security_ops->inode_ops->post_mkdir(dir,dentry, mode); } + security_ops->inode_ops->post_mkdir(dir,dentry, mode, error); return error; } @@ -1394,6 +1381,7 @@ if (!error) dentry->d_inode->i_flags |= S_DEAD; } + security_ops->return_status(error); } double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); if (!error) { @@ -1468,6 +1456,7 @@ if (!error) d_delete(dentry); } + security_ops->return_status(error); } } } @@ -1546,8 +1535,8 @@ up(&dir->i_zombie); if (!error) { inode_dir_notify(dir, DN_CREATE); - security_ops->inode_ops->post_symlink(dir, dentry, oldname); } + security_ops->inode_ops->post_symlink(dir, dentry, oldname,error); return error; } @@ -1626,8 +1615,8 @@ up(&dir->i_zombie); if (!error) { inode_dir_notify(dir, DN_CREATE); - security_ops->inode_ops->post_link(old_dentry, dir, new_dentry); } + security_ops->inode_ops->post_link(old_dentry, dir, new_dentry,error); return error; } @@ -1749,8 +1738,10 @@ return error; error = security_ops->inode_ops->rename(old_dir, old_dentry, new_dir, new_dentry); - if (error) + if (error) { + security_ops->return_status(error); return error; + } DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); @@ -1788,9 +1779,8 @@ if (!error) { d_move(old_dentry,new_dentry); - security_ops->inode_ops->post_rename(old_dir, old_dentry, - new_dir, new_dentry); } + security_ops->inode_ops->post_rename(old_dir, old_dentry, new_dir, new_dentry, error); out_unlock: up(&old_dir->i_sb->s_vfs_rename_sem); return error; @@ -1822,8 +1812,10 @@ return -EPERM; error = security_ops->inode_ops->rename(old_dir, old_dentry, new_dir, new_dentry); - if (error) + if (error) { + security_ops->return_status(error); return error; + } DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); @@ -1839,7 +1831,7 @@ if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) { d_move(old_dentry, new_dentry); } - security_ops->inode_ops->post_rename(old_dir, old_dentry, new_dir, new_dentry); + security_ops->inode_ops->post_rename(old_dir, old_dentry, new_dir, new_dentry,error); return 0; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/open.c ./fs/open.c --- /work/bitkeeper/lsm/fs/open.c Fri Jun 22 13:56:06 2001 +++ ./fs/open.c Tue Jun 26 07:08:41 2001 @@ -29,8 +29,10 @@ if (sb->s_op && sb->s_op->statfs) { memset(buf, 0, sizeof(struct statfs)); retval = security_ops->sb_ops->statfs(sb); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } lock_kernel(); retval = sb->s_op->statfs(sb, buf); unlock_kernel(); @@ -111,6 +113,9 @@ if (!S_ISREG(inode->i_mode)) goto dput_and_out; + error=security_ops->inode_ops->truncate((const char *) nd.dentry->d_name.name,NULL,length); + if ( error ) + goto sec_out; error = permission(inode,MAY_WRITE); if (error) goto dput_and_out; @@ -141,6 +146,8 @@ } put_write_access(inode); +sec_out: + security_ops->return_status(error); dput_and_out: path_release(&nd); out: @@ -168,6 +175,11 @@ goto out; dentry = file->f_dentry; inode = dentry->d_inode; + + error=security_ops->inode_ops->ftruncate(fd,length); + if ( error ) + goto out_putf; + error = -EACCES; if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) goto out_putf; @@ -179,6 +191,7 @@ if (!error) error = do_truncate(dentry, length); out_putf: + security_ops->return_status(error); fput(file); out: return error; @@ -248,6 +261,7 @@ } error = notify_change(nd.dentry, &newattrs); dput_and_out: + security_ops->inode_ops->post_utime(nd.dentry,&newattrs,error); path_release(&nd); out: return error; @@ -292,6 +306,7 @@ } error = notify_change(nd.dentry, &newattrs); dput_and_out: + security_ops->inode_ops->post_utime(nd.dentry,&newattrs,error); path_release(&nd); out: return error; @@ -312,6 +327,11 @@ if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; + /* LSM-ToDo: + * We need a hook here for audit to record filename and mode. + * All the uid/cap mangling should be moved into a hook. + * --- offer + */ old_fsuid = current->fsuid; old_fsgid = current->fsgid; old_cap = current->cap_effective; @@ -353,12 +373,16 @@ if (IS_ERR(name)) goto out; + error = security_ops->inode_ops->chdir(name); + if (error) + goto sec_out; + error = 0; if (path_init(name,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd)) error = path_walk(name, &nd); putname(name); if (error) - goto out; + goto sec_out; error = permission(nd.dentry->d_inode,MAY_EXEC); if (error) @@ -368,6 +392,8 @@ dput_and_out: path_release(&nd); +sec_out: + security_ops->return_status(error); out: return error; } @@ -389,6 +415,10 @@ mnt = file->f_vfsmnt; inode = dentry->d_inode; + error=security_ops->inode_ops->fchdir(fd); + if (error) + goto out_putf; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out_putf; @@ -397,6 +427,7 @@ if (!error) set_fs_pwd(current->fs, mnt, dentry); out_putf: + security_ops->return_status(error); fput(file); out: return error; @@ -413,26 +444,28 @@ if (IS_ERR(name)) goto out; + error=security_ops->chroot(name); + if ( error ) + goto sec_out; + path_init(name, LOOKUP_POSITIVE | LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); error = path_walk(name, &nd); putname(name); if (error) - goto out; + goto sec_out; error = permission(nd.dentry->d_inode,MAY_EXEC); if (error) goto dput_and_out; - error = -EPERM; - if (!capable(CAP_SYS_CHROOT)) - goto dput_and_out; - set_fs_root(current->fs, nd.mnt, nd.dentry); set_fs_altroot(); error = 0; dput_and_out: path_release(&nd); +sec_out: + security_ops->return_status(error); out: return error; } @@ -452,6 +485,10 @@ dentry = file->f_dentry; inode = dentry->d_inode; + err=security_ops->inode_ops->fchmod(fd,mode); + if ( err ) + goto out_putf; + err = -EROFS; if (IS_RDONLY(inode)) goto out_putf; @@ -465,6 +502,7 @@ err = notify_change(dentry, &newattrs); out_putf: + security_ops->return_status(err); fput(file); out: return err; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/proc/base.c ./fs/proc/base.c --- /work/bitkeeper/lsm/fs/proc/base.c Fri Jun 22 13:56:06 2001 +++ ./fs/proc/base.c Mon Jun 25 19:09:11 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(0,PTRACE_PEEKUSR,current,p)==0)) static ssize_t mem_read(struct file * file, char * buf, size_t count, loff_t *ppos) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/read_write.c ./fs/read_write.c --- /work/bitkeeper/lsm/fs/read_write.c Fri Jun 22 13:56:06 2001 +++ ./fs/read_write.c Mon Jun 25 17:08:02 2001 @@ -131,7 +131,7 @@ ssize_t (*read)(struct file *, char *, size_t, loff_t *); ret = -EINVAL; if (file->f_op && (read = file->f_op->read) != NULL) { - ret = security_ops->file_ops->permission (file, MAY_READ); + ret = security_ops->file_ops->permission (fd, MAY_READ); if (!ret) ret = read(file, buf, count, &file->f_pos); } @@ -161,7 +161,7 @@ ssize_t (*write)(struct file *, const char *, size_t, loff_t *); ret = -EINVAL; if (file->f_op && (write = file->f_op->write) != NULL) { - ret = security_ops->file_ops->permission (file, MAY_WRITE); + ret = security_ops->file_ops->permission (fd, MAY_WRITE); if (!ret) ret = write(file, buf, count, &file->f_pos); } @@ -291,7 +291,7 @@ goto bad_file; if (file->f_op && (file->f_mode & FMODE_READ) && (file->f_op->readv || file->f_op->read)) { - ret = security_ops->file_ops->permission (file, MAY_READ); + ret = security_ops->file_ops->permission (fd, MAY_READ); if (!ret) ret = do_readv_writev(VERIFY_WRITE, file, vector, count); } @@ -314,7 +314,7 @@ goto bad_file; if (file->f_op && (file->f_mode & FMODE_WRITE) && (file->f_op->writev || file->f_op->write)) { - ret = security_ops->file_ops->permission (file, MAY_WRITE); + ret = security_ops->file_ops->permission (fd, MAY_WRITE); if (!ret) ret = do_readv_writev(VERIFY_READ, file, vector, count); } @@ -351,7 +351,7 @@ if (pos < 0) goto out; - ret = security_ops->file_ops->permission (file, MAY_READ); + ret = security_ops->file_ops->permission (fd, MAY_READ); if (ret) goto out; @@ -387,7 +387,7 @@ if (pos < 0) goto out; - ret = security_ops->file_ops->permission (file, MAY_WRITE); + ret = security_ops->file_ops->permission (fd, MAY_WRITE); if (ret) goto out; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/fs/super.c ./fs/super.c --- /work/bitkeeper/lsm/fs/super.c Fri Jun 22 13:56:06 2001 +++ ./fs/super.c Tue Jun 26 08:55:10 2001 @@ -1291,8 +1291,8 @@ return retval; retval = do_remount_sb(nd->mnt->mnt_sb, flags, data); - if (!retval) - security_ops->post_remount(nd->mnt, flags, data); + + security_ops->post_remount(nd->mnt, flags, data,retval); return retval; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/include/linux/security.h ./include/linux/security.h --- /work/bitkeeper/lsm/include/linux/security.h Fri Jun 22 13:56:10 2001 +++ ./include/linux/security.h Tue Jun 26 08:40:44 2001 @@ -25,6 +25,8 @@ #ifdef __KERNEL__ #include <linux/fs.h> +#include <linux/sched.h> +#include <linux/capability.h> #include <linux/binfmts.h> #include <linux/signal.h> #include <linux/resource.h> @@ -32,9 +34,10 @@ #include <linux/sem.h> #include <linux/shm.h> #include <linux/msg.h> +#include <linux/sysctl.h> /* Security plug operations */ -#define SECURITY_INTERFACE_VERSION 0x00000101 /* change this every time the security_operations structure changes */ +#define SECURITY_INTERFACE_VERSION 0x00000102 /* change this every time the security_operations structure changes */ struct binprm_security_ops { int (* alloc_security) (struct linux_binprm *bprm); // create per binprm security @@ -54,48 +57,56 @@ void (* free_security) (struct inode *inode); // free it int (* create) (struct inode *dir, struct dentry *dentry, int mode); - void (* post_create) (struct inode *dir, struct dentry *dentry, int mode); + void (* post_create) (struct inode *dir, struct dentry *dentry, int mode, int error); int (* link) (struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry); - void (* post_link) (struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry); + void (* post_link) (struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, int error); int (* unlink) (struct inode *dir, struct dentry *dentry); int (* symlink) (struct inode *dir, struct dentry *dentry, const char *old_name); - void (* post_symlink) (struct inode *dir, struct dentry *dentry, const char *old_name); + void (* post_symlink) (struct inode *dir, struct dentry *dentry, const char *old_name, int error); int (* mkdir) (struct inode *dir, struct dentry *dentry, int mode); - void (* post_mkdir) (struct inode *dir, struct dentry *dentry, int mode); + void (* post_mkdir) (struct inode *dir, struct dentry *dentry, int mode, int error); int (* rmdir) (struct inode *dir, struct dentry *dentry); int (* mknod) (struct inode *dir, struct dentry *dentry, int mode, dev_t dev); - void (* post_mknod) (struct inode *dir, struct dentry *dentry, int mode, dev_t dev); + void (* post_mknod) (struct inode *dir, struct dentry *dentry, int mode, dev_t dev, int error); int (* rename) (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); void (* post_rename) (struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry); + struct inode *new_dir, struct dentry *new_dentry, int error); int (* readlink) (struct dentry *dentry, char *buf, int bufsiz); int (* follow_link) (struct dentry *dentry, struct nameidata *nd); - int (* truncate) (struct inode *inode); + int (* truncate) (const char *path, struct inode *inode, loff_t length); /* allow for both real paths (inode=NULL) and vm (path=NULL) */ + int (* ftruncate) (int fd, loff_t length); int (* permission) (struct inode *inode, int mask); + int (* vfs_permission) (struct inode *inode, int mask); int (* revalidate) (struct dentry *dentry); int (* setattr) (struct dentry *dentry, struct iattr *attr); + int (* changeattr) (struct inode *inode, struct iattr *attr); void (* attach_pathlabel)(struct dentry *dentry, struct vfsmount *mnt); // DTE project needs this int (* stat) (struct inode *inode); + void (* post_utime)(struct dentry *dentry, struct iattr *attr, int error); + int (* chdir)(const char *path); + int (* fchdir) (int fd); + int (* chmod)(const char *path, mode_t mode); + int (* fchmod) (int fd,mode_t mode); }; struct file_security_ops { - int (* permission) (struct file *, int); + int (* permission) (int fd, int); int (* alloc_security) (struct file *); void (* free_security) (struct file *); /* cmw: essentially copied from struct file_operations */ - int (* llseek) (struct file *); - int (* read) (struct file *); - int (* write) (struct file *); - int (* ioctl) (struct file *, unsigned int cmd, unsigned long arg); - int (* mmap) (struct file *, unsigned long, unsigned long); + int (* llseek) (int fd); + int (* read) (int fd); + int (* write) (int fd); + int (* ioctl) (int fd, unsigned int cmd, unsigned long arg); + int (* mmap) (int fd, unsigned long, unsigned long); int (* mprotect) (struct vm_area_struct *, unsigned long); - int (* lock) (struct file *); - int (* readv) (struct file *); - int (* writev) (struct file *); - int (* fcntl) (struct file *, unsigned int, unsigned long); - int (* fcntl64) (struct file *, unsigned int, unsigned long); - int (* set_fowner) (struct file *file); + int (* lock) (int fd); + int (* readv) (int fd); + int (* writev) (int fd); + int (* fcntl) (int fd, unsigned int, unsigned long); + int (* fcntl64) (int fd, unsigned int, unsigned long); + int (* set_fowner) (int fd); int (* send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason); }; @@ -110,9 +121,15 @@ int (* setuid) (uid_t id0, uid_t id1, uid_t id2, int flags); int (* post_setuid) (uid_t old_ruid /* or fsuid */, uid_t old_euid, uid_t old_suid, int flags); int (* setgid) (gid_t id0, gid_t id1, gid_t id2, int flags); + int (* setpgid) (struct task_struct *p, pid_t pid, pid_t pgid); + int (* getpgid) (struct task_struct *p, pid_t pid); 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 (* setnice) (struct task_struct *p, int nice, int which, int who); + int (*setsid) (pid_t pid); + int (*getsid) (struct task_struct *p, pid_t pid); + int (* setrlimit) (unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim); + int (* prctl) (int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); int (* setscheduler) (struct task_struct *p, int policy); int (* kill) (struct task_struct *p, struct siginfo *info, int sig); int (* wait) (struct task_struct *p); @@ -121,6 +138,9 @@ int (* set_label) (char *filename); void (* reset_label) (void); + void (* exit) (int error_code); + int (* fork) (int error); + void (* kmod_set_label) (void); }; @@ -133,17 +153,27 @@ int (* delete_module) (const char *name_user); }; +/* this struct seemed to be missing--- + * I suspect Stephen will redo it soon--offer + */ +struct msg_msg_security_ops { + int (* alloc_security) (struct msg_msg *msg); + void (* free_security) (struct msg_msg *msg); + int (* test_message) (struct msg_msg *msg, long type, int mode); +}; + struct ipc_security_ops { int (* permission) (struct kern_ipc_perm *ipcp, short flag); int (* getinfo) (int id, int cmd); }; struct msg_queue_security_ops { - int (* create) (key_t key); // can i create - int (* permission) (void); - int (* setmaxqbytes) (void); - int (* setattr) (void); // can i set attributes - int (* delete) (void); // can i delete + int (* alloc_security) (struct msg_queue *msq); + void (*free_security) (struct msg_queue *msq); + int (* associate) (struct msg_queue *msq, int msqid, int msqflg); + int (*msgctl) (struct msg_queue *msq, int msqid, int cmd); + int (*msgsnd) (struct msg_queue *msq); + int (*msgrcv) (struct msg_queue *msq, struct msg_msg *msg, int msqid, int msgflg); }; struct shm_security_ops { @@ -177,14 +207,27 @@ void (* umount_close) (struct vfsmount *mnt); void (* umount_busy) (struct vfsmount *mnt); int (* remount) (struct vfsmount *mnt, unsigned long flags, void *data); - void (* post_remount) (struct vfsmount *mnt, unsigned long flags, void *data); + void (* post_remount) (struct vfsmount *mnt, unsigned long flags, void *data, int error); 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 (* ptrace) (long pid, long request, struct task_struct *parent, struct task_struct *child); + int (* setcapability) (int pid, struct task_struct * target, + __u32 version, + kernel_cap_t *inheritable, + kernel_cap_t * permitted, + kernel_cap_t * effective); // CAP_SETPCAP + int (* acct) (const char *path); int (* capable) (struct task_struct *tsk, int cap); + int (* sysctl) (ctl_table *table, int op); + int (* execve) (const char * path); + int (* return_pipe_status) (int error, int fd[2]); + int (* return_dup_status) (int error, int fd1, int fd2); + int (* bdflush) (int func); + int (* quotactl) (int cmd, int subcmd, const char *path, int fd); + int (* nfsserv) (int cmd); + int (* chroot)(const char *path); + struct binprm_security_ops * bprm_ops; struct super_block_security_ops * sb_ops; struct inode_security_ops * inode_ops; @@ -192,6 +235,7 @@ struct task_security_ops * task_ops; struct socket_security_ops * socket_ops; struct module_security_ops * module_ops; + struct msg_msg_security_ops * msg_msg_ops; struct ipc_security_ops * ipc_ops; struct msg_queue_security_ops * msg_queue_ops; struct shm_security_ops * shm_ops; @@ -200,6 +244,7 @@ /* allow module stacking */ int (* register_security) (const char *name, struct security_operations *ops); int (* unregister_security) (const char *name, struct security_operations *ops); + void (*return_status) (long); }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/ipc/msg.c ./ipc/msg.c --- /work/bitkeeper/lsm/ipc/msg.c Fri Jun 22 13:56:26 2001 +++ ./ipc/msg.c Mon Jun 25 10:14:50 2001 @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/list.h> +#include <linux/security.h> #include <asm/uaccess.h> #include "util.h" @@ -317,6 +318,7 @@ msg_unlock(id); } up(&msg_ids.sem); + security_ops->return_status(ret); return ret; } @@ -418,8 +420,10 @@ struct msq_setbuf setbuf; struct kern_ipc_perm *ipcp; - if (msqid < 0 || cmd < 0) + if (msqid < 0 || cmd < 0) { + security_ops->return_status(-EINVAL); return -EINVAL; + } version = ipc_parse_version(&cmd); @@ -429,8 +433,10 @@ { struct msginfo msginfo; int max_id; - if (!buf) + if (!buf) { + security_ops->return_status(-EFAULT); return -EFAULT; + } /* We must not return kernel stack data. * due to padding, it's not enough * to set all member fields. @@ -458,26 +464,35 @@ } max_id = msg_ids.max_id; up(&msg_ids.sem); - if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) + if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) { + security_ops->return_status(-EFAULT); return -EFAULT; - return (max_id < 0) ? 0: max_id; + } + if (max_id < 0) + max_id = 0; + security_ops->return_status(max_id); + return max_id; } case MSG_STAT: case IPC_STAT: { struct msqid64_ds tbuf; int success_return; - if (!buf) + if (!buf) { + security_ops->return_status(-EFAULT); return -EFAULT; - if(cmd == MSG_STAT && msqid >= msg_ids.size) + } + if(cmd == MSG_STAT && msqid >= msg_ids.size) { + security_ops->return_status(-EINVAL); return -EINVAL; - + } memset(&tbuf,0,sizeof(tbuf)); msq = msg_lock(msqid); - if (msq == NULL) + if (msq == NULL) { + security_ops->return_status(-EINVAL); return -EINVAL; - + } if(cmd == MSG_STAT) { success_return = msg_buildid(msqid, msq->q_perm.seq); } else { @@ -504,19 +519,27 @@ tbuf.msg_lspid = msq->q_lspid; tbuf.msg_lrpid = msq->q_lrpid; msg_unlock(msqid); - if (copy_msqid_to_user(buf, &tbuf, version)) + if (copy_msqid_to_user(buf, &tbuf, version)) { + security_ops->return_status(-EFAULT); return -EFAULT; + } + security_ops->return_status(success_return); return success_return; } case IPC_SET: - if (!buf) + if (!buf) { + security_ops->return_status(-EFAULT); return -EFAULT; - if (copy_msqid_from_user (&setbuf, buf, version)) + } + if (copy_msqid_from_user (&setbuf, buf, version)) { + security_ops->return_status(-EFAULT); return -EFAULT; + } break; case IPC_RMID: break; default: + security_ops->return_status(-EINVAL); return -EINVAL; } @@ -575,12 +598,14 @@ err = 0; out_up: up(&msg_ids.sem); + security_ops->return_status(err); return err; out_unlock_up: msg_unlock(msqid); goto out_up; out_unlock: msg_unlock(msqid); + security_ops->return_status(err); return err; } @@ -642,17 +667,24 @@ long mtype; int err; - if (msgsz > msg_ctlmax || (long) msgsz < 0 || msqid < 0) + if (msgsz > msg_ctlmax || (long) msgsz < 0 || msqid < 0) { + security_ops->return_status(-EINVAL); return -EINVAL; - if (get_user(mtype, &msgp->mtype)) + } + if (get_user(mtype, &msgp->mtype)) { + security_ops->return_status(-EFAULT); return -EFAULT; - if (mtype < 1) + } + if (mtype < 1) { + security_ops->return_status(-EINVAL); return -EINVAL; - + } msg = load_msg(msgp->mtext, msgsz); - if(IS_ERR(msg)) - return PTR_ERR(msg); - + if(IS_ERR(msg)) { + err = PTR_ERR(msg); + security_ops->return_status(err); + return err; + } msg->m_type = mtype; msg->m_ts = msgsz; @@ -669,7 +701,7 @@ if (ipcperms(&msq->q_perm, S_IWUGO)) goto out_unlock_free; - err = security_ops->msg_queue_ops->msgsnd(msq, msg, msqid, msgflg); + err = security_ops->msg_queue_ops->msgsnd(msq); if (err) goto out_unlock_free; @@ -718,6 +750,7 @@ out_free: if(msg!=NULL) free_msg(msg); + security_ops->return_status(err); return err; } @@ -750,13 +783,17 @@ int err; int mode; - if (msqid < 0 || (long) msgsz < 0) + if (msqid < 0 || (long) msgsz < 0) { + security_ops->return_status(-EINVAL); return -EINVAL; + } mode = convert_mode(&msgtyp,msgflg); msq = msg_lock(msqid); - if(msq==NULL) + if(msq==NULL) { + security_ops->return_status(-EINVAL); return -EINVAL; + } retry: err=-EACCES; if (ipcperms (&msq->q_perm, S_IRUGO)) @@ -805,6 +842,7 @@ msgsz = -EFAULT; } free_msg(msg); + security_ops->return_status(msgsz); return msgsz; } else { @@ -861,6 +899,7 @@ out_unlock: if(msqid!=-1) msg_unlock(msqid); + security_ops->return_status(err); return err; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/ipc/sem.c ./ipc/sem.c --- /work/bitkeeper/lsm/ipc/sem.c Fri Jun 22 13:56:26 2001 +++ ./ipc/sem.c Mon Jun 25 10:14:54 2001 @@ -181,7 +181,7 @@ if (nsems > sma->sem_nsems) err = -EINVAL; else if (ipcperms(&sma->sem_perm, semflg) || - security_ops->sem_ops->associate(sma, sem_buildid(id, sma->sem_perm.seq), semflg)) + (err=security_ops->sem_ops->associate(sma, sem_buildid(id, sma->sem_perm.seq), semflg))) err = -EACCES; else err = sem_buildid(id, sma->sem_perm.seq); @@ -189,6 +189,7 @@ } up(&sem_ids.sem); + security_ops->return_status(err); return err; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/ipc/shm.c ./ipc/shm.c --- /work/bitkeeper/lsm/ipc/shm.c Fri Jun 22 13:56:26 2001 +++ ./ipc/shm.c Mon Jun 25 10:14:54 2001 @@ -240,13 +240,14 @@ if (shp->shm_segsz < size) err = -EINVAL; else if (ipcperms(&shp->shm_perm, shmflg) || - security_ops->shm_ops->associate(shp, shm_buildid(id, shp->shm_perm.seq), shmflg)) + (err=security_ops->shm_ops->associate(shp, shm_buildid(id, shp->shm_perm.seq), shmflg))) err = -EACCES; else err = shm_buildid(id, shp->shm_perm.seq); shm_unlock(id); } up(&shm_ids.sem); + security_ops->return_status(err); return err; } @@ -380,8 +381,10 @@ struct shminfo64 shminfo; err = security_ops->ipc_ops->getinfo(shmid, cmd); - if (err) + if (err) { + security_ops->return_status(err); return err; + } memset(&shminfo,0,sizeof(shminfo)); shminfo.shmmni = shminfo.shmseg = shm_ctlmni; @@ -389,12 +392,15 @@ shminfo.shmall = shm_ctlall; shminfo.shmmin = SHMMIN; - if(copy_shminfo_to_user (buf, &shminfo, version)) + if(copy_shminfo_to_user (buf, &shminfo, version)) { + security_ops->return_status(-EFAULT); return -EFAULT; + } /* reading a integer is always atomic */ err= shm_ids.max_id; if(err<0) err = 0; + security_ops->return_status(err); return err; } case SHM_INFO: @@ -402,8 +408,10 @@ struct shm_info shm_info; err = security_ops->ipc_ops->getinfo(shmid, cmd); - if (err) + if (err) { + security_ops->return_status(err); return err; + } memset(&shm_info,0,sizeof(shm_info)); down(&shm_ids.sem); @@ -416,9 +424,12 @@ err = shm_ids.max_id; shm_unlockall(); up(&shm_ids.sem); - if(copy_to_user (buf, &shm_info, sizeof(shm_info))) + if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { + security_ops->return_status(-EFAULT); return -EFAULT; + } + security_ops->return_status(err < 0 ? 0 : err); return err < 0 ? 0 : err; } case SHM_STAT: @@ -458,8 +469,11 @@ tbuf.shm_lpid = shp->shm_lprid; tbuf.shm_nattch = shp->shm_nattch; shm_unlock(shmid); - if(copy_shmid_to_user (buf, &tbuf, version)) + if(copy_shmid_to_user (buf, &tbuf, version)) { + security_ops->return_status(-EFAULT); return -EFAULT; + } + security_ops->return_status(result); return result; } case SHM_LOCK: @@ -538,8 +552,10 @@ case IPC_SET: { - if(copy_shmid_from_user (&setbuf, buf, version)) + if(copy_shmid_from_user (&setbuf, buf, version)) { + security_ops->return_status(-EFAULT); return -EFAULT; + } down(&shm_ids.sem); shp = shm_lock(shmid); err=-EINVAL; @@ -579,6 +595,7 @@ up(&shm_ids.sem); return err; out_unlock: + security_ops->return_status(err); shm_unlock(shmid); return err; } @@ -637,6 +654,7 @@ err = security_ops->shm_ops->shmat(shp, shmid, shmaddr, shmflg); if (err) { shm_unlock(shmid); + security_ops->return_status(err); return err; } @@ -662,6 +680,7 @@ err = 0; if (IS_ERR(user_addr)) err = PTR_ERR(user_addr); + security_ops->return_status(err); return err; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/Makefile ./kernel/Makefile --- /work/bitkeeper/lsm/kernel/Makefile Fri Jun 22 13:56:10 2001 +++ ./kernel/Makefile Mon Jun 25 10:14:55 2001 @@ -15,7 +15,7 @@ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ sysctl.o acct.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o context.o \ - security.o + security.o std_sec.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/acct.c ./kernel/acct.c --- /work/bitkeeper/lsm/kernel/acct.c Fri Jun 22 13:56:10 2001 +++ ./kernel/acct.c Tue Jun 26 08:16:28 2001 @@ -156,16 +156,20 @@ { struct file *file = NULL, *old_acct = NULL; char *tmp; - int error; - - if (!capable(CAP_SYS_PACCT)) - return -EPERM; + long int error=0; if (name) { tmp = getname(name); error = PTR_ERR(tmp); + if (IS_ERR(tmp)) + return error; + + if ((error=security_ops->acct(tmp))) { + putname(tmp); goto out; + } + /* Difference from BSD - they don't do O_APPEND */ file = filp_open(tmp, O_WRONLY|O_APPEND, 0); putname(tmp); @@ -180,11 +184,11 @@ error = -EIO; if (!file->f_op->write) goto out_err; + } else { + if ((error = security_ops->acct(NULL))) + goto out_err; } - error = security_ops->acct(file); - if (error) - goto out_err; error = 0; lock_kernel(); @@ -211,6 +215,7 @@ filp_close(old_acct, NULL); } out: + security_ops->return_status(error); return error; out_err: filp_close(file, NULL); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/capability.c ./kernel/capability.c --- /work/bitkeeper/lsm/kernel/capability.c Fri Jun 22 13:56:10 2001 +++ ./kernel/capability.c Tue Jun 26 08:29:30 2001 @@ -128,33 +128,34 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) { - kernel_cap_t inheritable, permitted, effective; - __u32 version; + kernel_cap_t inheritable=0 , permitted=0, effective=0; + __u32 version=0; struct task_struct *target; - int error, pid; + int error=0, pid=-EINVAL; + + error=0; if (get_user(version, &header->version)) - return -EFAULT; + return -EFAULT; if (version != _LINUX_CAPABILITY_VERSION) { version = _LINUX_CAPABILITY_VERSION; if (put_user(version, &header->version)) - return -EFAULT; - return -EINVAL; + return -EFAULT; + return -EINVAL; } if (get_user(pid, &header->pid)) - return -EFAULT; - - if (pid && !capable(CAP_SETPCAP)) - return -EPERM; + return -EINVAL; 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))) - return -EFAULT; + return -EINVAL; + + error=0; + - error = -EPERM; spin_lock(&task_capability_lock); if (pid > 0 && pid != current->pid) { @@ -170,23 +171,12 @@ /* 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; - } + error = -EPERM; - /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ - if (!cap_issubset(effective, permitted)) { - goto out; - } + if (security_ops->setcapability(pid, target, version, &inheritable, + &permitted, &effective)) + goto out; /* having verified that the proposed changes are legal, we now put them into effect. */ @@ -212,5 +202,7 @@ } spin_out: spin_unlock(&task_capability_lock); + security_ops->return_status(error); return error; } + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/capability_plug.c ./kernel/capability_plug.c --- /work/bitkeeper/lsm/kernel/capability_plug.c Fri Jun 22 13:56:10 2001 +++ ./kernel/capability_plug.c Tue Jun 26 08:41:19 2001 @@ -45,8 +45,11 @@ return -EPERM; } -static int cap_ptrace(struct task_struct *parent, struct task_struct *child) +static int cap_ptrace(long pid, long request, struct task_struct *parent, struct task_struct *child) { + if ( pid == 1 ) + return -EPERM; + /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ if (!cap_issubset(child->cap_permitted, current->cap_permitted) && !cap_capable(current, CAP_SYS_PTRACE)) @@ -55,8 +58,8 @@ return 0; } -static int cap_setcapablity (void) {return 0;} -static int cap_acct (struct file *file) { return 0; } +static int cap_setcapablity (int pid, struct task_struct * target, __u32 version, kernel_cap_t *inheritable, kernel_cap_t * permitted, kernel_cap_t * effective) {return 0;} +static int cap_acct (const char *path) { return 0; } static int cap_binprm_alloc_security(struct linux_binprm *bprm) { @@ -145,44 +148,44 @@ static int cap_inode_alloc_security (struct inode *inode) {return 0;} static void cap_inode_free_security (struct inode *inode) {return;} static int cap_inode_create (struct inode *inode, struct dentry *dentry, int mask) {return 0;} -static void cap_inode_post_create (struct inode *inode, struct dentry *dentry, int mask) {return;} +static void cap_inode_post_create (struct inode *inode, struct dentry *dentry, int mask, int error) {return;} static int cap_inode_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) {return 0;} -static void cap_inode_post_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) {return;} +static void cap_inode_post_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry, int error) {return;} static int cap_inode_unlink (struct inode *inode, struct dentry *dentry) {return 0;} static int cap_inode_symlink (struct inode *inode, struct dentry *dentry, const char *name) {return 0;} -static void cap_inode_post_symlink (struct inode *inode, struct dentry *dentry, const char *name) {return;} +static void cap_inode_post_symlink (struct inode *inode, struct dentry *dentry, const char *name, int error) {return;} static int cap_inode_mkdir (struct inode *inode, struct dentry *dentry, int mask) {return 0;} -static void cap_inode_post_mkdir (struct inode *inode, struct dentry *dentry, int mask) {return;} +static void cap_inode_post_mkdir (struct inode *inode, struct dentry *dentry, int mask, int error) {return;} static int cap_inode_rmdir (struct inode *inode, struct dentry *dentry) {return 0;} static int cap_inode_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) {return 0;} -static void cap_inode_post_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) {return;} +static void cap_inode_post_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor, int error) {return;} static int cap_inode_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return 0;} -static void cap_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return;} +static void cap_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry, int error) {return;} static int cap_inode_readlink (struct dentry *dentry, char *name, int mask) {return 0;} static int cap_inode_follow_link (struct dentry *dentry, struct nameidata *nameidata) {return 0;} -static int cap_inode_truncate (struct inode *inode) {return 0;} +static int cap_inode_truncate (struct dentry *dentry, loff_t len) {return 0;} static int cap_inode_permission(struct inode *inode, int mask) { return 0; } static int cap_inode_revalidate (struct dentry *inode) {return 0;} static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr) { return 0; } static void cap_inode_attach_pathlabel(struct dentry *dentry, struct vfsmount *mnt) {return;} static int cap_inode_stat(struct inode *inode) {return 0;} -static int cap_file_permission (struct file *file, int mask) {return 0;} +static int cap_file_permission (int fd, int mask) {return 0;} static int cap_file_alloc_security (struct file *file) {return 0;} static void cap_file_free_security (struct file *file) {return;} -static int cap_file_llseek (struct file *file) {return 0;} -static int cap_file_read (struct file *file) {return 0;} -static int cap_file_write (struct file *file) {return 0;} -static int cap_file_ioctl (struct file *file, unsigned int command +static int cap_file_llseek (int fd) {return 0;} +static int cap_file_read (int fd) {return 0;} +static int cap_file_write (int fd) {return 0;} +static int cap_file_ioctl (int fd, unsigned int command , unsigned long arg) {return 0;} -static int cap_file_mmap (struct file *file, unsigned long prot, unsigned long flags) {return 0;} +static int cap_file_mmap (int fd, unsigned long prot, unsigned long flags) {return 0;} static int cap_file_mprotect (struct vm_area_struct *vma, unsigned long prot) {return 0;} -static int cap_file_lock (struct file *file) {return 0;} -static int cap_file_readv (struct file *file) {return 0;} -static int cap_file_writev (struct file *file) {return 0;} -static int cap_file_fcntl (struct file *file, unsigned int cmd, unsigned long arg) {return 0;} -static int cap_file_fcntl64 (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_lock (int fd) {return 0;} +static int cap_file_readv (int fd) {return 0;} +static int cap_file_writev (int fd) {return 0;} +static int cap_file_fcntl (int fd, unsigned int cmd, unsigned long arg) {return 0;} +static int cap_file_fcntl64 (int fd, unsigned int cmd, unsigned long arg) {return 0;} +static int cap_file_set_fowner (int fd) {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_task_create (void) {return 0;} @@ -279,8 +282,8 @@ static int cap_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags) { 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_setnice(struct task_struct *p, int nice, int which, int who) { return 0; } +static int cap_task_setrlimit(unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim) { return 0; } static int cap_task_setscheduler(struct task_struct *p, int policy) { return 0; } static int cap_task_wait (struct task_struct *p) {return 0;} @@ -302,11 +305,12 @@ static int cap_ipc_permission(struct kern_ipc_perm *ipcp, short flag) { return 0; } static int cap_ipc_getinfo(int id, int cmd) {return 0;} -static int cap_msg_queue_create (key_t key) {return 0;} -static int cap_msg_queue_permission (void) {return 0;} -static int cap_msg_queue_setmaxqbytes (void) {return 0;} -static int cap_msg_queue_setattr (void) {return 0;} -static int cap_msg_queue_delete (void) {return 0;} +static int cap_msg_queue_alloc_security(struct msg_queue *msq) {return 0;} +static void cap_msg_queue_free_security(struct msg_queue *msq) {return;} +static int cap_msg_queue_associate(struct msg_queue *msq, int msqid, int msqflg) {return 0;} +static int cap_msg_queue_msgctl(struct msg_queue *msq, int msqid, int cmd) {return 0;} +static int cap_msg_queue_msgsnd(struct msg_queue *msq) {return 0;} +static int cap_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, int msqid, int msgflg) {return 0;} static int cap_shm_alloc_security(struct shmid_kernel *shp) {return 0;} static void cap_shm_free_security(struct shmid_kernel *shp) {return;} @@ -414,11 +418,12 @@ }; static struct msg_queue_security_ops cap_msg_queue_ops = { - create: cap_msg_queue_create, - permission: cap_msg_queue_permission, - setmaxqbytes: cap_msg_queue_setmaxqbytes, - setattr: cap_msg_queue_setattr, - delete: cap_msg_queue_delete, + alloc_security: cap_msg_queue_alloc_security, + free_security: cap_msg_queue_free_security, + associate: cap_msg_queue_associate, + msgctl: cap_msg_queue_msgctl, + msgsnd: cap_msg_queue_msgsnd, + msgrcv: cap_msg_queue_msgrcv, }; static struct shm_security_ops cap_shm_ops = { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/exit.c ./kernel/exit.c --- /work/bitkeeper/lsm/kernel/exit.c Fri Jun 22 13:56:10 2001 +++ ./kernel/exit.c Mon Jun 25 10:14:57 2001 @@ -44,7 +44,6 @@ task_unlock(p); #endif atomic_dec(&p->user->processes); - security_ops->task_ops->free_security(p); free_uid(p->user); unhash_process(p); @@ -439,6 +438,7 @@ #ifdef CONFIG_BSD_PROCESS_ACCT acct_process(code); #endif + security_ops->task_ops->free_security(tsk); __exit_mm(tsk); lock_kernel(); @@ -485,6 +485,7 @@ asmlinkage long sys_exit(int error_code) { + security_ops->task_ops->exit(error_code); do_exit((error_code&0xff)<<8); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/fork.c ./kernel/fork.c --- /work/bitkeeper/lsm/kernel/fork.c Fri Jun 22 13:56:10 2001 +++ ./kernel/fork.c Mon Jun 25 10:14:58 2001 @@ -564,8 +564,10 @@ if (clone_flags & CLONE_PID) { /* This is only allowed from the boot up thread */ - if (current->pid) + if (current->pid) { + security_ops->task_ops->fork(-EPERM); return -EPERM; + } } current->vfork_sem = &sem; @@ -577,8 +579,10 @@ *p = *current; retval = -EAGAIN; - if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) + + if (security_ops->task_ops->fork(0)) goto bad_fork_free; + atomic_inc(&p->user->__count); atomic_inc(&p->user->processes); @@ -707,6 +711,7 @@ fork_out: if ((clone_flags & CLONE_VFORK) && (retval > 0)) down(&sem); + security_ops->return_status(retval); return retval; bad_fork_cleanup_mm: diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/module.c ./kernel/module.c --- /work/bitkeeper/lsm/kernel/module.c Fri Jun 22 13:56:10 2001 +++ ./kernel/module.c Tue Jun 26 08:14:29 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; @@ -343,6 +341,7 @@ put_mod_name(name); err0: unlock_kernel(); + security_ops->return_status(error); return error; } @@ -359,8 +358,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; @@ -593,6 +590,7 @@ err0: unlock_kernel(); kfree(name_tmp); + security_ops->return_status(error); return error; } @@ -656,6 +654,9 @@ goto out; } + if ((error=security_ops->module_ops->delete_module(NULL))) + goto out; + /* Do automatic reaping */ restart: something_changed = 0; @@ -693,6 +694,7 @@ error = 0; out: unlock_kernel(); + security_ops->return_status(error); return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/sched.c ./kernel/sched.c --- /work/bitkeeper/lsm/kernel/sched.c Fri Jun 22 13:56:10 2001 +++ ./kernel/sched.c Mon Jun 25 10:14:59 2001 @@ -849,12 +849,8 @@ * We don't have to worry. Conceptually one call occurs first * and we have a single winner. */ - if (increment < 0) { - if (!capable(CAP_SYS_NICE)) - return -EPERM; - if (increment < -40) - increment = -40; - } + if (increment < -40) + increment = -40; if (increment > 40) increment = 40; @@ -864,11 +860,14 @@ if (newprio > 19) newprio = 19; - retval = security_ops->task_ops->setnice(current, newprio); - if (retval) + retval = security_ops->task_ops->setnice(current, newprio,PRIO_PROCESS,0); + if (retval) { + security_ops->return_status(retval); return retval; + } current->nice = newprio; + security_ops->return_status(0); return 0; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/security.c ./kernel/security.c --- /work/bitkeeper/lsm/kernel/security.c Fri Jun 22 13:56:10 2001 +++ ./kernel/security.c Tue Jun 26 08:41:05 2001 @@ -45,12 +45,15 @@ static void dummy_umount_close (struct vfsmount *mnt) {return;} static void dummy_umount_busy (struct vfsmount *mnt) {return;} static int dummy_remount (struct vfsmount *mnt, unsigned long flags, void *data) {return 0;} -static void dummy_post_remount (struct vfsmount *mnt, unsigned long flags, void *data) {return;} +static void dummy_post_remount (struct vfsmount *mnt, unsigned long flags, void *data, int error) {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_ptrace (long pid, long request,struct task_struct *parent, struct task_struct *child) {return 0;} +static int dummy_setcapability (int pid, struct task_struct * target, __u32 version, kernel_cap_t *inheritable, kernel_cap_t * permitted, kernel_cap_t * effective) {return 0;} +static void dummy_exit (int error_code) {return;} +static int dummy_fork (int error_code) {return 0;} + +static int dummy_acct (int fd) {return 0;} static int dummy_capable(struct task_struct *tsk, int cap) { @@ -63,6 +66,13 @@ } static int dummy_sysctl (ctl_table * table, int op) {return 0;} +static int dummy_bdflush(int func) { return 0; } +static int dummy_quotactl(int cmd, int subcmd, const char * path, int id) {return 0;}; +static void dummy_return_status (long status) {return;} +static int dummy_return_pipe_status (int error, int fd[2]) { return 0 ; } +static int dummy_return_dup_status (int error, int fd1, int fd2) { return 0 ; } +static int dummy_execve (const char * path) { return 0 ; } +static int dummy_chroot(const char *path) {return 0;} static int dummy_binprm_alloc_security(struct linux_binprm *bprm) {return 0;} static void dummy_binprm_free_security (struct linux_binprm *bprm) {return;} @@ -75,43 +85,51 @@ static int dummy_inode_alloc_security (struct inode *inode) {return 0;} static void dummy_inode_free_security (struct inode *inode) {return;} static int dummy_inode_create (struct inode *inode, struct dentry *dentry, int mask) {return 0;} -static void dummy_inode_post_create (struct inode *inode, struct dentry *dentry, int mask) {return;} +static void dummy_inode_post_create (struct inode *inode, struct dentry *dentry, int mask, int error) {return;} static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) {return 0;} -static void dummy_inode_post_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) {return;} +static void dummy_inode_post_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry, int error) {return;} static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry) {return 0;} static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry, const char *name) {return 0;} -static void dummy_inode_post_symlink (struct inode *inode, struct dentry *dentry, const char *name) {return;} +static void dummy_inode_post_symlink (struct inode *inode, struct dentry *dentry, const char *name, int error) {return;} static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry, int mask) {return 0;} -static void dummy_inode_post_mkdir (struct inode *inode, struct dentry *dentry, int mask) {return;} +static void dummy_inode_post_mkdir (struct inode *inode, struct dentry *dentry, int mask, int retval, int error) {return;} static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry) {return 0;} static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) {return 0;} -static void dummy_inode_post_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) {return;} +static void dummy_inode_post_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor, int error) {return;} static int dummy_inode_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return 0;} -static void dummy_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) {return;} +static void dummy_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry, int error) {return;} static int dummy_inode_readlink (struct dentry *dentry, char *name, int mask) {return 0;} static int dummy_inode_follow_link (struct dentry *dentry, struct nameidata *nameidata) {return 0;} -static int dummy_inode_truncate (struct inode *inode) {return 0;} +static int dummy_inode_truncate (const char *path,struct inode *inode, loff_t length) {return 0;} +static int dummy_inode_ftruncate(int fd, loff_t length) {return 0;} static int dummy_inode_permission(struct inode *inode, int mask) { return 0; } +static int dummy_inode_vfs_permission(struct inode *inode, int mask) { return 0; } 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_changeattr(struct inode *inode, struct iattr *iattr) { return 0; } static void dummy_inode_attach_pathlabel(struct dentry *dentry, struct vfsmount *mnt) {return;} static int dummy_inode_stat(struct inode *inode) {return 0;} +static void dummy_inode_post_utime(struct dentry *dentry, struct iattr *attr, int error) {return;} +static int dummy_inode_chdir(const char *dir) {return 0;} +static int dummy_inode_fchdir(int fd) {return 0;} +static int dummy_inode_chmod(const char *dir, mode_t mode) {return 0;} +static int dummy_inode_fchmod(int fd, mode_t mode) {return 0;} -static int dummy_file_permission (struct file *file, int mask) {return 0;} +static int dummy_file_permission (int fd, int mask) {return 0;} static int dummy_file_alloc_security (struct file *file) {return 0;} static void dummy_file_free_security (struct file *file) {return;} -static int dummy_file_llseek (struct file *file) {return 0;} -static int dummy_file_read (struct file *file) {return 0;} -static int dummy_file_write (struct file *file) {return 0;} -static int dummy_file_ioctl (struct file *file, unsigned int command, unsigned long arg) {return 0;} -static int dummy_file_mmap (struct file *file, unsigned long prot, unsigned long flags) {return 0;} +static int dummy_file_llseek (int fd) {return 0;} +static int dummy_file_read (int fd) {return 0;} +static int dummy_file_write (int fd) {return 0;} +static int dummy_file_ioctl (int fd, unsigned int command, unsigned long arg) {return 0;} +static int dummy_file_mmap (int fd, unsigned long prot, unsigned long flags) {return 0;} static int dummy_file_mprotect (struct vm_area_struct *vma, unsigned long prot) {return 0;} -static int dummy_file_lock (struct file *file) {return 0;} -static int dummy_file_readv (struct file *file) {return 0;} -static int dummy_file_writev (struct file *file) {return 0;} -static int dummy_file_fcntl (struct file *file, unsigned int cmd, unsigned long arg) {return 0;} -static int dummy_file_fcntl64 (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_lock (int fd) {return 0;} +static int dummy_file_readv (int fd) {return 0;} +static int dummy_file_writev (int fd) {return 0;} +static int dummy_file_fcntl (int fd, unsigned int cmd, unsigned long arg) {return 0;} +static int dummy_file_fcntl64 (int fd, unsigned int cmd, unsigned long arg) {return 0;} +static int dummy_file_set_fowner (int fd) {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_task_create (void) {return 0;} @@ -121,8 +139,11 @@ 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_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_setnice (struct task_struct *p, int nice, int which, int who) {return 0;} +static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim) {return 0;} +static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) {return 0;} + static int dummy_task_setscheduler (struct task_struct *p, int policy) {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;} @@ -195,11 +216,19 @@ readlink: dummy_inode_readlink, follow_link: dummy_inode_follow_link, truncate: dummy_inode_truncate, + ftruncate: dummy_inode_ftruncate, permission: dummy_inode_permission, + vfs_permission: dummy_inode_vfs_permission, revalidate: dummy_inode_revalidate, setattr: dummy_inode_setattr, + changeattr: dummy_inode_changeattr, attach_pathlabel:dummy_inode_attach_pathlabel, stat: dummy_inode_stat, + post_utime: dummy_inode_post_utime, + chdir: dummy_inode_chdir, + fchdir: dummy_inode_fchdir, + chmod: dummy_inode_chmod, + fchmod: dummy_inode_fchmod, }; static struct file_security_ops dummy_file_ops = { @@ -231,9 +260,12 @@ setgroups: dummy_task_setgroups, setnice: dummy_task_setnice, setrlimit: dummy_task_setrlimit, + prctl: dummy_task_prctl, setscheduler: dummy_task_setscheduler, wait: dummy_task_wait, kill: dummy_task_kill, + exit: dummy_exit, + fork: dummy_fork, set_label: dummy_task_set_label, reset_label: dummy_task_reset_label, kmod_set_label: dummy_task_kmod_set_label @@ -300,10 +332,16 @@ ioperm: dummy_ioperm, iopl: dummy_iopl, ptrace: dummy_ptrace, - setcapability: dummy_setcapablity, + setcapability: dummy_setcapability, acct: dummy_acct, capable: dummy_capable, sysctl: dummy_sysctl, + execve: dummy_execve, + return_pipe_status: dummy_return_pipe_status, + return_dup_status: dummy_return_dup_status, + quotactl: dummy_quotactl, + bdflush: dummy_bdflush, + chroot: dummy_chroot, bprm_ops: &dummy_binprm_ops, sb_ops: &dummy_sb_ops, @@ -320,6 +358,7 @@ register_security: dummy_register, unregister_security: dummy_unregister, + return_status: dummy_return_status, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/std_sec.c ./kernel/std_sec.c --- /work/bitkeeper/lsm/kernel/std_sec.c Wed Dec 31 16:00:00 1969 +++ ./kernel/std_sec.c Tue Jun 26 08:50:23 2001 @@ -0,0 +1,398 @@ +/* + * linux/kernel/std_sec.c + * + */ + +#include <linux/mm.h> +#include <asm/uaccess.h> +#include <linux/capability.h> + + +static inline int std_capable(int cap) +{ + /* verbatim copy from sched.h definition of capable */ +#if 1 /* ok now */ + if (cap_raised(current->cap_effective, cap)) +#else + if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) +#endif + { + current->flags |= PF_SUPERPRIV; + return 1; + } + return 0; +} + +int std_acct(const char * name) +{ + if (!std_capable(CAP_SYS_PACCT)) return -EPERM; + return 0; +} + +int std_setcapability(const int pid, const struct task_struct * target, + const __u32 version, const kernel_cap_t *inheritable, + const kernel_cap_t * permitted, const kernel_cap_t * effective) +{ + int error=-EINVAL; + + if (!inheritable || !target || !permitted || !effective) + goto out; + + error = -EPERM; + + if (target->pid && !std_capable(CAP_SETPCAP)) + 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; + } + error=0; + +out: + return error; +} + + +int std_fork(int error) +{ + if (!error) + return (atomic_read(¤t->user->processes) >= current->rlim[RLIMIT_NPROC].rlim_cur); + return error; +} + +int std_ioperm(unsigned long from, unsigned long num, int turn_on, int retval) +{ + if ( turn_on && ! std_capable(CAP_SYS_RAWIO)) + return retval; + return 0; +} + +int std_iopl(unsigned int old, unsigned int level, int retval) +{ + if ( ! std_capable(CAP_SYS_RAWIO)) + return retval; + return 0; +} + +int std_vfs_permission(struct inode * inode,int mask) +{ + int mode = inode->i_mode; + + if ((mask & S_IWOTH) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; /* Nobody gets write access to a read-only fs */ + + if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; /* Nobody gets write access to an immutable file */ + + if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p(inode->i_gid)) + mode >>= 3; + + if (((mode & mask & S_IRWXO) == mask) || std_capable(CAP_DAC_OVERRIDE)) + return 0; + + /* read and search access */ + if ((mask == S_IROTH) || + (S_ISDIR(inode->i_mode) && !(mask & ~(S_IROTH | S_IXOTH)))) + if (std_capable(CAP_DAC_READ_SEARCH)) + return 0; + + return -EACCES; +} + +int std_ptrace(long pid, long request, struct task_struct *parent, struct task_struct *child ) +{ + if ( pid == 1 ) /* do not mess with in init */ + return -EPERM; + return 0; +} + +int std_bdflush(int func) +{ + return ( std_capable(CAP_SYS_ADMIN) ? 0 : -EPERM ); + +} + +int std_quotactl(int cmd, int subcmd, const char * path, int id) +{ + int ret=0; + + switch ( cmd ) { + case Q_SYNC: + case Q_GETSTATS: + break; + case Q_GETQUOTA: + if (((subcmd == USRQUOTA && current->euid != id) || + (subcmd == GRPQUOTA && !in_egroup_p(id))) && + !std_capable(CAP_SYS_RESOURCE)) + ret=-EPERM; + break; + default: + if ( !std_capable(CAP_SYS_RESOURCE)) + ret=-EPERM; + break; + } + return ret; +} + + +int std_inode_changeattr(struct inode *inode, struct iattr *attr) +{ +/* POSIX UID/GID verification for setting inode attributes. */ + int retval=-EPERM; + unsigned int ia_valid = attr->ia_valid; + + /* If force is set do it anyway. */ + if (ia_valid & ATTR_FORCE) + goto fine; + + /* Make sure a caller can chown. */ + if ((ia_valid & ATTR_UID) && + (current->fsuid != inode->i_uid || + attr->ia_uid != inode->i_uid) && !std_capable(CAP_CHOWN)) + goto error; + + /* Make sure caller can chgrp. */ + if ((ia_valid & ATTR_GID) && + (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid) && + !std_capable(CAP_CHOWN)) + goto error; + + /* Make sure a caller can chmod. */ + if (ia_valid & ATTR_MODE) { + if ((current->fsuid != inode->i_uid) && !std_capable(CAP_FOWNER)) + goto error; + /* Also check the setgid bit! */ + if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : + inode->i_gid) && !std_capable(CAP_FSETID)) + attr->ia_mode &= ~S_ISGID; + } + + /* Check for setting the inode time. */ + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { + if (current->fsuid != inode->i_uid && !std_capable(CAP_FOWNER)) + goto error; + } +fine: + retval = 0; +error: + return retval; +} + +int std_inode_setattr(struct dentry *dentry, struct iattr *attr) +{ + return std_inode_changeattr(dentry->d_inode,attr); +} + +int std_chroot(const char *path) +{ + if ( ! std_capable(CAP_SYS_CHROOT) ) + return -EPERM; + + return 0; +} + +int std_reboot (unsigned int cmd, void * arg) +{ + if (!std_capable(CAP_SYS_BOOT)) + return -EPERM; + + return 0; +} + +int std_sethostname (char *name, int len) +{ + if (!std_capable(CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} + +int std_setdomainname (char *name, int len) +{ + if (!std_capable(CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} + +int std_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) +{ + if (capable(CAP_SETUID)) + return 0; + + switch (flags) + { + case LSM_SETID_RE: + if (id0 != (uid_t) -1) + if (current->uid != id0 && current->euid != id0) + return -EPERM; + if (id1 != (uid_t) -1) + if (current->uid != id1 && current->euid != id1 && + current->suid != id1) + return -EPERM; + break; + case LSM_SETID_ID: + if (id0 != current->uid && id0 != current->suid) + return -EPERM; + break; + case LSM_SETID_RES: + if ((id0 != (uid_t) -1) && (id0 != current->uid) && + (id0 != current->euid) && (id0 != current->suid)) + return -EPERM; + if ((id1 != (uid_t) -1) && (id1 != current->uid) && + (id1 != current->euid) && (id1 != current->suid)) + return -EPERM; + if ((id2 != (uid_t) -1) && (id2 != current->uid) && + (id2 != current->euid) && (id2 != current->suid)) + return -EPERM; + break; + case LSM_SETID_FS: + if (id0 != current->uid && id0 != current->euid && + id0 != current->suid && id0 != current->fsuid) + return -EPERM; + break; + } + + return 0; +} + +int std_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags) +{ + if (capable(CAP_SETGID)) + return 0; + + switch (flags) + { + case LSM_SETID_RE: + if (id0 != (gid_t) -1) + if (current->gid != id0 && current->egid != id0) + return -EPERM; + if (id1 != (gid_t) -1) + if (current->gid != id1 && current->egid != id1 && + current->sgid != id1) + return -EPERM; + break; + case LSM_SETID_ID: + if (id0 != current->gid && id0 != current->sgid) + return -EPERM; + break; + case LSM_SETID_RES: + if ((id0 != (gid_t) -1) && (id0 != current->gid) && + (id0 != current->egid) && (id0 != current->sgid)) + return -EPERM; + if ((id1 != (gid_t) -1) && (id1 != current->gid) && + (id1 != current->egid) && (id1 != current->sgid)) + return -EPERM; + if ((id2 != (gid_t) -1) && (id2 != current->gid) && + (id2 != current->egid) && (id2 != current->sgid)) + return -EPERM; + break; + case LSM_SETID_FS: + if (id0 != current->gid && id0 != current->egid && + id0 != current->sgid && id0 != current->fsgid) + return -EPERM; + break; + } + + return 0; +} + +int std_task_setpgid (struct task_struct *p, pid_t pid, pid_t pgid) +{ + return 0; +} + +int std_task_getpgid (struct task_struct *p, pid_t pid) +{ + return 0; +} + +int std_task_setsid (pid_t pid) +{ + return 0; +} + +int std_task_getsid (struct task_struct *p, pid_t pid) +{ + return 0; +} + +int std_task_setgroups (int gidsetsize, gid_t *grouplist) +{ + if (!capable(CAP_SETGID)) + return -EPERM; + + return 0; +} + +int std_task_setnice (struct task_struct *p, int niceval, int which, int who) +{ + if (p->uid != current->euid && + p->uid != current->uid && !capable(CAP_SYS_NICE)) + return -EPERM; + + if (niceval < p->nice && !capable(CAP_SYS_NICE)) + return -EACCES; + + return 0; +} + +int std_task_setrlimit (unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim) +{ + 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) + if (new_rlim->rlim_cur > NR_OPEN || new_rlim->rlim_max > NR_OPEN) + return -EPERM; + + return 0; +} + +int std_task_prctl (int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + return 0; +} + +int std_create_module(const char * name, size_t size) +{ + if (!capable(CAP_SYS_MODULE)) + return -EPERM; + return 0; +} + + +int std_init_module(const char *name, struct module * mod) +{ + if (!capable(CAP_SYS_MODULE)) + return -EPERM; + return 0; +} + +int std_delete_module(const char *name) +{ + if (!capable(CAP_SYS_MODULE)) + return -EPERM; + return 0; +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/kernel/sys.c ./kernel/sys.c --- /work/bitkeeper/lsm/kernel/sys.c Fri Jun 22 13:56:10 2001 +++ ./kernel/sys.c Tue Jun 26 08:55:42 2001 @@ -209,30 +209,25 @@ if (niceval > 19) niceval = 19; + error = security_ops->task_ops->setnice(NULL, niceval, which, who); + if (error) { + security_ops->return_status(error); + return error; + } + error=-EINVAL; + read_lock(&tasklist_lock); for_each_task(p) { - int no_nice; if (!proc_sel(p, which, who)) continue; - no_nice = security_ops->task_ops->setnice(p, niceval); - if (no_nice) { - error = no_nice; - continue; - } - if (p->uid != current->euid && - p->uid != current->uid && !capable(CAP_SYS_NICE)) { - error = -EPERM; + error = security_ops->task_ops->setnice(p, niceval, which, who); + if (error) continue; - } - if (error == -ESRCH) - error = 0; - if (niceval < p->nice && !capable(CAP_SYS_NICE)) - error = -EACCES; - else - p->nice = niceval; + p->nice = niceval; } read_unlock(&tasklist_lock); + security_ops->return_status(error); return error; } @@ -278,26 +273,25 @@ char buffer[256]; int retval; - /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT)) - return -EPERM; + /* For safety, we require "magic" arguments. */ + if (magic1 != LINUX_REBOOT_MAGIC1 || + (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B)) { + return -EINVAL; + } retval = security_ops->reboot(cmd, arg); if (retval) { + security_ops->return_status(retval); return retval; } - /* For safety, we require "magic" arguments. */ - if (magic1 != LINUX_REBOOT_MAGIC1 || - (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && - magic2 != LINUX_REBOOT_MAGIC2B)) - return -EINVAL; - lock_kernel(); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); printk(KERN_EMERG "Restarting system.\n"); + security_ops->return_status(0); machine_restart(NULL); break; @@ -312,6 +306,7 @@ case LINUX_REBOOT_CMD_HALT: notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); printk(KERN_EMERG "System halted.\n"); + security_ops->return_status(0); machine_halt(); do_exit(0); break; @@ -319,6 +314,7 @@ case LINUX_REBOOT_CMD_POWER_OFF: notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); printk(KERN_EMERG "Power down.\n"); + security_ops->return_status(0); machine_power_off(); do_exit(0); break; @@ -326,20 +322,24 @@ case LINUX_REBOOT_CMD_RESTART2: if (strncpy_from_user(&buffer[0], (char *)arg, sizeof(buffer) - 1) < 0) { unlock_kernel(); + security_ops->return_status(-EFAULT); return -EFAULT; } buffer[sizeof(buffer) - 1] = '\0'; notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); + security_ops->return_status(0); machine_restart(buffer); break; default: unlock_kernel(); + security_ops->return_status(-EINVAL); return -EINVAL; } unlock_kernel(); + security_ops->return_status(0); return 0; } @@ -397,24 +397,10 @@ if (retval) return retval; - if (rgid != (gid_t) -1) { - if ((old_rgid == rgid) || - (current->egid==rgid) || - capable(CAP_SETGID)) - new_rgid = rgid; - else - return -EPERM; - } - if (egid != (gid_t) -1) { - if ((old_rgid == egid) || - (current->egid == egid) || - (current->sgid == egid) || - capable(CAP_SETGID)) - new_egid = egid; - else { - return -EPERM; - } - } + if (rgid != (gid_t) -1) + new_rgid = rgid; + if (egid != (gid_t) -1) + new_egid = egid; if (new_egid != old_egid) { current->dumpable = 0; @@ -426,6 +412,7 @@ current->fsgid = new_egid; current->egid = new_egid; current->gid = new_rgid; + security_ops->return_status(0); return 0; } @@ -440,10 +427,8 @@ int retval; retval = security_ops->task_ops->setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID); - if (retval) - return retval; - if (capable(CAP_SETGID)) + if (!retval) { if(old_egid != gid) { @@ -452,18 +437,9 @@ } current->gid = current->egid = current->sgid = current->fsgid = gid; } - else if ((gid == current->gid) || (gid == current->sgid)) - { - if(old_egid != gid) - { - current->dumpable=0; - wmb(); - } - current->egid = current->fsgid = gid; - } - else - return -EPERM; - return 0; + + security_ops->return_status(retval); + return retval; } static int set_user(uid_t new_ruid, int dumpclear) @@ -514,32 +490,24 @@ int retval; retval = security_ops->task_ops->setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } new_ruid = old_ruid = current->uid; new_euid = old_euid = current->euid; old_suid = current->suid; - if (ruid != (uid_t) -1) { + if (ruid != (uid_t) -1) new_ruid = ruid; - if ((old_ruid != ruid) && - (current->euid != ruid) && - !capable(CAP_SETUID)) - return -EPERM; - } - - if (euid != (uid_t) -1) { + if (euid != (uid_t) -1) new_euid = euid; - if ((old_ruid != euid) && - (current->euid != euid) && - (current->suid != euid) && - !capable(CAP_SETUID)) - return -EPERM; - } - if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) + if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) { + security_ops->return_status(-EAGAIN); return -EAGAIN; + } if (new_euid != old_euid) { @@ -575,19 +543,20 @@ int retval; retval = security_ops->task_ops->setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } old_ruid = new_ruid = current->uid; old_suid = current->suid; new_suid = old_suid; - if (capable(CAP_SETUID)) { - if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) - return -EAGAIN; - new_suid = uid; - } else if ((uid != current->uid) && (uid != new_suid)) - return -EPERM; + if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) { + security_ops->return_status(-EAGAIN); + return -EAGAIN; + } + new_suid = uid; if (old_euid != uid) { @@ -613,23 +582,16 @@ int retval; retval = security_ops->task_ops->setuid(ruid, euid, suid, LSM_SETID_RES); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; - - if (!capable(CAP_SETUID)) { - if ((ruid != (uid_t) -1) && (ruid != current->uid) && - (ruid != current->euid) && (ruid != current->suid)) - return -EPERM; - if ((euid != (uid_t) -1) && (euid != current->uid) && - (euid != current->euid) && (euid != current->suid)) - return -EPERM; - if ((suid != (uid_t) -1) && (suid != current->uid) && - (suid != current->euid) && (suid != current->suid)) - return -EPERM; } + if (ruid != (uid_t) -1) { - if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0) + if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0) { + security_ops->return_status(-EAGAIN); return -EAGAIN; + } } if (euid != (uid_t) -1) { if (euid != current->euid) @@ -665,20 +627,11 @@ int retval; retval = security_ops->task_ops->setgid(rgid, egid, sgid, LSM_SETID_RES); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; - - if (!capable(CAP_SETGID)) { - if ((rgid != (gid_t) -1) && (rgid != current->gid) && - (rgid != current->egid) && (rgid != current->sgid)) - return -EPERM; - if ((egid != (gid_t) -1) && (egid != current->gid) && - (egid != current->egid) && (egid != current->sgid)) - return -EPERM; - if ((sgid != (gid_t) -1) && (sgid != current->gid) && - (sgid != current->egid) && (sgid != current->sgid)) - return -EPERM; } + if (egid != (gid_t) -1) { if (egid != current->egid) { @@ -692,6 +645,7 @@ current->gid = rgid; if (sgid != (gid_t) -1) current->sgid = sgid; + security_ops->return_status(0); return 0; } @@ -719,26 +673,26 @@ int retval; retval = security_ops->task_ops->setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } old_fsuid = current->fsuid; - if (uid == current->uid || uid == current->euid || - uid == current->suid || uid == current->fsuid || - capable(CAP_SETUID)) + if (uid != old_fsuid) { - if (uid != old_fsuid) - { - current->dumpable = 0; - wmb(); - } - current->fsuid = uid; + current->dumpable = 0; + wmb(); } + current->fsuid = uid; retval = security_ops->task_ops->post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } + security_ops->return_status(old_fsuid); return old_fsuid; } @@ -751,21 +705,20 @@ int retval; retval = security_ops->task_ops->setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS); - if (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } old_fsgid = current->fsgid; - if (gid == current->gid || gid == current->egid || - gid == current->sgid || gid == current->fsgid || - capable(CAP_SETGID)) + if (gid != old_fsgid) { - if (gid != old_fsgid) - { - current->dumpable = 0; - wmb(); - } - current->fsgid = gid; + current->dumpable = 0; + wmb(); } + current->fsgid = gid; + + security_ops->return_status(old_fsgid); return old_fsgid; } @@ -818,6 +771,10 @@ if (!p) goto out; + err = security_ops->task_ops->setpgid(p, pid, pgid); + if (err) + goto out; + if (p->p_pptr == current || p->p_opptr == current) { err = -EPERM; if (p->session != current->session) @@ -846,24 +803,35 @@ out: /* All paths lead to here, thus we are safe. -DaveM */ read_unlock(&tasklist_lock); + security_ops->return_status(err); return err; } asmlinkage long sys_getpgid(pid_t pid) { + int retval; + if (!pid) { - return current->pgrp; + retval = security_ops->task_ops->getpgid(current, pid); + if (!retval) + retval = current->pgrp; + security_ops->return_status(retval); + return retval; } else { - int retval; struct task_struct *p; read_lock(&tasklist_lock); p = find_task_by_pid(pid); retval = -ESRCH; - if (p) - retval = p->pgrp; + if (p) { + retval = security_ops->task_ops->getpgid(p, pid); + + if (!retval) + retval = p->pgrp; + } read_unlock(&tasklist_lock); + security_ops->return_status(retval); return retval; } } @@ -876,19 +844,29 @@ asmlinkage long sys_getsid(pid_t pid) { + int retval; + if (!pid) { - return current->session; + retval = security_ops->task_ops->getsid(current, pid); + if (!retval) + retval = current->session; + security_ops->return_status(retval); + return retval; } else { - int retval; struct task_struct *p; read_lock(&tasklist_lock); p = find_task_by_pid(pid); retval = -ESRCH; - if(p) - retval = p->session; + if(p) { + retval = security_ops->task_ops->getsid(p, pid); + + if (!retval) + retval = p->session; + } read_unlock(&tasklist_lock); + security_ops->return_status(retval); return retval; } } @@ -896,8 +874,15 @@ asmlinkage long sys_setsid(void) { struct task_struct * p; - int err = -EPERM; + int err; + err = security_ops->task_ops->setsid(current->pid); + if (err) { + security_ops->return_status(err); + return err; + } + + err = -EPERM; read_lock(&tasklist_lock); for_each_task(p) { if (p->pgrp == current->pid) @@ -911,6 +896,7 @@ err = current->pgrp; out: read_unlock(&tasklist_lock); + security_ops->return_status(err); return err; } @@ -948,17 +934,18 @@ gid_t groups[NGROUPS]; int retval; - 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 (retval) + if (retval) { + security_ops->return_status(retval); return retval; + } memcpy(current->groups, groups, gidsetsize * sizeof(gid_t)); current->ngroups = gidsetsize; + security_ops->return_status(0); return 0; } @@ -1015,8 +1002,6 @@ 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)) @@ -1024,12 +1009,15 @@ nodename[len] = 0; errno = security_ops->sethostname(nodename); - if (errno) + if (errno) { + security_ops->return_status(errno); return errno; + } down_write(&uts_sem); memcpy(system_utsname.nodename, nodename, len+1); up_write(&uts_sem); + security_ops->return_status(0); return 0; } @@ -1059,8 +1047,6 @@ 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)) @@ -1068,13 +1054,16 @@ domainname[len] = 0; errno = security_ops->setdomainname(domainname); - if (errno) + if (errno) { + security_ops->return_status(errno); return errno; + } down_write(&uts_sem); memcpy(system_utsname.domainname, domainname, len+1); up_write(&uts_sem); - return errno; + security_ops->return_status(0); + return 0; } asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim) @@ -1120,20 +1109,14 @@ if (new_rlim.rlim_cur < 0 || new_rlim.rlim_max < 0) return -EINVAL; old_rlim = current->rlim + resource; - 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) { - if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN) - return -EPERM; - } - - retval = security_ops->task_ops->setrlimit(resource, &new_rlim); - if (retval) + retval = security_ops->task_ops->setrlimit(resource, &new_rlim, old_rlim); + if (retval) { + security_ops->return_status(retval); return retval; + } *old_rlim = new_rlim; + security_ops->return_status(0); return 0; } @@ -1210,6 +1193,12 @@ int error = 0; int sig; + error = security_ops->task_ops->prctl(option, arg2, arg3, arg4, arg5); + if (error) { + security_ops->return_status(error); + return error; + } + switch (option) { case PR_SET_PDEATHSIG: sig = arg2; @@ -1264,6 +1253,7 @@ error = -EINVAL; break; } + security_ops->return_status(error); return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/mm/filemap.c ./mm/filemap.c --- /work/bitkeeper/lsm/mm/filemap.c Fri Jun 22 13:56:10 2001 +++ ./mm/filemap.c Mon Jun 25 19:04:00 2001 @@ -1377,7 +1377,7 @@ if (retval) goto fput_in; - retval = security_ops->file_ops->permission (in_file, MAY_READ); + retval = security_ops->file_ops->permission (in_fd, MAY_READ); if (retval) goto fput_in; @@ -1398,7 +1398,7 @@ if (retval) goto fput_out; - retval = security_ops->file_ops->permission (out_file, MAY_WRITE); + retval = security_ops->file_ops->permission (out_fd, MAY_WRITE); if (retval) goto fput_out; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/mm/memory.c ./mm/memory.c --- /work/bitkeeper/lsm/mm/memory.c Fri Jun 22 13:56:10 2001 +++ ./mm/memory.c Mon Jun 25 18:53:45 2001 @@ -1044,11 +1044,13 @@ out_truncate: if (inode->i_op && inode->i_op->truncate) { - if (!security_ops->inode_ops->truncate(inode)) { + int error=security_ops->inode_ops->truncate(NULL,inode,offset); + if (! error) { lock_kernel(); inode->i_op->truncate(inode); unlock_kernel(); } + security_ops->return_status(error); } out: return; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/mm/mmap.c ./mm/mmap.c --- /work/bitkeeper/lsm/mm/mmap.c Fri Jun 22 13:56:10 2001 +++ ./mm/mmap.c Tue Jun 26 07:32:28 2001 @@ -291,11 +291,18 @@ break; } } - +/* LSM-ToDo + * The only place that this code is called from is the do_mmap2 code in arch. + * That already has a mmap hook. Do we really need another here ? This is + * important because we wanted to change the file_ops hooks to take fd's + * rather than struct file *. This is the only place where we don't have + * and can't get a fd. Comments ? --- offer. + error = security_ops->file_ops->mmap(file, flags, prot); if (error) return error; - + + */ /* Clear old maps */ error = -ENOMEM; if (do_munmap(mm, addr, len)) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet /work/bitkeeper/lsm/net/socket.c ./net/socket.c --- /work/bitkeeper/lsm/net/socket.c Fri Jun 22 13:56:22 2001 +++ ./net/socket.c Mon Jun 25 10:15:00 2001 @@ -72,6 +72,7 @@ #include <linux/cache.h> #include <linux/module.h> #include <linux/highmem.h> +#include <linux/security.h> #if defined(CONFIG_KMOD) && defined(CONFIG_NET) #include <linux/kmod.h> @@ -923,10 +924,12 @@ out: /* It may be already another descriptor 8) Not kernel problem. */ + security_ops->return_status(retval); return retval; out_release: sock_release(sock); + security_ops->return_status(retval); return retval; } @@ -975,16 +978,20 @@ err = put_user(fd1, &usockvec[0]); if (!err) err = put_user(fd2, &usockvec[1]); - if (!err) + if (!err) { + security_ops->return_status(0); return 0; + } sys_close(fd2); sys_close(fd1); + security_ops->return_status(err); return err; out_close_1: sock_release(sock2); sys_close(fd1); + security_ops->return_status(err); return err; out_release_both: @@ -992,6 +999,7 @@ out_release_1: sock_release(sock1); out: + security_ops->return_status(err); return err; } @@ -1016,6 +1024,7 @@ err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen); sockfd_put(sock); } + security_ops->return_status(err); return err; } @@ -1037,6 +1046,7 @@ err=sock->ops->listen(sock, backlog); sockfd_put(sock); } + security_ops->return_status(err); return err; } @@ -1092,6 +1102,7 @@ out_put: sockfd_put(sock); out: + security_ops->return_status(err); return err; out_release: @@ -1129,6 +1140,7 @@ out_put: sockfd_put(sock); out: + security_ops->return_status(err); return err; } @@ -1219,6 +1231,7 @@ out_put: sockfd_put(sock); out: + security_ops->return_status(err); return err; } @@ -1270,6 +1283,7 @@ } sockfd_put(sock); out: + security_ops->return_status(err); return err; } @@ -1342,6 +1356,7 @@ err=sock->ops->shutdown(sock, how); sockfd_put(sock); } + security_ops->return_status(err); return err; } @@ -1420,6 +1435,7 @@ out_put: sockfd_put(sock); out: + security_ops->return_status(err); return err; } @@ -1506,6 +1522,7 @@ out_put: sockfd_put(sock); out: + security_ops->return_status(err); return err; } _______________________________________________ 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 : Tue Jun 26 2001 - 13:21:51 PDT