#include #include #include #include #include /* #include "gaa.h" #include "gaa_simple.h" #include "gaa_util.h" #include "gaa_private.h" */ #include "cond_eval.h" //#include "host_parser.h" #define INADDR_NONE -1 typedef enum { HT_FAIL, HT_IP, HT_IPMASK, HT_IPCIDR, HT_HOSTNAME } host_type; typedef enum { HO_FAIL, HO_OR, HO_AND, HO_SUB, HO_NOT } host_operator; typedef enum { HA_ERROR=0, HA_ALLOW=1, HA_DENY=2 } host_answer; typedef struct { host_type type; char *from; unsigned long net; unsigned long mask; } hostset; typedef struct { host_operator op; hostset *oprand1; hostset *oprand2; } hostsets; #define MAX_TOKEN_LENGTH 50 #define MAX_ERROR_LENGTH 100 typedef struct { char *malloc_content; char *content; char *malloc_curtoken; char *curtoken; char *error; int branket_state; } host_parser; static int is_ip(const char *host) { while ((*host == '.') || isdigit(*host)) host++; return (*host == '\0'); } static int in_domain(const char *domain, const char *what) { int dl = strlen(domain); int wl = strlen(what); if ((wl - dl) >= 0) { if (strcasecmp(domain, &what[wl - dl]) != 0) return 0; /* Make sure we matched an *entire* subdomain --- if the user * said 'allow from good.com', we don't want people from nogood.com * to be able to get in. */ if (wl == dl) return 1; /* matched whole thing */ else return (domain[0] == '.' || what[wl - dl - 1] == '.'); } else return 0; } void host_initparser(host_parser *p, char *str) { p->malloc_content=(char *)malloc(strlen(str)*sizeof(char)+1); p->malloc_curtoken=(char *)malloc(MAX_TOKEN_LENGTH*sizeof(char)); p->error=(char *)malloc(MAX_ERROR_LENGTH*sizeof(char)); p->content=p->malloc_content; p->curtoken=0; strcpy(p->content,str); sprintf(p->error,"No Error"); p->branket_state=0; } void host_freeparser(host_parser *p) { free(p->malloc_content); free(p->malloc_curtoken); free(p->error); } void host_parsererror(host_parser *p, const char *fmt, /*args*/ ...) { va_list ap; va_start(ap, fmt); /* measure the required size */ vsprintf(p->error, fmt, ap); va_end(ap); } char *host_peektoken(host_parser *p) { if (p->branket_state==1) //left branket { p->curtoken=p->malloc_curtoken; *p->curtoken++='('; *p->curtoken='\0'; } else if (p->branket_state==2) //right branket { p->curtoken=p->malloc_curtoken; *p->curtoken++=')'; *p->curtoken='\0'; } else if (p->curtoken==0) { p->curtoken=p->malloc_curtoken; if (p->content==0 || *p->content=='\0') { *p->curtoken='\0'; return p->malloc_curtoken; } while (*p->content==' ') { p->content++; } if (*p->content=='(') { *p->curtoken++='('; *p->curtoken='\0'; p->content++; } else if (*p->content==')') { *p->curtoken++=')'; *p->curtoken='\0'; p->content++; } else { while (*p->content!=' ' && *p->content!='\0' && *p->content!=')' && *p->content!='(' && p->curtoken-p->malloc_curtokencurtoken++=*p->content++; } if (*p->curtoken=='(') { p->branket_state=1; } else if (*p->curtoken==')') { p->branket_state=2; } *p->curtoken='\0'; } } return p->malloc_curtoken; } char *host_gettoken(host_parser *p) { host_peektoken(p); p->curtoken=0; return p->malloc_curtoken; } /******************************************************************************* HOST_SET ::== host_ip | host_ipmask | host_ipcidr | host_hostname host_ip ::== dig "." [dig "." [dig "." [ dig ]]] host_ipmask ::== dig "." dig "." dig "." dig "/" dig "." dig "." dig "." dig host_ipcidr ::== dig "." dig "." dig "." dig "/" dig2 host_hostname ::== tok { "." tok } dig ::== 0..255 dig2 ::== 0..32 tok ::== {a..zA..Z0..9-} *******************************************************************************/ host_answer host_parse_set(host_parser *p, unsigned long ip, char* hostname) { host_type type; unsigned long net; unsigned long mask; char *nexttok; char *s; if ((nexttok=host_gettoken(p))==0) { host_parsererror(p,"token empty:ip"); return HA_ERROR; } if ((s = strchr(nexttok, '/'))) { /* trample on where, we won't be using it any more */ *s++ = '\0'; if (!is_ip(nexttok) || (net = inet_addr(nexttok)) == INADDR_NONE) { host_parsererror(p,"syntax error in network portion of network/netmask, net not valid"); return HA_ERROR; } /* is_ip just tests if it matches [\d.]+ */ if (!is_ip(s)) { host_parsererror(p,"syntax error in mask portion of network/netmask, mask not valid"); return HA_ERROR; } /* is it in /a.b.c.d form? */ if (strchr(s, '.')) { type = HT_IPMASK; mask = inet_addr(s); if (mask == INADDR_NONE) { host_parsererror(p,"syntax error in mask portion of network/netmask, mask /a.b.c.d not valid"); return HA_ERROR; } } else { /* assume it's in /nnn form */ type = HT_IPCIDR; mask = atoi(s); if (mask > 32 || mask <= 0) { host_parsererror(p,"syntax error in mask portion of network/netmask, mask /nnn not valid"); return HA_ERROR; } mask = 0xFFFFFFFFUL << (32 - mask); mask = htonl(mask); } net = net & mask; } else if (isdigit(*nexttok) && is_ip(nexttok)) { /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */ int shift; char *t; int octet; type = HT_IP; /* parse components */ s = nexttok; net = 0; mask = 0; shift = 24; while (*s) { t = s; if (!isdigit(*t)) { host_parsererror(p,"invalid ip address, not digit"); return HA_ERROR; } while (isdigit(*t)) { ++t; } if (*t == '.') { *t++ = 0; } else if (*t) { host_parsererror(p,"invalid ip address, not digit|."); return HA_ERROR; } if (shift < 0) { host_parsererror(p,"invalid ip address, only 4 octets allowed"); return HA_ERROR; } octet = atoi(s); if (octet < 0 || octet > 255) { host_parsererror(p,"each octet must be between 0 and 255 inclusive"); return HA_ERROR; } net |= octet << shift; mask |= 0xFFUL << shift; s = t; shift -= 8; } net = ntohl(net); mask = ntohl(mask); } else { type = HT_HOSTNAME; } if (type == HT_HOSTNAME) { return (in_domain(nexttok, hostname)) ? HA_ALLOW : HA_DENY ; } else if (type == HT_IP || type == HT_IPMASK || type == HT_IPCIDR ) { return ( (ip & mask ) == net ) ? HA_ALLOW : HA_DENY ; } return HA_ERROR; } /*************************************************** HOST_BRACKET::== "(" HOST_OR ")" | HOST_SET ***************************************************/ host_answer host_parse_or(host_parser*, unsigned long, char*); host_answer host_parse_bracket(host_parser *p, unsigned long ip, char* hostname) { char *nexttok; host_answer answer; nexttok=host_peektoken(p); if (strcmp(nexttok,"(")==0) { host_gettoken(p); //reject the token answer=host_parse_or(p,ip,hostname); if (answer==HA_ERROR) { return HA_ERROR; } nexttok=host_gettoken(p); if (strcmp(nexttok,")")!=0) { host_parsererror(p,"Grammer error: brackets not match"); return HA_ERROR; } return answer; } else { return host_parse_set(p,ip,hostname); } } /********************************************** HOST_NOT::== "NOT" HOST_NOT | HOST_BRACKET **********************************************/ host_answer host_parse_not(host_parser *p, unsigned long ip, char* hostname) { char *nexttok; host_answer answer; nexttok=host_peektoken(p); if (strcasecmp(nexttok,"not")==0) { host_gettoken(p); //reject the token answer=host_parse_not(p,ip,hostname); if (answer==HA_ERROR) { return HA_ERROR; } return (answer==HA_DENY) ? HA_ALLOW : HA_DENY; } else { return host_parse_bracket(p,ip,hostname); } } /****************************************************** HOST_SUB::== HOST_SUB "SUB" HOST_NOT | HOST_NOT --> HOST_SUB::== HOST_NOT HOST_SUB2 HOST_SUB2::== "SUB" HOST_NOT HOST_SUB2 | eps ******************************************************/ host_answer host_parse_sub2(host_parser *p, unsigned long ip, char* hostname, host_answer answer1) { host_answer answer2; char *nexttok; nexttok=host_peektoken(p); if (strcasecmp(nexttok,"sub")==0) { host_gettoken(p); //reject the token answer2=host_parse_not(p,ip,hostname); if (answer2==HA_ERROR) { return HA_ERROR; } return host_parse_sub2(p,ip,hostname, (answer1==HA_ALLOW && answer2==HA_DENY) ? HA_ALLOW : HA_DENY); } else { return answer1; } } host_answer host_parse_sub(host_parser *p, unsigned long ip, char* hostname) { host_answer answer; answer=host_parse_not(p,ip,hostname); if (answer==HA_ERROR) { return HA_ERROR; } return host_parse_sub2(p,ip,hostname,answer); } /****************************************************** HOST_AND::==HOST_SUB "AND" HOST_AND ******************************************************/ host_answer host_parse_and(host_parser *p, unsigned long ip, char* hostname) { host_answer answer1,answer2; char *nexttok; answer1=host_parse_sub(p,ip,hostname); if (answer1==HA_ERROR) { return HA_ERROR; } nexttok=host_peektoken(p); if (strcasecmp(nexttok,"and")==0) { host_gettoken(p); //reject the token answer2=host_parse_and(p,ip,hostname); if (answer2==HA_ERROR) { return HA_ERROR; } return (answer1==HA_ALLOW && answer2==HA_ALLOW) ? HA_ALLOW : HA_DENY; } else { return answer1; } } /****************************************************** HOST_OR::==HOST_AND "AND" HOST_OR ******************************************************/ host_answer host_parse_or(host_parser *p, unsigned long ip, char* hostname) { host_answer answer1,answer2; char *nexttok; answer1=host_parse_and(p,ip,hostname); if (answer1==HA_ERROR) { return HA_ERROR; } nexttok=host_peektoken(p); if (strcasecmp(nexttok,"or")==0) { host_gettoken(p); //reject the token answer2=host_parse_or(p,ip,hostname); if (answer2==HA_ERROR) { return HA_ERROR; } return (answer1==HA_ALLOW || answer2==HA_ALLOW) ? HA_ALLOW : HA_DENY; } else { return answer1; } } /****************************************************** HOST::==HOST_OR ******************************************************/ host_answer host_do_parse(host_parser *p, unsigned long ip, char* hostname) { host_answer answer; char *nexttok; answer=host_parse_or(p,ip,hostname); if (answer==HA_ERROR) { return HA_ERROR; } nexttok=host_peektoken(p); if (*nexttok!='\0') { //there's other string that can not be evaluated host_parsererror(p,"Unexpected tokens at the end"); return HA_ERROR; } return answer; } typedef struct { unsigned long ip; char *hostname; } host_info; gaa_status cb_check_cond_access_host (gaa_ptr gaa, gaa_sc_ptr sc, gaa_condition *cond, gaa_time_period *valid_time, gaa_request_right_ptr right, gaa_status rstatus, gaa_string_data estatus, gaa_status *output_flags, void *params) { host_parser p; host_answer answer; gaa_status ret=GAA_S_SUCCESS; int gothost = 0; unsigned long remoteip; char *remotehost; host_info *hi; hi=(host_info *)get_request_option(right,"host",cond->authority); if (hi==0) { return GAA_S_INVALID_ARG; } if (hi->hostname==0) { hi->hostname==""; } host_initparser(&p, cond->value); answer=host_do_parse(&p, hi->ip, hi->hostname); *output_flags |= GAA_COND_FLG_EVALUATED; if (answer==HA_ERROR) { //LOG2("Host Parser Report Error: %s",p.error); ret=GAA_S_POLICY_PARSING_FAILURE; } else if (answer==HA_ALLOW) { *output_flags |= GAA_COND_FLG_MET; } else //TA_DENY { //none } host_freeparser(&p); return ret; }