/**************************************************** */
/* Rule Set Based Access Control                      */
/* Implementation of the Access Control Decision      */
/* Facility (ADF) - Authorization module              */
/* File: rsbac/adf/auth/main.c                        */
/*                                                    */
/* Author and (c) 1999,2000: Amon Ott                 */
/*                                                    */
/* Last modified: 14/Apr/2000                         */
/**************************************************** */

#include <linux/string.h>
#include <rsbac/aci.h>
#include <rsbac/auth.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                      */
/************************************************* */

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

#ifdef CONFIG_RSBAC_AUTH_AUTH_PROT
static enum rsbac_adf_req_ret_t
         check_role_auth( 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;
    boolean inherit;

    /* test target's data_type */
    switch(target)
      {
        case T_FILE:
        case T_DIR:
          inherit = TRUE;
          break;
        default:
          inherit = FALSE;
      }
    if (rsbac_get_attr(target,
                       tid,
                       A_auth_may_setuid,
                       &i_attr_val1,
                       inherit))
      {
        printk(KERN_WARNING "check_role_auth(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* Access to objects with neither auth_may_setuid nor auth_may_set_cap set */
    /* is granted -> check auth_may_set_cap, if may_setuid is not set */
    if (!i_attr_val1.auth_may_setuid)
      {
        if (rsbac_get_attr(target,
                           tid,
                           A_auth_may_set_cap,
                           &i_attr_val1,
                           inherit))
          {
            printk(KERN_WARNING "check_role_auth(): rsbac_get_attr() returned error!\n");
            return(NOT_GRANTED);
          }
        /* Access to objects with neither auth_may_setuid nor auth_may_set_cap set is granted */
        if (!i_attr_val1.auth_may_set_cap)
          return(GRANTED);
      }

    /* One of both is set -> check role */
    /* test owner's system_role */
    i_tid.user = owner;
    if (rsbac_get_attr(T_USER,
                       i_tid,
                       A_system_role,
                       &i_attr_val1,
                       TRUE))
      {
        printk(KERN_WARNING "check_role_auth(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* Access is granted, if owner's system_role is security_officer */
    if (i_attr_val1.system_role == SR_security_officer)
      return(GRANTED);
    else
      return(NOT_GRANTED);
  }
#endif /* AUTH_AUTH_PROT */

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

enum rsbac_adf_req_ret_t
   rsbac_adf_request_auth (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_attribute_value_t i_attr_val1;
    #ifdef CONFIG_RSBAC_AUTH_AUTH_PROT
    union rsbac_target_id_t       i_tid;
    int err=0;
    #endif

    switch (request)
      {
        case R_CHANGE_OWNER:
            switch(target)
              {
                case T_FILE:
                  #ifdef CONFIG_RSBAC_AUTH_AUTH_PROT
                    return(check_role_auth(target,tid,owner));
                  #else
                    return(DO_NOT_CARE);
                  #endif

                case T_PROCESS:
                  if(attr != A_owner)
                    return(UNDEFINED);
                  /* if nothing changes - who cares? */
                  if(owner == attr_val.owner)
                    return(GRANTED);
                  /* check auth_may_setuid of process */
                  if (rsbac_get_attr(T_PROCESS,
                                     tid,
                                     A_auth_may_setuid,
                                     &i_attr_val1,
                                     FALSE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_auth(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* if auth_may_setuid is set, then grant */
                  if (i_attr_val1.auth_may_setuid)
                    return(GRANTED);

                  /* check, if the target uid is in capset, grant, if yes, deny, if not. */
                  if(rsbac_auth_p_capset_member(caller_pid, attr_val.owner))
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);

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

        case R_MODIFY_ATTRIBUTE:
            switch(attr)
              { /* owner must be changed by other request to prevent inconsistency */
                case A_owner:
                  return(NOT_GRANTED);

                /* Only protect itself, if asked to by configuration */
                #ifdef CONFIG_RSBAC_AUTH_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:
                /* All attributes (remove target!) */
                case A_none:
                  /* Security Officer? */
                  i_tid.user = owner;
                  if (rsbac_get_attr(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_auth(): 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);
                  else
                    return(NOT_GRANTED);
                #endif

                default:
                  return(DO_NOT_CARE);
              }

/* Only protect itself, if asked to by configuration */
#ifdef CONFIG_RSBAC_AUTH_AUTH_PROT
        /* all changing requests to files are checked */
        case R_APPEND_OPEN:
        case R_CHANGE_GROUP:
        case R_DELETE:
        case R_LINK_HARD:
        case R_MODIFY_ACCESS_DATA:
        case R_READ_WRITE_OPEN:
        case R_RENAME:
        case R_TRUNCATE:
        case R_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                  return(check_role_auth(target,tid,owner));
                /* all other cases are not checked */
                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? */
                  i_tid.user = owner;
                  if ((err=rsbac_get_attr(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE)))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_auth(): rsbac_get_attr() returned error %i!\n",err);
                      return(NOT_GRANTED);
                    }
                  /* grant only for secoff */
                  if (i_attr_val1.system_role == SR_security_officer)
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);

                default:
                  return(DO_NOT_CARE);
               };

        case R_MODIFY_PERMISSIONS_DATA:
            switch(target)
              {
                case T_FILE:
                  return(check_role_auth(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(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_auth(): 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);
                  
                /* all other cases are not checked */
                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);
                  /* Get role */
                  i_tid.user = owner;
                  if (rsbac_get_attr(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_auth(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* grant only for secoff */
                  if (i_attr_val1.system_role == SR_security_officer)
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);
                  
                /* all other cases are not checked */
                default: return(DO_NOT_CARE);
              }

        case R_SWITCH_LOG:
            switch(target)
              {
                case T_NONE:
                  /* test owner's system_role */
                  i_tid.user = owner;
                  if (rsbac_get_attr(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING "rsbac_adf_request_auth(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* security officer? -> grant  */
                  if (i_attr_val1.system_role == SR_security_officer)
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);

                /* all other cases are not checked */
                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 != AUTH)
                    )
                    return(DO_NOT_CARE);
                  /* test owner's system_role */
                  i_tid.user = owner;
                  if (rsbac_get_attr(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING "rsbac_adf_request_auth(): rsbac_get_attr() returned error!\n");
                      return(NOT_GRANTED);
                    }
                  /* security officer? -> grant  */
                  if (i_attr_val1.system_role == SR_security_officer)
                    return(GRANTED);
                  else
                    return(NOT_GRANTED);

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

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

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


/*****************************************************************************/
/* 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_auth(
                      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_CLONE:
            if (target == T_PROCESS)
              {
                /* Get auth_may_setuid from first process */
                if (rsbac_get_attr(T_PROCESS,
                                   tid,
                                   A_auth_may_setuid,
                                   &i_attr_val1,
                                   FALSE))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_auth(): rsbac_get_attr() returned error!\n");
                    return(-RSBAC_EREADFAILED);
                  }
                /* Get auth_may_set_cap from first process */
                if (rsbac_get_attr(T_PROCESS,
                                   tid,
                                   A_auth_may_set_cap,
                                   &i_attr_val2,
                                   FALSE))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_auth(): rsbac_get_attr() returned error!\n");
                    return(-RSBAC_EREADFAILED);
                  }
                /* Set auth_may_setuid for new process */
                if (rsbac_set_attr(T_PROCESS,
                                   new_tid,
                                   A_auth_may_setuid,
                                   i_attr_val1))
                  {
                    printk(KERN_WARNING "rsbac_adf_set_attr_auth(): rsbac_set_attr() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                /* Set auth_may_set_cap for new process */
                if (rsbac_set_attr(T_PROCESS,
                                   new_tid,
                                   A_auth_may_set_cap,
                                   i_attr_val2))
                  {
                    printk(KERN_WARNING "rsbac_adf_set_attr_auth(): rsbac_set_attr() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                /* copy capability list */
                if(rsbac_auth_copy_pp_capset(tid.process,new_tid.process))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_auth(): rsbac_auth_copy_pp_capset() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                return(0);
              }
            else
              return(0);

        case R_EXECUTE:
            switch(target)
              {
                case T_FILE:
                  /* reset auth_may_setuid and auth_may_set_cap for process */
                  i_tid.process = caller_pid;
                  /* First, set auth_may_setuid to program file's auth_may_setuid */
                  if (rsbac_get_attr(T_FILE,
                                     tid,
                                     A_auth_may_setuid,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_auth(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  if (rsbac_set_attr(T_PROCESS,
                                     i_tid,
                                     A_auth_may_setuid,
                                     i_attr_val1))
                    {
                      printk(KERN_WARNING "rsbac_adf_set_attr_auth(): rsbac_set_attr() returned error!\n");
                      return(-RSBAC_EWRITEFAILED);
                    }
                  /* Next, set auth_may_set_cap to program file's auth_may_set_cap */
                  if (rsbac_get_attr(T_FILE,
                                     tid,
                                     A_auth_may_set_cap,
                                     &i_attr_val1,
                                     FALSE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_auth(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  if (rsbac_set_attr(T_PROCESS,
                                     i_tid,
                                     A_auth_may_set_cap,
                                     i_attr_val1))
                    {
                      printk(KERN_WARNING "rsbac_adf_set_attr_auth(): rsbac_set_attr() returned error!\n");
                      return(-RSBAC_EWRITEFAILED);
                    }

                  /* copy file capability list from file to process */
                  if (rsbac_auth_copy_fp_capset(tid.file, caller_pid))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_auth(): rsbac_auth_copy_fp_capset() returned error!\n");
                      return(-RSBAC_EWRITEFAILED);
                    }
                  /* replace RSBAC_AUTH_OWNER_F_CAP by current owner */
                  if(rsbac_auth_p_capset_member(caller_pid, RSBAC_AUTH_OWNER_F_CAP))
                    {
                      /* remove it and set cap for owner */
                      if (rsbac_auth_add_to_p_capset(caller_pid, owner, owner))
                        {
                          printk(KERN_WARNING
                                 "rsbac_adf_set_attr_auth(): rsbac_auth_add_to_p_capset() returned error!\n");
                          return(-RSBAC_EWRITEFAILED);
                        }
                      if (rsbac_auth_remove_from_p_capset(caller_pid, RSBAC_AUTH_OWNER_F_CAP, RSBAC_AUTH_OWNER_F_CAP))
                        {
                          printk(KERN_WARNING
                                 "rsbac_adf_set_attr_auth(): rsbac_auth_remove_from_p_capset() returned error!\n");
                          return(-RSBAC_EWRITEFAILED);
                        }
                    }
                  return(0);

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

/* Only protect itself, if asked to by configuration */
#ifdef CONFIG_RSBAC_AUTH_AUTH_PROT
        /* remove all file capabilities on all changing requests to files */
        case R_APPEND_OPEN:
        case R_CHANGE_GROUP:
        case R_DELETE:
        case R_LINK_HARD:
        case R_MODIFY_ACCESS_DATA:
        case R_READ_WRITE_OPEN:
        case R_RENAME:
        case R_TRUNCATE:
        case R_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                  /* remove cap set */
                  if(rsbac_auth_remove_f_capset(tid.file))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_auth(): rsbac_auth_remove_f_capset() returned error!\n");
                      return(-RSBAC_EWRITEFAILED);
                    }
                  return(0);

                /* all other cases are not handled */
                default: return(0);
              }
#endif

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

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

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