Well folks, here's another mailbomb^H^H^H^H^H^H^H^Hversion of "stacker.c", the first stacking/multiplexing module for LSM. The comments now try to explain what the module does - and doesn't - and EVERY hook is now covered. I doubt it compiles, but at this point I'm really just hoping for feedback on its approach. Any comments like "watch for <surprise>" or "why not <much-more-clever-way-inserted-here>" would be very appreciated. Changes: - Better comments, esp. about the policy it implements and what LSM modules should do if they want to be stackable. I've realized several gotchas now, for example, the owlsm module isn't as stackable as you'd like because it builds in the "dummy" functionality, even when it's being stacked. It _should_ check if its "secondary" flag is set, and if it is, not do certain actions it got from dummy. - Changed "automatic_dummy" to "pseudo_dummy". Now, as long as pseudo_dummy is true, the module pretends that the "dummy" module is at the end of the list (NOT the beginning any more); loading the "capability" module automatically turns off pseudo_dummy. I'd like feedback about what the "pseudo_dummy" functionality should be; I'm trying to trade flexibility and convenience, as well as avoid nasty race conditions. - sys_security now works, but with some limitations; see my previous email about the need for child modules to send their id to the master stacking module. - all hooks are now implemented. - more TODOs. Shouldn't the number be getting smaller :-)? /* * "Stacker" LSM security module. Load this first as the * primary LSM module, and then you can load other LSM modules "under" it. * This module asks for permissions from ALL of the loaded modules, * and if any module says "no, it's forbidden", then it's forbidden. * The error code of the FIRST module that forbids the request is returned; * modules are asked in the order they were inserted. * The exception are the authoritative hooks (e.g., "capable") - in that * case, if ANY module grants the capability, it's granted. * When stacker first starts, it acts as though the "dummy" module * is also loaded, but this can be turned off later (see below). * Loading the "capabilities" module underneath stacker is a common option. * "Stacker" always calls all of the modules for every request, that way, * LSM modules that manipulate state won't get confused. * WARNING! Not all LSM modules can be combined with all other modules; * it's up to the administrator who is doing the stacking to determine * if the given stacking is appropriate. * * Copyright (C) 2002 David A. Wheeler <dwheelerat_private> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /* WARNING! THIS IS AN EARLY DRAFT. IT IS _NOT_ INTENDED FOR USE YET; I'm only releasing it so that people can start reviewing the code (e.g., is the approach reasonable, what's been forgotten, etc.). Look especially for the text marked "TODO". Testing level: "The source loads into vim. It probably doesn't compile. Run it only if you need to erase your hard drive.". */ #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/security.h> #include <linux/skbuff.h> #include <linux/netlink.h> /* Flag to keep track of how we were registered */ static int secondary; /* While automatic_dummy is true, the stacker module will act as though the "dummy" module is stacked underneath it and is first in line. This handles the time period when the stacker module has been loaded, but not all intended LSM modules have been stacked under it. The theory of operation is that the stacker module will be loaded, other LSM modules will then be loaded (underneath the stacker module), and then if desired automatic_dummy is turned off. */ /* TODO: should the automatic dummy be FIRST or LAST in the list while modules are being loaded? Or, are there different times depending on the function call? Currently it's "first". Check out the values where it's used, in particular, it overrides the modules underneath in some cases - is that reasonable? Need to re-examine all uses of automatic_dummy. */ static int automatic_dummy = 1; /* A "module entry" keeps track of one of the stacked modules */ struct module_entry { struct security_operations *next; char *module_name; struct security_operations *module_operations; }; /* The set of LSM modules stacked by this "stacker" module is stored as a singly linked list of module_entries pointed to by stacked_modules. It's initially NULL. */ static struct module_entry *stacked_modules; /* Maximum number of characters in a module name */ #define MAX_MODULE_NAME_LEN 32 /* Walk through the linked list of modules in stacked_modules and ask each (in turn) for their results, then return the results. If more than one module reports an error, return the FIRST one. Note that this ALWAYS calls ALL modules, since some modules may change state when called. TODO: Possibly add a "short-circuit" option that stops when anything says "no"; however, that only optimizes if a module reports an error, so it probably isn't worth it. */ #define RETURN_ERROR_IF_ANY_ERROR(CALL) \ int final_result = 0; \ int result; \ struct module_entry *p; \ for (p = stacked_modules; p; p->next) { \ result = p->module_operations->CALL; \ if (result && !final_result) final_result = result; \ } \ return final_result /* Walk through the linked list of modules in stacked_modules and ask each (in turn) for their results, then return the results. If any module reports success (0), return success, else fail. Note that this ALWAYS calls ALL modules, since some modules may change state when called. The value of stacked_modules is loaded into p, and then p is always used, to avoid some race conditions. TODO: Is there still a potential significant race problem due to compiler optimizations? This is used for authoritative calls, e.g., capable. */ #define RETURN_SUCCESS_IF_ANY_SUCCESS(CALL) \ int final_result = -EPERM; \ int result; \ struct module_entry *p = stacked_modules; \ if (!p) return -EPERM; \ final_result = p->module_operations->CALL; \ if (!final_result) return 0; \ for (p = p->next; p; p->next) { \ result = p->module_operations->CALL; \ if (!result) final_result = 0; \ } \ return final_result /* Call all modules in stacked_modules' CALL routine */ #define CALL_ALL(CALL) \ struct module_entry *p; \ for (p = stacked_modules; p; p->next) { \ p->module_operations->CALL; \ } /* TODO: Need to examine all functions in the dummy module that aren't simply "return" or "return 0" to see if perhaps some of their logic should be reproduced here.. and if so, how. That's what "automatic_dummy" is for. TODO: Authoritative hooks need to be handled differently, e.g., "capable". See the code below - is it right? TODO: I haven't really implemented the alloc_security/free_security hooks properly. I'm still thinking about the best way to do this, so the current code is basically a placeholder. */ static int stacker_sethostname (char *hostname) { RETURN_ERROR_IF_ANY_ERROR(sethostname(hostname)); } static int stacker_setdomainname (char *domainname) { RETURN_ERROR_IF_ANY_ERROR(setdomainname(hostname)); } static int stacker_reboot (unsigned int cmd) { RETURN_ERROR_IF_ANY_ERROR(reboot(cmd)); } static int stacker_ioperm (unsigned long from, unsigned long num, int turn_on) { RETURN_ERROR_IF_ANY_ERROR(ioperm(from, num, turn_on)); } static int stacker_iopl (unsigned int old, unsigned int level) { RETURN_ERROR_IF_ANY_ERROR(iopl(old, level)); } static int stacker_ptrace (struct task_struct *parent, struct task_struct *child) { RETURN_ERROR_IF_ANY_ERROR(ptrace(parent, child)); } static int stacker_capget (struct task_struct *target, kernel_cap_t * effective, kernel_cap_t * inheritable, kernel_cap_t * permitted) { RETURN_ERROR_IF_ANY_ERROR(capget(target, effective, inheritable, permitted)); } static int stacker_capset_check (struct task_struct *target, kernel_cap_t * effective, kernel_cap_t * inheritable, kernel_cap_t * permitted) { RETURN_ERROR_IF_ANY_ERROR(capset_check(target, effective, inheritable, permitted)); } static void stacker_capset_set (struct task_struct *target, kernel_cap_t * effective, kernel_cap_t * inheritable, kernel_cap_t * permitted) { CALL_ALL(capset_set(target, effective, inheritable, permitted)); } static int stacker_acct (struct file *file) { RETURN_ERROR_IF_ANY_ERROR(acct(file)); } static int stacker_capable (struct task_struct *tsk, int cap) { /* This is an AUTHORITATIVE hook, so it needs to be handled differently. */ if (automatic_dummy) /* TODO: check - can this infinitely recurse? */ if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0) /* capability granted */ return 0; RETURN_SUCCESS_IF_ANY_SUCCESS(capable(tsk, cap)) } /* TODO: Need to use RETURN_ERROR_IF_ANY_ERROR and related macros again and again below. */ static int stacker_sysctl (ctl_table * table, int op) { RETURN_ERROR_IF_ANY_ERROR(sysctl(table, op)); } static int stacker_sys_security (unsigned int id, unsigned int call, unsigned long *args) { /* TODO: Should bring the call down to the stacked modules, and should have a few calls it implements, e.g.: + stop stacking + list loaded LSM modules by name (in their order) + turn off automatic_dummy */ return -ENOSYS; } static int stacker_swapon (struct swap_info_struct *swap) { RETURN_ERROR_IF_ANY_ERROR(swapon(swap)); } static int stacker_swapoff (struct swap_info_struct *swap) { RETURN_ERROR_IF_ANY_ERROR(swapoff(swap)); } static int stacker_quotactl (int cmds, int type, int id, struct super_block *sb) { RETURN_ERROR_IF_ANY_ERROR(quotactl(cmds,type,id,sb)); } static int stacker_quota_on (struct file *f) { RETURN_ERROR_IF_ANY_ERROR(quota_on(f)); } static int stacker_syslog (int type) { RETURN_ERROR_IF_ANY_ERROR(syslog(type)); } static int stacker_netlink_send (struct sk_buff *skb) { if (automatic_dummy) { if (current->euid == 0) cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); else NETLINK_CB (skb).eff_cap = 0; } RETURN_ERROR_IF_ANY_ERROR(send(skb)); } static int stacker_netlink_recv (struct sk_buff *skb) { if (automatic_dummy) if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN)) return -EPERM; RETURN_ERROR_IF_ANY_ERROR(netlink_recv(skb)); } static int stacker_bprm_alloc_security (struct linux_binprm *bprm) { RETURN_ERROR_IF_ANY_ERROR(bprm_alloc_security(bprm)); } static void stacker_bprm_free_security (struct linux_binprm *bprm) { CALL_ALL(bprm_free_security(bprm)); } static void stacker_bprm_compute_creds (struct linux_binprm *bprm) { CALL_ALL(bprm_free_compute_creds(bprm)); } static int stacker_bprm_set_security (struct linux_binprm *bprm) { RETURN_ERROR_IF_ANY_ERROR(bprm_set_security(bprm)); } static int stacker_bprm_check_security (struct linux_binprm *bprm) { RETURN_ERROR_IF_ANY_ERROR(bprm_check_security(bprm)); } static int stacker_sb_alloc_security (struct super_block *sb) { RETURN_ERROR_IF_ANY_ERROR(sb_alloc_security(sb)); } static void stacker_sb_free_security (struct super_block *sb) { CALL_ALL(sb_free_security(sb)); } static int stacker_sb_statfs (struct super_block *sb) { RETURN_ERROR_IF_ANY_ERROR(sb_statfs(sb)); } static int stacker_mount (char *dev_name, struct nameidata *nd, char *type, unsigned long flags, void *data) { RETURN_ERROR_IF_ANY_ERROR(mount(dev_name, nd, type, flags, data)); } static int stacker_check_sb (struct vfsmount *mnt, struct nameidata *nd) { RETURN_ERROR_IF_ANY_ERROR(check_sb(mnt, nd)); } static int stacker_umount (struct vfsmount *mnt, int flags) { RETURN_ERROR_IF_ANY_ERROR(umount(mnt, flags)); } static void stacker_umount_close (struct vfsmount *mnt) { CALL_ALL(umount_close(mnt)); } static void stacker_umount_busy (struct vfsmount *mnt) { CALL_ALL(umount_busy(mnt)); } static void stacker_post_remount (struct vfsmount *mnt, unsigned long flags, void *data) { CALL_ALL(post_remount(mnt, flags, data)); } static void stacker_post_mountroot (void) { CALL_ALL(post_mountroot()); } static void stacker_post_addmount (struct vfsmount *mnt, struct nameidata *nd) { CALL_ALL(post_addmount(mnt, nd)); } static int stacker_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) { RETURN_ERROR_IF_ANY_ERROR(pivotroot(old_nd, new_nd)); } static void stacker_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) { CALL_ALL(post_pivotroot(old_nd, new_nd)); } static int stacker_inode_alloc_security (struct inode *inode) { RETURN_ERROR_IF_ANY_ERROR(inode_alloc_security(inode)); } static void stacker_inode_free_security (struct inode *inode) { CALL_ALL(inode_free_security(inode)); } static int stacker_inode_create (struct inode *inode, struct dentry *dentry, int mask) { RETURN_ERROR_IF_ANY_ERROR(inode_create(inode, dentry, mask)); } static void stacker_inode_post_create (struct inode *inode, struct dentry *dentry, int mask) { CALL_ALL(inode_post_create(inode, dentry, mask)); } static int stacker_inode_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_link(old_dentry, inode, new_dentry)); } static void stacker_inode_post_link (struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) { CALL_ALL(inode_post_link(old_dentry, inode, new_dentry)); } static int stacker_inode_unlink (struct inode *inode, struct dentry *dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_unlink(inode, dentry)); } static int stacker_inode_symlink (struct inode *inode, struct dentry *dentry, const char *name) { RETURN_ERROR_IF_ANY_ERROR(inode_symlink(inode, denstry, name)); } static void stacker_inode_post_symlink (struct inode *inode, struct dentry *dentry, const char *name) { CALL_ALL(inode_post_symlink(inode, dentry, name)); } static int stacker_inode_mkdir (struct inode *inode, struct dentry *dentry, int mask) { RETURN_ERROR_IF_ANY_ERROR(inode_mkdir(inode, dentry, mask)); } static void stacker_inode_post_mkdir (struct inode *inode, struct dentry *dentry, int mask) { CALL_ALL(inode_post_mkdir(inode, dentry, mask)); } static int stacker_inode_rmdir (struct inode *inode, struct dentry *dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_rmdir(inode, dentry)); } static int stacker_inode_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) { RETURN_ERROR_IF_ANY_ERROR(inode_mknod(inode, dentry, major, minor)); } static void stacker_inode_post_mknod (struct inode *inode, struct dentry *dentry, int major, dev_t minor) { CALL_ALL(inode_post_mknod(inode, dentry, major, minor)); } static int stacker_inode_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_rename(old_inode, old_dentry, new_inode, new_dentry)); } static void stacker_inode_post_rename (struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { CALL_ALL(inode_post_rename(old_inode, old_dentry, new_inode, new_dentry)); } static int stacker_inode_readlink (struct dentry *dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_readlink(dentry)); } static int stacker_inode_follow_link (struct dentry *dentry, struct nameidata *nameidata) { RETURN_ERROR_IF_ANY_ERROR(inode_follow_link(dentry, nameidata)); } static int stacker_inode_permission (struct inode *inode, int mask) { /* TODO: Think this one through!! */ RETURN_ERROR_IF_ANY_ERROR(inode_permission(inode, mask)); } static int stacker_inode_permission_lite (struct inode *inode, int mask) { /* TODO: Think this one through!! */ RETURN_ERROR_IF_ANY_ERROR(inode_permission_lite(inode, mask)); } static int stacker_inode_setattr (struct dentry *dentry, struct iattr *iattr) { RETURN_ERROR_IF_ANY_ERROR(inode_setattr(dentry, iattr)); } static int stacker_inode_getattr (struct vfsmount *mnt, struct dentry *dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_getattr(mnt,dentry); } static void stacker_post_lookup (struct inode *ino, struct dentry *d) { CALL_ALL(post_lookup(ino,d)); } static void stacker_delete (struct inode *ino) { CALL_ALL(delete(ino)); } static int stacker_inode_setxattr (struct dentry *dentry, char *name, void *value, size_t size, int flags) { RETURN_ERROR_IF_ANY_ERROR(inode_setxattr(dentry,name,value,size,flags)); } static int stacker_inode_getxattr (struct dentry *dentry, char *name) { RETURN_ERROR_IF_ANY_ERROR(inode_getxattr(dentry,name)); } static int stacker_inode_listxattr (struct dentry *dentry) { RETURN_ERROR_IF_ANY_ERROR(inode_listxattr(dentry)); } static int stacker_inode_removexattr (struct dentry *dentry, char *name) { RETURN_ERROR_IF_ANY_ERROR(inode_removexattr(dentry,name)); } static int stacker_file_permission (struct file *file, int mask) { /* TODO: Re-examine */ RETURN_ERROR_IF_ANY_ERROR(file_permission(file,mask)); } static int stacker_file_alloc_security (struct file *file) { RETURN_ERROR_IF_ANY_ERROR(file_alloc_security(file)); } static void stacker_file_free_security (struct file *file) { CALL_ALL(file_free_security(file)); } static int stacker_file_llseek (struct file *file) { RETURN_ERROR_IF_ANY_ERROR(file_llseek(file)); } static int stacker_file_ioctl (struct file *file, unsigned int command, unsigned long arg) { RETURN_ERROR_IF_ANY_ERROR(file_ioctl(file,command,arg)); } static int stacker_file_mmap (struct file *file, unsigned long prot, unsigned long flags) { RETURN_ERROR_IF_ANY_ERROR(file_mmap(file, prot, flags)); } static int stacker_file_mprotect (struct vm_area_struct *vma, unsigned long prot) { RETURN_ERROR_IF_ANY_ERROR(file_mprotect(vma,prot)); } static int stacker_file_lock (struct file *file, unsigned int cmd, int blocking) { RETURN_ERROR_IF_ANY_ERROR(file_lock(file,cmd,blocking)); } static int stacker_file_fcntl (struct file *file, unsigned int cmd, unsigned long arg) { RETURN_ERROR_IF_ANY_ERROR(file_fcntl(file,cmd,arg)); } /* TODO: Keep going... */ static int stacker_file_set_fowner (struct file *file) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_file_send_sigiotask (struct task_struct *tsk, struct fown_struct *fown, int fd, int reason) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_file_receive (struct file *file) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_create (unsigned long clone_flags) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_alloc_security (struct task_struct *p) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_task_free_security (struct task_struct *p) { CALL_ALL(); } static int stacker_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setpgid (struct task_struct *p, pid_t pgid) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_getpgid (struct task_struct *p) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_getsid (struct task_struct *p) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setgroups (int gidsetsize, gid_t * grouplist) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setnice (struct task_struct *p, int nice) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_setscheduler (struct task_struct *p, int policy, struct sched_param *lp) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_getscheduler (struct task_struct *p) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_wait (struct task_struct *p) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_kill (struct task_struct *p, struct siginfo *info, int sig) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_task_prctl (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_task_kmod_set_label (void) { CALL_ALL(); } static void stacker_task_reparent_to_init (struct task_struct *p) { /* TODO: The dummy module does: p->euid = p->fsuid = 0; should we do that too? */ CALL_ALL(); } static void stacker_ip_fragment (struct sk_buff *newskb, const struct sk_buff *oldskb) { CALL_ALL(); } static int stacker_ip_defragment (struct sk_buff *skb) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_ip_decapsulate (struct sk_buff *skb) { CALL_ALL(); } static void stacker_ip_encapsulate (struct sk_buff *skb) { CALL_ALL(); } static int stacker_ip_decode_options (struct sk_buff *skb, const char *optptr, unsigned char **pp_ptr) { if (!skb && !capable (CAP_NET_RAW)) { (const unsigned char *) *pp_ptr = optptr; return -EPERM; } RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_netdev_unregister (struct net_device *dev) { CALL_ALL(); } static int stacker_socket_create (int family, int type, int protocol) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_socket_post_create (struct socket *sock, int family, int type, int protocol) { CALL_ALL(); } static int stacker_socket_bind (struct socket *sock, struct sockaddr *address, int addrlen) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_connect (struct socket *sock, struct sockaddr *address, int addrlen) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_listen (struct socket *sock, int backlog) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_accept (struct socket *sock, struct socket *newsock) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_socket_post_accept (struct socket *sock, struct socket *newsock) { CALL_ALL(); } static int stacker_socket_sendmsg (struct socket *sock, struct msghdr *msg, int size) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_recvmsg (struct socket *sock, struct msghdr *msg, int size, int flags) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_getsockname (struct socket *sock) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_getpeername (struct socket *sock) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_setsockopt (struct socket *sock, int level, int optname) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_getsockopt (struct socket *sock, int level, int optname) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_shutdown (struct socket *sock, int how) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_unix_stream_connect (struct socket *sock, struct socket *other) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_socket_unix_may_send (struct socket *sock, struct socket *other) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_module_create (const char *name_user, size_t size) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_module_initialize (struct module *mod_user) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_module_delete (const struct module *mod) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_ipc_permission (struct kern_ipc_perm *ipcp, short flag) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_ipc_getinfo (int id, int cmd) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_msg_msg_alloc_security (struct msg_msg *msg) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_msg_msg_free_security (struct msg_msg *msg) { CALL_ALL(); } static int stacker_msg_queue_alloc_security (struct msg_queue *msq) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_msg_queue_free_security (struct msg_queue *msq) { CALL_ALL(); } static int stacker_msg_queue_associate (struct msg_queue *msq, int msqid, int msqflg) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_msg_queue_msgctl (struct msg_queue *msq, int msqid, int cmd) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg, int msqid, int msgflg) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg, struct task_struct *target, long type, int mode) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_shm_alloc_security (struct shmid_kernel *shp) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_shm_free_security (struct shmid_kernel *shp) { CALL_ALL(); } static int stacker_shm_associate (struct shmid_kernel *shp, int shmid, int shmflg) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_shm_shmctl (struct shmid_kernel *shp, int shmid, int cmd) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_shm_shmat (struct shmid_kernel *shp, int shmid, char *shmaddr, int shmflg) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_sem_alloc_security (struct sem_array *sma) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_sem_free_security (struct sem_array *sma) { CALL_ALL(); } static int stacker_sem_associate (struct sem_array *sma, int semid, int semflg) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_sem_semctl (struct sem_array *sma, int semid, int cmd) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_sem_semop (struct sem_array *sma, int semid, struct sembuf *sops, unsigned nsops, int alter) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_skb_alloc_security (struct sk_buff *skb) { RETURN_ERROR_IF_ANY_ERROR(); } static int stacker_skb_clone (struct sk_buff *newskb, const struct sk_buff *oldskb) { RETURN_ERROR_IF_ANY_ERROR(); } static void stacker_skb_copy (struct sk_buff *newskb, const struct sk_buff *oldskb) { CALL_ALL(); } static void stacker_skb_set_owner_w (struct sk_buff *skb, struct sock *sk) { CALL_ALL(); } static void stacker_skb_recv_datagram (struct sk_buff *skb, struct sock *sk, unsigned flags) { CALL_ALL(); } static void stacker_skb_free_security (struct sk_buff *skb) { CALL_ALL(); } static int stacker_register (const char *name, struct security_operations *ops) { /* This function is the primary reason for the stacker module. Add the name security_operations provided to us at the end of stacked_modules (the linked list of stacked modules). */ char *new_module_name; struct security_operations *new_module_operations; struct module_entry *new_module_entry; struct module_entry *p; /* Used to walk stacked_modules */ /* TODO: What should I check on re: security? Should I check for euid == 0? Has that already been checked? */ /* Note that we do NOT call the stacker_register entries of any currently installed modules, since we aren't installing a child under them. A future version should support a new security operation to forbid adding new modules. */ /* TODO: is GFP_KERNEL appropriate here? */ /* TODO: need strnlen */ new_module_name = kmalloc(strnlen(name, MAX_MODULE_NAME_LEN)+1, GFP_KERNEL); new_module_operations = kmalloc(sizeof(struct security_operations), GFP_KERNEL); new_module_entry = kmalloc(sizeof(struct module_entry), GFP_KERNEL); if (!new_module_name || !new_module_operations || !new_module_entry) { printk (KERN_INFO "Failure registering module - out of memory\n"); return -EINVAL; } new_module_name = strncpy(new_module_name, name, MAX_MODULE_NAME_LEN); new_module_name[MAX_MODULE_NAME_LEN-1] = '\0'; *new_module_operations = *ops; new_module_entry->module_name = new_module_name; new_module_entry->module_operations = new_module_operations; new_module_entry->next = NULL; /* Finally, add it. This must be the very last step, since once this code is executed the module will IMMEDIATELY go live. */ if (!stacked_modules) { stacked_modules = new_module_entry; } else { /* Find last module in the list of stacked_modules */ for (p = stacked_modules; p->next; p = p->next); p->next = new_module_entry; } return 0; } static int stacker_unregister (const char *name, struct security_operations *ops) { /* TODO: Eventually this should remove the corresponding entry from the stacked_modules list, but for simplicity that functionality hasn't been implemented yet. If this IS added: here are some key notes: - DON'T kfree() the corresponding data until ALL threads have passed it. The simple solution may be to simply leak a little memory in that case. - Add a security operation that "locks down" registering and unregistering and doesn't permit "unlocking". */ return -EINVAL; } struct security_operations stacker_ops = { sethostname: stacker_sethostname, setdomainname: stacker_setdomainname, reboot: stacker_reboot, ioperm: stacker_ioperm, iopl: stacker_iopl, ptrace: stacker_ptrace, capget: stacker_capget, capset_check: stacker_capset_check, capset_set: stacker_capset_set, acct: stacker_acct, capable: stacker_capable, sysctl: stacker_sysctl, sys_security: stacker_sys_security, swapon: stacker_swapon, swapoff: stacker_swapoff, quotactl: stacker_quotactl, quota_on: stacker_quota_on, syslog: stacker_syslog, netlink_send: stacker_netlink_send, netlink_recv: stacker_netlink_recv, unix_stream_connect: stacker_socket_unix_stream_connect, unix_may_send: stacker_socket_unix_may_send, bprm_alloc_security: stacker_bprm_alloc_security, bprm_free_security: stacker_bprm_free_security, bprm_compute_creds: stacker_bprm_compute_creds, bprm_set_security: stacker_bprm_set_security, bprm_check_security: stacker_bprm_check_security, sb_alloc_security: stacker_sb_alloc_security, sb_free_security: stacker_sb_free_security, sb_statfs: stacker_sb_statfs, sb_mount: stacker_mount, sb_check_sb: stacker_check_sb, sb_umount: stacker_umount, sb_umount_close: stacker_umount_close, sb_umount_busy: stacker_umount_busy, sb_post_remount: stacker_post_remount, sb_post_mountroot: stacker_post_mountroot, sb_post_addmount: stacker_post_addmount, sb_pivotroot: stacker_pivotroot, sb_post_pivotroot: stacker_post_pivotroot, inode_alloc_security: stacker_inode_alloc_security, inode_free_security: stacker_inode_free_security, inode_create: stacker_inode_create, inode_post_create: stacker_inode_post_create, inode_link: stacker_inode_link, inode_post_link: stacker_inode_post_link, inode_unlink: stacker_inode_unlink, inode_symlink: stacker_inode_symlink, inode_post_symlink: stacker_inode_post_symlink, inode_mkdir: stacker_inode_mkdir, inode_post_mkdir: stacker_inode_post_mkdir, inode_rmdir: stacker_inode_rmdir, inode_mknod: stacker_inode_mknod, inode_post_mknod: stacker_inode_post_mknod, inode_rename: stacker_inode_rename, inode_post_rename: stacker_inode_post_rename, inode_readlink: stacker_inode_readlink, inode_follow_link: stacker_inode_follow_link, inode_permission: stacker_inode_permission, inode_permission_lite: stacker_inode_permission_lite, inode_setattr: stacker_inode_setattr, inode_getattr: stacker_inode_getattr, inode_post_lookup: stacker_post_lookup, inode_delete: stacker_delete, inode_setxattr: stacker_inode_setxattr, inode_getxattr: stacker_inode_getxattr, inode_listxattr: stacker_inode_listxattr, inode_removexattr: stacker_inode_removexattr, file_permission: stacker_file_permission, file_alloc_security: stacker_file_alloc_security, file_free_security: stacker_file_free_security, file_llseek: stacker_file_llseek, file_ioctl: stacker_file_ioctl, file_mmap: stacker_file_mmap, file_mprotect: stacker_file_mprotect, file_lock: stacker_file_lock, file_fcntl: stacker_file_fcntl, file_set_fowner: stacker_file_set_fowner, file_send_sigiotask: stacker_file_send_sigiotask, file_receive: stacker_file_receive, task_create: stacker_task_create, task_alloc_security: stacker_task_alloc_security, task_free_security: stacker_task_free_security, task_setuid: stacker_task_setuid, task_post_setuid: stacker_task_post_setuid, task_setgid: stacker_task_setgid, task_setpgid: stacker_task_setpgid, task_getpgid: stacker_task_getpgid, task_getsid: stacker_task_getsid, task_setgroups: stacker_task_setgroups, task_setnice: stacker_task_setnice, task_setrlimit: stacker_task_setrlimit, task_setscheduler: stacker_task_setscheduler, task_getscheduler: stacker_task_getscheduler, task_wait: stacker_task_wait, task_kill: stacker_task_kill, task_prctl: stacker_task_prctl, task_kmod_set_label: stacker_task_kmod_set_label, task_reparent_to_init: stacker_task_reparent_to_init, socket_create: stacker_socket_create, socket_post_create: stacker_socket_post_create, socket_bind: stacker_socket_bind, socket_connect: stacker_socket_connect, socket_listen: stacker_socket_listen, socket_accept: stacker_socket_accept, socket_post_accept: stacker_socket_post_accept, socket_sendmsg: stacker_socket_sendmsg, socket_recvmsg: stacker_socket_recvmsg, socket_getsockname: stacker_socket_getsockname, socket_getpeername: stacker_socket_getpeername, socket_getsockopt: stacker_socket_getsockopt, socket_setsockopt: stacker_socket_setsockopt, socket_shutdown: stacker_socket_shutdown, socket_sock_rcv_skb: stacker_socket_sock_rcv_skb, skb_alloc_security: stacker_skb_alloc_security, skb_clone: stacker_skb_clone, skb_copy: stacker_skb_copy, skb_set_owner_w: stacker_skb_set_owner_w, skb_recv_datagram: stacker_skb_recv_datagram, skb_free_security: stacker_skb_free_security, ip_fragment: stacker_ip_fragment, ip_defragment: stacker_ip_defragment, ip_encapsulate: stacker_ip_encapsulate, ip_decapsulate: stacker_ip_decapsulate, ip_decode_options: stacker_ip_decode_options, ipc_permission: stacker_ipc_permission, ipc_getinfo: stacker_ipc_getinfo, netdev_unregister: stacker_netdev_unregister, module_create: stacker_module_create, module_initialize: stacker_module_initialize, module_delete: stacker_module_delete, msg_msg_alloc_security: stacker_msg_msg_alloc_security, msg_msg_free_security: stacker_msg_msg_free_security, msg_queue_alloc_security: stacker_msg_queue_alloc_security, msg_queue_free_security: stacker_msg_queue_free_security, msg_queue_associate: stacker_msg_queue_associate, msg_queue_msgctl: stacker_msg_queue_msgctl, msg_queue_msgsnd: stacker_msg_queue_msgsnd, msg_queue_msgrcv: stacker_msg_queue_msgrcv, shm_alloc_security: stacker_shm_alloc_security, shm_free_security: stacker_shm_free_security, shm_associate: stacker_shm_associate, shm_shmctl: stacker_shm_shmctl, shm_shmat: stacker_shm_shmat, sem_alloc_security: stacker_sem_alloc_security, sem_free_security: stacker_sem_free_security, sem_associate: stacker_sem_associate, sem_semctl: stacker_sem_semctl, sem_semop: stacker_sem_semop, register_security: stacker_register, unregister_security: stacker_unregister, }; #if defined(CONFIG_SECURITY_stacker_MODULE) # define MYNAME THIS_MODULE->name #else # define MYNAME "stacker" #endif static int __init stacker_init (void) { /* register ourselves with the security framework */ if (register_security (&stacker_ops)) { printk (KERN_INFO "Failure registering stacker module with the kernel\n"); /* try registering with primary module */ /* Note that this module allows itself to be stacked, but doesn't yet have a way to add LSM modules under it when that happens. Still, it might be stacked under ANOTHER module that DOES, and supporting levels of of stacking is more flexible. */ if (mod_reg_security (MY_NAME, &stacker_ops)) { printk (KERN_INFO "Failure registering stacker module " "with primary security module.\n"); return -EINVAL; } secondary = 1; } printk(KERN_INFO "stacker LSM initialized\n"); return 0; } static void __exit stacker_exit (void) { /* remove ourselves from the security framework */ if (secondary) { if (mod_unreg_security (MY_NAME, &stacker_ops)) printk (KERN_INFO "Failure unregistering stacker module " "with primary module.\n"); return; } if (unregister_security (&stacker_ops)) { printk (KERN_INFO "Failure unregistering stacker module with the kernel\n"); } } /* TODO: Double-check all the functions above. Are the right ones called? Are the parameters correct? */ module_init (stacker_init); module_exit (stacker_exit); MODULE_DESCRIPTION("LSM Stacker for installing multiple LSM modules simultaneously"); MODULE_AUTHOR("David A. Wheeler"); MODULE_LICENSE("GPL"); _______________________________________________ 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 : Fri Jul 19 2002 - 22:00:42 PDT