/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 1999 Rainer Wichmann                                      */
/*                                                                         */
/*  This program is free software; you can redistribute it                 */
/*  and/or modify                                                          */
/*  it under the terms of the GNU General Public License as                */
/*  published by                                                           */
/*  the Free Software Foundation; either version 2 of the License, or      */
/*  (at your option) any later version.                                    */
/*                                                                         */
/*  This program is distributed in the hope that it will be useful,        */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*  GNU General Public License for more details.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */

#include "config_xor.h"

#ifdef HOST_IS_HPUX          
#define _XOPEN_SOURCE_EXTENDED
#endif                       

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifndef S_SPLINT_S
#include <arpa/inet.h>
#else
#define AF_INET 2
#endif

#include <time.h>

#ifndef HAVE_LSTAT
#define lstat stat
#endif

#include "samhain.h"
#include "sh_error.h"
#include "sh_calls.h"

#undef  FIL__
#define FIL__  _("sh_calls.c")

char aud_err_message[64];

typedef struct cht_struct 
{
  char           * str;
  unsigned long    val;
} cht_type;

static cht_type aud_tab[] =
{
  { N_("execve"),    AUD_EXEC   },
  { N_("utime"),     AUD_UTIME  },
  { N_("unlink"),    AUD_UNLINK },
  { N_("dup"),       AUD_DUP    },
  { N_("chdir"),     AUD_CHDIR  },
  { N_("open"),      AUD_OPEN   },
  { N_("kill"),      AUD_KILL   },
  { N_("exit"),      AUD_EXIT   },
  { N_("fork"),      AUD_FORK   },
  { N_("setuid"),    AUD_SETUID },
  { N_("setgid"),    AUD_SETGID },
  { N_("pipe"),      AUD_PIPE   },
  { NULL,            0 }
};

/* Set aud functions
 */
int sh_aud_set_functions(const char * str_s)
{
  int i = 0;

  SL_ENTER(_("sh_aud_set_functions"));
  
  if (str_s == NULL)
    return -1;

  while (aud_tab[i].str != NULL)
    {
      if (NULL != sl_strstr (str_s, _(aud_tab[i].str)))
	{
	  sh.flag.audit     = 1;
	  sh.flag.aud_mask |= aud_tab[i].val;
	}
      ++i;
    }

  SL_RETURN(0,_("sh_aud_set_functions"));
}

  


/* Need to catch EINTR for these functions.
 */
long int retry_sigaction(char * file, int line,
			 int signum,  const  struct  sigaction  *act,
			 struct sigaction *oldact)
{
  int error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];
  errno              = 0;

  SL_ENTER(_("retry_sigaction"));

  do {
    val_retry = sigaction(signum, act, oldact);
  } while (val_retry < 0 && errno == EINTR);

  error = errno;
  if (val_retry < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_SIGACT, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) signum );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_sigaction"));
}

static struct in_addr bind_addr;
static int        use_bind_addr = 0;

int sh_calls_set_bind_addr (const char * str)
{
  static int reject = 0;

  if (reject == 1)
    return (0);

  if (sh.flag.opts == S_TRUE)  
    reject = 1;

  if (0 == /*@-unrecog@*/inet_aton(str, &bind_addr)/*@+unrecog@*/) 
    {
      return -1;
    }
  use_bind_addr = 1;
  return 0;
}


long int retry_connect(char * file, int line, int sockfd, 
		       struct sockaddr *serv_addr, int addrlen)
{
  int error;
  long int val_retry = 0;
  static struct sockaddr_in new_addr;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("retry_connect"));

  errno = 0;

  if (0 != use_bind_addr) 
    {
      memcpy(&new_addr.sin_addr, &bind_addr, sizeof(struct in_addr));
      new_addr.sin_family = AF_INET;
      
      val_retry = /*@-unrecog@*/bind(sockfd, 
				     (struct sockaddr*)&new_addr, 
				     sizeof(struct sockaddr_in))/*@+unrecog@*/;
    }

  if (val_retry == 0)
    {
      do {
	val_retry = 
	  /*@-unrecog@*/connect(sockfd, serv_addr, addrlen)/*@+unrecog@*/;
      } while (val_retry < 0 && errno == EINTR);
    }

  error = errno;
  if (val_retry != 0) {
    /* ugly cast back to struct sockaddr_in :-(
     */
    sh_error_handle ((-1), file, line, error, MSG_ERR_CONNECT, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     (long) sockfd,
		     /*@-unrecog -type@*/
		     (long) ntohs(((struct sockaddr_in *)serv_addr)->sin_port),
		     /*@+unrecog +type@*/
#ifdef HAVE_INET_ATON
		     /*@-unrecog -type@*/
		     inet_ntoa( ((struct sockaddr_in *)serv_addr)->sin_addr )
		     /*@+unrecog +type@*/
#else
		     _("unknown")
#endif
		     );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_connect"));
}

long int retry_accept(char * file, int line, int fd, 
		      struct sockaddr *serv_addr, int * addrlen)
{
  int  error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];

  ACCEPT_TYPE_ARG3 my_addrlen = (ACCEPT_TYPE_ARG3) *addrlen;

  errno              = 0;

  SL_ENTER(_("retry_accept"));

  do {
    val_retry = /*@-unrecog@*/accept(fd, serv_addr, &my_addrlen)/*@+unrecog@*/;
  } while (val_retry < 0 && errno == EINTR);
  *addrlen = (int) my_addrlen;
  error = errno;
  if (val_retry < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_ACCEPT, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) fd );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_accept"));
}

long int retry_lstat(char * file, int line, 
		     const char *file_name, struct stat *buf)
{
  int error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];
 
  SL_ENTER(_("retry_lstat"));

  do {
    val_retry = /*@-unrecog@*/lstat (file_name, buf)/*@+unrecog@*/;
  } while (val_retry < 0 && errno == EINTR);
  error = errno;
  if (val_retry < 0) {
      (void) sh_error_message(error, aud_err_message, 64);
      sh_error_handle ((-1), file, line, error, MSG_ERR_LSTAT, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       file_name );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_lstat"));
}

long int retry_stat(char * file, int line, 
		    const char *file_name, struct stat *buf)
{
  int error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];
 
  SL_ENTER(_("retry_stat"));

  do {
    val_retry = stat (file_name, buf);
  } while (val_retry < 0 && errno == EINTR);
  error = errno;
  if (val_retry < 0) {
      (void) sh_error_message(error, aud_err_message, 64);
      sh_error_handle ((-1), file, line, error, MSG_ERR_STAT, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       file_name );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_stat"));
}

long int retry_fstat(char * file, int line, int filed, struct stat *buf)
{
  int error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];
 
  SL_ENTER(_("retry_fstat"));

  do {
    val_retry = fstat (filed, buf);
  } while (val_retry < 0 && errno == EINTR);
  error = errno;
  if (val_retry < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_FSTAT, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) filed );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_fstat"));
}

long int retry_fcntl(char * file, int line, int fd, int cmd, long arg)
{
  int error;
  long int val_retry = -1;
  char errbuf[SH_ERRBUF_SIZE];
  errno              = 0;

  SL_ENTER(_("retry_fcntl"));

  if (cmd == F_GETFD || cmd == F_GETFL)
    {
      do {
	val_retry = fcntl(fd, cmd);
      } while (val_retry < 0 && errno == EINTR);
    }
  else
    {
      do {
	val_retry = fcntl(fd, cmd, arg);
      } while (val_retry < 0 && errno == EINTR);
    }
  error = errno;
  if (val_retry < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_FCNTL, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) fd, (long) cmd, arg );
  }
  errno = error;    
  SL_RETURN(val_retry, _("retry_fcntl"));
}

long int retry_msleep (int sec, int millisec)
{
  int result = 0;
#if defined(HAVE_NANOSLEEP)
  struct timespec req, rem;
#endif

  SL_ENTER(_("retry_fcntl"));

  errno  = 0;
  if (millisec > 999) millisec = 999;
  if (millisec < 0)   millisec = 0;
  if (sec < 0)         sec = 0;

#if defined(HAVE_NANOSLEEP)
  /*@-usedef@*/
  req.tv_sec  = sec;                   rem.tv_sec  = 0;
  req.tv_nsec = millisec * 1000000;    rem.tv_nsec = 0;
  /*@+usedef@*/
  do {
    result = /*@-unrecog@*/nanosleep(&req, &rem)/*@+unrecog@*/;

    req.tv_sec = rem.tv_sec;   rem.tv_sec  = 0;
    req.tv_nsec = rem.tv_nsec; rem.tv_nsec = 0;
    
  } while ((result == -1) && (errno == EINTR));
#else
  if (sec > 0)
    {
      sleep (sec); /* nanosleep not available */
    }
  else
    {
#ifdef HAVE_USLEEP
      if (millisec > 0)
	{
	  usleep(1000 * millisec);
	}
#else
      if (millisec > 0)
	{
	  sleep (1);
	}
#endif
    }
#endif
  SL_RETURN(result, _("retry_msleep"));
}

/***************************************************
 *
 *   Audit these functions.
 *
 ***************************************************/

long int retry_aud_execve  (char * file, int line, 
			    const  char *dateiname, char * argv[],
			    char * envp[])
{
  uid_t a = geteuid();
  gid_t b = getegid();
  int   i;
  int   error;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("retry_aud_execve"));

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_EXEC) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_EXEC,
		     dateiname, (long) a, (long) b );
  do {
    i = execve(dateiname, argv, envp);
  } while (i < 0 && errno == EINTR);

  error = errno;
  if (i < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_EXEC, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       dateiname, (long) a, (long) b );
  }
  errno = error;    
  SL_RETURN(i, _("retry_aud_execve"));
}


long int retry_aud_utime (char * file, int line, 
			   char * path, struct utimbuf *buf)
{
  long int val_return;
  int  error;
  char errbuf[SH_ERRBUF_SIZE];
  errno      = 0;

  SL_ENTER(_("retry_aud_utime"));

  do {
    val_return = utime (path, buf);
  } while (val_return < 0 && errno == EINTR);

  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_UTIME) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_UTIME,
		     path, 
		     (unsigned long) buf->actime, 
		     (unsigned long) buf->modtime);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_UTIME, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       path, 
		       (unsigned long) buf->actime, 
		       (unsigned long) buf->modtime);
  }
  errno = error;
  SL_RETURN(val_return, _("retry_aud_utime"));
}

long int retry_aud_unlink (char * file, int line, 
			   char * path)
{
  long int val_return;
  int error;
  char errbuf[SH_ERRBUF_SIZE];
  errno      = 0;

  SL_ENTER(_("retry_aud_unlink"));

  do {
    val_return = unlink (path);
  } while (val_return < 0 && errno == EINTR);

  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_UNLINK) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_UNLINK,
		     path);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_UNLINK, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       path);
  }
  errno = error;
  SL_RETURN(val_return, _("retry_aud_unlink"));
}

long int retry_aud_dup2 (char * file, int line, 
			 int fd, int fd2)
{
  long int val_return;
  int error;
  char errbuf[SH_ERRBUF_SIZE];
  errno      = 0;

  SL_ENTER(_("retry_aud_dup2"));

  do {
    val_return = dup2 (fd, fd2);
  } while (val_return < 0 && errno == EINTR);

  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_DUP) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_DUP,
		      (long) fd, val_return);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_DUP, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) fd, val_return);
  }
  errno = error;
  SL_RETURN(val_return, _("retry_aud_dup2"));
}

long int retry_aud_dup (char * file, int line, 
			int fd)
{
  long int val_return;
  int error;
  char errbuf[SH_ERRBUF_SIZE];
  errno      = 0;

  SL_ENTER(_("retry_aud_dup"));

  do {
    val_return = dup (fd);
  } while (val_return < 0 && errno == EINTR);
  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_DUP) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_DUP,
		     (long) fd, val_return);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_DUP, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) fd, val_return);
  }
  errno = error;
  SL_RETURN(val_return, _("retry_aud_dup"));
}


  
long int retry_aud_chdir (char * file, int line, 
			  const char *path)
{
  long int val_return;
  int      error      = 0;
  char errbuf[SH_ERRBUF_SIZE];
  errno      = 0;

  SL_ENTER(_("retry_aud_chdir"));

  do {
    val_return = chdir (path);
  } while (val_return < 0 && errno == EINTR);

  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_CHDIR) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_CHDIR,
		     path);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, error, MSG_ERR_CHDIR, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       path);
  }
  errno = error;
  SL_RETURN(val_return, _("retry_aud_chdir"));
}


long int aud_open_noatime (char * file, int line, int privs,
			   const char *pathname, int flags, mode_t mode,
			   int * o_noatime)
{
  long int val_return;
  int error;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("aud_open"));

  val_return = open (pathname, *o_noatime|flags, mode);
  if ((val_return < 0) && (*o_noatime != 0))
    {
      val_return = open (pathname, flags, mode);
      if (val_return >= 0)
	*o_noatime = 0;
    }
  error = errno;
  /*@-noeffect@*/
  (void) privs; /* fix compiler warning */
  /*@+noeffect@*/

  if (val_return < 0)
    {
      (void) sh_error_message(error, aud_err_message, 64);
    }

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_OPEN) != 0)
    {
      sh_error_handle ((-1), file, line, 0, MSG_AUD_OPEN,
		       pathname, (long) flags, (long) mode, val_return);
    }
  if (val_return < 0) {
    sh_error_handle ((-1), file, line, error, MSG_ERR_OPEN, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     pathname, (long) flags, (long) mode, val_return);
  }
  errno = error;
  SL_RETURN(val_return, _("aud_open"));
}

long int aud_open (char * file, int line, int privs,
		   const char *pathname, int flags, mode_t mode)
{
  long int val_return;
  int error;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("aud_open"));

  val_return = open (pathname, flags, mode);
  error = errno;
  /*@-noeffect@*/
  (void) privs; /* fix compiler warning */
  /*@+noeffect@*/

  if (val_return < 0)
    {
      (void) sh_error_message(error, aud_err_message, 64);
    }

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_OPEN) != 0)
    {
      sh_error_handle ((-1), file, line, 0, MSG_AUD_OPEN,
		       pathname, (long) flags, (long) mode, val_return);
    }
  if (val_return < 0) {
    sh_error_handle ((-1), file, line, error, MSG_ERR_OPEN, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     pathname, (long) flags, (long) mode, val_return);
  }
  errno = error;
  SL_RETURN(val_return, _("aud_open"));
}
  
long int aud_kill (char * file, int line, pid_t pid, int sig)
{
  int  myerror;
  long int val_return = kill (pid, sig);
  char errbuf[SH_ERRBUF_SIZE];
  myerror = errno;

  SL_ENTER(_("aud_kill"));

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_KILL) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_KILL,
		      (long) pid, (long) sig);
  if (val_return < 0) {
      sh_error_handle ((-1), file, line, myerror, MSG_ERR_KILL, 
		       sh_error_message(myerror, errbuf, sizeof(errbuf)),
		       (long) pid, (long) sig);
  }
  errno = myerror;
  SL_RETURN(val_return, _("aud_kill"));
}
  
/*@noreturn@*/
void aud_exit (char * file, int line, int fd)
{
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_EXIT) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_EXIT,
		      (long) fd);

  SL_ENTER(_("aud_exit"));

  sh.flag.exit = fd;
  exit(fd);
}

/*@noreturn@*/
void aud__exit (char * file, int line, int fd)
{
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_EXIT) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_EXIT,
		      (long) fd);

  SL_ENTER(_("aud__exit"));

  sh.flag.exit = fd;
  _exit(fd);
}

pid_t aud_fork (char * file, int line)
{
  int error;
  pid_t i = fork();
  char errbuf[SH_ERRBUF_SIZE];

  error = errno;
  SL_ENTER(_("aud_fork"));

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_FORK) != 0 && (i > 0))
    sh_error_handle ((-1), file, line, 0, MSG_AUD_FORK,
		      (long) i);
  if (i == (pid_t) -1) {
    sh_error_handle ((-1), file, line, error, MSG_ERR_FORK, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     (long) i);
  }
  errno = error;
  SL_RETURN(i, _("aud_fork"));
}

int aud_setuid (char * file, int line, uid_t uid)
{
  int error = 0;
  int i = 0;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("aud_setuid"));

  if (uid != (uid_t) 0) { 
    i = setuid(uid);
    error = errno;
  }
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_SETUID) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_SETUID,
		     (long) uid);
  if (uid == (uid_t) 0) {
    i = setuid(uid);
    error = errno;
  }
  if (i < 0) {
    sh_error_handle ((-1), file, line, error, MSG_ERR_SETUID, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     (long) uid);
  }
  errno = error;
  SL_RETURN(i, _("aud_setuid"));
}

int aud_setgid (char * file, int line, gid_t gid)
{
  int error = 0;
  int i = 0;
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("aud_setgid"));

  if (gid != (gid_t) 0) {
    i = setgid(gid);
    error = errno;
  }

  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_SETGID) != 0)
    sh_error_handle ((-1), file, line, 0, MSG_AUD_SETGID,
		      (long) gid);
  if (gid == (gid_t) 0) {
    i = setgid(gid);
    error = errno;
  }
  if (i < 0) {
    sh_error_handle ((-1), file, line, error, MSG_ERR_SETGID, 
		     sh_error_message(error, errbuf, sizeof(errbuf)),
		     (long) gid);
  }
  errno = error;
  SL_RETURN(i, _("aud_setgid"));
}

int aud_pipe (char * file, int line, int * modus)
{
  int error;
  int i = pipe (modus);
  char errbuf[SH_ERRBUF_SIZE];

  SL_ENTER(_("aud_pipe"));

  error = errno;
  if (sh.flag.audit != 0 && (sh.flag.aud_mask & AUD_PIPE) != 0)
    {
      if (i < 0)
	sh_error_handle ((-1), file, line, 0, MSG_AUD_PIPE,
			 (long) 0, (long) 0);
      else
	sh_error_handle ((-1), file, line, 0, MSG_AUD_PIPE,
			 (long) modus[0], (long) modus[1]);
    }
  if (i < 0) {
    if (i < 0)
      sh_error_handle ((-1), file, line, error, MSG_ERR_PIPE, 
		       sh_error_message(error),
		       (long) 0, (long) 0);
    else
      sh_error_handle ((-1), file, line, error, MSG_ERR_PIPE, 
		       sh_error_message(error, errbuf, sizeof(errbuf)),
		       (long) modus[0], (long) modus[1]);
  }
  SL_RETURN(i, _("aud_pipe"));
}
