/**************************************************** */
/* Rule Set Based Access Control                      */
/* Implementation of the Access Control Decision      */
/* Facility (ADF) - Access Control Lists (ACL)        */
/* File: rsbac/adf/acl/acl_main.c                     */
/*                                                    */
/* Author and (c) 1999,2000: Amon Ott                 */
/*                                                    */
/* Last modified: 19/Sep/2000                         */
/**************************************************** */

#include <linux/string.h>
#include <linux/malloc.h>
#include <rsbac/aci.h>
#include <rsbac/acl.h>
#include <rsbac/adf_main.h>
#include <rsbac/adf_syshelpers.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/getname.h>

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

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

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

boolean rsbac_acl_check_super(enum  rsbac_target_t target,
                              union rsbac_target_id_t tid,
                                    rsbac_uid_t user);

boolean rsbac_acl_check_right(enum  rsbac_target_t target,
                              union rsbac_target_id_t tid,
                                    rsbac_uid_t user,
                              enum  rsbac_adf_request_t request)
  {
    rsbac_acl_rights_vector_t i_rights;
    rsbac_acl_rights_vector_t i_rvec = (rsbac_acl_rights_vector_t) 1 << request;
    int                       err=0, tmperr;
    int                       i;
    rsbac_acl_group_id_t    * group_p;
    #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
    #endif

    /* Only check implemented targets */
    switch(target)
      {
        case T_FILE:
        case T_DIR:
        case T_DEV:
        case T_IPC:
        case T_SCD:
        case T_USER:
        case T_PROCESS:
          break;
        default:
          return TRUE;
      }
    /* inherited own rights */
    i_rights = 0;
    err = rsbac_acl_get_rights(target, tid, ACLS_USER, (rsbac_acl_subject_id_t) user, &i_rights, TRUE);
    if(err)
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_acl_check_right(): rsbac_acl_get_rights() returned error %s!\n",
                   get_error_name(tmp,err));
            kfree(tmp);
          }
        return FALSE;
      }
    if(i_rights & i_rvec)
      return(TRUE);

    /* add group and role rights */
    /* group everyone */
    i_rights = 0;
    err = rsbac_acl_get_rights(target, tid, ACLS_GROUP, RSBAC_ACL_GROUP_EVERYONE,
                               &i_rights, TRUE);
    if(err)
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_acl_check_right(): rsbac_acl_get_rights() returned error %s!\n",
                   get_error_name(tmp,err));
            kfree(tmp);
          }
        return FALSE;
      }
    if(i_rights & i_rvec)
      return(TRUE);

    #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
    /* use user role */
    /* first get role */
    i_tid.user = user;
    if (rsbac_get_attr(T_USER,
                       i_tid,
                       A_rc_def_role,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "rsbac_acl_check_right(): rsbac_get_attr() for process rc_role returned error!\n");
      }
    else
      {
        i_rights = 0;
        err = rsbac_acl_get_rights(target, tid, ACLS_ROLE, i_attr_val1.rc_role,
                                   &i_rights, TRUE);
        if(err)
          {
            char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

            if(tmp)
              {
                printk(KERN_WARNING
                       "rsbac_acl_check_right(): rsbac_acl_get_rights() returned error %s!\n",
                       get_error_name(tmp,err));
                kfree(tmp);
              }
            return FALSE;
          }
        if(i_rights & i_rvec)
          return(TRUE);
      }
    #endif

    /* other groups */
    /* first get user groups */
    group_p = NULL;
    err = rsbac_acl_get_user_groups(user, &group_p);
    if(err<0)
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_acl_check_right(): rsbac_acl_get_user_groups() returned error %s!\n",
                   get_error_name(tmp,err));
            kfree(tmp);
          }
        return err;
      }
    for(i=0; i<err; i++)
      {
        tmperr = rsbac_acl_get_rights(target, tid, ACLS_GROUP, group_p[i],
                                      &i_rights, TRUE);
        if(tmperr)
          {
            char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

            if(tmp)
              {
                printk(KERN_WARNING
                       "rsbac_acl_check_right(): rsbac_acl_get_rights() returned error %s!\n",
                       get_error_name(tmp, tmperr));
                kfree(tmp);
              }
            return FALSE;
          }
        if(i_rights & i_rvec)
          {
            kfree(group_p);
            return(TRUE);
          }
      }
    if(group_p)
      kfree(group_p);

    /* SUPERVISOR? */
    return rsbac_acl_check_super(target, tid, user);
  };

boolean rsbac_acl_check_forward(enum  rsbac_target_t target,
                                union rsbac_target_id_t tid,
                                      rsbac_uid_t user,
                                      rsbac_acl_rights_vector_t rights)
  {
    rsbac_acl_rights_vector_t i_rights = 0;
    rsbac_acl_rights_vector_t i_rvec = ((rsbac_acl_rights_vector_t) 1 << ACLR_FORWARD) | rights;
    int                       err=0;


    /* Only check implemented targets */
    switch(target)
      {
        case T_FILE:
        case T_DIR:
        case T_DEV:
        case T_IPC:
        case T_SCD:
        case T_USER:
        case T_PROCESS:
          break;
        default:
          return TRUE;
      }
    /* get effective rights */
    err = rsbac_acl_sys_get_rights(target, tid, ACLS_USER, (rsbac_acl_subject_id_t) user, &i_rights, TRUE);
    if(err)
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_acl_check_forward(): rsbac_acl_sys_get_rights() returned error %s!\n",
                   get_error_name(tmp,err));
            kfree(tmp);
          }
        return FALSE;
      }
    if((i_rights & i_rvec) == i_rvec)
      return(TRUE);
    else
      return(FALSE);
  };

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

enum rsbac_adf_req_ret_t
   rsbac_adf_request_acl (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)
  {
    switch (request)
      {
        case R_READ_ATTRIBUTE:
        case R_MODIFY_ATTRIBUTE:
            switch(attr)
              { /* owner must be changed by other request to prevent inconsistency */
                case A_owner:
                  if(request == R_READ_ATTRIBUTE)
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);

                /* Only protect AUTH, if asked to by configuration */
                #ifdef CONFIG_RSBAC_ACL_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:
                  tid.scd = AST_auth_administration;
                  if (rsbac_acl_check_right(T_SCD, tid, owner, request))
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);
                #endif

                case A_pseudo:
                case A_log_array_low:
                case A_log_array_high:
                case A_log_program_based:
                case A_log_user_based:
                  if (!rsbac_acl_check_right(target, tid, owner, request))
                    return(NOT_GRANTED);
                  else
                    return(GRANTED);

                /* All attributes (remove target!) */
                case A_none:
                  if (!rsbac_acl_check_right(target, tid, owner, request))
                    return(NOT_GRANTED);
                  #ifdef CONFIG_RSBAC_ACL_AUTH_PROT
                  tid.scd = AST_auth_administration;
                  if (!rsbac_acl_check_right(T_SCD, tid, owner, request))
                    return(NOT_GRANTED);
                  #endif
                  return(GRANTED);

                default:
                  return(DO_NOT_CARE);
              }

        case R_SWITCH_MODULE:
            switch(target)
              {
                case T_NONE:
                    if(attr_val.switch_target != ACL)
                      return(DO_NOT_CARE);

                    tid.scd = ST_other;
                    if (rsbac_acl_check_right(T_SCD, tid, owner, request))
                      return(GRANTED);
                    else
                      return(NOT_GRANTED);

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

/*********************/
        default:
          if(target == T_NONE)
            {
              target = T_SCD;
              tid.scd = ST_other;
            }
          if (rsbac_acl_check_right(target, tid, owner, request))
            return(GRANTED);
          else
            return(NOT_GRANTED);
      }
  }; /* end of rsbac_adf_request_acl() */


/*****************************************************************************/
/* 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_acl(
                      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)
  {
    /* Nothing to be done here */
    return(0);
  }; /* end of rsbac_adf_set_attr_acl() */

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