/*
 * Processing routings for  Distributed File Checker
 *
 * Filename checker_proc.c
 * Author:     Clive King
 * Comment:    Server routines
 * Version 1.1
 * Date    05 Nov 1993
 *
*/

#include <sys/time.h>
#include <malloc.h>
#include <rpc/rpc.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <syslog.h>
#ifdef SVR4
#include <sys/dirent.h>
#include <string.h>
#else
#include <sys/dir.h>
#include <strings.h>
#endif

#include "checker.h"
#include "checker_proc.h"

char *dirname;

namelist nl= NULL;
namelist *nlp;

/* 
 * routine called from server stub to check the attributes of the file
 * and to return the state of the file in question 
*/

access_struct *access_1(details)
     nametype *details;
{
  struct stat buf;
  static access_struct file_report;   /* head of the linked list */
  char *perms;  /* pointer to string to store permissions */
  char *owner;  /* pointer to string to store owener */
  char *grp;    /* pointer to string to store group */
  char *date;   /* pointer to string to store date */

  dirname = (char *)malloc( FILEMAX );
  perms = (char *)malloc( PERMAX );
  owner = (char *)malloc( NAMEMAX );
  grp = (char *)malloc( NAMEMAX );
  date = (char *)malloc( DATEMAX );

  /* split the fields up into the constituent parts */

  split_fields(details, dirname, perms, owner, grp, date);

  xdr_free(xdr_access_struct, (char *)&file_report);
  
  nlp = &file_report.access_struct_u.list;
  nl = *nlp = (namenode *)malloc(sizeof(namenode));
  file_report.access_struct_u.list->result = 0; 

  if (access(dirname, F_OK) != 0)    {
      add_error("", "Can not access file");
      return (&file_report);
    }

  if (stat(dirname, &buf) == -1)    {
      add_error("", "Can not stat file");
      return (&file_report);
    }
 
 /* check permissions of the file */
  check_perms(&buf, perms[0], S_ISUID, S_ISGID, S_ISVTX, "permissions");
  check_perms(&buf, perms[1], S_IRUSR, S_IWUSR, S_IXUSR, "owner permissions");
  check_perms(&buf, perms[2], S_IRGRP, S_IWGRP, S_IXGRP, "group permissions");
  check_perms(&buf, perms[3], S_IROTH, S_IWOTH, S_IXOTH, "other permissions");

  check_owner(&buf, owner);  /* check ownership */
  check_grp(&buf, grp);      /* check group */

  /* do not check date field if NO_DATE_CHECK, */
  if (strchr(date, NO_DATE_CHECK) == NULL)  
    check_date(&buf, date);

/* If the checks went well, put O.K. in the error field */
/* The client checks result to determine the test outcome */

  if (file_report.access_struct_u.list->result == 0)    {
      /* allocate first element in list to avoid xdr problems */
      add_error("", "O.K.");
      file_report.access_struct_u.list->result = 0;  
    }

  return (&file_report);
}

/*  *********************************************************  */
/* check_perms -- determine if file permissions  are correct
 *
 * Argument - pointer to files stat structure
 * Argument - string value of the correct file permissions
 * Argument - Mask to check read (or setuid) field
 * Argument - Mask to check write (or setgid) field
 * Argument - Mask to check execute (or sticky bit) field
 * Argument - error message base string
 * Result
 *   0 for correct permissions
 *   1 for incorrect permissions
 *   If an error occurs, then the global err_string is set.
 *
 * Global array err_string set with type of permissions error
 *   
 */


void check_perms(buf, value, rmask, wmask, xmask, errstr)
struct stat *buf;
char value;
int rmask;
int wmask;
int xmask;
char *errstr;
{
     int tmp;
     /* convert char to int */
     tmp = value - '0';

     if (buf->st_mode & rmask)    {       /* check read permssions 
*/
         if (tmp >= 4)
           tmp -= 4;   /* the order of the = and - is important here 
*/
         else
            if (rmask == S_ISUID)
               add_error("set uid", errstr);
         else
            add_error("read", errstr);
       }
     else
         if (tmp  >= 4)    {
           if (rmask == S_ISUID)
            add_error("set uid", errstr);
         else
            add_error("read", errstr);
            tmp -= 4;
           }

     if (buf->st_mode & wmask)    {       /* check write permissions */
         if (tmp  >= 2)
             tmp -= 2; 
         else
           if (wmask == S_ISGID)
            add_error("set gid", errstr);
         else
            add_error("write", errstr);
       }
     else
         if (tmp  >= 2)    {
           if (wmask == S_ISGID)
            add_error("set gid", errstr);
         else
            add_error("write", errstr);
            tmp -= 2;
           }

     if (buf->st_mode & xmask) {     /* check execute permissions */
         if (tmp  >= 1)
             tmp -= 1; 
         else
           if (xmask == S_ISVTX)
            add_error("sticky", errstr);
         else
            add_error("execute", errstr);
      }
     else
         if (tmp  >= 1)    {

           if (xmask == S_ISVTX)
            add_error("sticky", errstr);
           else
            add_error("execute", errstr);
            tmp -= 1;
           }
}

/*  ***********************************************************  */
/* check_owner -- determine if the owner of the file is correct
 *
 * Argument -  pointer to files stat structure
 * Argument -  string value the group should be
 * Result
 *   0 for correct ownership
 *   1 for incorrect ownership
 *   If an error occurs, then the global err_string is set.
 *
 *   Global array err_string is set with error string and correct owner name
 *   
 */
void check_owner(buffer, own)
     struct stat *buffer;
     char *own;
{
  struct passwd *pass;

  if ((pass = getpwnam(own)) == NULL)    {
      add_error(own, " : no such user on this system");
      return;
    }

  if (buffer->st_uid != pass->pw_uid)
    add_error("incorrect owner : should be ", own);
}

/*  ***********************************************************  */
/* check_owner -- determine if the group of the file is correct
 *
 * Argument -  pointer to files stat structure
 * Argument -  string value the group should be
 * Result
 *   0 for correct group
 *   1 for incorrect group
 *   If an error occurs, then the global err_string is set.
 *
 *   Global array err_string is set with error string and correct group name
 *   
 */
void check_grp(buffer, grp)
     struct stat *buffer;
     char *grp;
{
  struct passwd *pass;
  struct group *gr;

  /* search through the groups file for the appropriate gid */
  if ((gr = getgrnam(grp)) == NULL)    {
    /* if the gid is not in groups but the file is owned by a user
       we need to check the gid field in the passwd file */

 
      if ((pass = getpwuid(atoi(grp))) == NULL) {
          add_error(grp, " : no such group on this system");
          return;
        }
      else  
          if (buffer->st_gid != pass->pw_gid) {
              add_error("incorrect group : should be ", grp);
              return;
            }
    }
  else   
      if (buffer->st_gid != gr->gr_gid) {
        add_error("incorrect group : should be ", grp);
        return;
      }
}

/*  ***********************************************************  */
/* check_date -- Checks the date of the last modification time of the file 
 *               is after the latest acceptable revision date for that file
 *
 * Argument -  pointer to files stat structure
 * Argument -  string value the date should be
 *
 * Result
 *   0 for correct date
 *   1 for incorrect date
 *   If an error occurs, then the global err_string is set.
 *
 *   Global array err_string is set with error string and correct date
 *
 * Notes
 *   Both UK and US date styles available, UK by default
 *   Compile with -DUSDATE for US style dates
 */

void check_date(buffer, dat)
     struct stat *buffer;
     char *dat;
{
  char *tmp;
  int sep_count=0;
  char day[3];
  char month[3];
  char year[5];
  char nowdate[18];
  char thedate[18];
  struct tm tmtime;

  int x = 0;
  tmp = dat;

  while(*tmp != NULL) {
      if ( *tmp == DATE_SEPARATOR ) {
          x = 0;
          sep_count++;
        }
      else {
        switch (sep_count) {

          case DATE_FIELD_ONE : {
              day[x] = *tmp;
              day[x+1] = NULL;
              break;
            }
          case DATE_FIELD_TWO : {
              month[x] = *tmp;
              month[x+1] = NULL;
              break;
            }
          case 2 : {
              year[x] = *tmp;
              year[x+1] = NULL;
              break;
            }
          }
        x++;
      }
     tmp++;
    }

  /* get the date of the file */
  tmtime = *gmtime(&buffer->st_mtime);

  /* get it into a useable format of 01 0 and 1991 */
  if (strftime(thedate, 18, "%Y%m%d", &tmtime) == 0)    {
      add_error("strftime failed", "possible problem with date field");
      return;
    }

  (void) sprintf(nowdate,"%s%s%s", year, month, day);
  
  if (atoi(thedate) < atoi(nowdate)) {
      add_error(dat, " is too old");
      return;
    }
}

/*  ***********************************************************  */
/* add_error -- adds an error to the current node
 * Arguments
 *   char * - the type of error 
 *   char * - nature of problem
 * Result
 *   adds an error message to the err_string field of the current node
 */

void add_error(type, str)
     char *type;
     char *str;
{
  char error_string[100];
  unsigned int strlength;

  (void) sprintf(error_string, "%s  %s", type, str);

  if ((nl = *nlp = (namenode *)malloc(sizeof(namenode))) == NULL) 
{
       (void) syslog(LOG_ERR, "malloc failed for checker client");
       exit(1);
      }

    
  strlength = strlen(error_string)+ strlen(dirname) + 5;
  if ((nl->err_string = (char *) malloc(strlength)) == NULL)  {
      (void) fprintf(stderr, "malloc failed");
      exit(1);
    }

  (void) sprintf(nl->err_string, "%s : %s", dirname, error_string);
  nl->result = 1;
  nlp = &nl->next;
  *nlp = NULL;
}

/*  ***********************************************************  */
/* split_fields -- Split a line passed to client into constituent parts
 * 
 * Arguments
 *    details - Line passed from the client
 *    filename - name of the file : derived from details
 *    perm - permissions on the file : derived from details
 *    own - owner of the file : derived from details
 *    grp -group of the file : derived from details
 *    dates - : derived from details
 *   
 * Result - returned via filename, perm, own, grp and dates
 */

void split_fields(details, filename, perm, own, grp, dates)
nametype *details;
char *filename;
char *perm;
char *own;
char *grp;
char *dates;
  {
  int x=0;
  int seperator_count=0;
  char *det;

  /* to add more field, add to the case statement */
  for(det=*details;(*det != '\n') && (*det != NULL);det++) {
      if (*det == DETAILS_SEPARATOR)    {
          seperator_count++;
          x=0;
         }
      else {
          switch (seperator_count) {
          case 0:  {
              filename[x] = *det;
              filename[x+1] = NULL;
              break;
            }
          case 1 : {
              perm[x] = *det;
              perm[x+1] = NULL;
              break;
            }
          case 2 : {
              own[x] = *det;
              own[x+1] = NULL;

              break;
            }
          case 3 : {
              grp[x] = *det;
              grp[x+1] = NULL;
              break;
            }
          case 4 : {
              dates[x] = *det;
              dates[x+1] = NULL;
              break;
            }
          }
          x++;
        }
    }
  }
