/*************************************************** */
/* Rule Set Based Access Control                     */
/* Implementation of the Access Control Decision     */
/* Facility (ADF) - Mandatory Access Control         */
/* File: rsbac/adf/mac/syscalls.c                    */
/*                                                   */
/* Author and (c) 1999-2001: Amon Ott <ao@rsbac.org> */
/*                                                   */
/* Last modified: 24/Aug/2001                        */
/*************************************************** */

#include <linux/string.h>
#include <rsbac/types.h>
#include <rsbac/aci.h>
#include <rsbac/adf_main.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/getname.h>

/************************************************* */
/*           Global Variables                      */
/************************************************* */

/************************************************* */
/*          Internal Help functions                */
/************************************************* */

/************************************************* */
/*          Externally visible functions           */
/************************************************* */

/*****************************************************************************/
/* This function allows processes to set their own current security level    */
/* via sys_rsbac_mac_set_curr_seclevel() system call.                        */
/* The level must keep within the min_write_open/max_read_open-boundary and  */
/* must not be greater than owner_sec_level. Setting current_sec_level by    */
/* this function also turns off auto-levelling via mac_auto.                 */

int  rsbac_mac_set_curr_seclevel(rsbac_security_level_t level)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val1;

    /* get maximum security level */
    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_security_level,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    /* if level is too high -> error */
    if (level > attr_val1.security_level)
      return(-EPERM);
     
    /* check against upper/write boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_min_write_open,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (level > attr_val1.min_write_open)
      return(-EPERM);

    /* check against lower/read boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_max_read_open,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (level < attr_val1.max_read_open)
      return(-EPERM);

    /* OK, checks passed: set mac_auto to OFF */
    attr_val1.mac_auto = FALSE;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_mac_auto,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_seclevel(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }

    /* ...and set current_sec_level to level */
    attr_val1.current_sec_level = level;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_current_sec_level,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_seclevel(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }
    return(0);
  };

/*  To make programmers happy: getting current level as well */

rsbac_security_level_t  rsbac_mac_get_curr_seclevel(void)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val;

    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_current_sec_level,
                       &attr_val,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_get_curr_seclevel(): rsbac_get_attr() returned error!\n");
        return(SL_none);
      }
    return(attr_val.current_sec_level);
  };

/*****************************************************************************/
/* These functions allow processes to set their own maximum security level   */
/* (= owner_sec_level) via sys_rsbac_mac_set_max_seclevel() system call.     */
/* The level must keep within the min_write_open/max_read_open-boundary and  */
/* must not be greater than the process owner's security_level.              */
/* Remember: owner_sec_level is inherited on fork!                           */

int  rsbac_mac_set_max_seclevel(rsbac_security_level_t level)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val1;

    /* get old maximum security level */
    tid.process = current->pid;

    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_security_level,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    /* if level is too high -> error */
    if (level > attr_val1.security_level)
      return(-EPERM);
     
    /* check against upper/write boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_min_write_open,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (level > attr_val1.min_write_open)
      return(-EPERM);

    /* check against lower/read boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_max_read_open,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_seclevel(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (level < attr_val1.max_read_open)
      return(-EPERM);

    /* set new max security_level to level */
    attr_val1.security_level = level;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_security_level,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_seclevel(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }

    /* ...and set current_sec_level to level */
    attr_val1.current_sec_level = level;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_current_sec_level,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_seclevel(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }

    return(0);
  };

/*  To make programmers happy: getting max security level as well */

rsbac_security_level_t  rsbac_mac_get_max_seclevel(void)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val;

    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_security_level,
                       &attr_val,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_get_max_seclevel(): rsbac_get_attr() returned error!\n");
        return(SL_none);
      }
    return(attr_val.security_level);
  };

int rsbac_mac_set_curr_categories(rsbac_mac_category_vector_t categories)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val1;

    /* get maximum categories */
    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_mac_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    /* if new set is no subset -> error */
    if (!( (attr_val1.mac_categories & categories)
           == categories))
      return(-EPERM);
     
    /* check against upper/write boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_min_write_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (!( (attr_val1.mac_categories & categories)
           == categories))
      return(-EPERM);

    /* check against lower/read boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_max_read_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (!( (attr_val1.mac_categories & categories)
           == attr_val1.mac_categories))
      return(-EPERM);

    /* OK, checks passed: set mac_auto to OFF */
    attr_val1.mac_auto = FALSE;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_mac_auto,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_categories(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }

    /* ...and set mac_curr_categories to level */
    attr_val1.mac_categories = categories;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_mac_curr_categories,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_curr_categories(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }
    return(0);
  };

int rsbac_mac_get_curr_categories(rsbac_mac_category_vector_t * categories_p)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val;

    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_mac_curr_categories,
                       &attr_val,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_get_curr_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    *categories_p = attr_val.mac_categories;
    return(0);
  }

int rsbac_mac_set_max_categories(rsbac_mac_category_vector_t categories)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val1;

    /* get old maximum categories */
    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_mac_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    /* if new set is no subset -> error */
    if (!( (attr_val1.mac_categories & categories)
           == categories))
      return(-EPERM);
     
    /* check against upper/write boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_min_write_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (!( (attr_val1.mac_categories & categories)
           == categories))
      return(-EPERM);

    /* check against lower/read boundary */ 
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_max_read_categories,
                       &attr_val1,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if (!( (attr_val1.mac_categories & categories)
           == attr_val1.mac_categories))
      return(-EPERM);

    /* OK, checks passed: set mac_categories and */
    /* mac_curr_categories to categories */
    attr_val1.mac_categories = categories;
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_mac_curr_categories,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_categories(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }
    if (rsbac_set_attr(MAC, T_PROCESS,
                       tid,
                       A_mac_categories,
                       attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_set_max_categories(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }
    return(0);
  };

int rsbac_mac_get_max_categories(rsbac_mac_category_vector_t * categories_p)
  {
    union rsbac_target_id_t       tid;
    union rsbac_attribute_value_t attr_val;

    tid.process = current->pid;
    if (rsbac_get_attr(MAC,
                       T_PROCESS,
                       tid,
                       A_mac_categories,
                       &attr_val,
                       FALSE))
      { /* failed! */
        printk(KERN_WARNING "rsbac_mac_get_max_categories(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    *categories_p = attr_val.mac_categories;
    return(0);
  }


/* end of rsbac/adf/mac/syscalls.c */
