/* * Copyright (c) 2001 Jean-Baptiste Marchand, Hervé Schauer Consultants. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jean-Baptiste Marchand * at Hervé Schauer Consultants. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * * Rules parsing code * */ #define MAXLEN 64 #include #include #include #include #include #include #include #include "my_fltdefs.h" #include "pktflt.h" #include "filters.h" #include "rules_status.h" /* parsing variables */ char *rule_start = NULL; unsigned short length; /* length to the end of the rule */ char *rule; /* position pointer in the current rule */ char *word = NULL; unsigned short word_length; int trackMeSymbol = 0; extern char *icmp_type_keyword[]; extern struct pf_interface *interfaces; /* macros */ #define IS_SPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n')) #define EXIT_ERROR \ { \ /* syntax error */ \ *parse_error = ((rule - word_length) - rule_start); \ return PF_RULE_ERR; \ } \ #define CMP_ERRORS(res) \ if (res < - 1) { \ /* missing keyword space */ \ *parse_error = (((rule + (res)) + 1) - rule_start); \ return PF_RULE_ERR; \ } \ #define STRNCMP(str, len) \ cmp = my_strncmp((str), (len)); \ CMP_ERRORS(cmp) \ void skip_space(void) { while (length > 0 && IS_SPACE(*rule)) { rule++; length--; } } void next_word(void) { char *start; skip_space(); start = rule; word_length = 0; while (length > 0 && (!IS_SPACE(*rule))) { rule++; length--; word_length++; } if (word) { free(word); /* free previous current word */ word = NULL; } if (word_length) { word = (char *) malloc ((word_length + 1) * sizeof(char)); memset(word, 0, word_length + 1); strncpy(word, start, word_length); } } char my_strncmp(unsigned char *str, unsigned short cmp_len) { char res; if ((cmp_len > word_length) && (length == 0)) return -1; /* rule too short */ res = strncmp(word, str, cmp_len); if (res == -1 || res == 1) return 1; /* strings differ */ if ((res == 0) && (word_length > cmp_len)) return -( (word_length - cmp_len) + 1); /* missing space */ return res; } unsigned short is_number(char *number) { long value = 0; char *first_error; errno = 0; value = strtol(number, &first_error, 10); if (number[0] == '\0' || *first_error != '\0') errno = EINVAL; if (errno == ERANGE) errno = EINVAL; if (value < 0) /* no negative value */ errno = EINVAL; return (unsigned short) (value); } char get_ip_mask(unsigned long *src_addr, unsigned long *src_mask, char *dnslog) { unsigned char dotted_form = 0; /* TRUE if the mask is x.y.z.t form */ unsigned char addr_length = 0; unsigned char mask; char *addr; char *my_word; unsigned short my_word_length; //**&& added BB on 2007.04.29 char hostname[MAXLEN]; WSADATA wsadata; char localIP[15]; int ret; FILE * myLog; char * my_word_fixed; PDNS_RECORD testout = 0; IP4_ADDRESS midIP = 0; int firstq = 1; int cond = 0; int lcount = 0; size_t hname_len = 0; int err; //**&& end BB add my_word = word; my_word_length = word_length; while ((!IS_SPACE(*my_word)) && (*my_word != '/') && (*my_word != '[') && strncmp(my_word,"me",2)!=0 && (my_word_length > 0)) { my_word++; my_word_length--; addr_length++; } if (strncmp(my_word,"me",2)==0) { //**&& BB - find localhost ip address, prompted by "me" symbol err = WSAStartup(0x0101, &wsadata); strncpy(localIP,"-",15); if(err == 0) { if ((ret = gethostname(hostname, MAXLEN)) == 0) { if(GetIpAddress (hostname, localIP)) { *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr(localIP); } else { myLog = fopen(dnslog,"a"); fprintf(myLog,"\n > local 'me' symbol FAILED : ( %-16s : %s )", localIP, hostname); fclose(myLog); *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr("127.0.0.1"); return -1; } } WSACleanup(); } if(strncmp(localIP,"-",15)==0) { *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr("127.0.0.1"); return -1; } *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr(localIP); if(trackMeSymbol==0 && dnslog != NULL) { myLog = fopen(dnslog,"a"); fprintf(myLog,"\n > local 'me' symbol resolved : ( %-16s : %s )", localIP, hostname); fclose(myLog); trackMeSymbol=1; } } else if (*my_word =='[') { //**&& BB - find remote host ip address, prompted by "[" symbol if(strlen(my_word) > MAXLEN) hname_len = MAXLEN; else hname_len = strlen(my_word); my_word_fixed = (char *) malloc ((hname_len + 1) * sizeof(char)); memset(my_word_fixed, 0, hname_len + 1); strncpy(my_word_fixed, my_word+1, hname_len-2); err = WSAStartup(0x0101, &wsadata); strncpy(localIP,"-",15); if(err == 0) { if(strlen(my_word_fixed) > 0 && strlen(my_word_fixed) <= MAXLEN) if(GetIpAddress (my_word_fixed, localIP)) { *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr(localIP); } else { myLog = fopen( dnslog,"a"); fprintf(myLog,"\n > Remote DNS lookup FAILED : ( %-16s : %s )", localIP, my_word_fixed); fclose(myLog); *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr("127.0.0.1"); return -1; } WSACleanup(); } if(strncmp(localIP,"-",15)==0) { *src_mask = inet_addr("255.255.255.255"); *src_addr = inet_addr("127.0.0.1"); return -1; } if(dnslog != NULL) { myLog = fopen( dnslog,"a"); fprintf(myLog,"\n > Remote DNS lookup resolved : ( %-16s : %s )", localIP, my_word_fixed); fclose(myLog); } } else { if (!addr_length) return -1; /* no IP address */ addr = malloc((addr_length + 1)* sizeof(char)); memset(addr, 0, addr_length + 1); memcpy(addr, my_word - addr_length, addr_length); *src_addr = inet_addr(addr); if (*src_addr == INADDR_NONE) return -1; /* bad IP address */ free(addr); addr_length = 0; if (*my_word == '/') { while ((!IS_SPACE((*my_word)) && (my_word_length > 0))) { if (*my_word == '.') dotted_form = 1; my_word++; my_word_length--; addr_length++; } if (dotted_form) { /* mask is given as x.y.z.t form */ addr = malloc(addr_length * sizeof(char)); memset(addr, 0, addr_length); memcpy(addr, (my_word + 1) - addr_length, addr_length - 1); *src_mask = inet_addr(addr); if (*src_mask == INADDR_NONE) return -1; /* bad IP mask */ free(addr); } else { mask = atoi((my_word + 1)- addr_length); if (mask < 1 && mask > 32) return -1; /* mask can be /1 to /32 */ *src_mask = htonl(INADDR_NONE << (32 - mask)); } } else { /* no explicit mask, assuming /32 */ *src_mask = inet_addr("255.255.255.255"); if (*src_mask == INADDR_NONE) return -1; /* should not happen */ } } /* normalize IP address */ *src_addr = *src_addr & *src_mask; return 1; } //**&& BB added 2007.04.29 get IP address of local host machine //**&& originally used gethostname, now using geraddrinfo BOOL GetIpAddress(char *hostname, char* retVal) { int error; struct addrinfo *result; char * tempval; struct sockaddr_in dest; error = getaddrinfo(hostname, NULL, NULL, &result); //**&& future: more checking below, and possibly consider the other returned addr records? if(WSAGetLastError()==0 && error==0) if(result->ai_family == AF_INET) strncpy(retVal, inet_ntoa((*(struct sockaddr_in*)result->ai_addr).sin_addr),15); else return 0; else return 0; return 1; } /* parse a filtering rule pointed by the 'the_rule' parameter and convert the filter to its equivalent in a 'struct pf_filter' structure, pointed by the 'pf_filter' parameter. return value is one of the PF_RULE_xxx value. In the case of PF_RULE_ERR, the position of the syntax error is written at the location of the 'parse_error' pointer */ char parse_rule(char *the_rule, struct pf_filter *pf_filter, unsigned char *parse_error, char *dnslog) { char src_any_flag = 0; char dst_any_flag = 0; char cmp; /* result of strings comparison */ unsigned short proto; /* IP proto number */ unsigned short port; /* TCP|UDP port number */ unsigned char icmp_type; /* ICMP type */ unsigned char icmp_code; /* ICMP code */ unsigned short index; /* interface index */ unsigned long *src_ip_addr; unsigned long *src_ip_mask; unsigned long *dst_ip_addr; unsigned long *dst_ip_mask; GLOBAL_FILTER small_frags = 0; #if 0 GLOBAL_FILTER strong_host = 0; #endif /* GF_STRONGHOST */ #if 0 GLOBAL_FILTER check_frags = 0; #endif /* GF_FRAGCACHE */ char all_ifs = 0; char ask_delete = 0; char ask_brief_stats = 0; char ask_detailed_stats = 0; char ask_rules = 0; char ask_numbered_rules = 0; char ask_flush = 0; char create_flag = 1; //trackMeSymbol = 0; /* initialize global variables */ rule_start = the_rule; rule = rule_start; length = strlen(the_rule); /* initialize the filter */ pf_filter->next = NULL; /* filter is added at the end of the list */ pf_filter->filter_flags = 0; pf_filter->src_flags = 0; pf_filter->dst_flags = 0; pf_filter->filter->dwFilterFlags = 0; /* no FD_FLAGS_NOSYN */ pf_filter->filter->dwRule = -1; /* set when the filter is activated */ pf_filter->filter->pfatType = PF_IPV4; /* only IPv4 is supported */ pf_filter->filter->wSrcPort = 0; /* from any port */ pf_filter->filter->wDstPort = 0; /* to any port */ pf_filter->filter->dwProtocol = FILTER_PROTO_ANY; pf_filter->filter->fLateBound = 0; /* no late bounding */ /* allocate dynamic variables */ src_ip_addr = (unsigned long *) malloc(1 * sizeof(unsigned long)); src_ip_mask = (unsigned long *) malloc(1 * sizeof(unsigned long)); dst_ip_addr = (unsigned long *) malloc(1 * sizeof(unsigned long)); dst_ip_mask = (unsigned long *) malloc(1 * sizeof(unsigned long)); /* go ! */ next_word(); if (length == 0) /* empty line */ return PF_RULE_INVAL; STRNCMP("option", 6); if (cmp == 0) { next_word(); STRNCMP("small_frags", 11); if (cmp == 0) { small_frags = GF_FRAGMENTS; goto ifname; } #if 0 STRNCMP("strong_host", 10); if (cmp == 0) { strong_host = GF_STRONGHOST; goto ifname; } #endif /* GF_STRONGHOST */ #if 0 STRNCMP("check_frags", 11); if (cmp == 0) { check_frags = GF_FRAGCACHE; goto ifname; } #endif /* GF_FRAGCACHE */ EXIT_ERROR; } /* delete rule */ STRNCMP("delete", 6); if (cmp == 0) { next_word(); if (!word_length) EXIT_ERROR; /* keep the rule number in filter_flags field */ pf_filter->filter_flags = (unsigned char) is_number(word); if (errno == EINVAL) /* invalid rule number */ EXIT_ERROR; ask_delete = 1; goto ifname; } /* brief stats */ STRNCMP("stats", 5); if (cmp == 0) { ask_brief_stats = 1; goto ifname; } /* detailed stats */ STRNCMP("Stats", 5); if (cmp == 0) { ask_detailed_stats = 1; goto ifname; } /* list */ STRNCMP("list", 4); if (cmp == 0) { ask_rules = 1; goto ifname; } /* list with rules numbers */ STRNCMP("List", 4); if (cmp == 0) { ask_numbered_rules = 1; goto ifname; } /* flush */ STRNCMP("flush", 5); if (cmp == 0) { ask_flush = 1; goto ifname; } /* pass | block */ STRNCMP("pass", 4); if (cmp == 0) pf_filter->filter_flags |= FILTER_PASS; else { STRNCMP("block", 5); if (cmp == 0) pf_filter->filter_flags |= FILTER_DROP; else EXIT_ERROR; } /* in | out */ next_word(); STRNCMP("in", 2); if (cmp == 0) pf_filter->filter_flags |= FILTER_IN; else { STRNCMP("out", 3); if (cmp == 0) pf_filter->filter_flags |= FILTER_OUT; else EXIT_ERROR; } ifname: /* on ifname */ next_word(); STRNCMP("on", 2); if (cmp != 0) EXIT_ERROR; next_word(); if (!word) EXIT_ERROR; /* either "all" or "{eth,ppp,sl,lo,tr,fd,if}*" */ if (strncmp(word, "all", 3) == 0) { if (ask_flush) all_ifs = 1; else EXIT_ERROR; } else { errno = EINVAL; if (!strncmp(word, "eth", 3) || !strncmp(word, "ppp", 3)) { index = is_number(word + 3); } if (!strncmp(word, "sl", 2) || !strncmp(word, "lo", 2) || !strncmp(word, "tr", 2) || !strncmp(word, "fd", 2) || !strncmp(word, "if", 2)) { index = is_number(word + 2); } if (errno == EINVAL) { /* invalid interface number */ EXIT_ERROR; } } if (ask_delete || ask_brief_stats || ask_detailed_stats || ask_rules || ask_numbered_rules || ask_flush) /* don't want to create interface in these cases */ create_flag = 0; if (all_ifs) pf_filter->iface = (struct pf_interface *) &interfaces; /* pointer to the head of the list */ else pf_filter->iface = get_interface(index, create_flag); if (small_frags /* || strong_host || check_frags */ ) { /* global filter */ pf_filter->iface->small_frags |= small_frags; #if 0 pf_filter->iface->strong_host |= strong_host; #endif /* GF_STRONGHOST */ #if 0 pf_filter->iface->check_frags |= check_frags; #endif /* GF_FRAGCACHE */ return PF_RULE_GF; } if (ask_delete) /* delete rule */ return PF_RULE_DELETE; if (ask_brief_stats) { /* brief statistics */ return PF_RULE_BRIEF_STATS; } if (ask_detailed_stats) { /* detailed statistics */ return PF_RULE_DETAILED_STATS; } if (ask_rules) { /* list rules */ return PF_RULE_LIST; } if (ask_numbered_rules) { /* list rules with associated numbers */ return PF_NUMBERED_RULE_LIST; } if (ask_flush) { /* flush rules */ return PF_RULE_FLUSH; } /* proto tcp | udp | icmp | any */ next_word(); STRNCMP("proto", 5); if (cmp != 0) goto srcdst; /* any protocols is the default */ next_word(); STRNCMP("tcp", 3); if (cmp == 0) { pf_filter->filter->dwProtocol = FILTER_PROTO_TCP; pf_filter->filter->wSrcPort = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wSrcPortHighRange = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wDstPort = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wDstPortHighRange = FILTER_TCPUDP_PORT_ANY; next_word(); goto srcdst; } STRNCMP("udp", 3); if (cmp == 0) { pf_filter->filter->dwProtocol = FILTER_PROTO_UDP; pf_filter->filter->wSrcPort = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wSrcPortHighRange = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wDstPort = FILTER_TCPUDP_PORT_ANY; pf_filter->filter->wDstPortHighRange = FILTER_TCPUDP_PORT_ANY; next_word(); goto srcdst; } STRNCMP("icmp", 4); if (cmp == 0) { pf_filter->filter->dwProtocol = FILTER_PROTO_ICMP; pf_filter->filter->wSrcPort = FILTER_ICMP_TYPE_ANY; pf_filter->filter->wDstPort = FILTER_ICMP_CODE_ANY; next_word(); goto srcdst; } STRNCMP("any", 3); if (cmp == 0) { pf_filter->filter->dwProtocol = FILTER_PROTO_ANY; next_word(); goto srcdst; } proto = is_number(word); if (errno == EINVAL) /* invalid number */ EXIT_ERROR; if (proto) { pf_filter->filter->dwProtocol = FILTER_PROTO(proto); next_word(); goto srcdst; } srcdst: /* all */ STRNCMP("all", 3); if (cmp == 0) { /* equivalent of 'from any to any' */ src_any_flag = 1; dst_any_flag = 1; pf_filter->filter->SrcAddr = ANY_IP_ADDR; pf_filter->filter->SrcMask = ANY_MASK_ADDR; pf_filter->filter->DstAddr = ANY_IP_ADDR; pf_filter->filter->DstMask = ANY_MASK_ADDR; goto established; } /* from */ STRNCMP("from", 4); if (cmp != 0) EXIT_ERROR; next_word(); STRNCMP("any", 3); if (cmp == 0) { src_any_flag = 1; pf_filter->filter->SrcAddr = ANY_IP_ADDR; pf_filter->filter->SrcMask = ANY_MASK_ADDR; } else { if (!get_ip_mask(src_ip_addr, src_ip_mask, dnslog)) EXIT_ERROR; pf_filter->filter->SrcAddr = (PBYTE) src_ip_addr; pf_filter->filter->SrcMask = (PBYTE) src_ip_mask; } /* src port */ if (pf_filter->filter->dwProtocol == FILTER_PROTO_TCP || pf_filter->filter->dwProtocol == FILTER_PROTO_UDP) { next_word(); STRNCMP("port", 4); if (cmp != 0) goto to; next_word(); STRNCMP("=", 1); if (cmp) STRNCMP("eq", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = port; pf_filter->filter->wSrcPortHighRange = port; pf_filter->src_flags |= TCP_UDP_EQ_PORT; next_word(); goto to; } STRNCMP(">=", 2); if (cmp) STRNCMP("ge", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = port; pf_filter->filter->wSrcPortHighRange = TCP_UDP_HIGHEST_PORT; pf_filter->src_flags |= TCP_UDP_GE_PORT; next_word(); goto to; } STRNCMP("<=", 2); if (cmp) STRNCMP("le", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = TCP_UDP_LOWEST_PORT; pf_filter->filter->wSrcPortHighRange = port; pf_filter->src_flags |= TCP_UDP_LE_PORT; next_word(); goto to; } STRNCMP(">", 1); if (cmp) STRNCMP("gt", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = port + 1; pf_filter->filter->wSrcPortHighRange = TCP_UDP_HIGHEST_PORT; pf_filter->src_flags |= TCP_UDP_GT_PORT; next_word(); goto to; } STRNCMP("<", 1); if (cmp) STRNCMP("lt", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = TCP_UDP_LOWEST_PORT; pf_filter->filter->wSrcPortHighRange = port - 1; pf_filter->src_flags |= TCP_UDP_LT_PORT; next_word(); goto to; } /* ports interval */ port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wSrcPort = port + 1; next_word(); STRNCMP("><", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; if (port < pf_filter->filter->wSrcPort) /* upper port is lower than lower port */ EXIT_ERROR; pf_filter->filter->wSrcPortHighRange = port - 1; pf_filter->src_flags |= TCP_UDP_INTERVAL_PORT; next_word(); } } else next_word(); to: /* to */ STRNCMP("to", 2); if (cmp != 0) EXIT_ERROR; next_word(); STRNCMP("any", 3); if (cmp == 0) { dst_any_flag = 1; pf_filter->filter->DstAddr = ANY_IP_ADDR; pf_filter->filter->DstMask = ANY_MASK_ADDR; } else { if (!get_ip_mask(dst_ip_addr, dst_ip_mask, dnslog)) EXIT_ERROR; pf_filter->filter->DstAddr = (PBYTE) dst_ip_addr; pf_filter->filter->DstMask = (PBYTE) dst_ip_mask; } if (pf_filter->filter->dwProtocol == FILTER_PROTO_ANY) goto end; /* dst port */ if (pf_filter->filter->dwProtocol == FILTER_PROTO_TCP || pf_filter->filter->dwProtocol == FILTER_PROTO_UDP) { next_word(); STRNCMP("port", 4); if (cmp != 0) goto established; next_word(); STRNCMP("=", 1); if (cmp) STRNCMP("eq", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = port; pf_filter->filter->wDstPortHighRange = port; pf_filter->dst_flags |= TCP_UDP_EQ_PORT; next_word(); goto established; } STRNCMP(">=", 2); if (cmp) STRNCMP("ge", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = port; pf_filter->filter->wDstPortHighRange = TCP_UDP_HIGHEST_PORT; pf_filter->dst_flags |= TCP_UDP_GE_PORT; next_word(); goto established; } STRNCMP("<=", 2); if (cmp) STRNCMP("le", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = TCP_UDP_LOWEST_PORT; pf_filter->filter->wDstPortHighRange = port; pf_filter->dst_flags |= TCP_UDP_LE_PORT; next_word(); goto established; } STRNCMP(">", 1); if (cmp) STRNCMP("gt", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = port + 1; pf_filter->filter->wDstPortHighRange = TCP_UDP_HIGHEST_PORT; pf_filter->dst_flags |= TCP_UDP_GT_PORT; next_word(); goto established; } STRNCMP("<", 1); if (cmp) STRNCMP("lt", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = TCP_UDP_LOWEST_PORT; pf_filter->filter->wDstPortHighRange = port - 1; pf_filter->dst_flags |= TCP_UDP_LT_PORT; next_word(); goto established; } /* ports interval */ port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; pf_filter->filter->wDstPort = port + 1; next_word(); STRNCMP("><", 2); if (cmp == 0) { next_word(); port = is_number(word); if (errno == EINVAL) /* invalid port */ EXIT_ERROR; if (port < TCP_UDP_LOWEST_PORT || port > TCP_UDP_HIGHEST_PORT) /* inexistent port */ EXIT_ERROR; if (port < pf_filter->filter->wDstPort) /* upper port is lower than lower port */ EXIT_ERROR; pf_filter->filter->wDstPortHighRange = port - 1; pf_filter->dst_flags |= TCP_UDP_INTERVAL_PORT; next_word(); goto established; } EXIT_ERROR; } /* established */ established: if (pf_filter->filter->dwProtocol == FILTER_PROTO_TCP) { STRNCMP("established", 11); if (cmp == 0) pf_filter->filter->dwFilterFlags = FD_FLAGS_NOSYN; goto end; } /* icmp-type */ if (pf_filter->filter->dwProtocol == FILTER_PROTO_ICMP) { next_word(); STRNCMP("icmp-type", 9); if (cmp == 0) { icmp_type = 0; next_word(); while (icmp_type_keyword[icmp_type] && strcmp(word, icmp_type_keyword[icmp_type])) { icmp_type++; } if (icmp_type_keyword[icmp_type] == NULL) { /* trying with decimal ICMP type */ icmp_type = (unsigned char) is_number(word); if (icmp_type == -1) EXIT_ERROR; } pf_filter->filter->wSrcPort = icmp_type; /* icmp-code */ next_word(); STRNCMP("code", 4); if (cmp == 0) { icmp_code = (unsigned char) is_number(rule); if (!icmp_code) EXIT_ERROR; pf_filter->filter->wDstPort = icmp_code; } else /* any ICMP code */ pf_filter->filter->wDstPort = FILTER_ICMP_CODE_ANY; } else { /* any ICMP type with any ICMP code */ pf_filter->filter->wSrcPort = FILTER_ICMP_TYPE_ANY; pf_filter->filter->wDstPort = FILTER_ICMP_CODE_ANY; } } end: /* test if we've read the whole rule */ next_word(); if (word_length != 0) EXIT_ERROR; /* test if the current rule defines a default behavior */ if (src_any_flag && dst_any_flag && (pf_filter->filter->dwProtocol == FILTER_PROTO_ANY)) { if (pf_filter->iface->ih == NULL) { /* we don't want to modify default behavior in the case * of multiple default rules */ if (pf_filter->filter_flags & FILTER_IN) { if (pf_filter->filter_flags & FILTER_PASS) pf_filter->iface->inAction = PF_ACTION_FORWARD; else /* PF_ACTION_DROP */ pf_filter->iface->inAction = PF_ACTION_DROP; } else /* FILTER_OUT */ { if (pf_filter->filter_flags & FILTER_PASS ) pf_filter->iface->outAction = PF_ACTION_FORWARD; else /* PF_ACTION_DROP */ pf_filter->iface->outAction = PF_ACTION_DROP; } } /* wait for the second default rule or open the interface */ return PF_RULE_DFLT; } /* test validity of the filters, according to default rules */ if (pf_filter->filter_flags & FILTER_IN) { if ((pf_filter->iface->inAction == PF_ACTION_FORWARD) && (pf_filter->filter_flags & FILTER_PASS)) { /* incoherent, if default is pass, rules can only block, * thanks to the powerful filtering engine of Windows * 2000... */ client_printf("error: default input rule is pass, filters can only block\n"); return PF_RULE_INVAL; } if ((pf_filter->iface->inAction == PF_ACTION_DROP) && (pf_filter->filter_flags & FILTER_DROP)) { /* see comment above */ client_printf("error: default input rule is block, filters can only pass\n"); return PF_RULE_INVAL; } } if (pf_filter->filter_flags & FILTER_OUT) { if ((pf_filter->iface->outAction == PF_ACTION_FORWARD) && (pf_filter->filter_flags & FILTER_PASS)) { /* see comment above */ client_printf("error: output rule is pass, filters can only block\n"); return PF_RULE_INVAL; } if ((pf_filter->iface->outAction == PF_ACTION_DROP) && (pf_filter->filter_flags & FILTER_DROP)) { /* see comment above */ client_printf("error: output rule is block, filters can only pass\n"); return PF_RULE_INVAL; } } return PF_RULE_ADD; }