/************************************************* */
/* Rule Set Based Access Control                   */
/* Implementation of the Access Control Decision   */
/* Facility (ADF) - Role Compatibility             */
/* File: rsbac/adf/rc/syscalls.c                   */
/*                                                 */
/* Author and (c) 1999: Amon Ott                   */
/*                                                 */
/* Last modified: 23/Jul/99                        */
/************************************************* */

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

#ifndef NULL
#define NULL ((void *) 0)
#endif

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

/************************************************* */
/*           Declarations                          */
/************************************************* */

#ifdef CONFIG_RSBAC_RC
/* from rsbac/adf/rc/main.c */
int rsbac_rc_test_role_admin(boolean modify);
#endif

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

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

/* Here we only check access rights and pass on to rc_data_structures */

/* All roles are always there, so instead of creation, we supply a copy for */
/* initialization. There is always the well-defined role general to copy.   */
int rsbac_rc_sys_copy_role (rsbac_rc_role_id_t from_role,
                            rsbac_rc_role_id_t to_role)
  {
#ifdef CONFIG_RSBAC_RC
    int                           err;
    /* test rc_role of process / modify */
    if ((err=rsbac_rc_test_role_admin(TRUE)))
      return err;
#endif
    /* pass on */
    return(rsbac_rc_copy_role(from_role, to_role));
  };

/* Getting values */
int rsbac_rc_sys_get_item (enum  rsbac_rc_target_t       target,
                           union rsbac_rc_target_id_t    tid,
                           enum  rsbac_rc_item_t         item,
                           union rsbac_rc_item_value_t * value_p)
  {
#ifdef CONFIG_RSBAC_RC
    int                           err;

    switch(item)
      {
        case RI_name:
        case RI_type_fd_name:
        case RI_type_dev_name:
        case RI_type_ipc_name:
        case RI_type_process_name:
        case RI_type_scd_name:
          /* getting names is always allowed */
          break;

        default:
          /* test rc_role of process / no modify */
          if ((err=rsbac_rc_test_role_admin(FALSE)))
            return err;
      }
#endif

    /* pass on */
    return(rsbac_rc_get_item(target, tid, item, value_p));
  };

/* Setting values */
int rsbac_rc_sys_set_item (enum  rsbac_rc_target_t       target,
                           union rsbac_rc_target_id_t    tid,
                           enum  rsbac_rc_item_t         item,
                           union rsbac_rc_item_value_t   value)
  {
#ifdef CONFIG_RSBAC_RC
    int                           err;

    /* test rc_role of process / modify */
    if ((err=rsbac_rc_test_role_admin(TRUE)))
      return err;
#endif

    /* pass on */
    return(rsbac_rc_set_item(target, tid, item, value));
  };

/* Set own role, if allowed ( = in role_comp vector of current role) */
int rsbac_rc_sys_change_role (rsbac_rc_role_id_t role)
  {
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
#ifdef CONFIG_RSBAC_RC
    int                           err;
    union rsbac_rc_target_id_t    i_rc_tid;
    union rsbac_rc_item_value_t   i_rc_item_val1;

    i_tid.process = current->pid;
    /* get rc_role of process */
    if ((err=rsbac_get_attr(T_PROCESS,
                       i_tid,
                       A_rc_role,
                       &i_attr_val1,
                       TRUE)))
      {
        printk(KERN_WARNING
               "rsbac_rc_sys_change_role(): rsbac_get_attr() returned error %i!\n",err);
        return -RSBAC_EREADFAILED;
      }

    /* get role_comp of role */
    i_rc_tid.role = i_attr_val1.rc_role;
    if ((err=rsbac_rc_get_item(RT_ROLE,
                                i_rc_tid,
                                RI_role_comp,
                                &i_rc_item_val1)))
      {
        printk(KERN_WARNING
               "rsbac_rc_sys_change_role(): rsbac_rc_get_item() returned error %i!\n",err);
        return -RSBAC_EREADFAILED;
      }

    /* deny, if old role is not compatible with new role */
    if (!(i_rc_item_val1.role_comp & ((rsbac_rc_role_vector_t) 1 << role) ))
      return -EPERM;
#endif

    /* OK, check passed. Set role. */
    i_tid.process = current->pid;
    i_attr_val1.rc_role = role;
    if (rsbac_set_attr(T_PROCESS,
                       i_tid,
                       A_rc_role,
                       i_attr_val1))
      { /* failed! */
        printk(KERN_WARNING "rsbac_rc_sys_change_role(): rsbac_set_attr() returned error!\n");
        return(-RSBAC_EWRITEFAILED);
      }
    /* Ready. */
    return(0);
  };

/* Getting own effective rights */
int rsbac_rc_sys_get_eff_rights (enum  rsbac_target_t       target,
                                 union rsbac_target_id_t    tid,
                                       rsbac_rc_request_vector_t * request_vector)
  {
    union rsbac_target_id_t       i_tid;
    enum  rsbac_attribute_t       i_attr = A_none;
    union rsbac_attribute_value_t i_attr_val1;
    int                           err;
    enum  rsbac_rc_item_t         i_rc_item;
    union rsbac_rc_target_id_t    i_rc_tid;
    union rsbac_rc_item_value_t   i_rc_item_val1;

    i_tid.process = current->pid;
    /* get rc_role of process */
    if ((err=rsbac_get_attr(T_PROCESS,
                       i_tid,
                       A_rc_role,
                       &i_attr_val1,
                       TRUE)))
      {
        printk(KERN_WARNING
               "rsbac_rc_sys_get_eff_rights(): rsbac_get_attr() returned error %i!\n",err);
        return -RSBAC_EREADFAILED;
      }

    switch(target)
      {
        case T_FILE:
        case T_DIR:
          i_attr = A_rc_type_fd;
          i_rc_item = RI_type_comp_fd;
          break;
        case T_DEV:
          i_attr = A_rc_type;
          i_rc_item = RI_type_comp_dev;
          break;
        case T_IPC:
          i_attr = A_rc_type;
          i_rc_item = RI_type_comp_ipc;
          break;
        case T_PROCESS:
          i_attr = A_rc_type;
          i_rc_item = RI_type_comp_process;
          break;
        case T_SCD: /* special case! */
          if(tid.scd >= RSBAC_RC_MAX_TYPE)
            return -RSBAC_EINVALIDTARGET;
          i_rc_item = RI_type_comp_scd;
          break;
        default:
          return -RSBAC_EINVALIDTARGET;
      }
    /* get type_comp_xxx of role for target */
    i_rc_tid.role = i_attr_val1.rc_role;
    if ((err=rsbac_rc_get_item(RT_ROLE,
                                i_rc_tid,
                                i_rc_item,
                                &i_rc_item_val1)))
      {
        printk(KERN_WARNING
               "rsbac_rc_sys_get_eff_rights(): rsbac_rc_get_item() returned error %i!\n",err);
        return -RSBAC_EREADFAILED;
      }

    /* get rc_type of target */
    if(target == T_SCD)
      {
        i_attr_val1.rc_type = tid.scd;
      }
    else
      {
        if ((err=rsbac_get_attr(target,
                                tid,
                                i_attr,
                                &i_attr_val1,
                                TRUE)))
          {
            printk(KERN_WARNING
                   "rsbac_rc_sys_get_eff_rights(): rsbac_get_attr() returned error %i!\n",err);
            return -RSBAC_EREADFAILED;
          }
      }

    /* extract value */
    *request_vector = i_rc_item_val1.type_comp[i_attr_val1.rc_type];
    /* Ready. */
    return(0);
  };

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