#include <stdio.h>
#include <dbm.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "Error.h"
#include "Parse.h"
#include "server.h"
#include "authorize.h"
#define TRUE 1
#define FALSE 0
#define EXACT 2
extern int debug;
Trap setUsers(), setFile(),setHosts();
Keywords authorizeWords[] =
{
  { "groupfile",	setFile },
  { "users",		setUsers },
  { "hosts",		setHosts },

/*
  { "platforms",	setPlatforms },
  { "failuremsg",	setMsg },
*/
  { NULL,		NULL }
};

char *filename;
char *user;
char *hostname;
char *hostaddr;
u_long   addr;
datum userData;
ParseBlock *testblock;
typedef struct _authinfo
{
  char *s;
  int size;
  int length;
} authinfo;

/*
 * ERRORS:
 *
 * APP_DBMOPEN: FATAL
 * Couldn't open database.
 */
Trap setFile(block, line)
     MasterBlock *block;
     char *line;
{
  int len;

  while (isspace(*line)) line++;
  len = strlen(line);

  if (len)
    {
      filename = (char *)malloc(len + 1);
      if (filename != NULL)
	strcpy(filename, line);
      else
	Return(MEMORY_ALLOC, len + 1);
    }
  else
    Return(APP_BADSTRING, NULL);

  if (0 != dbminit(filename))
    Return(APP_DBMOPEN, filename);

  return OK;
}

Trap setUsers(block, line)
     MasterBlock *block;
     char *line;
{
  authinfo *info;

  if (Parse_GetCurrentData(block, USERAUTHINFO,
			   (caddr_t *)&info, sizeof(authinfo)))
    {
      if (Error_Severity == S_FATAL)
	return CHECK;
      else
	Error_Pop();
    }
  testblock = Parse_CurrentBlock(block);
  while (isspace(*line)) line++;

  if (*line != '\0')
    {
      if (info->size == 0)
	{
	  info->length = strlen(line);
	  info->size = info->length + 1;
	  info->s = (char *)malloc(info->size);
	  if (info->s == NULL)
	    Return(MEMORY_ALLOC, info->size);
	  strcpy(info->s, line);
	}
      else
	{
	  info->length += strlen(line) + 1; /* +1 for space */
	  info->size = info->length + 1;
	  info->s = (char *)realloc(info->size);
	  if (info->s == NULL)
	    Return(MEMORY_ALLOC, info->size);
	  strcat(info->s, " ");
	  strcat(info->s, line);
	}
    }

  return OK;
}
Trap setHosts(block, line)
     MasterBlock *block;
     char *line;
{
  authinfo *info;

  if (Parse_GetCurrentData(block, HOSTAUTHINFO,
			   (caddr_t *)&info, sizeof(authinfo)))
    {
      if (Error_Severity == S_FATAL)
	return CHECK;
      else
	Error_Pop();
    }

  while (isspace(*line)) line++;

  if (*line != '\0')
    {
      if (info->size == 0)
	{
	  info->length = strlen(line);
	  info->size = info->length + 1;
	  info->s = (char *)malloc(info->size);
	  if (info->s == NULL)
	    Return(MEMORY_ALLOC, info->size);
	  strcpy(info->s, line);
	}
      else
	{
	  info->length += strlen(line) + 1; /* +1 for space */
	  info->size = info->length + 1;
	  info->s = (char *)realloc(info->size);
	  if (info->s == NULL)
	    Return(MEMORY_ALLOC, info->size);
	  strcat(info->s, " ");
	  strcat(info->s, line);
	}
    }

  return OK;
}
      
Trap Authorize_LoadInfo(what)
     Adr *what;
{
  datum key;
  struct in_addr add;

  user = what->user;
  hostname = what->hostname;
  addr = what->ip;
  add.s_addr = what->ip;
  hostaddr = inet_ntoa(add);
  printf("add = %s\n",hostaddr);
  key.dptr = what->user;
  key.dsize = strlen(what->user) + 1;
  userData = fetch(key);

  return OK;
}

int checkList(what, where)
     char *what, *where;
{
  int len;
  int rc;
  len = strlen(what);

  if (debug)
    printf("in checklist\n");
  while (1)
    {
      while (isspace(*where)) where++;
      rc = 1;
      if (*where == '-') 
	{
	  rc = -1; /* negative acl */
	  where++;
	}
      if ((!strncasecmp(what, where, len)) &&
	(where[len] == '\0' || where[len] == ' ')) /* no partial matches */
	return rc;
      if (debug)
	printf("what = <%s>, where = <%s>\n",what,where);
      while (!isspace(*where) && *where != '\0') where++;
      if (*where == '\0')
	return 0;
    }
}

/* for now an "*" means match any number of tokens and a "$" means match one */
 /* assumes the matched part of the host & wildexpr have been removed */
/* can change logic so that if wild expr has a "*" return true otherwise the
token counds must be == */

int right_num_tokens(char *host, char *wildexpr)
{ 
char *cp;   
int hdots = 0, xdots = 0, xlen;

xlen = strlen(wildexpr);
if ((xlen == 1) && (*wildexpr == '*'))  /* single * matches anything */
  return TRUE;
cp = host;
while (cp = index(cp,'.'))	         /* count dots in hostname */
	  hdots++,cp++;
cp = wildexpr;
while (cp =  index(cp,'.'))	 /* count dots in wildexpr */
	  xdots++,cp++;
if (debug)
  printf("before case hdots = %d, xdots = %d\n",hdots,xdots);
switch (hdots) 
  {
  case 0:          /* either & or * will match 1 token */
    if (xlen == 1)
      return TRUE;
    else
      return FALSE;
  break;

  case 1:
    if (xdots == 1)
      return TRUE;
    else
      return FALSE;
  break;

  case 2:
    if (xdots == 2)
      return TRUE;
    else if (strchr(wildexpr,'*') && (xdots < 2))
      return TRUE;
    else 
      return FALSE;
  break;

  case 3:
    if (xdots == 3)
      return TRUE;
    else if (strchr(wildexpr,'*') && (xdots < 3))
      return TRUE;
    else 
      return FALSE;
  break;

  default:
    return FALSE; /* error , or maybe we have to do 1 more num */
  break;
  }

}

int hostMatches(char *host,char *realaddr, char *hostexr)
{        /* we are altering the strings here, either make copys to work with
	    or do it before calling */

char  *cp, *wildpart, *explpart,hostexpr[30],realhost[30];
int  len;

strcpy(hostexpr,hostexr);
strcpy(realhost,host);
cp = index(hostexpr,' ');  /* null terminate after 1st token */
if (cp)
  *cp = '\0';

if (debug)
  printf("checking hostexpr = <%s>, realhost = <%s>\n",hostexpr,realhost);
cp = hostexpr;
if (isdigit((int) *cp))  /* ip addr */
  {
    cp = hostexpr + strlen(hostexpr) - 1;
     if (*cp == '*' || *cp == '$')       /* wild card */
      {
	while ((cp >= hostexpr) && (*cp == '*' || *cp == '$' 
	     || *cp == '.' )) cp--;    /* move cp to something explicit */
	 cp++;                         /* move to dot */
	if ((cp) && (*cp == '.')) {    /* break into wildpart, explpart */
	  *cp = '\0';
	  cp++;
	  explpart = hostexpr;
	  wildpart = cp;
	}   
	else
	  return FALSE; 	/* bad expression */

	cp = strstr(realaddr,explpart); /* see if the expicit part matches
					   last part of real name */
if (debug)
  printf("explpart = <%s>\n",explpart);
	if (!cp)
	  return FALSE; 
	cp = realaddr + strlen(explpart);
/* need to find the correct place to break */ 

	*cp = '\0'; /* chop off the part of realaddr already matched */
	cp++;
	/* count tokens in realaddr not already matched */
if (debug)
  printf("realleft = <%s>, wildpart = <%s>\n",cp,wildpart);
	if (right_num_tokens(cp,wildpart))
	  return TRUE;
	else 
	  return FALSE;
      }
    else                       /* exact match */
      {
	if (!strcasecmp(realhost,hostexpr))
	  return TRUE;
	else
	  return FALSE;
      }

  }
else             /* hostname */
  {
    if (strcmp(hostname,"") == 0)
      return FALSE;
    if (*cp == '*' || *cp == '$')         /* wild card */
      {
	while (cp && (*cp == '*' || *cp == '$' 
	     || *cp == '.')) cp++;     /* move cp to something explicit */
	if (cp != hostexpr)             /* we moved */
	  cp--; 	                /* now move back to the dot */
	if (*cp == '.') { 	        /* break into wildpart, explpart */
	  *cp = '\0';
	  cp++;
	  explpart = cp;
	  wildpart = hostexpr;
	}  
	else
	  {
	    printf("cp = <%s>\n",cp);
	    return FALSE;  	/* bad expression */
	  }
	  
	cp = strstr(realhost,explpart); /* see if the expicit part matches
					   last part of real name */
if (debug)
  printf("explpart = <%s>\n",explpart);
	if (!cp)
	  return FALSE;
	cp--;
	if (*cp != '.')
	  return FALSE;  	/* bad expression */	  
	*cp = '\0';        /* chop off the part of realhost already matched */
	                   /* count tokens in realhost not already matched */
if (debug)
  printf("realleft = <%s>, wildpart = <%s>\n",realhost,wildpart);
	if (right_num_tokens(realhost,wildpart))
	  return TRUE;
	else 
	  return FALSE;
      }
    else                       /* exact match */
      {
	if (!strcasecmp(realhost,hostexpr))
	  return EXACT;
	else
	  return FALSE;
      }
  }
}
int in_Group(char *group) /* see if host is in group */
{
char *cp,thegroup[30];
int cnt = 0, except = FALSE,rc, negative = FALSE, positive = FALSE;
datum key,group_rec;
DBM *d;

  d = dbm_open("hostauth", O_RDONLY, 0644);
  if (!d)
    {
      fprintf(stderr, "Couldn't touch hostauth.dir (%d).\n", errno);
      exit(1);
    }

while (!isspace(*cp) && (*cp != '\0')) cnt++,cp++; 
strncpy(thegroup,group +1,++cnt); 
key.dptr = thegroup;
key.dsize = strlen(key.dptr) +1;
if (debug)
  printf("key = <%s>, size = %d\n",key.dptr,key.dsize);
group_rec = dbm_fetch(d,key);                  /* get group line from db */
if (!group_rec.dptr)           /* group not found  - XXX should be error */
  return FALSE;
if (debug)
  printf("the group <%s> = <%s>\n",key.dptr,group_rec.dptr);

/* for each token in group 
   see if its an exception
   then do a hostmatch */

cp = group_rec.dptr;
while (*cp != '\0') {
  while (isspace(*cp)) cp++;
  if (*cp == '-')
    {
      except = TRUE;
      cp++;
    }
  else
    except = FALSE;
  if (rc =hostMatches(hostname,hostaddr,cp))
    {
      dbm_close(d);
      printf("rc = %d,except = %d\n",rc,except);
/* if explicit return immediately else wait until all tokens are parsed */
      if ((except == TRUE) && (rc = EXPLICIT))
	return FALSE;
      else if (except == TRUE)
	negative = TRUE;      
      else if (rc == EXPLICIT)
	return EXPLICIT;
      else
        postive = TRUE;
    }
  while (!isspace(*cp) && (*cp != '\0')) cp++;
}
dbm_close(d);
if (negative) /* found an exception && no explicit positive */
  return FALSE;
if (positive)
  return TRUE;
return FALSE;
}

int userAuthorized(mb, pb)
     MasterBlock *mb;
     ParseBlock *pb;
{

  authinfo *info;
  char *ptr;
  int rc, result = 0;


  Parse_SetCurrentBlock(mb, pb);

  if (Parse_GetCurrentData(mb, USERAUTHINFO, (caddr_t *)&info, 0))
    return 1; /* XXX think a little more before giving access? */

  rc = checkList(user, info->s);
  if (rc == -1)  return 0; /* found a specific negative acl */
  if (rc == 1)   return 1; /* found a specific positive acl */

  ptr = userData.dptr;
  if (ptr == NULL)
    return 0;

  while (ptr - userData.dptr < userData.dsize)
    {
      rc = checkList(ptr, info->s);
      if (debug)
	printf("return from checklist = %d\n",rc);
      if (rc == -1)  return 0;      /* found a negative acl */
      if (rc == 1)   result = 1; /* found a positive acl, so keep looking */
      while (*ptr != '\0') ptr++;
      ptr++;
    }
  return result;
}

int hostAuthorized(mb, pb)
     MasterBlock *mb;
     ParseBlock *pb;
{
int result = 0,rc,negative = FALSE, positive = FALSE,negacl;
int positive_expl = FALSE;
authinfo *info;
char *cp,aclline[1024];
 
Parse_SetCurrentBlock(mb, pb);
if (Parse_GetCurrentData(mb, HOSTAUTHINFO, (caddr_t *)&info, 0))
  return 1; /* XXX think a little more before giving access? */

bcopy(info->s,aclline,info->size);

cp = aclline;
/* chk each token which is not a group  */
/* if exact match, or negative return   */

while (*cp != '\0') {
  while (isspace(*cp)) cp++;
  if (*cp == '-')
    {
      negacl = TRUE;
      cp++;
    }
  else
    negacl == FALSE;
  printf(" negacl = %d\n",negacl);

  if (*cp != '#')   /* if not a group */
    {
      if (rc = hostMatches(hostname,hostaddr,cp))
	{
/* if explicit return immediately else wait until all tokens are parsed */
	  if ((negacl) && (rc == EXPLICIT))
	    return FALSE; 
	  else if (negacl)
	    negative = TRUE;  /* XXX there might be an explicit positive */
	  else if (rc == EXPLICIT)
	    return TRUE;
	  else
	    positive = TRUE;
	}
    }
  while (!isspace(*cp) && (*cp != '\0')) cp++;
}

/* for each token which is a group */
bcopy(info->s,aclline,info->size);
cp = aclline;
while (*cp != '\0') {        

  while (isspace(*cp)) cp++;
  if (*cp == '-')
    {
      negacl = TRUE;
      cp++;
    }
  else
    negacl == FALSE;
  printf(" negacl = %d\n",negacl);
  if (*cp == '#')   /* if a group */
    {
      printf("processing a group acl\n");
      if (negacl || !positive) /* skip if you have positive already
				     and this is not a neg acl */
      {
	if (rc = in_Group(cp)) /* in group */
	  {
	    printf("rc = %d, negacl = %d\n",rc,negacl);
	    if ((negacl == TRUE) && (rc == EXPLICIT))
	      return FALSE;
	    else if (negacl == TRUE)
	      negative = TRUE;
	    else if (rc == EXPLICIT)
	      positive_expl = TRUE;    /* why not just return ??? 
					  in case of negative expl? */
	    else
	      positive = TRUE;
	  }
      }
    }
  while (!isspace(*cp) && (*cp != '\0')) cp++;
}
if (positive_expl)
  return TRUE;
if (negative)
  return FALSE;
return positive; /* either no positive found or positive found AND no neg */
}

/*
 * ERRORS:
 *
 * APP_USERAUTH: INFO
 * User authorization failed.
 *
 * APP_HOSTAUTH: INFO
 * Host authorization failed.
 */
Trap Authorized(mb, pb)
     MasterBlock *mb;
     ParseBlock *pb;
{

if (!userAuthorized(mb,pb))
  Return(APP_USERAUTH,NULL);

if (!hostAuthorized(mb,pb))
  Return(APP_HOSTAUTH,NULL);

return OK;
}

