/**************************************************** */
/* Rule Set Based Access Control                      */
/* Implementation of the Access Control Decision      */
/* Facility (ADF) - Security Information Modification */
/* File: rsbac/adf/sim/main.c                         */
/*                                                    */
/* Author and (c) 1999-2002: Amon Ott <ao@rsbac.org>  */
/*                                                    */
/* Last modified: 13/Mar/2002                         */
/**************************************************** */

#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>
#include <rsbac/network.h>

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

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

static enum rsbac_adf_req_ret_t
         check_role_sim(  enum  rsbac_target_t          target,
                          union rsbac_target_id_t       tid,
                                rsbac_uid_t             owner)
  {
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_attribute_value_t i_attr_val2;
    boolean inherit;

    /* test target's data_type */
    switch(target)
      {
        case T_FILE:
        case T_DIR:
        case T_FIFO:
        case T_SYMLINK:
        case T_NETOBJ:
          inherit = TRUE;
          break;
        default:
          inherit = FALSE;
      }
    if (rsbac_get_attr(SIM,
                           target,
                       tid,
                       A_data_type,
                       &i_attr_val1,
                       inherit))
      {
        printk(KERN_WARNING "check_role_sim(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* Access to other data_types than SI is always granted */
    if (i_attr_val1.data_type != DT_SI)
      return(GRANTED);

    /* test owner's sim_role */
    i_tid.user = owner;
    if (rsbac_get_attr(SIM,
                           T_USER,
                       i_tid,
                       A_sim_role,
                       &i_attr_val2,
                       TRUE))
      {
        printk(KERN_WARNING "check_role_sim(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
      
    /* Access is granted, if owner's sim_role is security_officer */

    if (i_attr_val2.system_role == SR_security_officer)
      return(GRANTED);
    else
      return(NOT_GRANTED);
  }

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
static enum rsbac_adf_req_ret_t
         check_role_sim_netobj(enum  rsbac_adf_request_t     request,
                               union rsbac_target_id_t       tid,
                                     rsbac_uid_t             owner)
  {
    union rsbac_target_id_t       i_tid;
    enum  rsbac_attribute_t       i_attr;
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_attribute_value_t i_attr_val2;

    if(rsbac_net_remote_request(request))
      i_attr = A_remote_data_type;
    else
      i_attr = A_local_data_type;
    /* test target's data_type */
    if (rsbac_get_attr(SIM,
                       T_NETOBJ,
                       tid,
                       i_attr,
                       &i_attr_val1,
                       TRUE))
      {
        printk(KERN_WARNING "check_role_sim(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* Access to other data_types than SI is always granted */
    if (i_attr_val1.data_type != DT_SI)
      return(GRANTED);

    /* test owner's sim_role */
    i_tid.user = owner;
    if (rsbac_get_attr(SIM,
                       T_USER,
                       i_tid,
                       A_sim_role,
                       &i_attr_val2,
                       TRUE))
      {
        printk(KERN_WARNING "check_role_sim(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
      
    /* Access is granted, if owner's sim_role is security_officer */

    if (i_attr_val2.system_role == SR_security_officer)
      return(GRANTED);
    else
      return(NOT_GRANTED);
  }
#endif

static enum rsbac_adf_req_ret_t
  sim_check_secoff_role(rsbac_uid_t owner)
  {
    union rsbac_target_id_t i_tid;
    union rsbac_attribute_value_t i_attr_val1;

    i_tid.user = owner;
    if (rsbac_get_attr(FC,
                       T_USER,
                       i_tid,
                       A_fc_role,
                       &i_attr_val1,
                       TRUE))
      {
        printk(KERN_WARNING
               "sim_check_secoff_role(): rsbac_get_attr() returned error!\n");
               return(NOT_GRANTED);
      }
    /* if correct role, then grant */
    if (i_attr_val1.system_role == SR_security_officer)
      return(GRANTED);
    else
      return(NOT_GRANTED);
  }

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

enum rsbac_adf_req_ret_t
   rsbac_adf_request_sim (enum  rsbac_adf_request_t     request,
                                rsbac_pid_t             caller_pid,
                          enum  rsbac_target_t          target,
                          union rsbac_target_id_t       tid,
                          enum  rsbac_attribute_t       attr,
                          union rsbac_attribute_value_t attr_val,
                                rsbac_uid_t             owner)
  {
    enum  rsbac_adf_req_ret_t result = DO_NOT_CARE;
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
#ifdef CONFIG_RSBAC_SIM_ROLE_PROT
    union rsbac_attribute_value_t i_attr_val2;
#endif

    switch (request)
      {
        case R_ADD_TO_KERNEL:
            switch(target)
              {
                case T_NONE:
                  return(DO_NOT_CARE);

                /* all other cases are unknown */
                default:
                  return(DO_NOT_CARE);
              }


        case R_ALTER:
            /* only for IPC */
            switch(target)
              {
                case T_IPC:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
             }

        case R_APPEND_OPEN:
        case R_READ_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                case T_FIFO:
                case T_DEV:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_CHANGE_GROUP:
            switch(target)
              {
                case T_FILE:
                case T_DIR:
                case T_FIFO:
                case T_SYMLINK:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));

                /* all other cases */
                default: return(DO_NOT_CARE);
              }

        case R_CHANGE_OWNER:
            switch(target)
              {
                case T_FILE:
                case T_DIR:
                case T_FIFO:
                case T_SYMLINK:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));
                  
                case T_PROCESS:
                  #ifdef CONFIG_RSBAC_SIM_ROLE_PROT
                  if(attr != A_owner)
                    return(UNDEFINED);
                  /* Administrator or secoff? */
                  i_tid.user = owner;
                  if (rsbac_get_attr(SIM,
                                     T_USER,
                                     i_tid,
                                     A_sim_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_sim(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* if general user or secoff, then grant */
                  if (   (i_attr_val1.system_role == SR_user)
                      || (i_attr_val1.system_role == SR_security_officer)
                     )
                    return(GRANTED);
                  /* old owner is sys-admin */
                  /* get target user's role */
                  i_tid.user = attr_val.owner;
                  if (rsbac_get_attr(SIM,
                                     T_USER,
                                     i_tid,
                                     A_sim_role,
                                     &i_attr_val2,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_sim(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* if target is security officer -> deny */
                  if(i_attr_val2.system_role == SR_security_officer)
                    return(NOT_GRANTED);
                  else
                    return(GRANTED);
                  #endif /* ROLE_PROT */

                  /* fall through */
                case T_NONE:
                  return(DO_NOT_CARE);
                /* all other cases are unknown */
                default:
                  return(DO_NOT_CARE);
              }

        /* Creating dir or (pseudo) file IN target dir! */
        case R_CREATE:
            switch(target)
              {
                case T_DIR: 
                  return(check_role_sim(target,tid,owner));
                /* IPC is always granted */
                case T_IPC:
                  return(GRANTED);

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
                case T_NETTEMP:
                  return sim_check_secoff_role(owner);

                case T_NETOBJ:
                  return(check_role_sim_netobj(request,tid,owner));
#endif

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_DELETE:
            switch(target)
              {
                case T_FILE:
                case T_DIR:
                case T_FIFO:
                case T_SYMLINK:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
                case T_NETTEMP:
                  return sim_check_secoff_role(owner);
#endif

                /* all other cases */
                default:
                  return(DO_NOT_CARE);
              }

        case R_EXECUTE:
            switch(target)
              {
                default:
                  return(DO_NOT_CARE);
              }

        case R_GET_STATUS_DATA:
            switch(target)
              {
                case T_SCD:
                  /* target rsbaclog? only for secoff */
                  if (tid.scd != ST_rsbaclog)
                    return(GRANTED);
                  /* Secoff? */
                  return sim_check_secoff_role(owner);

                default:
                  return(DO_NOT_CARE);
               };

        case R_LINK_HARD:
            switch(target)
              {
                case T_FILE:
                case T_FIFO:
                case T_SYMLINK:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_MODIFY_ACCESS_DATA:
        case R_RENAME:
            switch(target)
              {
                case T_FILE:
                case T_DIR:
                case T_FIFO:
                case T_SYMLINK:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_MODIFY_ATTRIBUTE:
            switch(attr)
              {
                case A_system_role:
                case A_sim_role:
                case A_data_type:
                case A_local_data_type:
                case A_remote_data_type:
                #ifdef CONFIG_RSBAC_SIM_GEN_PROT
                case A_log_array_low:
                case A_log_array_high:
                case A_log_program_based:
                case A_log_user_based:
                case A_symlink_add_uid:
                case A_symlink_add_rc_role:
                case A_linux_dac_disable:
                #endif
                #ifdef CONFIG_RSBAC_SIM_AUTH_PROT
                case A_auth_may_setuid:
                case A_auth_may_set_cap:
                case A_auth_add_f_cap:
                case A_auth_remove_f_cap:
                #endif
                /* All attributes (remove target!) */
                case A_none:
                  /* Security Officer? */
                  return sim_check_secoff_role(owner);

                default:
                  return(DO_NOT_CARE);
              }

        case R_MODIFY_PERMISSIONS_DATA:
            switch(target)
              {
                case T_FILE:
                case T_DIR:
                case T_FIFO:
                case T_SYMLINK:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));
                  
                case T_SCD:
                  #ifdef CONFIG_RSBAC_USER_MOD_IOPERM
                  if(tid.scd == ST_ioports)
                    return GRANTED;
                  #endif
                  /* Security Officer? */
                  i_tid.user = owner;
                  if (rsbac_get_attr(SIM,
                                     T_USER,
                                     i_tid,
                                     A_sim_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_sim(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* if sec_officer, then grant */
                  if (i_attr_val1.system_role == SR_security_officer)
                    return(GRANTED);
                  /* For booting: if administrator and ioports, then grant */
                  if (   (i_attr_val1.system_role == SR_administrator)
                      && (tid.scd == ST_ioports) )
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);
                  
#ifdef CONFIG_RSBAC_ALLOW_DAC_DISABLE
                /* switching Linux DAC */
                case T_NONE:
                  /* Security Officer? */
                  return sim_check_secoff_role(owner);
#endif

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_MODIFY_SYSTEM_DATA:
            switch(target)
              {
                case T_SCD:
                  /* target not rsbaclog? no problem -> grant */
                  if (tid.scd != ST_rsbaclog)
                    return(GRANTED);
                  return sim_check_secoff_role(owner);
                  
                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_MOUNT:
        case R_UMOUNT:
            switch(target)
              {
                case T_DIR:
                case T_DEV:
                  /* test access to dir/dev */
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_SWITCH_LOG:
            switch(target)
              {
                case T_NONE:
                  /* test owner's sim_role */
                  return sim_check_secoff_role(owner);

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_SWITCH_MODULE:
            switch(target)
              {
                case T_NONE:
                  /* we need the switch_target */
                  if(attr != A_switch_target)
                    return(UNDEFINED);
                  /* do not care for other modules */
                  if(   (attr_val.switch_target != SIM)
                     #ifdef CONFIG_RSBAC_SIM_AUTH_PROT
                     && (attr_val.switch_target != AUTH)
                     #endif
                     #ifdef CONFIG_RSBAC_SOFTMODE
                     && (attr_val.switch_target != SOFTMODE)
                     #endif
                    )
                    return(DO_NOT_CARE);
                  /* test owner's sim_role */
                  return sim_check_secoff_role(owner);

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_TRUNCATE:
            switch(target)
              {
                case T_FILE:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_WRITE:
            switch(target)
              {
                case T_DIR:
#ifdef CONFIG_RSBAC_RW
                case T_FILE:
                case T_FIFO:
                case T_DEV:
                case T_IPC:
#endif
                  return(check_role_sim(target,tid,owner));

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
                case T_NETTEMP:
                  return sim_check_secoff_role(owner);

                case T_NETOBJ:
                  return(check_role_sim_netobj(request,tid,owner));
#endif

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

        case R_WRITE_OPEN:
            switch(target)
              {
                case T_DEV:
                case T_FILE:
                case T_FIFO:
                case T_IPC:
                  return(check_role_sim(target,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
        case R_BIND:
        case R_LISTEN:
        case R_ACCEPT:
        case R_CONNECT:
        case R_SEND:
        case R_RECEIVE:
            switch(target)
              {
                case T_NETOBJ:
                  return(check_role_sim_netobj(request,tid,owner));

                /* all other cases are unknown */
                default: return(DO_NOT_CARE);
              }
#endif

/*********************/
        default: return DO_NOT_CARE;
      }

    return(result);
  }; /* end of rsbac_adf_request_sim() */


/*****************************************************************************/
/* If the request returned granted and the operation is performed,           */
/* the following function can be called by the AEF to get all aci set        */
/* correctly. For write accesses that are performed fully within the kernel, */
/* this is usually not done to prevent extra calls, including R_CLOSE for    */
/* cleaning up.                                                              */
/* The second instance of target specification is the new target, if one has */
/* been created, otherwise its values are ignored.                           */
/* On success, 0 is returned, and an error from rsbac/error.h otherwise.     */

int  rsbac_adf_set_attr_sim(
                      enum  rsbac_adf_request_t     request,
                            rsbac_pid_t             caller_pid,
                      enum  rsbac_target_t          target,
                      union rsbac_target_id_t       tid,
                      enum  rsbac_target_t          new_target,
                      union rsbac_target_id_t       new_tid,
                      enum  rsbac_attribute_t       attr,
                      union rsbac_attribute_value_t attr_val,
                            rsbac_uid_t             owner)
  {
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_attribute_value_t i_attr_val2;

    switch (request)
      {
        case R_CREATE:
            switch(target)
              {
                case T_IPC: 
                  /* Get sim_role of process owner... */
                  i_tid.user = owner;
                  if (rsbac_get_attr(SIM,
                                     T_USER,
                                     i_tid,
                                     A_sim_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_sim(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* Derive data_type for this ipc item from owner's */
                  /* sim_role */
                  if (i_attr_val1.system_role == SR_security_officer)
                    {
                      i_attr_val2.data_type = DT_SI;
                      /* set data_type for new IPC item */
                      if (rsbac_set_attr(SIM,
                                         T_IPC,
                                         tid,
                                         A_data_type,
                                         i_attr_val2))
                        {
                          printk(KERN_WARNING "rsbac_adf_set_attr_sim(): rsbac_set_attr() returned error!\n");
                          return(-RSBAC_EWRITEFAILED);
                        }
                    }
                  return(0);

#ifdef CONFIG_RSBAC_SIM_NET_OBJ_PROT
                case T_NETOBJ: 
                  /* Get sim_role of process owner... */
                  i_tid.user = owner;
                  if (rsbac_get_attr(SIM,
                                     T_USER,
                                     i_tid,
                                     A_sim_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_sim(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* Derive data_type for this netobj item from owner's */
                  /* sim_role */
                  if (i_attr_val1.system_role == SR_security_officer)
                    {
                      i_attr_val2.data_type = DT_SI;
                      /* set data_type for new IPC item */
                      if (rsbac_set_attr(SIM,
                                         target,
                                         tid,
                                         A_local_data_type,
                                         i_attr_val2))
                        {
                          printk(KERN_WARNING "rsbac_adf_set_attr_sim(): rsbac_set_attr() returned error!\n");
                          return(-RSBAC_EWRITEFAILED);
                        }
                    }
                  return(0);
#endif
                  
                /* all other cases are unknown */
                default:
                  return(0);
              }

/*********************/
        default: return(0);
      }

    return(0);
  }; /* end of rsbac_adf_set_attr_sim() */

/* end of rsbac/adf/sim/main.c */
