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

#include <linux/string.h>
#include <asm/uaccess.h>
#include <rsbac/aci.h>
#include <rsbac/adf_main.h>
#include <rsbac/debug.h>
#include <rsbac/error.h>
#include <rsbac/ms_strings.h>
#include <rsbac/helpers.h>
#include <rsbac/getname.h>
#include <linux/proc_fs.h>

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

static rsbac_ms_all_malware_t all_malware = RSBAC_MS_ALL_STRINGS;

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

/* open_by_dentry */
/* This is done by hand (copy from rsbac_read_open), because system calls */
/* are currently blocked by rsbac semaphores */

static int open_by_dentry(struct dentry * file_dentry_p, struct file * file_p)
  {
    int tmperr;

    if ( !(S_ISREG(file_dentry_p->d_inode->i_mode)) )
      { /* this is not a file! -> error! */
        printk(KERN_WARNING
               "open_by_dentry(): expected file is not a file!\n");
        return (-RSBAC_EREADFAILED);
      }
    /* Now we fill the file structure, and */
    /* if there is an open func, use it, otherwise ignore */
    if ((tmperr = init_private_file(file_p, file_dentry_p, O_RDONLY)))
      {
        printk(KERN_WARNING
               "open_by_dentry(): could not open file!\n");
        return (-RSBAC_EREADFAILED);
      }
    /* Without a read function we get into troubles -> error */
    if ((!file_p->f_op) || (!file_p->f_op->read))
      {
        printk(KERN_WARNING
               "open_by_dentry(): file read function missing!\n");
        if (file_p->f_op && file_p->f_op->release)
          file_p->f_op->release(file_dentry_p->d_inode,file_p);
        return(-RSBAC_EREADFAILED);
      }
    return 0;
  }

/* do_scan() */
/* This function scans the given file for malware, returning a boolean value */
/* whether the file is accepted or not. */

static boolean do_scan(struct dentry * dentry_p)
  {
    struct file  file;
    int i, j, len, c, tmperr=1;
    mm_segment_t oldfs;
    boolean retval = TRUE;
    u_long chunk = 0;
    char * kbuf;
    u_int count = 0;
    int str_nr[RSBAC_MS_NR_MALWARE];
    int str_offset[RSBAC_MS_NR_MALWARE];
    char * pos;
    char * bufend = NULL;
    boolean buffer_end;

    if(!dentry_p)
      {
        printk(KERN_WARNING
               "do_scan(): called with NULL dentry_p!\n");
        return(FALSE);
      }
    if(!dentry_p->d_inode)
      {
        printk(KERN_WARNING
               "do_scan(): called with NULL dentry_p->d_inode!\n");
        return(FALSE);
      }
    /* allocate help buffer */
    kbuf = (char *) kmalloc(RSBAC_MS_MAX_STR_LEN + RSBAC_MS_CHUNK_SIZE, GFP_KERNEL);
    if(!kbuf)
      {
        printk(KERN_WARNING "do_scan(): could not allocate buffer memory!\n");
        return(FALSE);
      }
    /* clear buffer start / step back part */
    memset(kbuf,0,RSBAC_MS_MAX_STR_LEN);
    /* open */
    if(rsbac_debug_adf_ms)
      printk(KERN_DEBUG "do_scan(): opening file (dev %u, inode %lu)\n",
                          dentry_p->d_inode->i_dev,
                          dentry_p->d_inode->i_ino);
    if(open_by_dentry(dentry_p,&file))
      {
        printk(KERN_WARNING
               "do_scan(): file open failed!\n");
        return(FALSE);
      }
    /* OK, now we can start reading */
    /* There is a read function for this file, so fill buffer as far */
    /* as possible. A positive return value means a read success,   */
    /* 0 end of file and a negative value an error.                 */

    /* Set current user space to kernel space, because read() writes */
    /* to user space */
    oldfs = get_fs();
    set_fs(KERNEL_DS);

    if(rsbac_debug_adf_ms)
      printk(KERN_DEBUG "do_scan(): starting to read\n");

    for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
      {
        str_nr[i] = 0;
        str_offset[i] = 0;
      }
    while(tmperr > 0)
      {
        /* concat new data from file */
        tmperr = file.f_op->read(&file,
                                 kbuf + RSBAC_MS_MAX_STR_LEN,
                                 RSBAC_MS_CHUNK_SIZE,
                                 &file.f_pos);
        /* if none read, end of file is reached -> file OK -> return TRUE */
        if(!tmperr)
          {
            goto out;
          }
        if (tmperr < 0)
          {
            printk(KERN_WARNING "do_scan: read error from file!\n");
            retval = FALSE;
            goto out;
          }
        /* begin scan */
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG
                 "do_scan(): starting to scan chunk %lu, %i bytes\n",
                 chunk, tmperr);
        bufend = kbuf + RSBAC_MS_MAX_STR_LEN + tmperr;
        j=0;
        while((j<RSBAC_MS_NR_MALWARE) && (retval))
          {
            /* entry is first new char */
            pos = kbuf + RSBAC_MS_MAX_STR_LEN;
            buffer_end = FALSE;
            count = 0;

            /* in current string str_nr[j] we were at position str_offset[j] */
            c = str_offset[j];
            for(i = str_nr[j]; i<RSBAC_MS_NR_STRINGS; i++)
              {
                len = strlen(all_malware[j].string[i]);
                do
                  {
                    /* if next char in string is read char or '.', advance */
                    if (   (*pos == all_malware[j].string[i][c])
                        || (all_malware[j].string[i][c] == '.')
                       )
                      {
                        c++;
                      }
                    /* else: back to string pos c=0 and back with buffer pos */
                    else
                      { /* but only, if c > 0 */
                        if(c)
                          {
                            /* go back in buffer */
                            pos -= c;
                            count -= c;
                            /* reset c */
                            c=0;
                          }
                      }
                    /* advance... */
                    pos++;
                    count++;
                  }
                while (   (c < len)
                       && (count < tmperr)); /* end of do */
                /* buffer end reached? -> store values and go on with next entry */
                if(count >= tmperr)
                  {
                    /* check for last char match */
                    if(c == len)
                      {
                        c = 0;
                        i++;
                      }
                    /* Has last char match triggered full match? If not, go on here */
                    if(i<RSBAC_MS_NR_STRINGS)
                      {
                        /* set string no. */
                        str_nr[j] = i;
                        /* set string offset */
                        str_offset[j] = c;
                        /* note buffer end for this malware entry */
                        buffer_end = TRUE;
                        break;
                      }
                  }
                /* no segment end or last char full match -> string was matched */
                if(rsbac_debug_adf_ms)
                  printk(KERN_DEBUG "do_scan(): string %i for %s matched at byte %u\n",
                         i, all_malware[j].name, count);
                /* reset str position counter for next string */
                c = 0;
              } /* end of for (running through all search strings) */

            if(!buffer_end)
              {
                /* OK, all strings were found -> file is probably infected */
                printk(KERN_WARNING
                       "do_scan(): file (dev %u, inode %lu) is probably infected by %s!\n",
                       dentry_p->d_inode->i_dev,
                       dentry_p->d_inode->i_ino,
                       all_malware[j].name);
                retval = FALSE;
                goto out;
              }
            j++;
          } /* end of while loop for all malware entries */

        /* all entries reached buffer end: continue */
        /* fill backbuf (start of buffer) */
        memcpy(kbuf, bufend - RSBAC_MS_MAX_STR_LEN, RSBAC_MS_MAX_STR_LEN);
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG
                 "do_scan(): finished scan for chunk %lu\n",
                 chunk);
        chunk++;
      }

out:
    /* Set current user space back to user space, because read() writes */
    /* to user space */
    set_fs(oldfs);

/* do we need this here? */
    if (file.f_op->release)
      file.f_op->release(dentry_p->d_inode,&file);
    /* return FALSE = rejected, TRUE = accepted */
    return(retval);
  }
  
/* scan() */
/* This function checks the scanned status for given file, calls do_scan, if */
/* unscanned, sets the scanned status and returns whether file is accepted or */
/* not. */

static boolean scan(struct rsbac_fs_file_t file,
                    rsbac_pid_t caller_pid,
                    boolean execute)
  {
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_target_id_t       i_tid;
    struct dentry * dentry_p = file.dentry_p;

    /* just in case... */
    if(!dentry_p || !dentry_p->d_inode)
      {
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG "scan(): refused scan for file without proper dentry and inode\n");
        return(TRUE);
      }
    /* currently no check or scanned status change for non-regular files */
    if(!S_ISREG(dentry_p->d_inode->i_mode))
      {
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG "scan(): refused scan for non-regular file\n");
        return(TRUE);
      }
    /* no check or scanned status change for procfs files */
    if(dentry_p->d_inode->i_sb->s_magic == PROC_SUPER_MAGIC)
      {
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG "scan(): refused scan for procfs file\n");
        return(TRUE);
      }
    /* get ms_trusted for process */
    i_tid.process = caller_pid;
    if (rsbac_get_attr(T_PROCESS,
                       i_tid,
                       A_ms_trusted,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "scan(): rsbac_get_attr() returned error!\n");
        return(FALSE);
      }
    /* no check or scanned status change for trusted processes */
    if(   (i_attr_val1.ms_trusted == MT_full)
       || (   (i_attr_val1.ms_trusted == MT_read)
           && !execute
          )
      )
      return(TRUE);

    i_tid.file = file;
    /* get scanned status for file */
    if (rsbac_get_attr(T_FILE,
                       i_tid,
                       A_ms_scanned,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "scan(): rsbac_get_attr() returned error!\n");
        return(FALSE);
      }
    /* accepted with current or higher level? */
    if(i_attr_val1.ms_scanned >= MS_LEVEL)
      return(TRUE);
    /* rejected with any level? */
    if(i_attr_val1.ms_scanned == MS_rejected)
      return(FALSE);

    /* default: scan and set to level or MS_rejected*/
    if(do_scan(dentry_p))
      {
        i_attr_val1.ms_scanned = MS_LEVEL;
        if (rsbac_set_attr(T_FILE,
                           i_tid,
                           A_ms_scanned,
                           i_attr_val1))
          {
            printk(KERN_WARNING
                   "scan(): rsbac_set_attr() returned error!\n");
            return(FALSE);
          }
        return(TRUE);
      }
    else
      {
        i_attr_val1.ms_scanned = MS_rejected;
        if (rsbac_set_attr(T_FILE,
                           i_tid,
                           A_ms_scanned,
                           i_attr_val1))
          {
            printk(KERN_WARNING
                   "scan(): rsbac_set_attr() returned error!\n");
          }
        return(FALSE);
      }
  }; /* end of scan() */

static int reset_scanned(struct rsbac_fs_file_t file)
  {
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_target_id_t       i_tid;

    /* get scanned status for file */
    i_tid.file=file;
    if (rsbac_get_attr(T_FILE,
                       i_tid,
                       A_ms_scanned,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "reset_scanned(): rsbac_get_attr() returned error!\n");
        return(-RSBAC_EREADFAILED);
      }
    if(i_attr_val1.ms_scanned != MS_unscanned)
      {
        i_attr_val1.ms_scanned = MS_unscanned;
        if(rsbac_set_attr(T_FILE,
                          i_tid,
                          A_ms_scanned,
                          i_attr_val1))
          {
            printk(KERN_WARNING "reset_scanned(): rsbac_set_attr() returned error!\n");
            return(-RSBAC_EWRITEFAILED);
          }
      }
    return(0);
  };

/* do_scan_sock() */
/* This function scans the given segment for malware, returning a decision */
/* whether the segment is accepted or not. */
/* The scanning state is first retrieved from IPC ACI and later saved there */
/* before returning. */

#ifdef CONFIG_RSBAC_MS_SOCK
static enum rsbac_adf_req_ret_t
  do_scan_sock(union  rsbac_target_id_t tid,
                      rsbac_pid_t       caller_pid,
               struct ms_segment_t      segment)
  {
    char * kbuf;
    int oldbuflen;
    int i=0,c=0,len=0,j=0;
    int count = 0;
    int str_nr[RSBAC_MS_NR_MALWARE];
    int str_offset[RSBAC_MS_NR_MALWARE];
    char * pos;
    char * bufend;
    char * acibuf;
    boolean is_infected = FALSE;
    boolean buffer_end;
    enum  rsbac_attribute_t       i_attr;
    union rsbac_attribute_value_t i_attr_val1;
    union rsbac_target_id_t       i_tid;

    /* get ms_scanned for socket */
    if (rsbac_get_attr(T_IPC,
                       tid,
                       A_ms_scanned,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* reject, if previously active rejected */
    if(i_attr_val1.ms_scanned == MS_active_rejected)
      {
        /* save segment length */
        i_attr_val1.ms_buflen = segment.len;
        if(rsbac_set_attr(T_IPC,
                          tid,
                          A_ms_buflen,
                          i_attr_val1))
          {
            printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
          }
        /* return not_granted */
        return(NOT_GRANTED);
      }
    /* reject and blank, if previously rejected */
    if(i_attr_val1.ms_scanned == MS_rejected)
      {
        clear_user_buf(segment.ubuf,segment.len);
        return(NOT_GRANTED);
      }
    /* grant access for empty segment */
    if(!segment.len)
      return(GRANTED);

    /* get ms_buflen for sock */
    if (rsbac_get_attr(T_IPC,
                       tid,
                       A_ms_buflen,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    oldbuflen = i_attr_val1.ms_buflen;
    /* allocate help buffer */
    kbuf = (char *) kmalloc(oldbuflen + segment.len + 1, GFP_KERNEL);
    if(!kbuf)
      {
        printk(KERN_WARNING "do_scan_sock(): could not allocate buffer memory!\n");
        return(NOT_GRANTED);
      }
    /* get old buffer */
    if (rsbac_get_attr(T_IPC,
                       tid,
                       A_ms_backbuf,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    acibuf = i_attr_val1.ms_backbuf;
    if(oldbuflen)
      {
        memcpy(kbuf,acibuf,oldbuflen);
      }
    /* get ms_str_nr for sock */
    if (rsbac_get_attr(T_IPC,
                       tid,
                       A_ms_str_nr,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
      str_nr[i] = i_attr_val1.ms_str_nr[i];
    /* get ms_str_offset for sock */
    if (rsbac_get_attr(T_IPC,
                       tid,
                       A_ms_str_offset,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
      str_offset[i] = i_attr_val1.ms_str_offset[i];
    /* concat new data from user space */
    rsbac_get_user(kbuf+oldbuflen,segment.ubuf,segment.len);
    /* begin scan */
    if(rsbac_debug_adf_ms)
      printk(KERN_DEBUG
             "do_scan_sock(): starting to scan on sock %lu, %i bytes\n",
             (u_long) tid.ipc.id.sock, segment.len);
    bufend = kbuf + (oldbuflen + segment.len);
    j=0;
    while((j<RSBAC_MS_NR_MALWARE) && (!is_infected))
      {
        /* entry is first new char */
        pos = kbuf + oldbuflen;
        buffer_end = FALSE;
        count = 0;

        /* in current string str_nr[j] we were at position str_offset[j] */
        c = str_offset[j];
        for(i = str_nr[j]; i<RSBAC_MS_NR_STRINGS; i++)
          {
            len = strlen(all_malware[j].string[i]);
            do
              {
                /* if next char in string is read char or '.', advance */
                if (   (*pos == all_malware[j].string[i][c])
                    || (all_malware[j].string[i][c] == '.')
                   )
                  {
                    c++;
                  }
                /* else: back to string pos c=0 and back with buffer pos */
                else
                  { /* but only, if c > 0 */
                    if(c)
                      {
                        /* go back in buffer */
                        pos -= c;
                        count -= c;
                        /* reset c */
                        c=0;
                      }
                  }
                /* advance... */
                pos++;
                count++;
              }
            while (   (c < len)
                   && (count < segment.len)); /* end of do */
            /* buffer end reached? -> store values and go on with next entry */
            if(count >= segment.len)
              {
                /* check for last char match */
                if(c == len)
                  {
                    c = 0;
                    i++;
                  }
                /* Has last char match triggered full match? If not, go on here */
                if(i<RSBAC_MS_NR_STRINGS)
                  {
                    /* set string no. */
                    str_nr[j] = i;
                    /* set string offset */
                    str_offset[j] = c;
                    /* note buffer end for this malware entry */
                    buffer_end = TRUE;
                    break;
                  }
              }
            /* no segment end or last char full match -> string was matched */
            if(rsbac_debug_adf_ms)
              printk(KERN_DEBUG "do_scan_sock(): string %i for %s matched at byte %i\n",
                     i, all_malware[j].name, count);
            /* reset str position counter for next string */
            c = 0;
          } /* end of for (running through all search strings) */

        if(!buffer_end)
          {
            /* OK, all strings were found -> stream is probably infected */
            printk(KERN_WARNING
                   "do_scan_sock(): stream for process %i is probably infected by %s\n",
                   caller_pid, all_malware[j].name);
            is_infected = TRUE;
            break;
          }
        j++;
      } /* end of while loop for all malware entries */

    /* if no infection, all entries reached buffer end: clean up and return */
    if(!is_infected)
      {
        /* set new str_nr[i] */
        for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
          i_attr_val1.ms_str_nr[i] = str_nr[i];
        if(rsbac_set_attr(T_IPC,
                          tid,
                          A_ms_str_nr,
                          i_attr_val1))
          {
            printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            return(NOT_GRANTED);
          }
        /* set new str_offset[i] */
        for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
          i_attr_val1.ms_str_offset[i] = str_offset[i];
        if(rsbac_set_attr(T_IPC,
                          tid,
                          A_ms_str_offset,
                          i_attr_val1))
          {
            printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            return(NOT_GRANTED);
          }
        /* set new backbuflen */
        if(oldbuflen + segment.len >= RSBAC_MS_MAX_STR_LEN)
          i_attr_val1.ms_buflen = RSBAC_MS_MAX_STR_LEN;
        else
          i_attr_val1.ms_buflen = oldbuflen+segment.len;
        if(rsbac_set_attr(T_IPC,
                          tid,
                          A_ms_buflen,
                          i_attr_val1))
          {
            printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            return(NOT_GRANTED);
          }
        /* fill backbuf (memory position is hold in acibuf) */
        memcpy(acibuf, bufend - i_attr_val1.ms_buflen, i_attr_val1.ms_buflen);
        if(rsbac_debug_adf_ms)
          printk(KERN_DEBUG
                 "do_scan_sock(): finished scan on sock %lu\n",
                 (u_long) tid.ipc.id.sock);
        return(GRANTED);
      }

    /* OK, stream is infected -> block depending on process attributes */
    /* get ms_sock_trusted_tcp/udp for process */
    i_tid.process = caller_pid;
    if(segment.prot == MP_TCP)
      i_attr = A_ms_sock_trusted_tcp;
    else
      i_attr = A_ms_sock_trusted_udp;
    if (rsbac_get_attr(T_PROCESS,
                       i_tid,
                       i_attr,
                       &i_attr_val1,
                       FALSE))
      {
        printk(KERN_WARNING
               "do_scan_sock(): rsbac_get_attr() returned error!\n");
        return(NOT_GRANTED);
      }
    /* no check or scanned status change for trusted processes */
    switch(i_attr_val1.ms_sock_trusted_tcp)
      {
        case MS_not_trusted:
          /* erase buffer contents */
          clear_user_buf(segment.ubuf,segment.len);
          /* set scan result */
          i_attr_val1.ms_scanned = MS_rejected;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_scanned,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            }
          return(NOT_GRANTED);

        case MS_active:
          /* set scan result */
          i_attr_val1.ms_scanned = MS_active_rejected;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_scanned,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            }
          /* save segment length */
          i_attr_val1.ms_buflen = segment.len;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_buflen,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
            }
          return(NOT_GRANTED);

        case MS_full:
          /* reset all ACI values to avoid problems */
          i_attr_val1.ms_buflen = 0;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_buflen,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
              return(NOT_GRANTED);
            }
          /* set string no. */
          for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
            i_attr_val1.ms_str_nr[i] = 0;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_str_nr,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
              return(NOT_GRANTED);
            }
          /* set string offset */
          for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
            i_attr_val1.ms_str_offset[i] = 0;
          if(rsbac_set_attr(T_IPC,
                            tid,
                            A_ms_str_offset,
                            i_attr_val1))
            {
              printk(KERN_WARNING "do_scan_sock(): rsbac_set_attr() returned error!\n");
              return(NOT_GRANTED);
            }
          return(GRANTED);

        default:
          printk(KERN_WARNING "do_scan_sock(): invalid value for MS_sock_trusted_tcp/udp!\n");
          return(NOT_GRANTED);
      }
  }
#endif /* CONFIG_RSBAC_MS_SOCK */

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

enum rsbac_adf_req_ret_t
   rsbac_adf_request_ms  (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;
/*    enum  rsbac_target_t          i_target;
*/
    union rsbac_target_id_t       i_tid;
/*    enum  rsbac_attribute_t       i_attr;
*/
    union rsbac_attribute_value_t i_attr_val1;
#ifdef CONFIG_RSBAC_MS_ROLE_PROT
    union rsbac_attribute_value_t i_attr_val2;
#endif

    switch (request)
      {
        case R_CHANGE_OWNER:
            switch(target)
              {
                case T_PROCESS:
                  #ifdef CONFIG_RSBAC_MS_ROLE_PROT
                  if(attr != A_owner)
                    return(UNDEFINED);
                  /* Administrator or secoff? */
                  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_ms(): 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(T_USER,
                                     i_tid,
                                     A_system_role,
                                     &i_attr_val2,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_request_ms(): 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_FILE:
                case T_DIR:
                case T_IPC:
                case T_NONE:
                  return(DO_NOT_CARE);
                /* all other cases are undefined */
                default:
                  return(DO_NOT_CARE);
              }

        case R_EXECUTE:
            switch(target)
              {
                case T_FILE:
                  /* scan file as execute */
                  if (scan(tid.file,caller_pid,TRUE))
                    { /* passed scanner: grant */
                      return(GRANTED);
                    }
                  else
                    return(NOT_GRANTED);

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

        case R_MODIFY_ATTRIBUTE:
            switch(attr)
              { /* owner must be changed by other request to prevent inconsistency */
                case A_owner:
                case A_ms_backbuf:
                case A_ms_buflen:
                case A_ms_str_nr:
                case A_ms_str_offset:
                  return(NOT_GRANTED);
                case A_ms_scanned:
                case A_ms_trusted:
                #ifdef CONFIG_RSBAC_MS_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? */
                  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_ms(): 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);

                default:
                  return(DO_NOT_CARE);
              }

        case R_READ_ATTRIBUTE:
            switch(attr)
              {
                /* every user may see scan status of files and sockets */
                case A_ms_scanned:
                  return(GRANTED);
                /* ...but only security officers may see other attributes */
                case A_owner:
                case A_ms_trusted:
                case A_ms_backbuf:
                case A_ms_buflen:
                case A_ms_str_nr:
                case A_ms_str_offset:
                  /* 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_ms(): 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);

                default:
                  return(DO_NOT_CARE);
              }

#ifdef CONFIG_RSBAC_MS_SOCK
        case R_READ:
            switch(target)
              {
                case T_IPC:
                  if(tid.ipc.type != I_sock)
                    return(DO_NOT_CARE);
                  if(!tid.ipc.id.sock || (attr != A_ms_segment))
                    return(UNDEFINED);
                  return(do_scan_sock(tid,
                                      caller_pid,
                                      attr_val.ms_segment));

                default:
                  return(DO_NOT_CARE);
              }
#endif

#ifdef CONFIG_RSBAC_MS_READ
        case R_READ_OPEN:
            switch(target)
              {
                case T_FILE:
                  /* scan file as non-execute */
                  if (scan(tid.file, caller_pid, FALSE))
                    { /* passed scanner: grant */
                      return(GRANTED);
                    }
                  else
                    return(NOT_GRANTED);
                case T_IPC:
                case T_DIR:
                case T_DEV:
                  return(DO_NOT_CARE);
                /* all other cases are undefined */
                default:
                  return(DO_NOT_CARE);
              }

        case R_READ_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                  /* scan file as non-execute */
                  if (scan(tid.file, caller_pid, FALSE))
                    { /* passed scanner: grant */
                      return(GRANTED);
                    }
                  else
                    return(NOT_GRANTED);

                case T_DEV:
                case T_IPC:
                  return(DO_NOT_CARE);
                /* all other cases are undefined */
                default:
                  return(DO_NOT_CARE);
              }
#endif

        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 != MS)
                    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_ms(): 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 undefined */
                default: return(DO_NOT_CARE);
              }
              
/*********************/
        default: return DO_NOT_CARE;
      }

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


/*****************************************************************************/
/* 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. Because of this, the write boundary is not adjusted - there  */
/* is no user-level writing anyway...                                        */
/* 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_ms(
                      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;
    union rsbac_attribute_value_t i_attr_val3;

    switch (request)
      {
        case R_APPEND_OPEN:
        case R_READ_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                  reset_scanned(tid.file);
                case T_IPC:
                case T_DEV:
                  return(0);
                  break;
                /* all other cases are undefined */
                default:
                  return(0);
              }

        case R_CLONE:
            if (target == T_PROCESS)
              {
                /* Get ms_trusted from first process */
                if (rsbac_get_attr(T_PROCESS,
                                   tid,
                                   A_ms_trusted,
                                   &i_attr_val1,
                                   FALSE))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                    return(-RSBAC_EREADFAILED);
                  }
                /* Get ms_sock_trusted_tcp from first process */
                if (rsbac_get_attr(T_PROCESS,
                                   tid,
                                   A_ms_sock_trusted_tcp,
                                   &i_attr_val2,
                                   FALSE))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                    return(-RSBAC_EREADFAILED);
                  }
                /* Get ms_sock_trusted_udp from first process */
                if (rsbac_get_attr(T_PROCESS,
                                   tid,
                                   A_ms_sock_trusted_udp,
                                   &i_attr_val3,
                                   FALSE))
                  {
                    printk(KERN_WARNING
                           "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                    return(-RSBAC_EREADFAILED);
                  }
                /* Set ms_trusted for new process, if set for first */
                if (   i_attr_val1.ms_trusted
                    && (rsbac_set_attr(T_PROCESS,
                                       new_tid,
                                       A_ms_trusted,
                                       i_attr_val1)) )
                  {
                    printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                /* Set ms_sock_trusted_tcp for new process, if set for first */
                if (   i_attr_val2.ms_sock_trusted_tcp
                    && (rsbac_set_attr(T_PROCESS,
                                       new_tid,
                                       A_ms_sock_trusted_tcp,
                                       i_attr_val2)) )
                  {
                    printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                /* Set ms_sock_trusted_udp for new process, if set for first */
                if (   i_attr_val3.ms_sock_trusted_udp
                    && (rsbac_set_attr(T_PROCESS,
                                       new_tid,
                                       A_ms_sock_trusted_udp,
                                       i_attr_val3)) )
                  {
                    printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                    return(-RSBAC_EWRITEFAILED);
                  }
                return(0);
              }
            else
              return(0);

        case R_EXECUTE:
            switch(target)
              {
                case T_FILE:
                  /* get ms_trusted for file */
                  if (rsbac_get_attr(T_FILE,
                                     tid,
                                     A_ms_trusted,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* get for process */
                  i_tid.process = caller_pid;
                  if (rsbac_get_attr(T_PROCESS,
                                     i_tid,
                                     A_ms_trusted,
                                     &i_attr_val2,
                                     FALSE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* and set for process, if different */
                  if(i_attr_val1.ms_trusted != i_attr_val2.ms_trusted)
                    if (rsbac_set_attr(T_PROCESS,
                                       i_tid,
                                       A_ms_trusted,
                                       i_attr_val1))
                      {
                        printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                        return(-RSBAC_EWRITEFAILED);
                      }

                  /* get ms_sock_trusted_tcp for file */
                  if (rsbac_get_attr(T_FILE,
                                     tid,
                                     A_ms_sock_trusted_tcp,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* get for process */
                  i_tid.process = caller_pid;
                  if (rsbac_get_attr(T_PROCESS,
                                     i_tid,
                                     A_ms_sock_trusted_tcp,
                                     &i_attr_val2,
                                     FALSE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* and set for process, if different */
                  if(i_attr_val1.ms_sock_trusted_tcp 
                      != i_attr_val2.ms_sock_trusted_tcp)
                    if (rsbac_set_attr(T_PROCESS,
                                       i_tid,
                                       A_ms_sock_trusted_tcp,
                                       i_attr_val1))
                      {
                        printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                        return(-RSBAC_EWRITEFAILED);
                      }

                  /* get ms_sock_trusted_udp for file */
                  if (rsbac_get_attr(T_FILE,
                                     tid,
                                     A_ms_sock_trusted_udp,
                                     &i_attr_val1,
                                     TRUE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* get for process */
                  i_tid.process = caller_pid;
                  if (rsbac_get_attr(T_PROCESS,
                                     i_tid,
                                     A_ms_sock_trusted_udp,
                                     &i_attr_val2,
                                     FALSE))
                    {
                      printk(KERN_WARNING
                             "rsbac_adf_set_attr_ms(): rsbac_get_attr() returned error!\n");
                      return(-RSBAC_EREADFAILED);
                    }
                  /* and set for process, if different */
                  if(i_attr_val1.ms_sock_trusted_udp 
                      != i_attr_val2.ms_sock_trusted_udp)
                    if (rsbac_set_attr(T_PROCESS,
                                       i_tid,
                                       A_ms_sock_trusted_udp,
                                       i_attr_val1))
                      {
                        printk(KERN_WARNING "rsbac_adf_set_attr_ms(): rsbac_set_attr() returned error!\n");
                        return(-RSBAC_EWRITEFAILED);
                      }
                  return(0);

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

        case R_WRITE_OPEN:
            switch(target)
              {
                case T_FILE:
                  reset_scanned(tid.file);
                  return(0);
                /* we do not care about devs */
                case T_DEV:
                  return(0);
                /* all other cases are undefined */
                default:
                  return(0);
              }


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

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

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