intercepting system calls

From: Philippe Biondi (philippe.biondi@enst-bretagne.fr)
Date: Thu Apr 12 2001 - 01:44:17 PDT

  • Next message: Crispin Cowan: "intercepting system calls"

    Hi all
    
    On 12 Apr 2001, Anil B. Somayaji wrote:
    >
    > System calls _are_ a well-defined abstraction level - it is the
    > abstraction of functions that userspace programs can invoke in the
    > kernel.  It is a fundamental mechanism - system calls are how
    > processes talk with the kernel.  Even memory-mapped IO and raw socket
    > accesses start with system calls.  Except for maybe a few exceptions
    > (which I can't think of at the moment - anyone, please correct me), a
    > process without system calls is a process locked away in its own box,
    > incapable of interacting with the outside world.  Sure, it is
    > Turing-complete, but it isn't too interesting.
    
    I completely agree this vision.
    But I may suggest to place hooks (enforcer component) at the place the
    old checks are already done.
    
    
    It can be in the syscall itself :
    
    sys_create_module(const char *name_user, size_t size)
    {
    	char *name;
    	long namelen, error;
    	struct module *mod;
    	unsigned long flags;
    
    	if (!capable(CAP_SYS_MODULE))
    		return -EPERM;
    
    
    In there :
    
    static int open_port(struct inode * inode, struct file * filp)
    {
    	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
    }
    
    
    In some more generic functions, like
    
    int path_walk(const char * name, struct nameidata *nd)
    [...]
    		err = permission(inode, MAY_EXEC);
    
    
    The general architecture should be a modular one (!) :
    * An enforcer componnent :
      It is in fact a lot of hooks in the current code that are called when
      the process do a syscall (which does not mean in the sys_* function)
      These hooks are already there! (capable() calls, permission tests...)
    * A decider component, that may change easily.
      It will be called by the hooks and must decide to allow or not
      the operation. It must have sufficient data to decide. We must note that
      is must be called in process context, so that it is possible to store
      per-process data in the task_struct.
    
    
    Well I think most of us agree that. It is easy to say.
    Whoever disagree with this scheme should speak now or to keep quiet
    forever ;)
    
    
    
    The problems that it raise and that we must resolve :
    * How to attach AC data to processes ?
    * How can we guarantee that we did not forget a check point ?
    * How can we manage security policies changes/cohabitation ?
    * How can we ensure a safe boot with security policy in a module ?
    * ...
    
    We can be inspired by the way the binfmt stuff work : a list of structs of
    functions. One struct by binary format and a default one.
    
    The enforcer code could provide everything needed to register new security
    policies, and implement the hooks to jump to the right policy.
    
    The task_struct could store informations like the preferred security
    policy, it's ability to accept the default policy (a good death is better
    than a bad life ;)), and everything needed by the decider of the prefered
    policy.
    
    The default policy will be the vanilla kernel one (DAC+capabilities) which
    does not need any AC data.
    
    We can use multiple policies for a process using a chained list of AC
    datas in the task struct.
    
    A big proble would be : how to recognize a binary and attach it its AC
    data. LIDS uses device/inode numbers. Is it the best one ? (even if it
    works well)
    
    Another big problem is the data persistence.
    We need more data than what is on the fs. If we extend the fs we loose the
    possibility to stand above the VFS layer. We must work above the VFS layer
    to work the same with every fs.
    The data can't be stored in an extended ELF format for security reasons
    (everybody can write a file).
    
    We could add rules the same way firewalling rules are : in a boot script
    using a userspace tool.
    This mean that the policy is not there at boot time. It is not a problem
    for those who use security policies as modules, but it is for those who
    want to apply their security policy to boot scripts.
    
    An idea (utopia?) could be some persistent data mechanism in the kernel,
    that could also be used for firewalling rules and other parameters
    everywhere in the kernel. (I don't thought a lot about it... storing all
    that in a file near the kernel image ? in the kernel image ? in a separate
    dedicated partition ?)
    
    
    Well, I'm sorry about this mail being a mess...
    
    Best regards, Phil.
    
    --
    Philippe Biondi
    Systems administrator
    Webmotion Inc.
    http://www.webmotion.com
    mailto:philippe.biondiat_private
    Fax. (613) 260-9545
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:15:22 PDT