#if !defined(lint) && !defined(__INSIGHT__)
static char sos__rcsid[] = "interface.c,v 1.1.1.1 1995/06/16 21:10:47 seth Exp";
static char sos__copyright[] = "Copyright (c) 1994, 1995 SOS Corporation";
static char sos__contact[] = "SOS Corporation <sos-info@soscorp.com> +1 800 SOS UNIX";
#endif /* not lint */

/*
 * ++Copyright Released Product++
 *
 * Copyright (c) 1994, 1995 Sources of Supply Corporation ("SOS").
 * All rights reserved.
 *
 * The SOS Released Product License Agreement specifies the terms and
 * conditions for redistribution.  You may find the License Agreement
 * in the file LICENSE.
 *
 * SOS Corporation
 * 461 5th Ave.; 16th floor
 * New York, NY 10017
 *
 * +1 800 SOS UNIX
 * <sos-info@soscorp.com>
 *
 * --Copyright Released Product--
 */

/*
 * External entry points for core Brimstone functions
 */

#include "sos.h"
#include "interface.h"
#include "readconf.h"



/*
 * Authentication state engine
 */
int BS_State(BS_MasterStates MS, char *user, u_int *nextstate,
	     u_int *req_input, sos_string *outbuf, sos_string *inbuf)
{
  struct userinfo_t *vuser;
  struct auth_t *pauth;
  int ret, savenstate = *nextstate;
  int (*func)(char *user, char *acode, u_int *nextstate,
	      u_int *req_input, sos_string *outbuf, sos_string *inbuf);

  func = NULL;

  if (!outbuf)
    {
      sprintf(bs_auth_extended,"Null out-buf");
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }
  if (outbuf->len < BS_IOLEN)
    {
      sprintf(bs_auth_extended,"Supplied buffer is too small (%d)",outbuf->len);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }
  memset(outbuf->str,0,outbuf->len);
  *nextstate = 0;
  *req_input = BS_NO_INPUT;

  if (MS == BS_GetChallenge)
    {
      /* XXX - Should you return if you cannot get a default challenge?  Hmm. */
      if ((ret = BS_GenDefChallenge(outbuf->str,outbuf->len)) != BS_WIN)
	return(ret);
      /* Default challenge gets input even though it is ignored */
      *req_input = BS_SIMPLE_INPUT;
    }

  if ((ret = readconfig(NULL)) != BS_WIN) return(ret);
  
  if ((vuser = (struct userinfo_t *)ht_search(UserTbl,user)) == NULL)
    {
      sprintf(bs_auth_extended,"User %s does not appear in database",user);
      soslog(SOSLOG_AUTH_FAILURE, bs_auth_extended);
      return(BS_LOSE);
    }

  /*
   * Check to see if the user is enabled and not expired
   */
  if (!vuser->ena || ((vuser->expire > 0) && (vuser->expire < sos_days(0))))
    {
      if (!vuser->ena)
	{
	  sprintf(bs_auth_extended,"User %s has been disabled",user);
	  soslog(SOSLOG_AUTH_FAILURE, bs_auth_extended);
	}
      else
	{
	  sprintf(bs_auth_extended,"User %s has expired %d days ago",
		  user,(vuser->expire < sos_days(0)));
	  soslog(SOSLOG_AUTH_FAILURE, bs_auth_extended);
	}
      return(BS_LOSE);
    }

  if (!vuser->auth || !(pauth = findauth(vuser->auth)))
    {
      sprintf(bs_auth_extended,"User %s has invalid authentication type (%s)",
	      user,vuser->auth);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }

  if (savenstate)
    {
      func = pauth->state;
      *nextstate = savenstate;
    }
  else
    switch (MS)
      {
      case BS_GetChallenge:
	func = pauth->challenge;
	break;
      case BS_Authenticate:
	func = pauth->authenticate;
	break;
      case BS_Passwd:
	func = pauth->passwd;
	break;
      }

  if (func)
    return (*func)(user,vuser->acode,nextstate,req_input,outbuf,inbuf);

  return(BS_LOSE);
}



/*
 * External form for access list checks
 *
 * Note:  this code is duplicated in bsRmnt
 */
int
BS_CheckACL(char *user, char *srcip, char *srcservstr, char *dstip, char *dstservstr)
{
  struct in_addr dstaddr, srcaddr;
  int dstserv, srcserv;
  int epoch;
  time_t localTime;
  struct tm *pdate;
  
  /*
   * Get the local time and convert it to the correct format.
   *
   * Note: Timezones are a sticky problem for bsRmnt, but
   * we are assuming local timezone for local mode
   */

  /* Get time into localtime */
  time(&localTime);

  pdate = (struct tm *)localtime((time_t *)&localTime);
  localTime += pdate->tm_gmtoff;
  epoch = sos_days(localTime);
  
  /* Find IP address */
  if (sos_getabyfoo(srcip, &srcaddr) < 0)
    {
      sprintf(bs_auth_extended,"Invalid host name (%s)",srcip);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }
  if (sos_getabyfoo(dstip, &dstaddr) < 0)
    {
      sprintf(bs_auth_extended,"Invalid host name (%s)",dstip);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }

  srcserv = sos_getsbyfoo(srcservstr);
  dstserv = sos_getsbyfoo(dstservstr);
  if ((int)srcserv == -1)
    {
      sprintf(bs_auth_extended,"Invalid service name (%s)",srcservstr);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }
  if ((int)dstserv == -1)
    {
      sprintf(bs_auth_extended,"Invalid service name (%s)",dstservstr);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return(BS_ERROR);
    }

  dprintf("Checking for user %s, Day %d, Hour %d, epoch %d, Source (%x,%d) Dest (%x,%d)\n",
	  user,pdate->tm_wday,pdate->tm_hour,epoch,srcaddr,srcserv,dstaddr,dstserv);
  
  
  return (BS_CheckACL_Aux(user,pdate->tm_wday,pdate->tm_hour,epoch,srcaddr,srcserv,dstaddr,dstserv));
}



/*
 * External, numerical form for access list checks
 *
 * Note:  this code is duplicated in bsRmnt
 */
int
BS_CheckACL_Num(char *user, 
		struct in_addr srcaddr, unsigned short srcserv,
		struct in_addr dstaddr, unsigned short dstserv)
{
  int epoch;
  time_t localTime;
  struct tm *pdate;
  
  /*
   * Get the local time and convert it to the correct format.
   *
   * Note: Timezones are a sticky problem for bsRmnt, but
   * we are assuming local timezone for local mode
   */

  /* Get time into localtime */
  time(&localTime);
  pdate = (struct tm *)localtime((time_t *)&localTime);
  localTime += pdate->tm_gmtoff;

  epoch = sos_days(localTime);
  
  dprintf("Checking for user %s, Day %d, Hour %d, epoch %d, Source (%x,%d) Dest (%x,%d)\n",
	  user,pdate->tm_wday,pdate->tm_hour,epoch,srcaddr,srcserv,dstaddr,dstserv);
  
  
  return (BS_CheckACL_Aux(user,pdate->tm_wday,pdate->tm_hour,epoch,srcaddr,srcserv,dstaddr,dstserv));
}



/*
 * Verify that a given user/date/address/service combination
 * are allowed by the rules database
 */
int
BS_CheckACL_Aux(char *user, int wday, int hour, int epoch,
		struct in_addr srcaddr, unsigned short srcserv,
		struct in_addr dstaddr, unsigned short dstserv)
{
  struct userinfo_t *vuser;
  struct timeinfo_t date;
  int retval;
  struct in_addr *srcinaddr;
  struct in_addr *dstinaddr;

  if ((retval = readconfig(NULL)) != BS_WIN) return(retval);
  
  if ((vuser = (struct userinfo_t *)ht_search(UserTbl,user)) == NULL)
    {
      sprintf(bs_auth_extended,"User %s does not appear in database",user);
      soslog(SOSLOG_AUTH_FAILURE, bs_auth_extended);
      return(BS_LOSE);
    }

  /* Construct bitmask from day of week and hour of day */
  date.day = days2bit(wday,-1);
  date.hour = hour2bit(hour,-1);

  /* Really check the data file, this time with feeling (and recursion) */
  retval = CheckGroup(vuser->group, date, epoch, srcaddr, srcserv, dstaddr, dstserv);

  srcinaddr = (struct in_addr *)&srcaddr;
  dstinaddr = (struct in_addr *)&dstaddr;
  switch ( retval )
    {
    case AUTH_CONTINUE:
      {
	/*
	 * Have to watch multiple calls to inet_ntoa in a single printf.
	 * Static buffers are a pain, no?
	 */ 
	char tmphname[40];
	sprintf(bs_auth_extended,"No positive match");
	strcpy (tmphname,(char *)inet_ntoa(*(struct in_addr *)srcinaddr));
      soslog(SOSLOG_AUTH_FAILURE, "Verify failed for user: %s, srcaddr: %s, srcprt: %d, dstaddr: %s, dstprt: %d\n", 
	     user, 
	     tmphname,
	     srcserv, 
	     inet_ntoa(*dstinaddr), 
	     dstserv);
      }
      retval = BS_LOSE;
      break;
	
    case BS_WIN:
      {
	/*
	 * Have to watch multiple calls to inet_ntoa in a single printf.
	 * Static buffers are a pain, no?
	 */ 
	char tmphname[40];
	sprintf(bs_auth_extended,"No positive match");
	strcpy (tmphname,(char *)inet_ntoa(*(struct in_addr *)srcinaddr));
	soslog(SOSLOG_AUTH_SUCCESS, "Verify succeeded for user: %s, srcaddr: %s, srcprt: %d, dstaddr: %s, dstprt: %d\n", 
	       user, 
	       tmphname,
	       srcserv, 
	       inet_ntoa(*dstinaddr), 
	       dstserv);
      }
      break;

    case BS_LOSE:
      {
	char tmphname[40];
	strcpy (tmphname,(char *)inet_ntoa(*(struct in_addr *)srcinaddr));
	soslog(SOSLOG_AUTH_FAILURE, "Verify failed for user: %s, srcaddr: %s, srcprt: %d, dstaddr: %s, dstprt: %d\n", 
	     user, 
	     tmphname,
	     srcserv, 
	     inet_ntoa(*dstinaddr), 
	     dstserv);
      }
      break;

    default:
      soslog(SOSLOG_DEBUG, SOSLOG_TYPE_EVENT, SOSLOG_NO_PERROR,
	     "Uknown return value from CheckGroup\n");
      break;
    }
	
  return retval;

}



/*
 * Cleanup any droppings
 */
void BS_Cleanup(char *user)
{
  struct userinfo_t *vuser;
  struct auth_t *pauth;
  int ret;
  void (*func)(char *user, char *acode);

  if ((ret = readconfig(NULL)) != BS_WIN) return;
  
  if ((vuser = (struct userinfo_t *)ht_search(UserTbl,user)) == NULL)
    {
      sprintf(bs_auth_extended,"User %s does not appear in database",user);
      soslog(SOSLOG_AUTH_FAILURE, bs_auth_extended);
      return;
    }

  if (!vuser->auth || !(pauth = findauth(vuser->auth)))
    {
      sprintf(bs_auth_extended,"User %s has invalid authentication type (%s)",
	      user,vuser->auth);
      soslog(SOSLOG_AUTH_ERROR, bs_auth_extended);
      return;
    }

  func = pauth->cleanup;

  if (func)
    (*func)(user,vuser->acode);

  return;
}



/*
 * Check to see if we need to reload
 */
int bs_check_reload()
{
  dict_obj htcur;
  struct fileinfo_t *f;
  struct stat buf;

  if (!FileTbl)
    return(BS_WIN);	/* XXX should this be an error? */

  /* We delete as we go along, so we are always at minimum */
  for(htcur = ht_minimum(FileTbl);htcur != NULL;htcur = ht_successor(FileTbl,htcur))
    {
      f = (struct fileinfo_t *)htcur;

      if (stat(f->file,&buf) < 0)
	{
	  sprintf(bs_auth_extended,"Could not stat file %s (%d)",f->file,errno);
	  return(BS_ERROR);
	}

      if (buf.st_mtime > f->buf.st_mtime)
	{
	  return(bs_reload());
	}
    }

  return(BS_WIN);
}



/*
 * Reload the brimstone configuration files
 */
int bs_reload()
{
  int ret;

  if ( bs_isconfig() )
    {
      syslog(LOG_NOTICE,"reload(): reloading brimstone config file");
      if ((ret = unlinkACL()) != BS_WIN)
	{
	  syslog(LOG_ERR,"reload(): sosauth unlink failed (%s)",bs_auth_extended);
	  return(ret);
	}
      if ((ret = readconfig(NULL)) != BS_WIN)
	{
	  syslog(LOG_ERR,"reload(): sosauth readconfig failed (%s)",bs_auth_extended);
	  return(ret);
	}
      sprintf(bs_auth_extended,"Something is quite out of sorts");
    }
  else
    {
      syslog(LOG_NOTICE,"reload(): brimstone config reload not required (!init)");
    }

  return(BS_WIN);
}
