/************************************************** */
/* Rule Set Based Access Control                    */
/* Implementation of the Access Control Decision    */
/* Facility (ADF) - REG / Decision Module Registration */
/* File: rsbac/adf/reg/main.c                       */
/*                                                  */
/* Author and (c) 1999: Amon Ott                    */
/*                                                  */
/* Last modified: 06/Dec/99                         */
/************************************************** */

#include <linux/string.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/module.h>
#include <rsbac/types.h>
#include <rsbac/reg.h>
#include <rsbac/aci.h>
#include <rsbac/adf.h>
#include <rsbac/adf_main.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/getname.h>
#include <rsbac/proc_fs.h>

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

struct rsbac_reg_entry_t rsbac_reg_entry[RSBAC_REG_NR_ENTRIES];

/************************************************* */
/*           PROC support                          */
/************************************************* */

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
reg_modules_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
reg_modules_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
  int len = 0;
  int i;
  int count = 0;
  off_t pos   = 0;
  off_t begin = 0;

  union rsbac_target_id_t       rsbac_target_id;
  union rsbac_attribute_value_t rsbac_attribute_value;

  if (!rsbac_is_initialized())
    return (-ENOSYS);

  if (rsbac_debug_aef) printk(KERN_DEBUG "reg_modules_proc_info(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_GET_STATUS_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      return -EPERM;
    }
  len += sprintf(buffer, "RSBAC REG registered decision modules\n-------------------------------------\n");
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
    {
      if(rsbac_reg_entry[i].handle == 0)
        continue;

      /* found entry -> show it */
      count++;
      if(rsbac_reg_entry[i].name[0] == 0)
        len += sprintf(buffer + len, "(no name)\n");
      else
        len += sprintf(buffer + len, "%s\n",
                       rsbac_reg_entry[i].name);
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        goto out;
    }

  len += sprintf(buffer + len, "\n %i of %i module entries used.\n",
                 count, RSBAC_REG_NR_ENTRIES);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}
#endif /* PROC */

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

void __init rsbac_reg_init(void)
  {
    int i;
    struct proc_dir_entry * tmp_entry_p;
    
    if (rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_reg_init(): RSBAC already initialized\n");
      }
    else
      {
        /* init data structures */
        printk(KERN_INFO "rsbac_reg_init(): Initializing RSBAC: REG module registration\n");

        for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
          {
            rsbac_reg_entry[i].handle        = 0;
            rsbac_reg_entry[i].name[0]       = 0;
            rsbac_reg_entry[i].request_func  = NULL;
            rsbac_reg_entry[i].set_attr_func = NULL;
            rsbac_reg_entry[i].need_overwrite_func = NULL;
            rsbac_reg_entry[i].write_func    = NULL;
            rsbac_reg_entry[i].mount_func    = NULL;
            rsbac_reg_entry[i].umount_func   = NULL;
            rsbac_reg_entry[i].switch_on     = FALSE;
          }
        /* init proc entry */
        #if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
        tmp_entry_p = create_proc_entry("reg_modules",
                                        S_IFREG | S_IRUGO,
                                        proc_rsbac_root_p);
        if(tmp_entry_p)
          {
            tmp_entry_p->get_info = reg_modules_proc_info;
          }
        #endif
      }
  }

/*
 * Register an ADF decision module
 * Returns given positive handle or negative error code
 */

EXPORT_SYMBOL(rsbac_reg_register);

long rsbac_reg_register  (struct rsbac_reg_entry_t  entry)
  {
    int i;
    
    /* check entry */
    if(   (   !entry.request_func
           && !entry.set_attr_func
           && !entry.need_overwrite_func
           && !entry.write_func
          )
       || (entry.handle <= 0)
      )
      return -RSBAC_EINVALIDVALUE;

    /* find unused entry field */
    for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
      {
        if(rsbac_reg_entry[i].handle == entry.handle)
          {
            printk(KERN_INFO "rsbac_reg_register: Handle in use, registering failed: %s.\n",
                   entry.name);
            return -RSBAC_EEXISTS;
          }
      }
    for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
      {
        if(   rsbac_reg_entry[i].request_func
           || rsbac_reg_entry[i].set_attr_func
           || rsbac_reg_entry[i].need_overwrite_func
           || rsbac_reg_entry[i].write_func
           || rsbac_reg_entry[i].mount_func
           || rsbac_reg_entry[i].umount_func
           || rsbac_reg_entry[i].handle)
          continue;

        /* found empty entry */
        rsbac_reg_entry[i].handle = entry.handle;
        strncpy(rsbac_reg_entry[i].name, entry.name, RSBAC_REG_NAME_LEN);
        rsbac_reg_entry[i].name[RSBAC_REG_NAME_LEN] = 0;
        printk(KERN_INFO "rsbac_reg_register: Registering decision module: %s.\n",
               rsbac_reg_entry[i].name);
        rsbac_reg_entry[i].request_func = entry.request_func;
        rsbac_reg_entry[i].set_attr_func = entry.set_attr_func;
        rsbac_reg_entry[i].need_overwrite_func = entry.need_overwrite_func;
        rsbac_reg_entry[i].write_func = entry.write_func;
        rsbac_reg_entry[i].mount_func = entry.mount_func;
        rsbac_reg_entry[i].umount_func = entry.umount_func;
        rsbac_reg_entry[i].switch_on = entry.switch_on;
        /* ready */
        return(entry.handle);
      }
    /* No entry available -> error */
    printk(KERN_INFO "rsbac_reg_register: No entry available, registering failed: %s.\n",
           entry.name);
    return -RSBAC_ECOULDNOTADDITEM;
  };

/*
 * Switch module on or off - for 'normal' modules this is done by general
 * function. This is a dummy, if module switching is disabled.
 */

EXPORT_SYMBOL(rsbac_reg_switch);

int rsbac_reg_switch    (int    handle, boolean value)
  {
#ifdef CONFIG_RSBAC_SWITCH
    int i;

    for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
      {
        if(rsbac_reg_entry[i].handle != handle)
          continue;

        /* found entry */
        rsbac_reg_entry[i].switch_on = value;
        /* ready */
        return(0);
      }
    return(-RSBAC_EINVALIDTARGET);
#else
    return(-RSBAC_EINVALIDTARGET);
#endif
  };

/*
 * Unregister an ADF decision module
 * Returns 0 on success or negative error code. Be careful not to unregister
 * modules you did not register yourself.
 */

EXPORT_SYMBOL(rsbac_reg_unregister);

int rsbac_reg_unregister(int    handle)
  {
    int i;

    for(i=0; i<RSBAC_REG_NR_ENTRIES; i++)
      {
        if(rsbac_reg_entry[i].handle != handle)
          continue;

        /* found entry -> delete it */
        printk(KERN_INFO "rsbac_reg_unregister: Unregistering decision module: %s.\n",
               rsbac_reg_entry[i].name);
        rsbac_reg_entry[i].handle = 0;
        rsbac_reg_entry[i].name[0] = 0;
        rsbac_reg_entry[i].request_func = NULL;
        rsbac_reg_entry[i].set_attr_func = NULL;
        rsbac_reg_entry[i].need_overwrite_func = NULL;
        rsbac_reg_entry[i].write_func = NULL;
        rsbac_reg_entry[i].mount_func    = NULL;
        rsbac_reg_entry[i].umount_func   = NULL;
        rsbac_reg_entry[i].switch_on = FALSE;
        /* ready */
        return(0);
      }
    return(-RSBAC_EINVALIDTARGET);
  };

/* end of rsbac/adf/reg/reg_main.c */
