* Philippe Biondi (philippe.biondi@enst-bretagne.fr) wrote: > Let's put the problem in place : we have the (veryveryvery) generic > scheme: > | > | +---------+ > | | Decider | > | +---------+ > | ^ > | | (1) > | V > | +-------+ > user space | +--| hooks |---+ > | | +-------+ | > App ----+-->| Syscall code | > | +--------------+ > > The question is : what is the arrow (1) ? In other words what is the > semantic behind such calls to the decider ? > > Your approach was to say that the decider had to answer to : > "Can 'current' exec <syscall> with <parameters>" > I'll prefer > "Can 'current' do <type-of-action> with <object(s)>" > even if it seems very MAC-oriented No, I think we are misunderstanding each other. I am strictly _not_ interested in watching just syscalls. In fact, I believe only a few syscalls will need the hooks. The fact remains that syscalls are the user applications' interface to the kernel. We are tyring to protect kernel objects (in my mind). Therefore, the syscall interface is a good place to start looking at what a user app can acutally do to a kernel object. For example, some kernel objects contain linked lists that are used only internally. I don't think we care about protecting this kind of kernel object data (if the user app can't change it). I am trying to look at the handful of kernel objects and ask "what can a user app do to that object that we need to protect?" I think we have the same goals. > Do we need to intercept each file-related syscall to enforce simple > rules like "Nobody write into /sbin" ? No. We simply need a rule for write. But what about "foo can read /lib/libc.so.6" and "bar can create in /tmp"? For any individual policy statement, there may be only one hook. But the composite policy will rely on many hooks. Am I missing something? I actually left some file operations out intentionally (poll and readdir). At first I looked only at things that allowed you to alter the state of a kernel object. This is more straightforward for the task struct...change the uid value or priority value. For the file and inode structs, the operations already provide the interface. So then we begin deciding which operations we care about and want to expose hooks for. > What about considering as a 1st approximation to replace every call to > permission : > ./fs/open.c: error = permission(inode,MAY_WRITE); > ./fs/open.c: (error = permission(inode,MAY_WRITE)) != 0) > ./fs/open.c: if ((error = permission(inode,MAY_WRITE)) != 0) > ./fs/open.c: res = permission(nd.dentry->d_inode, mode); > [...] > ./fs/exec.c: error = permission(nd.dentry->d_inode, MAY_READ | > MAY_EXEC); > ./fs/exec.c: int err = permission(inode, MAY_EXEC); > ./fs/exec.c: permission(bprm->file->f_dentry->d_inode,MAY_READ)) > ./fs/super.c: if (permission(nd->dentry->d_inode, MAY_WRITE)) > [...] In my proposal permission is part of the inode operations. It is an obvious multiplexor for all the inode operations requests (and maybe some of the other inode operations' hooks can go away). My only thought is whether we want to "replace" these calls or add our hook in the call. I don't like the idea of giving every module the responsibility of re-coding standard UNIX permissions checks. And in the case where no security module is loaded we can't leave system without standard permissions checks...that is inacceptable. Perhaps keeping the calls to permission, and changing permission to look like: int permission(struct inode * inode,int mask) { int retval; if (inode->i_op && inode->i_op->permission) { lock_kernel(); retval = inode->i_op->permission(inode, mask); if (!retval) retval = security_ops->inode_ops->permission(inode, mask); unlock_kernel(); return retval; } retval = vfs_permission(inode, mask); if (!retval) retval = security_ops->inode_ops->permission(inode, mask); return retval; } > > and every call to capable() : > ./fs/read_write.c: if (inode && > S_ISBLK(inode->i_mode) && (!capable(CAP_SYS_RAWIO))) > ./fs/read_write.c: if (inode && > S_ISBLK(inode->i_mode) && (!capable(CAP_SYS_RAWIO))) { > ./fs/buffer.c: if (!capable(CAP_SYS_ADMIN)) { > ./fs/open.c: if (!capable(CAP_SYS_CHROOT)) { > ./fs/open.c: if (capable(CAP_SYS_TTY_CONFIG)) { > [...] This is what I was doing when I started thinking about how to abstract the interface. Capabilities gives 28 different modes (CAP_XXX) but this will probably not be enough granularity for most security modules. CAP_SYS_ADMIN is a real offender here...it's a bit like the kitchen sink ;-) I think most security modules will want to differentiate between mounting devices, removing semaphores and enabling/disabling swap. CAP_SYS_ADMIN does not capture this. If we develop an interface that allows you to export to the module more granularity that the module needs, it can simply ignore it. So the capabilites module will map requests to mount devices, remove semaphores, and enable/disable swap to the same internal policy; the one that implements CAP_SYS_ADMIN. > by a call to a function with could implement the already existing scurity > model : DAC and capabilities. > The behaviour is quite simple : the function must have enough parameters > to know what action is intended to do : ("write to this inode", "being ADMIN > capable",..) and know the subject (let's keep on with the MAC terminology :)) > because we are in process context. All what is needed to do is comparing > some security policy data stored in the current task struct (current->uid, > euid, cap_permitted...) with what is needed to be allowed to do the > action. No more than what is already done. It sounds to me like you are suggesting one function, something like: is_this_legal(action, credientials, additional data...) While this is a nice way to look at it, I don't think we'll be able to implement it that way. There are so many different types of actions that need different types of additional data. > > > The next step would be to > * transform this function is something like a hub, with a register()... > * add a place for more fine grained security policy data in the task struct > * add a hook for inheritage rules of security policy data. > * reimplement the same security model using the new place in task struct > (instead of ->uid,..), the hook of inheritage, and the hub function If you notice, in the interface I proposed there is a hook for adding per task security data that is opaque to the kernel. This is to be unique for each security module. Also, there is a mechanism for the module to register its module-specific security_operations. But I disagree with the ->uid part. If no security module is loaded the interface has a set of dummy functions (they all immediately return success). This way we leave the traditional permission checks in tact, and just add one function call that immediately returns as the overhead (wherever our hooks are). This seems least intrusive and most likely to be accepted. -chris _______________________________________________ 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 : Sat Apr 14 2001 - 17:45:04 PDT