/*
 * Copyright (c) 2000-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/*
 * OpenFWTK project generation 2 API configuration functions
 *
 * (C) Copyright 2001,2002 ArkanoiD <ark@eltex.ru>
 * (C) Copyright 2000 Eberhard Mattes <em-gw@windhager.de>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include "firewall.h"
#include "firewall2.h"
#include "fwfunc.h"
#include "auth.h"

static char* moduleId ATTR_UNUSED = "$Id: cfg2.c,v 1.21 2007/09/10 02:49:13 arkenoi Exp $";

int auth_perm(Cfg*,char*,char*,char*,char**);

/*
 *	proxy_conf_* functions are taken from Eberhard Mattes' libemfw
 *	function names are changed to avoid conflicts because functions
 *	are not always exactly same. Say, config_uid etc do not have
 *	things required by other libem functions like fastdaemon.
 */

/* 	Copyright 2000 by Eberhard Mattes <em-gw@windhager.de>
   	Donated to the public domain.  No warranty.

 */

void
proxy_conf_arg_count (Cfg *cf, int n)
{
  if (cf->argc == n)
    return;
  syslog (LLEV, "fwtkcfgerr: %s must have %d parameter%s, line %d",
	  cf->op, n, (n == 1 ? "" : "s"), cf->ln);
  exit (1);
}

int 
proxy_conf_int (Cfg *confp, const char *name, int lo, int hi, int def)
{
  char *end;
  Cfg *cf;
  long n;

  cf = cfg_get (name, confp);
  if (cf == (Cfg *)0)
    return def;
  proxy_conf_arg_count (cf, 1);
  errno = 0;
  n = strtol (cf->argv[0], &end, 10);
  if (end == cf->argv[0] || *end != 0 || errno != 0 || n < lo || n > hi)
    {
      syslog (LLEV, "fwtkcfgerr: bad value for %s, line %d",
	      name, cf->ln);
      exit (1);
    }
  return (int)n;
}

char*
proxy_conf_string (Cfg *confp, const char *name)
{
  Cfg *cf = cfg_get (name, confp);
  if (cf == (Cfg *)0)
    return NULL;
  proxy_conf_arg_count (cf, 1);
  return cf->argv[0];
}

int 
proxy_conf_userid (Cfg *confp)
{
  Cfg *cf;
  int uid;

  if ((cf = cfg_get ("userid", confp)) == (Cfg *)0) 
    return -1;
  proxy_conf_arg_count (cf, 1);
  if ((uid = mapuid (cf->argv[0])) == -1)
    {
      syslog (LLEV, "fwtkcfgerr: cannot map %s to uid", cf->argv[0]);
      exit (1);
    }
  proxy_uid = uid;	
  return uid;
}

int 
proxy_conf_groupid (Cfg *confp)
{
  Cfg *cf;
  int gid;

  if ((cf = cfg_get ("groupid", confp)) == (Cfg *)0)
    return -1;
  proxy_conf_arg_count (cf, 1);
  if ((gid = mapgid (cf->argv[0])) == -1)
    {
      syslog (LLEV, "fwtkcfgerr: cannot map %s to gid", cf->argv[0]);
      exit(1);
    }
  proxy_gid = gid;	
  return gid;
}

char*
proxy_conf_chroot (Cfg *confp)
{
  char *s;
  if ((s = proxy_conf_string(proxy_confp,"directory")))
     strlcpy(proxy_chroot,s,sizeof(proxy_chroot));
  return s;
}

int 
proxy_conf_timeout (Cfg *confp)
{
  return(proxy_timeout = proxy_conf_int(confp,"timeout",1,1440,PROXY_TIMEOUT));
}


Cfg* 
proxy_conf_hosts (Cfg *confp, char *rladdr, char *riaddr)
{
  Cfg *cf = cfg_get ("hosts", confp);
  int i;

  while (cf != (Cfg *)0)
    {
      for (i = 0; i < cf->argc && cf->argv[i][0] != '-'; ++i)
	{
	  if (hostmatch (cf->argv[i], riaddr))
	    {
	      if (cf->flags & PERM_DENY)
		goto deny;
	      syslog (LLEV, "permit host=%s/%s use of gateway",
		      rladdr, riaddr);
	      return cf;
	    }
	}
      cf = cfg_get ("hosts", (Cfg *)0);
    }
deny:
  syslog (LLEV, "deny host=%s/%s use of gateway", rladdr, riaddr);
  return (Cfg *)0;
}

int
proxy_conf_diffserv_codepoint(Cfg *confp, char* policy)
{
	char *p;
	long l;
	if (((l=strtol(policy,&p,0)) >= 0) && (l<=0xff) && (!(*p))) 
		return((int)l);
	else {
		Cfg* cfp;
		cfp = cfg_get("diffserv-codepoint",confp);
		while (cfp) {
			if((cfp->argc<1)||(cfp->argv[0][0]=='-')) {
				syslog(LLEV,"fwtkcfgerr: diffserv-codepoint needs an argument, line %d",cfp->ln);
				exit(1);
			} 
			if (!(strcmp(cfp->argv[0],policy))) {
				if (((l=strtol(cfp->argv[1],&p,0)) >= 0) && (l<=0xff) && (!(*p)))  
					return((int)l); 
				else {
					syslog(LLEV,"fwtkcfgerr: invalid diffserv-codepoint value, line %d",cfp->ln);
					exit(1);
				}
			} 

			cfp = cfg_get("diffserv-codepoint",NULL);
		}
		syslog(LLEV,"fwtkcfgerr: invalid diffserv-codepoint %.128s",policy);
		exit(1);
	}
}

int
proxy_check_dest(list,xtnd)
char	**list;
int	xtnd;
{
	if(searchlisth(proxy_stats.dst,list)) {
		syslog(LLEV,"deny host %.256s/%.20s connect to %.256s",
		       proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst);
		return(1);
	}
	if (xtnd) {
		switch (auth_perm(proxy_confp,proxy_stats.authuser,proxy_name,
				  proxy_stats.dst,NULL)) {
		case -1:
			syslog(LLEV,"no match for %.256s user=%.100s in netperm-table",proxy_name,proxy_stats.authuser);
			return(1);
		case 1:
			syslog(LLEV,"deny host=%.512s/%.20s connect to %.512s user=%.100s",proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst,proxy_stats.authuser);
			return(1);
		}
	}
	syslog(LLEV,"permit host=%.512s/%.20s connect to %.512s:%d",
	       proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst,
	       proxy_stats.port);
	return(0);
}

					

void
proxy_parse_options(Cfg *cf,fwparm options[])
{
	int	x;
	fwparm*	option;

	for (x=1; x < cf->argc; x++) {

		if (cf->argv[x][0] != '-')
			continue;

		for (option = options; option->type ; option++) {
			if (!strcmp(cf->argv[x],option->name)) {
				switch (option->type) {
				case FWPARM_BOOL:
					*((int*)(option->value)) = (int) 1;
					break;
				case FWPARM_INT:
					if (++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: %s needs an argument, line %d",option->name,cf->ln);
						exit(1);
					}
					*((int*) option->value) = atoi(cf->argv[x]);
					break;
				case FWPARM_STRING:
					if (++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: %s needs an argument, line %d",option->name,cf->ln);
						exit(1);
					}
					strlcpy((char*)option->value,cf->argv[x],MAX_STR);
					break;
				case FWPARM_CHAR:
					if (++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: %s needs an argument, line %d",option->name,cf->ln);
						exit(1);
					}
					if (strlen(cf->argv[x]) == 1)
						*((char*)option->value) = cf->argv[x][0];
					else {
						syslog(LLEV,"fwtkcfgerr: %s can be one character only, line %d",option->name,cf->ln);
						exit(1);
					}
					break;
				case FWPARM_LIST:
					if(++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: malformed line %d: missing option",cf->ln);
						exit(1);
					}
					freelist((char***) option->value);
					if(!strcmp(cf->argv[x],"{")) {
						while(1) {
							if(++x >= cf->argc) {
								syslog(LLEV,"fwtkcfgerr: malformed line %d: missing option",cf->ln);
								exit(1);
							}
							if(!strcmp(cf->argv[x],"}"))
								break;
							addlist(cf->argv[x],(char***) option->value);
						}
					} else 
						addlist(cf->argv[x],(char***) option->value);
					break;
				case FWPARM_PORT:
					if (++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: %s needs an argument, line %d",option->name,cf->ln);
						exit(1);
					}
					*((int*) option->value)  = str_to_port(cf->argv[x]);
					break;
				case FWPARM_DSCP:
					if (++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: %s needs an argument, line %d",option->name,cf->ln);
						exit(1);
					} 
					*((int*) option->value) = (int) proxy_conf_diffserv_codepoint(proxy_confp,cf->argv[x]);
					break;
				case FWPARM_OPPERMIT:
				case FWPARM_OPLOG:
				case FWPARM_OPDENY:
				case FWPARM_OPAUTH:
				case FWPARM_OPXTND:
					if(++x >= cf->argc) {
						syslog(LLEV,"fwtkcfgerr: malformed line %d: missing option",cf->ln);
						exit(1);
					}
					if(!strcmp(cf->argv[x],"{")) {
						while(1) {
							int flag;
							struct hash_entry *he;
							if(++x >= cf->argc) {
								syslog(LLEV,"fwtkcfgerr: malformed line %d: missing option",cf->ln);
								exit(1);
							}
							if(!strcmp(cf->argv[x],"}"))
								break;
							flag = 077 & option->type;
							if ((he=find_vhe2((struct hash_descr*)
									  option->value,cf->argv[x],
									  strlen(cf->argv[x]))))
								he->flags = flag ? he->flags | flag : 0;
							else {
								syslog(LLEV,"fwtkcfgerr: unknown operation %s,line %d",
								       cf->argv[x],cf->ln);
								exit(1);
							}
						}
						break;
					} else {
/*	ugly	*/
						int flag;
						struct hash_entry *he;
						flag = 077 & option->type;
						if ((he=find_vhe2((struct hash_descr*)
								  option->value,cf->argv[x],
								  strlen(cf->argv[x]))))
							he->flags = flag ? he->flags | flag : 0;
						else {
							syslog(LLEV,"fwtkcfgerr: unknown operation %s,line %d",
							       cf->argv[x],cf->ln);
							exit(1);
						}
						break;
					}
				default:
					syslog(LLEV,"fwtkcfgerr: unknown parameter type");
					exit(1);
				}
				break;
			}
		}
		if (!option->type) {
			syslog(LLEV,"fwtkcfgerr: unknown option %s,line %d", cf->argv[x],cf->ln);
			exit(1);
		}
	}
}


