/************************************************* */
/* Rule Set Based Access Control                   */
/* Implementation of the Access Control Decision   */
/* Facility (ADF) - Authentification module        */
/* File: rsbac/adf/auth/syscalls.c                 */
/*                                                 */
/* Author and (c) 1999: Amon Ott                   */
/*                                                 */
/* Last modified: 08/Sep/99                        */
/************************************************* */

#include <linux/string.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <rsbac/types.h>
#include <rsbac/aci.h>
#include <rsbac/error.h>
#include <rsbac/acl.h>
#include <rsbac/getname.h>

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

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

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

/* from acl/acl_main.c */
int rsbac_acl_get_user(rsbac_uid_t * user_p)
  {
#ifdef CONFIG_RSBAC_USE_RSBAC_OWNER
    union rsbac_target_id_t       i_tid;
    union rsbac_attribute_value_t i_attr_val1;
    int err;

    /* getting process owner from rsbac system */
    i_tid.process = caller_pid;
    err = rsbac_get_attr(T_PROCESS,i_tid,A_owner,&i_attr_val1,FALSE);
    if (err)
      {
        char tmp[80];
        printk(KERN_WARNING
               "rsbac_acl_sys_set_acl_entry(): rsbac_get_attr() for owner returned error %s",
               get_error_name(tmp,err));
        return(-RSBAC_EREADFAILED);  /* something weird happened */
      }
    *user_p = i_attr_val1.owner;
    if (*user_p == RSBAC_NO_USER)
      { /* This process has not been registered yet */
        /* -> use unix owner */
        *user_p = current->uid;
      }
    else
      *user_p = i_attr_val.owner;
#else /* CONFIG_RSBAC_USE_RSBAC_OWNER not set -> always use linux owner */
    *user_p = current->uid;
#endif /* CONFIG_RSBAC_USE_RSBAC_OWNER */
    return(0);
  }

boolean rsbac_acl_check_super(enum  rsbac_target_t target,
                              union rsbac_target_id_t tid,
                                    rsbac_uid_t user)
  {
    boolean                   i_result = FALSE;
    int                       err=0;
    #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_PROCESS:
          break;
        default:
          return TRUE;
      }
    /* own right */
    err = rsbac_acl_get_super(target, tid, ACLS_USER, (rsbac_acl_subject_id_t) user, &i_result);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_check_super(): rsbac_acl_get_super() returned error %s!\n",
               get_error_name(tmp,err));
        return FALSE;
      }
    if(i_result)
      return(TRUE);

    /* try SUPERVISOR for group and role */
    /* group everyone */
    err = rsbac_acl_get_super(target, tid, ACLS_GROUP, RSBAC_ACL_GROUP_EVERYONE,
                              &i_result);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_check_super(): rsbac_acl_get_super() returned error %s!\n",
               get_error_name(tmp,err));
        return FALSE;
      }
    if(i_result)
      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_super(): rsbac_get_attr() for process rc_role returned error!\n");
      }
    else
      {
        err = rsbac_acl_get_super(target, tid, ACLS_ROLE, i_attr_val1.rc_role,
                                  &i_result);
        if(err)
          {
            char tmp[80];

            printk(KERN_WARNING
                   "rsbac_acl_check_super(): rsbac_acl_get_super() returned error %s!\n",
                   get_error_name(tmp,err));
            return FALSE;
          }
        if(i_result)
          return(TRUE);
      }
    #endif

    /* give up */
    return FALSE;
  };


#if defined(CONFIG_RSBAC_ACL)
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);

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);
#endif

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

int rsbac_acl_sys_set_acl_entry (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid,
                                 enum   rsbac_acl_subject_type_t    subj_type,
                                        rsbac_acl_subject_id_t      subj_id,
                                        rsbac_acl_rights_vector_t   rights)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL)
    rsbac_uid_t user;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
    /* first try access control right (SUPERVISOR is included) */
    if(!rsbac_acl_check_right(target, tid, user, ACLR_ACCESS_CONTROL))
      {
        /* no access control -> try forward for these rights */
        if(!rsbac_acl_check_forward(target, tid, user, rights))
          return(-EPERM);
      }
    if(rights & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR)
      {
        /* you must have SUPERVISOR to set SUPERVISOR */
        if(!rsbac_acl_check_super(target, tid, user))
          return -EPERM;
      }
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Set ACL. */
    err = rsbac_acl_set_acl_entry(target, tid, subj_type, subj_id, rights);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_set_acl_entry(): rsbac_acl_set_acl_entry() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_remove_acl_entry (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid,
                                 enum   rsbac_acl_subject_type_t    subj_type,
                                        rsbac_acl_subject_id_t      subj_id)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL)
    rsbac_uid_t user;
    rsbac_acl_rights_vector_t res_rights = 0;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
    /* first try access control right (SUPERVISOR is included) */
    if(!rsbac_acl_check_right(target, tid, user, ACLR_ACCESS_CONTROL))
      return(-EPERM);

    err = rsbac_acl_get_rights(target, tid, subj_type, subj_id, &res_rights, FALSE);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_remove_acl_entry(): rsbac_acl_get_rights() returned error %s!\n",
               get_error_name(tmp,err));
        return err;
      }
    if(res_rights & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR)
      {
        /* you must have SUPERVISOR to remove an entry with SUPERVISOR */
        if(!rsbac_acl_check_super(target, tid, user))
          return -EPERM;
      }
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Set ACL. */
    err = rsbac_acl_remove_acl_entry(target, tid, subj_type, subj_id);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_remove_acl_entry(): rsbac_acl_remove_acl_entry() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_remove_acl (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL)
    rsbac_uid_t user;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
    /* check SUPERVISOR */
    if(!rsbac_acl_check_super(target, tid, user))
      return(-EPERM);
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Set ACL. */
    err = rsbac_acl_remove_acl(target, tid);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_remove_acl(): rsbac_acl_remove_acl() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_add_to_acl_entry (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid,
                                 enum   rsbac_acl_subject_type_t    subj_type,
                                        rsbac_acl_subject_id_t      subj_id,
                                        rsbac_acl_rights_vector_t   rights)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL)
    rsbac_uid_t user;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
    /* first try access control right (SUPERVISOR is included) */
    if(!rsbac_acl_check_right(target, tid, user, ACLR_ACCESS_CONTROL))
      {
        /* no access control -> try forward for these rights */
        if(!rsbac_acl_check_forward(target, tid, user, rights))
          return(-EPERM);
      }
    if(rights & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR)
      {
        /* you must have SUPERVISOR to add SUPERVISOR */
        if(!rsbac_acl_check_super(target, tid, user))
          return -EPERM;
      }
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Set ACL. */
    err = rsbac_acl_add_to_acl_entry(target, tid, subj_type, subj_id, rights);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_add_to_acl_entry(): rsbac_acl_add_to_acl_entry() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_remove_from_acl_entry (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid,
                                 enum   rsbac_acl_subject_type_t    subj_type,
                                        rsbac_acl_subject_id_t      subj_id,
                                        rsbac_acl_rights_vector_t   rights)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL)
    rsbac_uid_t user;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
    /* first try access control right (SUPERVISOR is included) */
    if(!rsbac_acl_check_right(target, tid, user, ACLR_ACCESS_CONTROL))
      return(-EPERM);
    if(rights & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR)
      {
        /* you must have SUPERVISOR to revoke SUPERVISOR */
        if(!rsbac_acl_check_super(target, tid, user))
          return -EPERM;
      }
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Remove ACL. */
    err = rsbac_acl_remove_from_acl_entry(target, tid, subj_type, subj_id, rights);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_remove_from_acl_entry(): rsbac_acl_remove_from_acl_entry() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_set_mask    (enum   rsbac_target_t              target,
                               union  rsbac_target_id_t           tid,
                                      rsbac_acl_rights_vector_t   mask)
  {
    int err=0;

/* check only in non-maint mode */
#if defined(CONFIG_RSBAC_ACL) || defined (CONFIG_RSBAC_ACL_SUPER_FILTER)
    rsbac_uid_t user;

    if(rsbac_acl_get_user(&user))
      return -RSBAC_EREADFAILED;
#endif
#if defined(CONFIG_RSBAC_ACL)
    /* first try access control right (SUPERVISOR is included) */
    if(!rsbac_acl_check_right(target, tid, user, ACLR_ACCESS_CONTROL))
      return(-EPERM);
#endif /* CONFIG_RSBAC_ACL */

#ifdef CONFIG_RSBAC_ACL_SUPER_FILTER
    if(!(mask & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR))
      { /* trial to mask out SUPERVISOR */
        rsbac_acl_rights_vector_t res_rights = 0;

        /* you must have direct SUPERVISOR as a USER to set a mask without SUPERVISOR */
        /* get direct own rights (still uses default_fd_rights) */
        err = rsbac_acl_get_rights(target, tid, ACLS_USER, user, &res_rights, FALSE);
        if(err)
          return -RSBAC_EREADFAILED;
        if(!(res_rights & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR))
          mask |= RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR;
      }
#else
    /* SUPERVISOR must never be masked out */
    mask |= RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR;
#endif

    /* OK, checks passed. Set mask. */
    err = rsbac_acl_set_mask(target, tid, mask);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_set_mask(): rsbac_acl_set_mask() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_get_mask    (enum   rsbac_target_t              target,
                               union  rsbac_target_id_t           tid,
                                      rsbac_acl_rights_vector_t * mask_p)
  {
    int err=0;

/* no check */
#if defined(CONFIG_RSBAC_ACL)
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Get mask. */
    err = rsbac_acl_get_mask(target, tid, mask_p);
    if(err)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_get_mask(): rsbac_acl_get_mask() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

int rsbac_acl_sys_get_rights (enum   rsbac_target_t              target,
                                 union  rsbac_target_id_t           tid,
                                 enum   rsbac_acl_subject_type_t    subj_type,
                                        rsbac_acl_subject_id_t      subj_id,
                                        rsbac_acl_rights_vector_t * rights_p,
                                        boolean                     effective)
  {
    int err=0;
    rsbac_acl_rights_vector_t res_rights;
    #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

    /* no check (Attention: rsbac_acl_check_forward depends on this to be allowed!) */
#if defined(CONFIG_RSBAC_ACL)
#endif /* CONFIG_RSBAC_ACL */

    if(   (subj_type == ACLS_USER)
       && (subj_id == RSBAC_NO_USER)
      )
      rsbac_acl_get_user((rsbac_uid_t *) &subj_id);
    /* OK, check passed. Call ACL. */
    if(effective)
      {
        /* inherited own rights */
        res_rights = 0;
        err = rsbac_acl_get_rights(target, tid, subj_type, subj_id, &res_rights, TRUE);
        if(err)
          {
            char tmp[80];

            printk(KERN_WARNING
                   "rsbac_acl_sys_get_rights(): rsbac_acl_get_rights() returned error %s!\n",
                   get_error_name(tmp,err));
            return err;
          }
        *rights_p = res_rights;
        /* add group and role rights, if normal user */
        if(subj_type == ACLS_USER)
          {
            /* group everyone */
            res_rights = 0;
            err = rsbac_acl_get_rights(target, tid, ACLS_GROUP, RSBAC_ACL_GROUP_EVERYONE,
                                       &res_rights, TRUE);
            if(err)
              {
                char tmp[80];

                printk(KERN_WARNING
                       "rsbac_acl_sys_get_rights(): rsbac_acl_get_rights() returned error %s!\n",
                       get_error_name(tmp,err));
                return err;
              }
            *rights_p |= res_rights;

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

                    printk(KERN_WARNING
                           "rsbac_acl_sys_get_rights(): rsbac_acl_get_rights() returned error %s!\n",
                           get_error_name(tmp,err));
                    return err;
                  }
                *rights_p |= res_rights;
              }
            #endif
            /* check for SUPERVISOR right, if not yet there */
            if(   !(*rights_p & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR)
               && rsbac_acl_check_super(target, tid, subj_id)
              )
              *rights_p |= RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR;
          }
        else /* not ACLS_USER */
          {
            if(!(*rights_p & RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR))
              {
                boolean i_result = FALSE;

                /* check for SUPERVISOR right */
                /* own right */
                err = rsbac_acl_get_super(target, tid, subj_type, subj_id, &i_result);
                if(err)
                  {
                    char tmp[80];

                    printk(KERN_WARNING
                           "rsbac_acl_sys_get_rights(): rsbac_acl_get_super() returned error %s!\n",
                           get_error_name(tmp,err));
                  }
                else
                  if(i_result)
                    *rights_p |= RSBAC_ACL_SUPERVISOR_RIGHT_VECTOR;
              }
          }
      }
    else /* not effective = direct */
      {
        /* direct own rights (still uses default_fd_rights) */
        res_rights = 0;
        err = rsbac_acl_get_rights(target, tid, subj_type, subj_id, &res_rights, FALSE);
        if(!err)
          *rights_p = res_rights;
      }
    return err;
  }

int rsbac_acl_sys_get_tlist (enum   rsbac_target_t              target,
                             union  rsbac_target_id_t           tid,
                             struct rsbac_acl_entry_t           entry_array[],
                                    u_int                       maxnum)
  {
    int err=0;

    /* no check */
#if defined(CONFIG_RSBAC_ACL)
#endif /* CONFIG_RSBAC_ACL */

    /* OK, check passed. Call ACL. */
    err = rsbac_acl_get_tlist(target, tid, entry_array, maxnum);
    if(err<0)
      {
        char tmp[80];

        printk(KERN_WARNING
               "rsbac_acl_sys_get_tlist(): rsbac_acl_get_tlist() returned error %s!\n",
               get_error_name(tmp,err));
      }
    return err;
  }

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