Logo Search packages:      
Sourcecode: samhain version File versions  Download package

slib.c

#include "config_xor.h"


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>


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

#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif

#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#ifndef FD_SET
#define NFDBITS         32
#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#endif /* !FD_SET */
#ifndef FD_SETSIZE
#define FD_SETSIZE      32
#endif
#ifndef FD_ZERO
#define FD_ZERO(p)      memset((char *)(p), '\0', sizeof(*(p)))
#endif

#include "slib.h"
#include "sh_calls.h"
#define SH_NEED_PWD_GRP 1
#include "sh_static.h"

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

const uid_t sh_uid_neg = ((uid_t) -1);
const gid_t sh_gid_neg = ((gid_t) -1);
 
#undef BREAKEXIT
#if defined(SCREW_IT_UP) && defined(__linux__) && defined(__i386__)

#ifdef SH_DEBUG
#define BREAKEXIT(expr) \
  do { \
    int ixi; \
    for (ixi = 0; ixi < 8; ++ixi) { \
      if ((*(volatile unsigned *)((unsigned) expr + ixi) & 0xff) == 0xcc)  \
        { dlog(0, FIL__, __LINE__, _("BREAKEXIT")); _exit(EXIT_FAILURE); } \
      } \
    } \
  while (1 == 0)
#else
#define BREAKEXIT(expr) \
  do { \
    int ixi; \
    for (ixi = 0; ixi < 8; ++ixi) { \
      if ((*(volatile unsigned *)((unsigned) expr + ixi) & 0xff) == 0xcc) \
        _exit(EXIT_FAILURE); \
      } \
    } \
  while (1 == 0)
#endif

#else
#define BREAKEXIT(expr)
#endif

/****************************************************************
 *
 *  The debug/trace subsystem
 *
 ****************************************************************/

int slib_do_trace          = 0;
int slib_trace_fd          = -1;

static char trace_log[256] = { '\0' };
static int trace_level     = 0;
static FILE * trace_fp     = NULL;

int  sl_trace_use (char * dummy)
{
  if (dummy)
    slib_do_trace = 1;
  else
    slib_do_trace = 1;
  return 0;
}

int  sl_trace_file (char * str)
{
  if (!str)
    return -1;
  if (str[0] != '/')
    return -1;
  sl_strlcpy(trace_log, str, 256);
  return 0;
}

FILE * sl_tracefile_open(char * file, char * mode)
{
  FILE * xp = NULL;
  slib_trace_fd = open(file, O_WRONLY|O_CREAT|O_APPEND, 0600);
  if (slib_trace_fd >= 0)
    xp = fdopen(slib_trace_fd, mode);
  return xp;
}

void sl_trace_in(char * str, char * file, int line)
{
  int    i;
  if (trace_log[0] == '\0')
    {
      fprintf(stderr, "++ ");
      for (i = 0; i < trace_level; ++i)
      fprintf(stderr, ".  ");
      fprintf(stderr, "[%2d] %s \t - File %c%s%c at line %d\n", 
           trace_level, str, 0x22, file, 0x22, line);
    }
  else if (!sl_is_suid())
    {
      if (!trace_fp)
      trace_fp = sl_tracefile_open(trace_log, "a");
      if (trace_fp)
      {
        fprintf(trace_fp, "++ ");
        for (i = 0; i < trace_level; ++i)
          fprintf(trace_fp, ".  ");
        fprintf(trace_fp, "[%2d] %s \t - File %c%s%c at line %d\n", 
             trace_level, str, 0x22, file, 0x22, line);
      }
      else
      {
        perror(_("sl_trace_in: fopen"));
        _exit(1);
      }
    }
  ++trace_level;
}

void sl_trace_out(char * str, char * file, int line)
{
  int    i;

  --trace_level; if (trace_level < 0) trace_level = 0;

  if (trace_log[0] == '\0')
    {
      fprintf(stderr, "-- ");
      for (i = 0; i < trace_level; ++i)
      fprintf(stderr, ".  ");
      fprintf(stderr, _("[%2d] %s \t - File %c%s%c at line %d\n"), 
           trace_level, str, 0x22, file, 0x22, line);
    }
  else if (!sl_is_suid())
    {
      if (!trace_fp)
      trace_fp = sl_tracefile_open(trace_log, "a");
      if (trace_fp)
      {
        fprintf(trace_fp, "-- ");
        for (i = 0; i < trace_level; ++i)
          fprintf(trace_fp, ".  ");
        fprintf(trace_fp, _("[%2d] %s \t - File %c%s%c at line %d\n"), 
             trace_level, str, 0x22, file, 0x22, line);
      }
      else
      {
        perror(_("sl_trace_out: fopen"));
        _exit(1);
      }
    }
}

extern int sh_log_console (char * msg);

static int dlogActive = 0;

/* this is called from sh_error_setprint()
 */
void dlog_set_active(int flag)
{
  dlogActive = flag;
}

/* flag = 0 debug messages
 *      = 1 descriptive error messages
 *      = 3 backtrace
 */
int dlog (int flag, char * file, int line,  const char *fmt, ...)
{
  va_list     ap;
  char        val[81];
  char        msg[512];
  char        tmp[512];
  int         retval = 0;
  int         i;

#ifdef SH_STEALTH
  /* 
   * do not even print descriptive failure messages in stealth mode
   */
  if (dlogActive == 0)
    return 0;
  if (dlogActive == 1 && flag == 0) /* debug requires debug level */
    return 0;
#else
  if (dlogActive <= 1 && flag == 0) /* debug requires debug level */
    return 0;
#endif

  if (flag == 1)
    {
      sprintf        (val, _("\n---------  %10s "), file);
      sl_strlcpy     (msg,    val,   80);
      sprintf        (val, _(" --- %6d ---------\n"), line);
      sl_strlcat     (msg,     val,   80);
      sh_log_console (msg);
    }

  va_start (ap, fmt);
  if (flag == 1)
    sl_strlcpy(tmp, fmt, 512);
  else
    sl_strlcpy(tmp, fmt, 256);
  retval = sl_strlen(tmp);
  if (retval > 0 && tmp[retval-1] == '\n')
    tmp[retval-1] = '\0';
  retval = 0;
  if (flag == 1)
    {
      sl_vsnprintf (msg, 511, tmp, ap);
    }
  else
    {
      sl_strlcpy   (msg,    "## ", 256);
      for (i = 0; i < trace_level; ++i)
      sl_strlcat (msg, ".  ", 256);
      sprintf      (val, _("[%2d] "), trace_level);
      sl_strlcat   (msg,     val,   256);
      sl_vsnprintf (&msg[sl_strlen(msg)], 255, tmp, ap);
      sl_snprintf  (tmp, 255, _(" \t - File %c%s%c at line %d"), 
                0x22, file, 0x22, line);
      sl_strlcat   (msg,     tmp,   512);
    }
  va_end (ap);
  if (flag != 0 || sl_is_suid())
    retval = sh_log_console (msg);
  else
    {
      if (trace_log[0] == '\0')
      {
        /* sh_log_console (msg); */
        fprintf(stderr, "%s\n", msg);
      }
      else
      {
        if (!trace_fp)
          trace_fp = sl_tracefile_open(trace_log, "a");
        if (trace_fp)
          {
            fprintf(trace_fp, "%s\n", msg);
          }
        else
          {
            perror(_("dlog: fopen"));
            _exit(1);
          }
      }
    }
  if (flag == 1)
    sh_log_console (_("\n----------------------------------------------\n"));
  return retval;
}

extern char aud_err_message[64];
char * sl_get_errmsg()
{
  return &aud_err_message[0];
}


#if defined(SL_DEBUG)
#define SL_MAX_MYSTACK 128

static char sl_mystack[SL_MAX_MYSTACK][32];
static int  sl_mystack_count = 0; 

void sl_stack_push(char * c, char * file, int line )
{
  if (slib_do_trace)
    sl_trace_in(c, file, line);
  if (c && sl_mystack_count < SL_MAX_MYSTACK)
    {
      strncpy(sl_mystack[sl_mystack_count], c, 31);
      sl_mystack[sl_mystack_count][31] = '\0';
      ++sl_mystack_count;
      /*
      fprintf(stderr, "#%03d %s\n", sl_mystack_count, 
            sl_mystack[sl_mystack_count-1]);
      */
    }
  return;
}

void sl_stack_pop(char * c, char * file, int line)
{
  if (slib_do_trace)
    sl_trace_out(c, file, line);
  if (sl_mystack_count > 0)
    {
      /*
      fprintf(stderr, " <- #%03d %s\n", sl_mystack_count,
            sl_mystack[sl_mystack_count-1]);
      */
      --sl_mystack_count;
    }
  return;
}

void sl_stack_print()
{
  int  i;
  /* FILE * dfile; */

  if (sl_mystack_count > 0)
    {
      sh_log_console(_("\nBacktrace:\n"));
      /* dlog(3, FIL__, __LINE__, _("\nBacktrace:\n")); */
      for (i = 0; i < sl_mystack_count; ++i)
      sh_log_console(sl_mystack[i]);
      /* dlog(3, FIL__, __LINE__, _("#%03d %s\n"), i, sl_mystack[i]); */
    } 
  return;
}

#endif


/*
 *  The global errno.
 *  On error, this is set to the return value of the function.
 */
long int sl_errno;


/* ---------------------------------------------------------------- 
 *
 *    Capability routines
 *
 * ---------------------------------------------------------------- */

int sl_useCaps = 0;

#ifdef FANCY_LIBCAP
#include <sys/capability.h>

/*
 * While these routines are tested and work, we don't use POSIX 
 * capabilities, as they don't seem to be useful (root can write 
 * to root-owned files anyway). Things would be more interesting
 * if we could switch to a non-root UID with just a few capabilities
 * enabled.
 */
int sl_drop_cap ()
{
  int              error;
  cap_t            caps;
  cap_flag_t       capflag;
  cap_flag_value_t capfval = CAP_CLEAR;
  cap_value_t      capvals_e[] =
  { 
    CAP_CHOWN,            CAP_FOWNER,        CAP_FSETID,
    CAP_LINUX_IMMUTABLE,  CAP_MKNOD,         CAP_NET_ADMIN,
    CAP_NET_BIND_SERVICE, CAP_NET_BROADCAST, CAP_NET_RAW,
    CAP_SYS_ADMIN,        CAP_SYS_BOOT,      CAP_SYS_CHROOT,
    CAP_SYS_PACCT,        CAP_SYS_PTRACE,    CAP_SYS_RAWIO,
    CAP_SYS_RESOURCE,     CAP_SYS_TIME,      CAP_SYS_TTY_CONFIG,
    CAP_SETGID,           CAP_SETUID,        CAP_KILL,
    CAP_DAC_OVERRIDE,
#if !defined(WITH_MESSAGE_QUEUE)
    CAP_IPC_OWNER,
#endif
    CAP_SYS_MODULE,       CAP_LEASE
  };
  cap_value_t      capvals_p[] =
  { 
    CAP_CHOWN,            CAP_LEASE,         CAP_FSETID,
    CAP_LINUX_IMMUTABLE,  CAP_MKNOD,         CAP_NET_ADMIN,
    CAP_NET_BIND_SERVICE, CAP_NET_BROADCAST, CAP_NET_RAW,
    CAP_SYS_ADMIN,        CAP_SYS_BOOT,      CAP_SYS_CHROOT,
    CAP_SYS_PACCT,        CAP_SYS_PTRACE,    CAP_SYS_RAWIO,
    CAP_SYS_RESOURCE,     CAP_SYS_TIME,      CAP_SYS_TTY_CONFIG,
#if !defined(WITH_EXTERNAL) && !defined(HAVE_UNIX_RANDOM)
    CAP_SETGID,           CAP_SETUID,        CAP_KILL,
#endif
#if !defined(SH_USE_SUIDCHK)
    CAP_DAC_OVERRIDE,     CAP_FOWNER,        
#endif
#if !defined(WITH_MESSAGE_QUEUE)
    CAP_IPC_OWNER,
#endif
    CAP_SYS_MODULE
  };

  if (0 == sl_useCaps) /* 0 = S_FALSE */
    {
      return 0;
    }

  if(NULL == (caps = cap_get_proc()))
    {
      return errno;
    }

  capflag = CAP_EFFECTIVE;
  if (0 != cap_set_flag(caps, capflag, sizeof(capvals_e)/sizeof(cap_value_t),
                  capvals_e, capfval))
    {
      error = errno;
      cap_free(caps);
      return error;
    }
  if (0 != cap_set_proc(caps))
    {
      error = errno;
      cap_free(caps);
      return error;
    }

  capflag = CAP_PERMITTED;
  if (0 != cap_set_flag(caps, capflag, sizeof(capvals_p)/sizeof(cap_value_t),
                  capvals_p, capfval))
    {
      error = errno;
      cap_free(caps);
      return error;
    }
  if (0 != cap_set_proc(caps))
    {
      error = errno;
      cap_free(caps);
      return error;
    }
  cap_free(caps);
  return 0;
}

int sl_drop_cap_int(int what)
{
#if defined(SL_DEBUG)
  char           * captext;
#endif
  cap_flag_t       capflag = CAP_EFFECTIVE;
  cap_flag_value_t capfval = CAP_CLEAR;
  cap_value_t      capvals_a[] = { CAP_SETGID, CAP_SETUID, CAP_KILL };
  cap_value_t      capvals_b[] = { CAP_DAC_OVERRIDE, CAP_FOWNER };
  cap_value_t    * capvals;
  int              nvals;
  int              error = 0;
  cap_t            caps = cap_get_proc();

  if (0 == sl_useCaps) /* 0 = S_FALSE */
    {
      return 0;
    }

  if (caps == NULL)
    {
      return errno;
    }

  switch (what) {
    case 1:
      capvals = capvals_a;
      nvals   = 3;
      capfval = CAP_CLEAR;
      break;
    case 2:
      capvals = capvals_a;
      nvals   = 3;
      capfval = CAP_SET;
      break;
    case 3:
      capvals = capvals_b;
      nvals   = 2;
      capfval = CAP_CLEAR;
      break;
    case 4:
      capvals = capvals_b;
      nvals   = 2;
      capfval = CAP_SET;
      break;
    default:
      return (0);
  }

  if (0 != cap_set_flag(caps, capflag, nvals, capvals, capfval))
    {
      error = errno;
      cap_free(caps);
      return error;
    }
  if (0 != cap_set_proc(caps))
    {
      error = errno;
      cap_free(caps);
      return error;
    }
#if defined(SL_DEBUG)
  captext = cap_to_text(caps, NULL);
  TPT(( 0, FIL__, __LINE__, _("msg=<cap_int %d: %s>\n"), what, captext));
  cap_free(captext);
#endif
  cap_free(caps);
  return 0;
}

int sl_drop_cap_sub()  { return sl_drop_cap_int(1); }
int sl_get_cap_sub()   { return sl_drop_cap_int(2); }
int sl_drop_cap_qdel() { return sl_drop_cap_int(3); }
int sl_get_cap_qdel()  { return sl_drop_cap_int(4); }

#else
int sl_drop_cap ()     { return 0; }
int sl_drop_cap_sub()  { return 0; }
int sl_get_cap_sub()   { return 0; }
int sl_drop_cap_qdel() { return 0; }
int sl_get_cap_qdel()  { return 0; }
#endif

/* ---------------------------------------------------------------- 
 *
 *    String handling routines
 *
 * ---------------------------------------------------------------- */
  
/*
 * A memset that does not get optimized away
 */
void *sl_memset(void *s, int c, size_t n)
{
  size_t i;
  volatile char *p = s;

  if (s == NULL || n <= 0)
    return s;
  
  for (i = 0; i < n; ++i)
    p[i] = (char) c;
  return s;
}

#if !defined(HOST_IS_I86SOLARIS)
#if !defined (_GNU_SOURCE)
extern int vsnprintf ( char *str, size_t n,
                   const char *format, va_list ap );
#endif
#endif

#if !defined (VA_COPY)
#if defined (__GNUC__) && defined (__PPC__) && (defined (_CALL_SYSV) || defined (_WIN32))
#define VA_COPY(ap1, ap2)     (*(ap1) = *(ap2))
#elif defined (VA_COPY_AS_ARRAY)
#define VA_COPY(ap1, ap2)     memmove ((ap1), (ap2), sizeof (va_list))
#else /* va_list is a pointer */
#define VA_COPY(ap1, ap2)     ((ap1) = (ap2))
#endif
#endif 

#if !defined(HAVE_VSNPRINTF) || defined(HAVE_BROKEN_VSNPRINTF)
static
size_t sl_printf_count (const char * fmt, va_list  vl)
{
  size_t  length       = 1;
  int  fini         = 0;
  int  islong       = 0;
  int  islonglong   = 0;
  int  islongdouble = 0;
  char * string_arg;

  SL_ENTER(_("sl_printf_count"));

  if (fmt == NULL)
    SL_IRETURN(SL_ENULL, _("sl_printf_count"));

  while (*fmt) {

    if ( (*fmt) == '%' ) { /* a format specifier */

      fmt++;        /* point to first char after '%' */

      fini = 0;
      islong = 0;
      islongdouble = 0;

      while (*fmt && (fini == 0) ) {
      
      switch (*fmt) {

      case '*':      /* field width supplied by an integer */
        length = length + va_arg (vl, int);
        ++fmt;
        break;
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        length = length + strtol (fmt, (char**) &fmt, 10);
        /* strtol makes FastForward to first invalid char */
        break;

      case 'l':   /* 'long' modifier */
        if (islong == 0)
          islong = 1;
        else
          {
            islonglong = 1;
            islong = 0;
          }
        ++fmt;
        break;

      case 'L':  /* 'long double' modifier */ 
#ifdef HAVE_LONG_DOUBLE   
        islongdouble = 1;
#else
        islong = 1;
#endif
        ++fmt;
        break;

      case 'd':
      case 'i': 
      case 'o':
      case 'u':
      case 'x':
      case 'X':
        if (islonglong == 1)
#ifdef HAVE_LONG_LONG
          (void) va_arg (vl, long long);
#else
          (void) va_arg (vl, long);
#endif
        else if (islong == 1)
          (void) va_arg (vl, long);
        else
          (void) va_arg (vl, int);
        islong = 0;
        islonglong = 0;
        length = length + 24;
        ++fmt;
        fini = 1;
        break;

      case 'D':
      case 'O':
      case 'U':
        (void) va_arg (vl, long);
        length = length + 24;
        fmt++;
        fini = 1;
        break;

      case 'e':
      case 'E':
      case 'f':
      case 'g':
#ifdef HAVE_LONG_DOUBLE   
        if (islongdouble == 1) {
          (void) va_arg (vl, long double);
          islongdouble = 0;
          length = length + 20;
          }
        else
#endif
          (void) va_arg (vl, double);
        length = length + 20;
        fini = 1;
        ++fmt;
        break;

      case 's':
        string_arg = va_arg (vl, char *);
        if (string_arg != NULL)
          length = length + sl_strlen (string_arg);
        else
          length = length + 16;
        fini = 1;
        ++fmt;
        break;

      case 'c':
        (void) va_arg (vl, int);
        length = length + 1;
        fini = 1;
        ++fmt;
        break;

      case 'p':
      case 'n':
        (void) va_arg (vl, void * );
        length = length + 32;
        fini = 1;
        ++fmt;
        break;

      case '%':            /* %% will print '%' */
        length = length + 1;
        fini = 1;
        ++fmt;
        break;

      default:
        length = length + 1;
        ++fmt;
        break;

      }  /* end switch */
      }    
      /* end parsing a single format specifier */
    } else {
      length = length + 1;
      fmt++;
    }
  }
  SL_IRETURN(length, _("sl_printf_count"));
}
#endif  /* #ifndef  HAVE_VSNPRINTF */

/*
 * An implementation of vsnprintf. va_start/va_end are in the caller
 * function.
 * Returns C99 (#bytes that would heve been written) on success.
 */
int sl_vsnprintf(char *str, size_t n,
             const char *format, va_list vl )
{
  int len = 0;
#if !defined(HAVE_VSNPRINTF) || defined(HAVE_BROKEN_VSNPRINTF)
  size_t         total;
  va_list       vl2;
#endif

  SL_ENTER(_("sl_vsnprintf"));
  if (str == NULL || format == NULL)
    SL_IRETURN(0, _("sl_vsnprintf"));

#if defined(HAVE_VSNPRINTF) && !defined(HAVE_BROKEN_VSNPRINTF)
  len = vsnprintf (str, n, format, vl);
  str[n-1] = '\0';
#else
  VA_COPY (vl2, vl);                   /* save the argument list           */
  total = sl_printf_count (format, vl);
  len   = (int) total;
  if (total < n) 
    {
      vsprintf (str, format, vl2);       /* program has checked that it fits */
      str[n-1] = '\0';
    }
  else 
    {
      sl_strlcpy (str, format, n);
      va_end(vl2);
      SL_IRETURN(len, _("sl_vsnprintf"));
    }
  va_end(vl2);
#endif
  SL_IRETURN(len, _("sl_vsnprintf"));
}

/*
 * An implementation of snprintf.
 * Returns SL_ENONE on success.
 * ENULL:  src || format == NULL
 * ERANGE: n out of range
 * ETRUNC: truncated
 */
int sl_snprintf(char *str, size_t n,
            const char *format, ... )
{
  va_list       vl;
#if !defined(HAVE_VSNPRINTF) || defined(HAVE_BROKEN_VSNPRINTF)
  size_t          total = 0;
  va_list       vl2;
#endif

  SL_ENTER(_("sl_snprintf"));
  if (str == NULL || format == NULL)
    SL_IRETURN(SL_ENULL, _("sl_snprintf"));
  
  va_start (vl, format);
#if defined(HAVE_VSNPRINTF) && !defined(HAVE_BROKEN_VSNPRINTF)
  vsnprintf (str, n, format, vl);
  str[n-1] = '\0';
#else
  VA_COPY (vl2, vl);                   /* save the argument list           */
  total = sl_printf_count (format, vl);
  if (total < n) 
    {
      vsprintf (str, format, vl2);     /* program has checked that it fits */
      str[n-1] = '\0';
    }
  else 
    {
      sl_strlcpy (str, format, n);
      va_end(vl2);
      va_end(vl);
      SL_IRETURN(SL_ETRUNC, _("sl_snprintf"));
    }
  va_end(vl2);
#endif  
  va_end(vl);
  SL_IRETURN(SL_ENONE, _("sl_snprintf"));
}

/*
 * Appends src to string dst of size siz (unlike strncat, siz is the
 * full size of dst, not space left).  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns SL_NONE on success, errcode on failure.
 *
 * ENULL:  dst == NULL
 * ERANGE: siz out of range
 * ETRUNC: src truncated
 */
int sl_strlcat(char * dst, /*@null@*/const char *src, size_t siz)
{
  register size_t dst_end;
  register size_t dst_free;

  register char       * p;
  register const char * q;

  if (dst == NULL)
    return SL_ENONE;
  if (src == NULL || src == "") 
    return SL_ENONE;

  if (siz > 0) {

    /* How much free space do we have ?
     */
    dst_end  = strlen(dst);
    dst_free = siz - dst_end - 1;

    p = &dst[dst_end];
    q = src;

    while (dst_free > 0 && *q != '\0')
      {
      *p++ = *q++;
      --dst_free;
      }

    /* NULL terminate dst.
     */
    *p = '\0';

    if (*q != '\0') 
      return SL_ETRUNC;
  }

  return SL_ENONE;
}

/*
 * An alternative implementation of the OpenBSD strlcpy() function.
 *
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns SL_NONE on success, errcode on failure.
 *
 * ENULL:  dst == NULL
 * ERANGE: siz out of range
 * ETRUNC: src truncated
 */
int sl_strlcpy(char * dst, /*@null@*/const char * src, size_t siz)
{
  /* SL_ENTER(_("sl_strlcpy")); */

  if (dst == NULL)
    return SL_ENULL;
  if (src == NULL)
    { 
      if (siz > 0) 
      dst[0] = '\0';
      return SL_ENONE;
    }


  if (siz > 0) {
    /* copy siz-1 characters 
     */
    (void) strncpy(dst, src, siz-1);

    /* NULL terminate
     */
    dst[siz-1] = '\0';
  }
  return SL_ENONE;
}

/*
 * A robust drop-in replacement of strncpy. strlcpy is preferable.
 */
char * sl_strncpy(char *dst, const char *src, size_t size)
{

#ifdef SL_FAIL_ON_ERROR
  SL_REQUIRE(dst != NULL, _("dst != NULL"));
  SL_REQUIRE(src != NULL, _("src != NULL"));
  SL_REQUIRE(size > 0, _("size > 0"));
#endif

  if (dst == NULL)
    {
      sl_errno = SL_ENULL;
      return (NULL);
    }
  if (size < 1)
    {
      sl_errno = SL_ERANGE;
      return (dst);
    }
  if (!src)
    {
      sl_errno = SL_ENULL;
      dst[0] = '\0';
    }
  else if (src[0] == '\0')
    dst[0] = '\0';
  else
    strncpy(dst, src, size);

  if (sl_strlen(src) >= size)
    {
      errno = ENOSPC;
      dst[size-1] = '\0';
    }
  return (dst);
}

/*
 * A robust drop-in replacement of strncat. strlcat is preferable.
 */
char * sl_strncat(char *dst, const char *src, size_t n)
{
#ifdef SL_FAIL_ON_ERROR
  SL_REQUIRE(dst != NULL, _("dst != NULL"));
  SL_REQUIRE(src != NULL, _("src != NULL"));
  SL_REQUIRE(n > 0, _("n > 0"));
#endif

  if (dst == NULL)
    {
      sl_errno = SL_ENULL;
      return (NULL);
    }
  if (n < 1)
    {
      sl_errno = SL_ERANGE;
      return (dst);
    }
  if (!src)
    {
      sl_errno = SL_ENULL;
      return (dst);
    }
  else if (src[0] == '\0')
    dst[0] = '\0';
  else
    strncat(dst, src, n);

  return (dst);
}


int sl_strcmp(const char * a, const char * b)
{
#ifdef SL_FAIL_ON_ERROR
  SL_REQUIRE (a != NULL, _("a != NULL"));
  SL_REQUIRE (b != NULL, _("b != NULL"));
#endif

  if (a != NULL && b != NULL)
    return (strcmp(a, b));
  else if (a == NULL && b != NULL)
    return (-1);
  else if (a != NULL && b == NULL)
    return (1);
  else
    return (-7); /* arbitrary */
}

int sl_strncmp(const char * a, const char * b, size_t n)
{
#ifdef SL_FAIL_ON_ERROR
  SL_REQUIRE (a != NULL, _("a != NULL"));
  SL_REQUIRE (b != NULL, _("b != NULL"));
  SL_REQUIRE (n > 0, _("n > 0"));
#endif

  if (a != NULL && b != NULL)
    return (strncmp(a, b, n));
  else if (a == NULL && b != NULL)
    return (-1);
  else if (a != NULL && b == NULL)
    return (1);
  else
    return (-7); /* arbitrary */
}

/* string searching
 */

char * sl_strstr (const char * haystack, const char * needle) 
{
#ifndef HAVE_STRSTR
  int             i;
  size_t          needle_len;
  size_t          haystack_len;
#endif
  
  if (haystack == NULL || needle == NULL)
    return NULL;
  if (*needle == '\0' || *haystack == '\0')
    return NULL;

#if defined(HAVE_STRSTR)
  return (strstr(haystack, needle));
#else
  needle_len   = strlen(needle);
  haystack_len = strlen(haystack);

  for (i = 0; i <= (haystack_len-needle_len); ++i)
    if (0 == sl_strncmp(&haystack[i], needle, needle_len))
      return (needle);
  return NULL;
#endif
}


/* ---------------------------------------------------------------- 
 *
 *    Privilege handling routines
 *
 * ---------------------------------------------------------------- */

  

static   uid_t   euid;
static   uid_t   ruid;
static   uid_t   ruid_orig;
static   gid_t   egid;
static   gid_t   rgid;
static   gid_t   rgid_orig;

static   int     uids_are_stored = SL_FALSE;
static   int     suid_is_set     = SL_TRUE;

#ifdef HAVE_SETRESUID
extern       int setresuid (uid_t truid, uid_t teuid, uid_t tsuid);
extern       int setresgid (gid_t trgid, gid_t tegid, gid_t tsgid);
#endif


/*
 * This function returns true if the program is SUID.
 * It calls abort() if the uid's are not saved already.
 */
int sl_is_suid()
{
  if (uids_are_stored == SL_FALSE)
    {
      if (getuid() == geteuid() && getgid() == getegid())
      return (0);     /* FALSE */
      else
      return (1);     /* TRUE  */
    }
  else
    {
      if (euid == ruid && egid == rgid)
      return (0);     /* FALSE */
      else
      return (1);     /* TRUE  */
    }
}

/*
 * This function returns the saved euid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_euid(uid_t * ret)
{
  SL_ENTER(_("sl_get_euid"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = euid;
  else
    *ret = geteuid();
  SL_IRETURN (SL_ENONE, _("sl_get_euid"));
}

uid_t sl_ret_euid()
{
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    return (euid);
  else
    return (geteuid());
}

/*
 * This function returns the saved egid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_egid(gid_t * ret)
{
  SL_ENTER(_("sl_get_egid"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = egid;
  else
    *ret = getegid();
  SL_IRETURN (SL_ENONE, _("sl_get_egid"));
}

/*
 * This function returns the saved ruid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_ruid(uid_t * ret)
{
  SL_ENTER(_("sl_get_ruid"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = ruid;
  else
    *ret = getuid();
  SL_IRETURN (SL_ENONE, _("sl_get_ruid"));
}

/*
 * This function returns the saved rgid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_rgid(gid_t * ret)
{
  SL_ENTER(_("sl_get_rgid"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = rgid;
  else
    *ret = getgid();
  SL_IRETURN (SL_ENONE, _("sl_get_rgid"));
}

/*
 * This function returns the saved original ruid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_ruid_orig(uid_t * ret)
{
  SL_ENTER(_("sl_get_ruid_orig"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = ruid_orig;
  else
    *ret = getuid();
  SL_IRETURN (SL_ENONE, _("sl_get_ruid_orig"));
}

/*
 * This function returns the saved original rgid.
 * It calls abort() if the uid's are not saved already.
 */
int sl_get_rgid_orig(gid_t * ret)
{
  SL_ENTER(_("sl_get_rgid_orig"));
  /* SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));*/
  if (uids_are_stored == SL_TRUE)
    *ret = rgid_orig;
  else
    *ret = getgid();
  SL_IRETURN (SL_ENONE, _("sl_get_rgid_orig"));
}

static int suid_warn_flag = 1;
static void suid_warn(int a)
{
  fprintf(stderr, _("ERROR:  open set/unset suid !!! %d\n"), a);
  return;
}

/*
 * This function sets the effective uid 
 * to the saved effective uid.
 * It will abort on failure.
 */
int sl_set_suid ()
{
  int retval;

  SL_ENTER(_("sl_set_suid"));

  if (uids_are_stored == SL_FALSE)
    {
      SL_IRETURN(SL_ENONE, _("sl_set_suid"));
    }

  SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));  

  if (ruid == euid && rgid == egid) 
    {
      suid_is_set = SL_TRUE;
      SL_IRETURN(SL_ENONE, _("sl_set_suid"));
    }  
  SL_REQUIRE(suid_is_set     == SL_FALSE, _("suid_is_set == SL_FALSE"));  

#if defined(HAVE_SETRESUID)
  retval = setresuid (sh_uid_neg, euid, sh_uid_neg);
  if (retval == 0) 
    retval = setresgid (sh_gid_neg, egid, sh_gid_neg);

#elif defined(HAVE_SETEUID)
  retval = seteuid (egid);
  if (retval == 0) 
    retval = setegid (euid);

  /* on AIX, setreuid does not behave well for non-root users.
   */
#elif defined(HAVE_SETREUID)
  retval = setreuid (ruid, euid);
  if (retval == 0) 
    retval = setregid (rgid, egid);

#else
  retval = setuid (euid);
  if (retval == 0) 
    retval = setgid (egid);
#endif
  if (suid_warn_flag == 1)
    suid_warn(1);
  suid_warn_flag = 1;

  SL_REQUIRE(retval == 0, _("retval == 0"));
  suid_is_set = SL_TRUE;
  SL_IRETURN(SL_ENONE, _("sl_set_suid"));
}

/*
 * This function sets the effective uid to the real uid.
 * It will abort on failure.
 */
int sl_unset_suid ()
{
  register int retval;

  SL_ENTER(_("sl_unset_suid"));

  if (uids_are_stored == SL_FALSE)
    {
      SL_IRETURN(SL_ENONE, _("sl_unset_suid"));
    }

  SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));

  if (ruid == euid && rgid == egid)
    {
      suid_is_set = SL_FALSE;
      SL_IRETURN(SL_ENONE, _("sl_unset_suid"));
    }  
  SL_REQUIRE(suid_is_set     == SL_TRUE, _("suid_is_set == SL_TRUE"));  

#if defined(HAVE_SETRESUID)
  retval = setresgid (sh_gid_neg, rgid, sh_gid_neg);
  if (retval == 0) 
    retval = setresuid (sh_uid_neg, ruid, sh_uid_neg);

#elif defined(HAVE_SETEUID)
  retval = setegid (rgid);
  if (retval == 0) 
    retval = seteuid (ruid);

#elif defined(HAVE_SETREUID)
  retval = setregid (egid, rgid);
  if (retval == 0) 
    retval = setreuid (euid, ruid);

#else
  retval = setgid (rgid);
  if (retval == 0) 
    retval = setuid (ruid);
#endif

  if (suid_warn_flag == 0)
    suid_warn(0);
  suid_warn_flag = 0;

  SL_REQUIRE(retval == 0, _("retval == 0"));
  suid_is_set = SL_FALSE;
  SL_IRETURN(SL_ENONE, _("sl_unset_suid"));
}


/*
 * This function saves the uid's.
 */
int sl_save_uids()
{
  SL_ENTER(_("sl_save_uids"));
  if (uids_are_stored == SL_TRUE) 
    SL_IRETURN(SL_EREPEAT, _("sl_save_uids"));

  ruid_orig = getuid();
  rgid_orig = getgid();
  egid = getegid();
  euid = geteuid();
  ruid = ruid_orig;
  rgid = rgid_orig;
  uids_are_stored = SL_TRUE;

  SL_IRETURN(SL_ENONE, _("sl_save_uids"));
}

/* 
 * This function drops SUID privileges irrevocably.
 * It set the effective uid to the original real uid.
 */
extern int  sh_unix_initgroups2 (uid_t in_pid, gid_t in_gid);
int sl_drop_privileges()
{
  SL_ENTER(_("sl_drop_privileges"));
  SL_REQUIRE(uids_are_stored == SL_TRUE, _("uids_are_stored == SL_TRUE"));

  SL_REQUIRE(setgid(rgid_orig) == 0, _("setgid(rgid_orig) == 0"));
  SL_REQUIRE(sh_unix_initgroups2(ruid_orig, rgid_orig) == 0, _("sh_unix_initgroups2(ruid_orig,rgid_orig) == 0"));
  SL_REQUIRE(setuid(ruid_orig) == 0, _("setuid(ruid_orig) == 0"));

  /* make sure that setuid(0) fails
   */
  SL_REQUIRE(setuid(0) < 0, _("setuid(0) < 0"));

  euid = ruid_orig;
  egid = rgid_orig;
  ruid = ruid_orig;
  rgid = rgid_orig;

  SL_IRETURN(SL_ENONE, _("sl_drop_privileges"));
}

/* 
 * Define a policy: Stay root.
 * Do nothing if not SUID.
 */
int sl_policy_get_root()
{
  SL_ENTER(_("sl_policy_get_root"));
  SL_REQUIRE(uids_are_stored == SL_FALSE, _("uids_are_stored == SL_FALSE"));

  SL_REQUIRE (sl_save_uids() == SL_ENONE, _("sl_save_uids() == SL_ENONE"));

  if (euid != ruid || egid != rgid)
    {
      SL_REQUIRE(setgid(egid) == 0, _("setgid(egid) == 0"));
      SL_REQUIRE(setuid(euid) == 0, _("setuid(euid) == 0"));
      SL_REQUIRE(ruid == getuid() && rgid == getgid(),
             _("ruid == getuid() && rgid == getgid()"));
      ruid = euid;
      rgid = egid;
    }
  suid_is_set = SL_TRUE;
  if (euid == 0)
    {
      SL_REQUIRE(sh_unix_initgroups2(euid, egid) == 0, _("sh_unix_initgroups2(euid,egid) == 0"));
    }
  SL_IRETURN(SL_ENONE, _("sl_policy_get_root"));
}

#include <pwd.h>

/* 
 * Define a policy: Get real (irrevocably).
 * This function drops SUID privileges irrevocably.
 * Do nothing if not SUID (? not true - drops if root).
 */

int sl_policy_get_real(char * user)
{
  struct passwd * tempres;

  SL_ENTER(_("sl_policy_get_real"));
  SL_REQUIRE(uids_are_stored == SL_FALSE, _("uids_are_stored == SL_FALSE"));
  SL_REQUIRE (sl_save_uids() == SL_ENONE, _("sl_save_uids() == SL_ENONE"));

  if (euid == 0 || ruid == 0)
    {
      tempres = sh_getpwnam(user);

      SL_REQUIRE (NULL != tempres, _("tempres != NULL"));
  
      rgid_orig = tempres->pw_gid;
      ruid_orig = tempres->pw_uid;
    }
  else
    {
      rgid_orig = rgid;
      ruid_orig = ruid;
    }

  SL_REQUIRE (sl_drop_privileges() == SL_ENONE,
            _("sl_drop_privileges() == SL_ENONE"));

  suid_is_set = SL_TRUE;
  SL_IRETURN(SL_ENONE, _("sl_policy_get_real"));
}


/* 
 * Define a policy: Get user.
 * Drops privileges.
 * Do nothing if not SUID.
 */
int sl_policy_get_user(char * user)
{
  struct passwd * tempres;

  SL_ENTER(_("sl_policy_get_user"));

  SL_REQUIRE(user != NULL, _("user != NULL"));
  SL_REQUIRE(uids_are_stored == SL_FALSE, _("uids_are_stored == SL_FALSE"));
  SL_REQUIRE (sl_save_uids() == SL_ENONE, _("sl_save_uids() == SL_ENONE"));

  if (euid != ruid || egid != rgid)
    {
      tempres = sh_getpwnam(user);

      SL_REQUIRE (NULL != tempres, _("tempres != NULL"));

#if 0
      rgid = tempres->pw_gid;
      ruid = tempres->pw_uid;
      SL_REQUIRE(sl_unset_suid() == SL_ENONE, 
             _("sl_unset_suid() == SL_ENONE"));
#endif
      SL_REQUIRE (sl_drop_privileges() == SL_ENONE,
              _("sl_drop_privileges() == SL_ENONE"));
    }
  SL_IRETURN(SL_ENONE, _("sl_policy_get_user"));
}



/* ---------------------------------------------------------------- 
 *
 *    File access routines
 *
 * ---------------------------------------------------------------- */

#define TOFFSET 0x1234

/* this would prevent opening files if the first 16 fds are open :( */ 
/* #define MAXFD   FOPEN_MAX                                        */

#define MAXFD   1024

typedef struct openfiles {
  SL_TICKET ticket;     /* The unique  ID.      */ 
  int fd;               /* The file descriptor. */
  char * path;          /* The file path.       */
} SL_OFILE; 

static SL_OFILE * ofiles[MAXFD]; 


static unsigned int nonce_counter = TOFFSET;

static
SL_TICKET sl_create_ticket (unsigned int myindex) 
{
  unsigned int high; /* index */ 
  unsigned int low;  /* nonce */

  SL_ENTER(_("sl_create_ticket"));

  if (myindex >= MAXFD) 
    SL_IRETURN (SL_EINTERNAL, _("sl_create_ticket")); 

  /* mask out the high bit and check that it is not used
   * -> verify that it fits into 16 bits as positive
   */
  high = (myindex + TOFFSET) & 0x7fff; 

  if (high != myindex + TOFFSET) 
    SL_IRETURN (SL_EINTERNAL, _("sl_create_ticket")); 

  low = nonce_counter & 0xffff;

  /* Overflow -> nonce too big.
   */
  if ((low != nonce_counter++) || low == 0)
    SL_IRETURN (SL_EINTERNAL, _("sl_create_ticket"));
 
  /* Wrap around the nonce counter.
   * This is a dirty trick.
   */
  if (nonce_counter > 0x7fff)
    nonce_counter = TOFFSET;

  SL_RETURN ((SL_TICKET) ((high << 16) | low), _("sl_create_ticket")); 
}

static 
int sl_read_ticket (SL_TICKET fno) 
{
  register unsigned myindex; 
  register SL_OFILE *of; 

  myindex = ((fno >> 16) & 0xffff) - TOFFSET;
  if (myindex >= MAXFD)
    return (SL_ETICKET);

  if (ofiles[myindex] == NULL)
    return (SL_ETICKET);

  if (ofiles[myindex]->ticket != fno)
    return (SL_ETICKET);

  if ((of = ofiles[myindex])->fd < 0 || of->fd >= MAXFD )
    return (SL_EINTERNAL);

  if (((of->ticket) & 0xffff) == 0)
    return (SL_EINTERNAL); 

  return (myindex); 
}

SL_TICKET sl_make_ticket (int fd, char * filename)
{
  SL_TICKET ticket;
  SL_ENTER(_("sl_make_ticket"));
  /* Make entry.
   */
  if (fd >= MAXFD || fd < 0)
     {
      SL_IRETURN(SL_TOOMANY, _("sl_make_ticket"));
     }

   if (ofiles[fd] != NULL)
    {
      SL_IRETURN(SL_EINTERNAL, _("sl_make_ticket"));
    }

  if ( (ofiles[fd] = (SL_OFILE *) malloc(sizeof(SL_OFILE))) == NULL)
    {
      SL_IRETURN(SL_EMEM, _("sl_make_ticket"));
    }

   if ( (ofiles[fd]->path = (char *) malloc( strlen(filename)+1) ) == NULL)
    {
      free(ofiles[fd]);
      ofiles[fd] = NULL;
      SL_IRETURN(SL_EMEM, _("sl_make_ticket"));
    }

  /* Get a ticket.
   */
  ticket = sl_create_ticket((unsigned int)fd);

  if (SL_ISERROR(ticket))
    {
      (void) free (ofiles[fd]->path);
      (void) free (ofiles[fd]);
      SL_IRETURN(ticket, _("sl_make_ticket"));
    }

  strcpy (ofiles[fd]->path, filename);                    /* Known to fit  */
  ofiles[fd]->ticket = ticket;
  ofiles[fd]->fd     = fd;

  SL_IRETURN(ticket, _("sl_make_ticket"));
}

#define SL_OPEN_MIN          113
#define SL_OPEN_FOR_READ     113
#define SL_OPEN_FOR_WRITE    114
#define SL_OPEN_FOR_RDWR     115
#define SL_OPEN_FOR_WTRUNC   116
#define SL_OPEN_FOR_RWTRUNC  117
#define SL_OPEN_SAFE_RDWR    118
#define SL_OPEN_FOR_FASTREAD 119
#define SL_OPEN_MAX          119

static mode_t  open_mode = (S_IWUSR|S_IRUSR|S_IRGRP);


static
int sl_open_file (char *filename, int mode, int priv)
{
  struct stat   lbuf;
  struct stat   buf;
  int           lstat_return;
  int           stat_return;
  int           fd;
  int           sflags;
  SL_TICKET     ticket;

  SL_ENTER(_("sl_open_file"));
  
#if !defined(O_NOATIME)
  /* 
   * bitwise 'or' with zero does not modify any bit 
   */
#define O_NOATIME 0
#endif
 
#if !defined(O_NONBLOCK)
#if defined(O_NDELAY)
#define O_NONBLOCK  O_NDELAY
#else
#define O_NONBLOCK  0
#endif
#endif

  if (filename == NULL)
    SL_IRETURN(SL_ENULL, _("sl_open_file"));
  if (mode < SL_OPEN_MIN || mode > SL_OPEN_MAX)
    SL_IRETURN(SL_EINTERNAL, _("sl_open_file"));
    
  /* "This system call always succeeds and the previous value of
   * the mask is returned." 
   */
  (void) umask (0); 

  if (mode == SL_OPEN_FOR_FASTREAD)
    {
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                 O_RDONLY|O_NOATIME|O_NONBLOCK, 0);
      if (fd >= 0) {
      sflags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags & ~O_NONBLOCK);
      }
      if (fd < 0)
      SL_IRETURN(SL_EBADFILE, _("sl_open_file"));
      goto createTicket;
    }

#ifdef USE_SUID
  if (priv == SL_YESPRIV)
    sl_set_suid();
#endif
  if (mode == SL_OPEN_FOR_READ)
    lstat_return = retry_stat (FIL__, __LINE__, filename, &lbuf);
  else
    lstat_return = retry_lstat(FIL__, __LINE__, filename, &lbuf);
#ifdef USE_SUID
  if (priv == SL_YESPRIV)
    sl_unset_suid();
#endif

  if (lstat_return == -1)
    {
      lstat_return = ENOENT;
      if ( (mode == SL_OPEN_FOR_READ && lstat_return == ENOENT) ||
         (errno != ENOENT))
      {
        TPT(( 0, FIL__, __LINE__, _("msg=<lstat: %s> errno=<%d>\n"), 
          filename, errno));
        SL_IRETURN(SL_EBADFILE, _("sl_open_file"));
      }
    }
  
  if ( (mode != SL_OPEN_FOR_READ) && (lstat_return != ENOENT) &&
       ( S_ISDIR(lbuf.st_mode) || (S_IWOTH & lbuf.st_mode) ) 
      )
    SL_IRETURN(SL_EBADFILE, _("sl_open_file"));

    
  /* O_NOATIME has an effect for read(). But write() ?.
   */
  switch (mode)
    {
    case SL_OPEN_FOR_READ:
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                 O_RDONLY|O_NOATIME|O_NONBLOCK, 0);
      if (fd >= 0) {
      sflags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags & ~O_NONBLOCK);
      }
      break;
    case SL_OPEN_FOR_WRITE:
      if (lstat_return == ENOENT)
            fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_WRONLY|O_CREAT|O_EXCL,    open_mode);
      else
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_WRONLY|O_CREAT|O_NOATIME, open_mode);
      break;
    case SL_OPEN_SAFE_RDWR:
      if (lstat_return == ENOENT)
            fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_RDWR|O_CREAT|O_EXCL,      open_mode);
      else
      SL_IRETURN(SL_EBADFILE, _("sl_open_file"));
      break;
    case SL_OPEN_FOR_RDWR:
      if (lstat_return == ENOENT)
            fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_RDWR|O_CREAT|O_EXCL,      open_mode);
      else
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_RDWR|O_CREAT|O_NOATIME,   open_mode);
      break;
    case SL_OPEN_FOR_WTRUNC:
      if (lstat_return == ENOENT)
            fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_WRONLY|O_CREAT|O_EXCL,            open_mode);
      else
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_WRONLY|O_CREAT|O_TRUNC|O_NOATIME, open_mode);
      break;
    case SL_OPEN_FOR_RWTRUNC:
      if (lstat_return == ENOENT)
            fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_RDWR|O_CREAT|O_EXCL,            open_mode);
      else
      fd = aud_open (FIL__, __LINE__, priv, filename, 
                   O_RDWR|O_CREAT|O_TRUNC|O_NOATIME, open_mode);
      break;
    default:
      SL_IRETURN(SL_EINTERNAL, _("sl_open_file"));
    }

  if (fd < 0)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<Error opening: %s> errno=<%d>\n"), 
          filename, errno));
      SL_IRETURN(SL_EBADFILE, _("sl_open_file"));
    }

#ifdef USE_SUID
  if (priv == SL_YESPRIV)
    sl_set_suid();
#endif
  stat_return = retry_fstat(FIL__, __LINE__, fd, &buf);
#ifdef USE_SUID
  if (priv == SL_YESPRIV)
    sl_unset_suid();
#endif

  if (stat_return < 0)
    {
      close (fd);
      SL_IRETURN(SL_EBADFILE, _("sl_open_file"));
    }

  if (lstat_return != ENOENT && buf.st_ino != lbuf.st_ino)
    {
      close (fd);
      SL_IRETURN(SL_EBOGUS, _("sl_open_file"));
    }

 createTicket:

  /* Make entry.
   */
  if (fd >= MAXFD)
     {
      close(fd);
      SL_IRETURN(SL_TOOMANY, _("sl_open_file"));
     }

   if (ofiles[fd] != NULL)
    {
      close(fd);
      SL_IRETURN(SL_EINTERNAL, _("sl_open_file"));
    }

  if ( (ofiles[fd] = (SL_OFILE *) malloc(sizeof(SL_OFILE))) == NULL)
    {
      close(fd);
      SL_IRETURN(SL_EMEM, _("sl_open_file"));
    }

   if ( (ofiles[fd]->path = (char *) malloc( strlen(filename)+1) ) == NULL)
    {
      free(ofiles[fd]);
      ofiles[fd] = NULL;
      close(fd);
      SL_IRETURN(SL_EMEM, _("sl_open_file"));
    }

  /* Get a ticket.
   */
  ticket = sl_create_ticket(fd);

  if (SL_ISERROR(ticket))
    {
      (void) free (ofiles[fd]->path);
      (void) free (ofiles[fd]);
      close(fd);
      SL_IRETURN(ticket, _("sl_open_file"));
    }

  strcpy (ofiles[fd]->path, filename);                    /* Known to fit  */
  ofiles[fd]->ticket = ticket;
  ofiles[fd]->fd     = fd;

  SL_IRETURN(ticket, _("sl_open_file"));
}

static
int check_fname_priv (char * fname, int priv)
{
  SL_ENTER(_("check_fname_priv"));
  if (fname == NULL)
    SL_IRETURN(SL_ENULL, _("check_fname_priv"));
  if (priv != SL_YESPRIV && priv != SL_NOPRIV)
    SL_IRETURN(SL_EINTERNAL, _("check_fname_priv"));
  SL_IRETURN(SL_ENONE, _("check_fname_priv"));
}
  
SL_TICKET sl_open_write (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_write"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_write"));

  status = sl_open_file(fname, SL_OPEN_FOR_WRITE, priv);
  SL_IRETURN(status, _("sl_open_write"));
}

SL_TICKET sl_open_read (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_read"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    {
      TPT(( 0, FIL__, __LINE__, 
          _("msg=<Error in check_fname_priv.> status=<%ld>\n"), 
          status));
      SL_IRETURN(status, _("sl_open_read"));
    }

  status = sl_open_file(fname, SL_OPEN_FOR_READ, priv);
  SL_IRETURN(status, _("sl_open_read"));
}

SL_TICKET sl_open_fastread (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_fastread"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_read"));

  status = sl_open_file(fname, SL_OPEN_FOR_FASTREAD, priv);
  SL_IRETURN(status, _("sl_open_fastread"));
}

SL_TICKET sl_open_rdwr (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_rdwr"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_rdwr"));

  status = sl_open_file(fname, SL_OPEN_FOR_RDWR, priv);
  SL_IRETURN(status, _("sl_open_rdwr"));
}

SL_TICKET sl_open_safe_rdwr (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_safe_rdwr"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_safe_rdwr"));

  status = sl_open_file(fname, SL_OPEN_SAFE_RDWR, priv);
  SL_IRETURN(status, _("sl_open_safe_rdwr"));
}

SL_TICKET sl_open_write_trunc (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_write_trunc"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_write_trunc"));

  status = sl_open_file(fname, SL_OPEN_FOR_WTRUNC, priv);
  SL_IRETURN(status, _("sl_open_write_trunc"));
}

SL_TICKET sl_open_rdwr_trunc (char * fname, int priv)
{
  long status;
  SL_ENTER(_("sl_open_rdwr_trunc"));

  if (SL_ENONE != (status = check_fname_priv (fname, priv)))
    SL_IRETURN(status, _("sl_open_rdwr_trunc"));

  status = sl_open_file(fname, SL_OPEN_FOR_RWTRUNC, priv);
  SL_IRETURN(status, _("sl_open_rdwr_trunc"));
}


int get_the_fd (SL_TICKET ticket)
{
  int fd;

  if (SL_ISERROR(fd = sl_read_ticket(ticket)))
    return (fd);

  if (ofiles[fd] == NULL || fd != ofiles[fd]->fd || fd < 0)
    return (SL_EINTERNAL);
  return (fd);
}

int sl_close (SL_TICKET ticket) 
{
  register int fd;

  SL_ENTER(_("sl_close"));

  if (SL_ISERROR(fd = get_the_fd (ticket)))
    SL_IRETURN(fd, _("sl_close"));

  /* This may fail, but what to do then ?
   */
  if (0 != close(fd) && ofiles[fd] != NULL)
    {
      TPT((0, FIL__, __LINE__, 
         _("msg=<Error closing file.>, path=<%s>, fd=<%d>\n"), 
         ofiles[fd]->path, fd));
    }

  if (ofiles[fd] != NULL)
    {
      (void) free(ofiles[fd]->path);
      (void) free(ofiles[fd]);
      ofiles[fd] = NULL;
    }

  SL_IRETURN(SL_ENONE, _("sl_close")); 
}

int sl_dropall(int fd, int except)
{
  while (fd < MAXFD)
    {
      if (ofiles[fd] != NULL && fd != except)
      {
        if (ofiles[fd]->path != NULL)
          (void) free(ofiles[fd]->path);
        (void) free(ofiles[fd]);
        ofiles[fd] = NULL;
      }
      ++fd;
    }
  return 0;
}


int sl_unlink (SL_TICKET ticket) 
{
  register int fd;

  SL_ENTER(_("sl_unlink"));

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_unlink"));

  if (retry_aud_unlink(FIL__, __LINE__, ofiles[fd]->path) < 0)
    SL_IRETURN(SL_EUNLINK, _("sl_unlink"));

  SL_IRETURN(SL_ENONE, _("sl_unlink")); 
}

  
int sl_seek (SL_TICKET ticket, off_t off_data) 
{
  register int fd;

  SL_ENTER(_("sl_seek"));

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_seek"));

  if (lseek(fd, off_data, SEEK_SET) == (off_t)-1)
    SL_IRETURN(SL_EREWIND, _("sl_seek"));

  SL_IRETURN(SL_ENONE, _("sl_seek")); 
}
    
int sl_rewind (SL_TICKET ticket) 
{
  register int fd;

  SL_ENTER(_("sl_rewind"));

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_rewind"));

  if (lseek (fd, 0L, SEEK_SET) == (off_t)-1)
    SL_IRETURN(SL_EREWIND, _("sl_rewind"));

  SL_IRETURN(SL_ENONE, _("sl_rewind")); 
}

int sl_forward (SL_TICKET ticket) 
{
  register int fd;

  SL_ENTER(_("sl_forward"));

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_forward"));

  if (lseek (fd, 0L, SEEK_END) == (off_t)-1)
    SL_IRETURN(SL_EFORWARD, _("sl_forward"));

  SL_IRETURN(SL_ENONE, _("sl_forward")); 
}


int sl_sync (SL_TICKET ticket) 
{
  register int fd;

  SL_ENTER(_("sl_sync"));

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_sync"));

  if (fsync (fd) == -1)
    SL_IRETURN(SL_ESYNC, _("sl_sync"));

  SL_IRETURN(SL_ENONE, _("sl_sync")); 
}


int sl_read_timeout (SL_TICKET ticket, void * buf_in, size_t count, 
                 int timeout)
{
  fd_set readfds;
  struct timeval tv;
  int sflags;
  int retval;

  int    fd;
  int    byteread = 0;
  int    bytes    = 0;
  char * buf;

  time_t tnow;
  time_t tstart;
  time_t tdiff;
  extern volatile int sig_termfast;
 
  if (count < 1)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<range error>")));
      return(SL_ERANGE);
    }
  if (buf_in == NULL)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
      return (SL_ENULL);
    }

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
      return (fd);
    }

  buf = (char *) buf_in;

  /* set to non-blocking mode 
   */
  sflags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
  retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags | O_NONBLOCK);

  tstart = time(NULL);
  tdiff  = 0;

  while (count > 0)
    {

      if (sig_termfast == 1) 
      {
        retry_fcntl(FIL__, __LINE__, fd, F_SETFL, 
                  sflags & ~O_NONBLOCK);
        return (SL_EREAD);
      }
        
      FD_ZERO(&readfds);
      FD_SET(fd, &readfds);

      if (tdiff >= timeout)
      {
        retry_fcntl(FIL__, __LINE__, fd, F_SETFL, 
                  sflags & ~O_NONBLOCK);
        return (SL_TIMEOUT);
      }

      /*
      tnow  = time(NULL);
      tdiff = tnow - tstart;
      */

      tv.tv_sec  = timeout - tdiff;
      tv.tv_usec = 0;
      
      retval = select (fd+1, &readfds, NULL, NULL, &tv);
      
      if (retval > 0)
      {
        byteread = read (fd, buf, count);
        if (byteread > 0) 
          {
            bytes += byteread; count -= byteread;
            buf += byteread;
            if (count == 0)
            break;
          }  
        else if (byteread == 0)
          {
            break;
          }
        else
          {
            if (errno == EINTR || errno == EAGAIN)
            {
              retry_msleep(1, 0);
              tnow  = time(NULL);
              tdiff = tnow - tstart;
              continue;
            }
            else
            {
              retry_fcntl(FIL__, __LINE__, fd, F_SETFL, 
                        sflags & ~O_NONBLOCK);
              return (SL_EREAD);
            }
          }
      }
      else if ((retval == -1) && (errno == EINTR || errno == EAGAIN))
      {
        retry_msleep(1, 0);
        tnow  = time(NULL);
        tdiff = tnow - tstart;
        continue;
      }
      else if (retval == 0)
      {
        retry_fcntl(FIL__, __LINE__, fd, F_SETFL, 
                  sflags & ~O_NONBLOCK);
        return (SL_TIMEOUT);
      }
      else
      {
        retry_fcntl(FIL__, __LINE__, fd, F_SETFL, 
                  sflags & ~O_NONBLOCK);
        return (SL_EREAD);
      }
      tnow  = time(NULL);
      tdiff = tnow - tstart;
    }

  retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags & ~O_NONBLOCK);
  return ((int) bytes);
}


int sl_read (SL_TICKET ticket, void * buf_in, size_t count)
{
  int fd;
  int byteread = 0;
  int bytes    = 0;

  char * buf;

  if (count < 1)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<range error>")));
      return(SL_ERANGE);
    }
  if (buf_in == NULL)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
      return (SL_ENULL);
    }

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
      return (fd);
    }

  buf = (char *) buf_in;

  do 
    {
      byteread = read (fd, buf, count);
      if (byteread > 0) 
      {
        bytes += byteread; count -= byteread;
        buf += byteread;
      }  
    } while ( byteread > 0 || 
            ( byteread < 0 && (errno == EINTR || errno == EAGAIN)) 
            );

 
  if (byteread == (-1))
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<read error> errno=<%d>\n"), errno));
      return (SL_EREAD);
    }
  return (bytes);
}

int sl_read_fast (SL_TICKET ticket, void * buf_in, size_t count)
{
  int fd;
  int byteread = 0;

  char * buf;

  if (count < 1)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<range error>")));
      return(SL_ERANGE);
    }
  if (buf_in == NULL)
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
      return (SL_ENULL);
    }

  if (SL_ISERROR(fd = get_the_fd(ticket)))
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
      return (fd);
    }

  buf = (char *) buf_in;

  do 
    {
      byteread = read (fd, buf, count);
      if (byteread >= 0) 
      {
        return (byteread);
      }  
    } while ( byteread < 0 && (errno == EINTR || errno == EAGAIN));

 
  if (byteread == (-1))
    {
      TPT(( 0, FIL__, __LINE__, _("msg=<read error> errno=<%d>\n"), errno));
      return (SL_EREAD);
    }
  return (0);
}


int sl_write (SL_TICKET ticket, void * msg_in, long nbytes)
{
  long bytewritten;
  long bytecount;
  int  fd;

  char * msg; 

  SL_ENTER(_("sl_write"));

  if (nbytes < 1)
    SL_IRETURN(SL_ERANGE, _("sl_write"));
  if (msg_in == NULL)
    SL_IRETURN(SL_ENULL, _("sl_write"));
  if (SL_ISERROR(fd = get_the_fd(ticket)))
    SL_IRETURN(fd, _("sl_write"));

  msg = (char *) msg_in;

  /* write
   */
  bytecount    = 0;
  bytewritten  = 0;
  while (bytecount < nbytes) 
    {    
      if ((bytewritten = write (fd, msg, nbytes-bytecount)) > 0) 
      {
        bytecount += bytewritten;
        msg       += bytewritten;    /* move buffer pointer forward */
      }
      else if (bytewritten <= 0)
      {
        if ( errno == EINTR || errno == EAGAIN) /* try again */
            continue;
        else 
          SL_IRETURN(SL_EWRITE, _("sl_write"));
      }
    }
  SL_IRETURN(SL_ENONE, _("sl_write"));
}

int sl_write_line (SL_TICKET ticket, void * msg, long nbytes)
{
  int  status;

  SL_ENTER(_("sl_write_line"));

  status = sl_write(ticket,  msg, nbytes); 
  if (!SL_ISERROR(status))
    status = sl_write(ticket,  "\n", 1);

  SL_IRETURN(status, _("sl_write_line"));
}


/* ---------------------------------------------------------------- 
 *
 *    Trustfile interface
 *
 * ---------------------------------------------------------------- */

extern uid_t rootonly[];
extern int   EUIDSLOT;
extern int   ORIG_EUIDSLOT;

extern char  tf_path[MAXFILENAME];  /* Error path for trust function. */
extern uid_t tf_euid;                   /* Space for EUID of process.     */


char * sl_error_string(int errorcode)
{
  switch (errorcode)
    {
    case SL_EBOGUS: 
      return _("Bogus file. Modified during access.");
    case SL_EWRITE: 
      return _("Write error.");
    case SL_EREAD: 
      return _("Read error.");
    case SL_ESYNC: 
      return _("Error in fsync().");
    case SL_EFORWARD: 
      return _("Error in lseek().");
    case SL_EREWIND: 
      return _("Error in lseek().");
    case SL_EUNLINK: 
      return _("Error in unlink().");
    case SL_EMEM: 
      return _("Out of memory.");
    case SL_EINTERNAL: 
      return _("Internal error.");
    case SL_ETICKET:
      return _("Bad ticket.");
    case SL_EREPEAT: 
      return _("Illegal repeated use of function.");
    case SL_ERANGE: 
      return _("Argument out of range.");
    case SL_ENULL: 
      return _("Dereferenced NULL pointer.");

    case SL_EBADUID: 
      return _("Owner not trustworthy.");
    case SL_EBADGID:
      return _("Group writeable and member not trustworthy.");
    case SL_EBADOTH:
      return _("World writeable.");
    case SL_EBADFILE:
      return _("File access error.");
    case SL_EBADNAME:
      return _("Invalid filename (prob. too long or null).");

    case SL_ETRUNC:
      return _("Truncation occured.");
    case SL_ESTAT:
      return _("stat() failed.");
    default:
      return _("Unknown error.");
    }
}



char * sl_trust_errfile()
{
  return &tf_path[0];
}

extern uid_t tf_baduid;
uid_t   sl_trust_baduid()
{
  return tf_baduid;
}

extern gid_t tf_badgid;
gid_t   sl_trust_badgid()
{
  return tf_badgid;
}


static int trust_count = 0;

int  sl_trust_purge_user ()
{
  int i;

  EUIDSLOT = ORIG_EUIDSLOT;
  trust_count = 0;

  for (i = EUIDSLOT; i < (EUIDSLOT + 15); ++i) 
    rootonly[i] = sh_uid_neg;
  return 0;
}

int  sl_trust_add_user (uid_t pwid)
{
  SL_ENTER(_("sl_trust_add_user"));

  if (trust_count == 15)
    SL_IRETURN(SL_ERANGE, _("sl_trust_add_user"));
  
  rootonly[EUIDSLOT] = pwid;
  ++EUIDSLOT;
  ++trust_count;

  SL_IRETURN(SL_ENONE, _("sl_trust_add_user"));
}

int sl_trustfile_euid(char * filename, uid_t teuid)
{
  long status;
  SL_ENTER(_("sl_trustfile_euid"));

  tf_path[0] = '\0';
  if (filename == NULL || filename[0] == '\0')
    SL_IRETURN(SL_EBADNAME, _("sl_trustfile_euid"));

  tf_euid = teuid;
  status = sl_trustfile(filename, NULL, NULL);
  SL_IRETURN(status, _("sl_trustfile_euid"));
}



Generated by  Doxygen 1.6.0   Back to index