/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipfirewall_userdef_http.c,v $ $Revision: 1.30.10.2 $ * $Source: /home/interpeak/CVSRoot/ipfirewall/src/ipfirewall_userdef_http.c,v $ * $Author: svc-cmnet $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Markus Carlstedt ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * This library provides a rudimentary content filter for the HTTP packet.It * performs the URL filtering, proxy filtering, cookie blocking, Java applet and * ActiveX control filtering actions. The URL filtering is done based on the * user specified URL database. Each entry of the database can specify either a * URL path or a URL keyword to match. The proxy filtering matches all the packets * which have absolute URI in its request line, i.e. the URI has the form: * http://xxx.xxx.xxx/xxx. The cookie filtering replaces the cookie with the same * length of the meaningless data in the request packet. It does not prevent the * cookie from being set to your host. It simply prevents the cookie from being * transmitted back to the server. Java applet and ActiveX control filterings * simply block the requests for the files with extension .zip, .class, .jar; * and .ocx,.cab respectively. It does not inspect the HTML OBJECT or APPLET tags * or the signature of the object binary content. * This content filter utilizes the firewall extension handler service * registration mechanism to allow user add/remove the filtering functions * described above dynamically. The content filter operates in the context of * the firewall extension handler installed on a firewall rule intercepting the * outbound HTTP traffic. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #define IPFIREWALL_C #include "ipfirewall.h" #include "ipfirewall_h.h" #ifdef IPFIREWALL_USE_HTTP_FILTER /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* *=========================================================================== * HTTP Filter identifier length *=========================================================================== */ #define IPFIREWALL_HTTP_FILTER_MAX_ID 32 /* *=========================================================================== * URL filter type *=========================================================================== */ #define IPFIREWALL_CLASS_INDIVIDUAL 1 /* filter specific URLs */ #define IPFIREWALL_CLASS_ABS_URI 2 /* filter absolute URI URLs */ /* *=========================================================================== * URL match type *=========================================================================== */ #define IPFIREWALL_URL_SPECIFIC_PATH 1 /* URL path match */ #define IPFIREWALL_URL_KEYWORD 2 /* URL keyword match */ /* *=========================================================================== * Object filter type *=========================================================================== */ #define IPFIREWALL_OBJ_FILTER_JAVA 1 /* Java object filter type */ #define IPFIREWALL_OBJ_FILTER_ACTIVEX 2 /* ActiveX control filter type */ /* *=========================================================================== * Constants *=========================================================================== */ #define IPFIREWALL_ONE_SPACE 1 #define IPFIREWALL_TOKEN_GET "GET" #define IPFIREWALL_TOKEN_HTTP "http" #define IPFIREWALL_TOKEN_HOST "Host" #define IPFIREWALL_HTTP_S2H_LEN 7 #define IPFIREWALL_TMNATOR_TYPE_SPECIFIC 1 #define IPFIREWALL_TMNATOR_TYPE_GENERIC 2 #define IPFIREWALL_LOOSE_MATCH 0x10 #define IPFIREWALL_STRICT_MATCH 0x20 #define IPFIREWALL_FILE_EXT_MAXLEN 10 #define IPFIREWALL_SMALL_PATTERN_SIZE 5 #define IPFIREWALL_UCHAR_MAX 255 /* *=========================================================================== * Macros *=========================================================================== */ #define IPFIREWALL_IS_STRING_SAME(str,strid) (ipcom_memcmp(str,strid,ipcom_strlen(strid)) == 0) #define IPFIREWALL_NEXT_FIELD_POS(curToken) (ipcom_strlen(curToken) + IPFIREWALL_ONE_SPACE) #define IPFIREWALL_TMNATOR_TYPE(t) ((t) & 0xff) #define IPFIREWALL_TMNATOR_MATCH(t) (((t) >> 8) & 0xff) #define IPFIREWALL_STR_GENERIC_TMNATOR(ch) ((ch) == '/' || (ch) == '\r' || (ch) == 0) #define IPFIREWALL_ALIGNED(x, align) (((unsigned long)(x) & (align - 1)) == 0) /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* *=========================================================================== * Ipfirewall_url *=========================================================================== */ typedef struct Ipfirewall_url_struct { Ipcom_list list_url; char *url; /* URL string */ Ip_u16 len; /* the host length of URL, or entire length if keyword */ Ip_u16 attr; /* path or keyword type */ } Ipfirewall_url; /* *=========================================================================== * Ipfirewall_http *=========================================================================== */ typedef struct Ipfirewall_http_struct { Ipcom_list list_http; Ipcom_list head_url; Ip_bool proxy; Ip_bool cookie; Ip_bool java; Ip_bool activex; char identifier[IPFIREWALL_HTTP_FILTER_MAX_ID]; } Ipfirewall_http; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_GLOBAL Ipcom_list head_http; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipfirewall_http_bms_str_search *=========================================================================== * Description: Search using the Boyer-Moore-Sunday (Quick Search) algorithm * The Boyer-Moore-Sunday algorithm is a more efficient simplification of * the Boyer-Moore algorithm. It performs comparisons between a character * in the pattern and a character in the text buffer from left to right. * After each mismatch it uses bad character heuristic to shift the * pattern to the right. For more details on the algorithm, refer to * "A Very Fast Substring Search Algorithm", Daniel M. Sunday, Communications * of the ACM, Vol. 33 No. 8, August 1990, pp. 132-142. * It has a O(Pattern Length x Text Buffer Length) worst-case time complexity. * But empirical results have shown that this algorithm is one of the * fastest in practice. * Parameters: pattern - pattern to search for * pattern_len - length of the pattern * buffer - text buffer to search in * buffer_len - length of the text buffer * case_sensitive - case-sensitive search * Returns: A pointer to the located pattern, or a IP_NULL pointer if the * pattern is not found */ IP_STATIC char * ipfirewall_http_bms_str_search(char *pattern, int pattern_len, char *buffer, int buffer_len, Ip_bool case_sensitive) { int skip_table[IPFIREWALL_UCHAR_MAX + 1]; int i; int b; char *p_pattern; char *p_buffer; char *p_buffer_compare; char *p_pattern_end = pattern + pattern_len; char *p_buffer_end = buffer + buffer_len - pattern_len + 1; /* Validation check */ if ((pattern == IP_NULL) || (pattern_len == 0) || (buffer == IP_NULL) || (buffer_len == 0) || (pattern_len > buffer_len)) { return IP_NULL; } /* Setup the skip table for the pattern */ for (i = 0; i < IPFIREWALL_UCHAR_MAX + 1; i++) skip_table[(Ip_u8)i] = pattern_len + 1; for (p_pattern = pattern; p_pattern < p_pattern_end; p_pattern++) { if (case_sensitive == IP_TRUE) skip_table[(Ip_u8)*p_pattern] = (int)(p_pattern_end - p_pattern); else { b = ipcom_tolower((int)*p_pattern); skip_table[b] = (int)(p_pattern_end - p_pattern); b = ipcom_toupper((int)*p_pattern); skip_table[b] = (int)(p_pattern_end - p_pattern); } } /* Main loop is the fast skip loop */ for (p_buffer = buffer; p_buffer < p_buffer_end; p_buffer += skip_table[(Ip_u8)*(p_buffer + pattern_len)]) { /* Comparison loop */ if (case_sensitive == IP_TRUE) { for (p_pattern = pattern, p_buffer_compare = p_buffer; (p_pattern < p_pattern_end) && (*p_pattern == *p_buffer_compare); p_pattern++, p_buffer_compare++) ; } else { for (p_pattern = pattern, p_buffer_compare = p_buffer; (p_pattern < p_pattern_end) && (toupper((int)*p_pattern) == toupper((int)*p_buffer_compare)); p_pattern++, p_buffer_compare++) ; } if (p_pattern == p_pattern_end) return p_buffer; /* match */ } /* no match */ return IP_NULL; } /* *=========================================================================== * ipfirewall_http_bf_str_search *=========================================================================== * Description: Search using the Brute Force algorithm * The Brute Force algorithm is the simplest string search * algorithm. It performs comparisons between a character in * the pattern and a character in the text buffer from left to * right. After each attempt it shifts the pattern by one position * to the right. The Brute Force algorithm requires no * pre-processing and no extra space. It has a O(Pattern Length * x Text Buffer Length) worst-case time complexity. * Parameters: pattern - pattern to search for * pattern_len - length of the pattern * buffer - text buffer to search in * buffer_len - length of the text buffer * case_sensitive - case-sensitive search * Returns: A pointer to the located pattern, or a IP_NULL pointer if the * pattern is not found */ IP_STATIC char * ipfirewall_http_bf_str_search(char *pattern, int pattern_len, char *buffer, int buffer_len, Ip_bool case_sensitive) { char *p_pattern; char *p_buffer; char *p_buffer_compare; char *p_pattern_end = pattern + pattern_len; char *p_buffer_end = buffer + buffer_len - pattern_len + 1; /* Validation check */ if ((pattern == IP_NULL) || (pattern_len == 0) || (buffer == IP_NULL) || (buffer_len == 0) || (pattern_len > buffer_len)) { return IP_NULL; } for (p_buffer = buffer; p_buffer < p_buffer_end; p_buffer++) { if (case_sensitive == IP_TRUE) { for (p_pattern = pattern, p_buffer_compare = p_buffer; (p_pattern < p_pattern_end) && (*p_pattern == *p_buffer_compare); p_pattern++, p_buffer_compare++) ; } else { for (p_pattern = pattern, p_buffer_compare = p_buffer; (p_pattern < p_pattern_end) && (toupper((int)*p_pattern) == toupper((int)*p_buffer_compare)); p_pattern++, p_buffer_compare++) ; } if (p_pattern == p_pattern_end) return p_buffer; /* match */ } /* no match */ return IP_NULL; } /* *=========================================================================== * ipfirewall_http_fast_str_search *=========================================================================== * Description: Search by optimally choosing the search algorithm. * Depending on the pattern size, this function uses either the * Boyer-Moore-Sunday algorithm or the Brute Force algorithm. The * Boyer-Moore-Sunday algorithm requires pre-processing, * therefore for small patterns it is better to use the Brute * Force algorithm. * Parameters: pattern - pattern to search for * pattern_len - length of the pattern * buffer - text buffer to search in * buffer_len - length of the text buffer * case_sensitive - case-sensitive search * Returns: A pointer to the located pattern, or a IP_NULL pointer if the * pattern is not found */ IP_STATIC char * ipfirewall_http_fast_str_search(char *pattern, int pattern_len, char *buffer, int buffer_len, Ip_bool case_sensitive) { if (pattern_len >= IPFIREWALL_SMALL_PATTERN_SIZE) return ipfirewall_http_bms_str_search(pattern, pattern_len, buffer, buffer_len, case_sensitive); else return ipfirewall_http_bf_str_search(pattern, pattern_len, buffer, buffer_len, case_sensitive); } /* *=========================================================================== * ipfirewall_http_str_st_len *=========================================================================== * Description: Returns the length of a string with specific terminator * Parameters: str - pointer to the string * terminator - terminating character * maxlen - maximum length to search * Returns: length of string */ IP_STATIC int ipfirewall_http_str_st_len(char *str, char terminator, int maxlen) { int i; for (i = 0; i < maxlen; i++) { if (str[i] == terminator) return i; } return 0; } /* *=========================================================================== * ipfirewall_http_is_ci_string_same *=========================================================================== * Description: Compare two case insensitive strings * Parameters: str - pointer to string one. * id_str - pointer to string two. * Returns: IP_TRUE if strings are the same or IP_FALSE if not. */ IP_STATIC Ip_bool ipfirewall_http_is_ci_string_same(char *str, char *id_str) { Ip_u32 len, i; len = (Ip_u32)ipcom_strlen(id_str); for (i = 0; i < len; i++) if (ipcom_tolower((Ip_u8)str[i]) != ipcom_tolower((Ip_u8)id_str[i])) return IP_FALSE; return IP_TRUE; } /* *=========================================================================== * ipfirewall_http_str_ci_cmp *=========================================================================== * Description: Compare two case insensitive strings with specific * terminators. * Parameters: str1 - pointer to string one. * str2 - pointer to string two. * typematch - compare type. * Returns: IP_TRUE if strings are the same or IP_FALSE if not. */ IP_STATIC Ip_bool ipfirewall_http_str_ci_cmp(char *str1, char *str2, int typematch, char term1, char term2) { int cnt = 0; Ip_bool cond1; Ip_bool cond2; for(;;) { if (IPFIREWALL_TMNATOR_TYPE(typematch) == IPFIREWALL_TMNATOR_TYPE_SPECIFIC) { cond1 = (str1[cnt] == term1); cond2 = (str2[cnt] == term2); } else /* TMNATOR_TYPE_GENERIC */ { cond1 = IPFIREWALL_STR_GENERIC_TMNATOR(str1[cnt]); cond2 = IPFIREWALL_STR_GENERIC_TMNATOR(str2[cnt]); } if (cond1 && cond2) return IP_TRUE; else if (cond1 || cond2) { if (IPFIREWALL_TMNATOR_MATCH(typematch) == IPFIREWALL_LOOSE_MATCH) return IP_TRUE; else return IP_FALSE; } if (ipcom_tolower((Ip_u8)str1[cnt]) != ipcom_tolower((Ip_u8)str2[cnt])) return IP_FALSE; cnt++; } } /* *=========================================================================== * ipfirewall_http_find *=========================================================================== * Description: This function searches for a HTTP filter. * Parameters: id - identifier * Returns: Pointer to the HTTP filter defined structure or IP_NULL if not found. */ IP_STATIC Ipfirewall_http * ipfirewall_http_find(const char *id) { Ipfirewall_http *fw_http; /* Check input */ if(id == IP_NULL) return IP_NULL; fw_http = IPCOM_LIST_FIRST(&head_http); while(fw_http != IP_NULL) { if(ipcom_strcmp(fw_http->identifier, id) == 0) return fw_http; fw_http = IPCOM_LIST_NEXT(&fw_http->list_http); } return IP_NULL; } /* *=========================================================================== * ipfirewall_field_name_find *=========================================================================== * Description: This routine searches a string in a structured message. * It assumes the searched string always starts at the begining * of each line of data terminated by \r\n characters. A message * should be terminated by \r\n preceded by the line terminator * \r\n. * Parameters: buf - pointer to the message. * id - string to search for. * len - length of the HTTP payload. * pos - offset where the string was found * Returns: IP_TRUE = string found * IP_FALSE = string not found */ IP_STATIC Ip_bool ipfirewall_field_name_find(char *buf, char *id, int len, int *pos) { int search_len; char *start = buf; if (id == IP_NULL) return IP_FALSE; search_len = len; while (1) { /* skip all space */ while (search_len > 0 && buf[0] == ' ') { buf++; search_len--; } if (search_len < (int)ipcom_strlen(id)) { IPCOM_LOG1(DEBUG, "ipfirewall_field_name_find() :: string not '%s' found - reached end of message", id); return IP_FALSE; } if (IPFIREWALL_IS_STRING_SAME(buf, id)) { IPCOM_LOG2(DEBUG, "ipfirewall_field_name_find() :: found string '%s' att offset %d", id, buf - start); break; /* Breaks out of outer loop */ } /* advance to next line */ while (search_len > 1) { if (buf[0] == '\r' && buf[1] == '\n') break; /* Breaks out of inner loop */ buf++; search_len--; } if (search_len <= 1) { IPCOM_LOG1(DEBUG, "ipfirewall_field_name_find() :: string '%s' not found - line end not found", id); return IP_FALSE; } buf += 2; search_len -= 2; if (search_len > 1) { if (buf[0] == '\r' && buf[1] == '\n') { /* reach the end mark \r\n\r\n */ IPCOM_LOG1(DEBUG, "ipfirewall_field_name_find() :: string '%s' not found - reached end mark", id); return IP_FALSE; } } } *pos = (int)(buf - start); return IP_TRUE; } /* *=========================================================================== * ipfirewall_http_checksum_adjust *=========================================================================== * Description: Adjust the TCP checksum for modified packet. Assume data * starts at even boundary with even length. * Parameters: cksum - original checksum. * buf - pointer to modified data. * len - modified data length. * is_adding_data - IP_TRUE if data is added. IP_TRUE if data is * removed. * Returns: The new checksum. */ IP_STATIC Ip_u32 ipfirewall_http_checksum_adjust(Ip_u32 cksum, Ip_u16 *buf, int len, Ip_bool is_adding_data) { cksum = (~cksum & 0x0000FFFF); if (is_adding_data == IP_FALSE) { /* removing data */ while (len > 0) { cksum = cksum - ((*buf++) & 0x0000FFFF); if ((Ip_s32) cksum <= 0x00000000L) { --cksum; cksum = cksum & 0x0000FFFF; } len -= 2; } } else { /* adding data */ while (len > 0) { cksum = cksum + ((*buf++) & 0x0000FFFF); if (cksum & 0x00010000) { ++cksum; cksum = cksum & 0x0000FFFF; } len -= 2; } } cksum = ~cksum; return cksum; } /* *=========================================================================== * ipfirewall_http_match_cookie *=========================================================================== * Description: This function prevent a cookie from being sent back to * the web server by overwritting the cookie in the client's * request message header. It must therefore recalculate the * TCP checksum. * Parameters: buf - pointer to the HTTP payload. * len - length of the HTTP payload. * tcp - pointer to tcp header * Returns: 1 = object found * 0 = object not found; */ IP_STATIC int ipfirewall_http_match_cookie(Ip_u8 *buf, int len, Ipcom_tcp_header *tcp) { Ip_u16 *chksum_start; int ret, offset, cookielen, cslen, cstokenlen; Ip_u32 new_checksum = 0; ret = 0; if (ipfirewall_field_name_find((char *)buf, "Cookie", len, &offset) == IP_TRUE) { buf += offset; len -= offset; cookielen = ipfirewall_http_str_st_len((char *)buf, '\r', len); if (!IPFIREWALL_ALIGNED(buf, 2)) { chksum_start = (Ip_u16 *)(buf - 1); cslen = (cookielen & 0x1) ? cookielen + 1 : cookielen + 2; } else { chksum_start = (Ip_u16 *)buf; cslen = (cookielen & 0x1) ? cookielen + 1 : cookielen; } /* subtract the cookie checksum data from TCP checksum */ new_checksum = ipfirewall_http_checksum_adjust(tcp->checksum, chksum_start, cslen, IP_FALSE); /* remove the Cookie information by filling some junk data */ cstokenlen = ipfirewall_http_str_st_len((char *)buf, ':', len); ipcom_memset(buf, 'd', (Ip_size_t)cstokenlen); /* not to overwrite the : and space */ ipcom_memset(buf + cstokenlen + 2, 'x', (Ip_size_t)(cookielen - (cstokenlen + 2))); /* add the new Cookie data to the adjusted TCP checksum */ tcp->checksum = (Ip_u16)ipfirewall_http_checksum_adjust(new_checksum, chksum_start, cslen, IP_TRUE); IPCOM_LOG0(DEBUG, "ipfirewall_http_match_cookie() :: hiding cookie"); ret = 1; } return ret; } /* *=========================================================================== * ipfirewall_http_match_object *=========================================================================== * Description: This function matches binary objects in HTTP GET requests. * It prevents certain executable files from running in the web * browser by blocking the requests to the corresponding type * of files. Currently it blocks the Java Applet by blocking * the files with extension .class, .jar and .zip; and ActiveX * control by blocking the files with extension .ocx and .cab. * Parameters: buf - pointer to the HTTP payload. * len - length of the HTTP payload. * type - type of object * Returns: 1 = object found * 0 = object not found; */ IP_STATIC int ipfirewall_http_match_object(Ip_u8 *buf, int len, int type) { int cnt, ret; ret = 0; if (IPFIREWALL_IS_STRING_SAME(buf, IPFIREWALL_TOKEN_GET)) { /* advance pass GET token and space */ buf += IPFIREWALL_NEXT_FIELD_POS(IPFIREWALL_TOKEN_GET); len = len - (int)IPFIREWALL_NEXT_FIELD_POS(IPFIREWALL_TOKEN_GET); cnt = ipfirewall_http_str_st_len((char *)buf, ' ' , len); buf += cnt; len -= cnt; /* search for file extension seperator */ for (cnt=0; (cnt < IPFIREWALL_FILE_EXT_MAXLEN) && (*buf-- != '.'); cnt++); if (cnt < IPFIREWALL_FILE_EXT_MAXLEN) { buf += 2; if (type == IPFIREWALL_OBJ_FILTER_ACTIVEX) { if (ipfirewall_http_is_ci_string_same((char *)buf,"ocx") || ipfirewall_http_is_ci_string_same((char *)buf,"cab") ) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match_object() :: found activex object"); ret = 1; } } else if (type == IPFIREWALL_OBJ_FILTER_JAVA) { if (ipfirewall_http_is_ci_string_same((char *)buf,"class") || ipfirewall_http_is_ci_string_same((char *)buf,"jar") || ipfirewall_http_is_ci_string_same((char *)buf,"zip") ) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match_object() :: found java object"); ret = 1; } } } } return ret; } /* *=========================================================================== * ipfirewall_http_match_url *=========================================================================== * Description: Performs the URL filtering for the HTTP packets. * This routine filters the HTTP request packets based on the * given URL database. It performs two match actions: the path * match and keyword match. The path match means both the host * name and the file path of the provided URL string matches the * packet's ones. However, the given file path just needs to * match the beginning portion of the packet's path. The keyword * match means thegiven string matches any part of the packet's URL. * Parameters: buf - pointer to the HTTP payload. * len - length of the HTTP payload. * type - URL filter type. * Returns: 1 = object found * 0 = object not found; */ IP_STATIC int ipfirewall_http_match_url(Ip_u8 *buf, int len, int type, Ipfirewall_http *fw_http) { Ipfirewall_url *fw_url; int cnt, pathlen, hostlen; Ip_u8 *host, *path; /* skip the method token, advance to URI */ cnt = ipfirewall_http_str_st_len((char *)buf, ' ', len); buf += cnt + 1; len -= cnt + 1; /* Request-URI = "*" | absoluteURI | abs_path | authority * "*" and authority forms are used by OPTION and CONNECT methods * absoluteURI = scheme ":" ( hier_part | opaque_part ) * hier_part = ( net_path | abs_path ) [ "?" query ] * net_path = "//" authority [ abs_path ] * abs_path = "/" path_segments * * Host name case insensitive, scheme case insensitive */ /* consider absoluteURI and abs_path */ if (ipfirewall_http_str_ci_cmp((char *)buf, IPFIREWALL_TOKEN_HTTP, IPFIREWALL_TMNATOR_TYPE_SPECIFIC, ':', 0) == IP_TRUE) { /* absoluteURI * HTTP/1.1 clients only generates absoluteURI in requests to * proxies(RFC2616, 5.1.2). */ if (type == IPFIREWALL_CLASS_ABS_URI) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match_url() :: Found absolute URI"); return 1; } buf += IPFIREWALL_HTTP_S2H_LEN; len -= IPFIREWALL_HTTP_S2H_LEN; host = buf; hostlen = ipfirewall_http_str_st_len((char *)host, '/', len); path = buf + hostlen; pathlen = ipfirewall_http_str_st_len((char *)path, ' ', len - hostlen); /* terminated by space */ } else { /* not absolute URI */ if (type == IPFIREWALL_CLASS_ABS_URI) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match_url() :: Not an absolute URI"); return 0; } /* abs_path */ path = buf; pathlen = ipfirewall_http_str_st_len((char *)path, ' ', len); if (ipfirewall_field_name_find((char *)buf, IPFIREWALL_TOKEN_HOST, len, &cnt) == IP_FALSE) { IPCOM_LOG0(INFO, "ipfirewall_http_match_url() :: No 'Host:' token in request"); return 0; } buf += cnt; len -= cnt; buf += IPFIREWALL_NEXT_FIELD_POS(IPFIREWALL_TOKEN_HOST); len = len - (int)IPFIREWALL_NEXT_FIELD_POS(IPFIREWALL_TOKEN_HOST); while (*(char *)(buf) == ' ') { buf++; len--; } host = buf; hostlen = ipfirewall_http_str_st_len((char *)host, '\r', len); } ip_assert(fw_http != IP_NULL); for (fw_url = IPCOM_LIST_FIRST(&fw_http->head_url); fw_url != IP_NULL; fw_url = IPCOM_LIST_NEXT(&fw_url->list_url)) { if (fw_url->attr == IPFIREWALL_URL_SPECIFIC_PATH) { if (ipfirewall_http_str_ci_cmp((char *)fw_url->url, (char *)host, IPFIREWALL_TMNATOR_TYPE_GENERIC, 0, 0)) { if (fw_url->url[fw_url->len] == 0 || IPFIREWALL_IS_STRING_SAME(path, fw_url->url + fw_url->len)) { IPCOM_LOG1(DEBUG, "ipfirewall_http_match_url() :: Matched path: '%s'", fw_url->url); break; } } } else { /* IPFIREWALL_URL_KEYWORD */ if (ipfirewall_http_fast_str_search((char *)fw_url->url, fw_url->len, (char *)host, hostlen, IP_FALSE) || ipfirewall_http_fast_str_search((char *)fw_url->url, fw_url->len, (char *)path, pathlen, IP_TRUE)) { IPCOM_LOG1(DEBUG, "ipfirewall_http_match_url() :: Matched keyword: '%s'", fw_url->url); break; } } } /* check if matches */ if (fw_url != IP_NULL) { return 1; } else { IPCOM_LOG0(DEBUG, "ipfirewall_http_match_url() :: No keyword or path matched."); return 0; } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipfirewall_http_match *=========================================================================== * Description: HTTP filter match function. * Parameters: pkt - pointer to the ipcom packet * cookie - a cookie set at registration. * info - user defined info buffer * Returns: 1 = rule matched. * 0 = rule did not match. */ IP_PUBLIC int ipfirewall_http_match(Ipcom_pkt *pkt, void *cookie, void *info) { Ipfirewall_http *fw_http; Ipcom_tcp_header *tcp; Ip_u8 ver, protocol = 0, is_frag = 0, first_frag = 0, *http_pkt; Ip_u16 hlen; int http_len, tcp_hlen, ret; (void)cookie; fw_http = *(Ipfirewall_http **)info; ret = 0; ip_assert(fw_http != IP_NULL); /* Check version */ ver = (Ip_u8)(pkt->data[pkt->ipstart] >> 4); if (ver == 4) hlen = (Ip_u16)((pkt->data[pkt->ipstart] & 0x0f) << 2); else if (ver == 6) hlen = IPCOM_IP6_HEADER_LENGTH; else { IPCOM_LOG0(ERR, "ipfirewall_http_match() :: Unknown version."); return 0; } /* Check that the packet is long enough for the IP header */ if(pkt->end - pkt->ipstart < (int)hlen) { IPCOM_LOG0(ERR, "ipfirewall_http_match() :: Packet too short for IP header."); return 0; } #ifdef IPCOM_USE_INET if (ver == 4) { Ipcom_ip_header *ip4; ip4 = (Ipcom_ip_header *)&pkt->data[pkt->ipstart]; protocol = ip4->protocol; /* Set MTU to MAX_ULONG */ ipfirewall_get_frag4_info(ip4, pkt, 0xffffffff, &is_frag, &first_frag); } #endif #ifdef IPCOM_USE_INET6 if (ver == 6) { Ipcom_ip6_header *ip6; ip6 = (Ipcom_ip6_header *)&pkt->data[pkt->ipstart]; /* Set MTU to MAX_ULONG */ if (ipfirewall_get_frag6_info(ip6, pkt, 0xffffffff, &is_frag, &first_frag, &protocol, &hlen) < 0) { IPCOM_LOG0(ERR, "ipfirewall_http_match() :: Failed to parse IPv6 extension headers."); return 0; } } #endif /* Cannot match fragments other than the first */ if (is_frag == 1 && first_frag == 0) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match() :: Skipped fragment."); return 0; } /* Verify that protocol is TCP */ if (protocol != IP_IPPROTO_TCP) { IPCOM_LOG0(DEBUG, "ipfirewall_http_match() :: Skipped non-TCP packet."); return 0; } /* Check that the packet is long enough for mandatory TCP header */ if(pkt->end - pkt->ipstart - (int)hlen < IPCOM_TCP_HEADER_LENGTH) { IPCOM_LOG0(ERR, "ipfirewall_http_match() :: Packet too short for mandatory TCP header."); return 0; } /* Store pointer to TCP header */ tcp = (Ipcom_tcp_header *)&pkt->data[pkt->ipstart + (int)hlen]; tcp_hlen = ip_ntohs(tcp->offset_flags)>>10; /* Check that the packet is long enough for the TCP header */ if(pkt->end - pkt->ipstart - (int)hlen < tcp_hlen) { IPCOM_LOG0(ERR, "ipfirewall_http_match() :: Packet too short for TCP header."); return 0; } /* Store pointer to HTTP packet */ http_pkt = (Ip_u8 *)tcp + tcp_hlen; http_len = pkt->end - pkt->ipstart - (int)hlen - tcp_hlen; /* Check that HTTP data is long enough to contain at least "GET / HTTP/1.0\r\n\r\n" */ if (http_len >= 18) { /* Match URL filter if at least one URL is inserted */ if (ret == 0 && fw_http->head_url.size > 0) ret = ipfirewall_http_match_url(http_pkt, http_len, IPFIREWALL_CLASS_INDIVIDUAL, fw_http); /* Match proxy filter if enabled */ if (ret == 0 && fw_http->proxy == IP_TRUE) ret = ipfirewall_http_match_url(http_pkt, http_len, IPFIREWALL_CLASS_ABS_URI, IP_NULL); /* Match java filter if enabled */ if (ret == 0 && fw_http->java == IP_TRUE) ret = ipfirewall_http_match_object(http_pkt, http_len, IPFIREWALL_OBJ_FILTER_JAVA); /* Match activex filter if enabled */ if (ret == 0 && fw_http->activex == IP_TRUE) ret = ipfirewall_http_match_object(http_pkt, http_len, IPFIREWALL_OBJ_FILTER_ACTIVEX); /* Remove cookies if cookie filter is enabled */ if (fw_http->cookie == IP_TRUE) { ipfirewall_http_match_cookie(http_pkt, http_len, tcp); } } return ret; } /* *=========================================================================== * ipfirewall_http_check *=========================================================================== * Description: HTTP filter check function. * cookie - a cookie set at registration. * info - parameter string specified in the rule syntax * infolen - length of user defined info buffer * Returns: 1 = rule parameters accepted * 0 = rule parameters not accepted */ IP_PUBLIC int ipfirewall_http_check(void *cookie, void *info, unsigned int infolen) { Ipfirewall_http *fw_http; (void)cookie; /* Verify that there is a HTTP filter matching the rule parameter string */ fw_http = ipfirewall_http_find(info); if (fw_http == IP_NULL) { IPCOM_LOG1(ERR, "ipfirewall_http_check() :: HTTP filter '%s' not found", info); return 0; } /* Store pointer to filter for faster access in match function */ if (infolen < (int)sizeof(fw_http)) return 0; ipcom_memcpy(info, &fw_http, sizeof(fw_http)); return 1; } /* *=========================================================================== * ipfirewall_http_destroy *=========================================================================== * Description: HTTP filter destroy function. * cookie - a cookie set at registration. * info - parameter string specified in the rule syntax * infolen - length of user defined info buffer */ IP_PUBLIC void ipfirewall_http_destroy(void *cookie, void *info, unsigned int infolen) { (void)cookie; (void)info; (void)infolen; } /* *=========================================================================== * ipfirewall_http_insert_url_filter *=========================================================================== * Description: Add a URL path or keyword to a HTTP filter. * Parameters: id - filter identifier. * url - keyword or URL path. * keyword - IP_TRUE for keyword match or IP_FALSE for URL path. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_insert_url_filter(const char *id, const char *url, Ip_bool keyword) { void *suspend_handle; Ipfirewall_http *fw_http; Ipfirewall_url *fw_url; Ip_err ret = IPCOM_SUCCESS; /* Find HTTP filter */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_url() :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http == IP_NULL) { IPCOM_LOG1(ERR, "ipfirewall_http_insert_url() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; goto out; } /* Allocate the URL structure */ fw_url = ipcom_malloc(sizeof(*fw_url)); if(fw_url == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_url() :: Could not allocate memory for URL match entry"); ret = IPCOM_ERR_NO_MEMORY; goto out; } /* Populate the URL structure */ ipcom_memset(fw_url, 0, sizeof(*fw_url)); fw_url->url = ipcom_malloc(ipcom_strlen(url) + 1); if (fw_url->url == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_url() :: Could not allocate memory for URL string"); ipcom_free(fw_url); ret = IPCOM_ERR_NO_MEMORY; goto out; } /* Skip leading "http://" */ if (ipfirewall_http_str_ci_cmp((char *)url, IPFIREWALL_TOKEN_HTTP, IPFIREWALL_TMNATOR_TYPE_SPECIFIC, ':', 0) == IP_TRUE) if (IPFIREWALL_IS_STRING_SAME((url + ipcom_strlen(IPFIREWALL_TOKEN_HTTP)),"://")) url += IPFIREWALL_HTTP_S2H_LEN; ipcom_strcpy(fw_url->url, url); if (keyword == IP_FALSE) { fw_url->len = (Ip_u16)ipfirewall_http_str_st_len(fw_url->url, '/', (int)ipcom_strlen(fw_url->url)); if (!fw_url->len) fw_url->len = (Ip_u16)ipcom_strlen(fw_url->url); fw_url->attr = IPFIREWALL_URL_SPECIFIC_PATH; } else { fw_url->len = (Ip_u16)ipcom_strlen(fw_url->url); fw_url->attr = IPFIREWALL_URL_KEYWORD; } ipcom_list_insert_last(&fw_http->head_url, &fw_url->list_url); out: ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_insert_proxy_filter *=========================================================================== * Description: Enable proxy filter in a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_insert_proxy_filter(const char *id) { void *suspend_handle; Ipfirewall_http *fw_http; Ip_err ret = IPCOM_SUCCESS; /* Find HTTP filter */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_proxy_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { fw_http->proxy = IP_TRUE; } else { IPCOM_LOG1(ERR, "ipfirewall_http_insert_proxy_filter() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; } ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_insert_cookie_filter *=========================================================================== * Description: Enable cookie filter in a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_insert_cookie_filter(const char *id) { void *suspend_handle; Ipfirewall_http *fw_http; Ip_err ret = IPCOM_SUCCESS; /* Find HTTP filter */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_cookie_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { fw_http->cookie = IP_TRUE; } else { IPCOM_LOG1(ERR, "ipfirewall_http_insert_cookie_filter() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; } ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_insert_java_filter *=========================================================================== * Description: Enable java filter in a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_insert_java_filter(const char *id) { void *suspend_handle; Ipfirewall_http *fw_http; Ip_err ret = IPCOM_SUCCESS; /* Find HTTP filter */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_java_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { fw_http->java= IP_TRUE; } else { IPCOM_LOG1(ERR, "ipfirewall_http_insert_java_filter() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; } ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_insert_activex_filter *=========================================================================== * Description: Enable active filter in a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_insert_activex_filter(const char *id) { void *suspend_handle; Ipfirewall_http *fw_http; Ip_err ret = IPCOM_SUCCESS; /* Find HTTP filter */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_insert_activex_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { fw_http->activex = IP_TRUE; } else { IPCOM_LOG1(ERR, "ipfirewall_http_insert_activex_filter() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; } ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_add_filter *=========================================================================== * Description: Add a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_add_filter(const char *id) { void *suspend_handle; Ipfirewall_http *fw_http; Ip_err ret = IPCOM_SUCCESS; /* Check for duplicate */ suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_add_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { IPCOM_LOG1(ERR, "ipfirewall_http_add_filter() :: HTTP filter '%s' already exists", id); ret = IPCOM_ERR_DUPLICATE; goto out; } if(ipcom_strlen(id) >= sizeof(fw_http->identifier)) { ret = IPCOM_ERR_FAILED; goto out; } /* Allocate the HTTP filter structure */ fw_http = ipcom_malloc(sizeof(*fw_http)); if(fw_http == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_add_filter() :: Could not allocate memory for HTTP filter entry"); ret = IPCOM_ERR_NO_MEMORY; goto out; } /* Populate the HTTP filter structure */ ipcom_memset(fw_http, 0, sizeof(*fw_http)); ipcom_strcpy(fw_http->identifier, id); ipcom_list_init(&fw_http->head_url); /* Insert the HTTP filter structure in list */ ipcom_list_insert_last(&head_http, &fw_http->list_http); out: ipnet_resume_stack(suspend_handle); return ret; } /* *=========================================================================== * ipfirewall_http_remove_filter *=========================================================================== * Description: Remove a HTTP filter. * Parameters: id - filter identifier. * Returns: IPCOM_SUCCESS or error code. */ IP_PUBLIC Ip_err ipfirewall_http_remove_filter(const char *id) { void *suspend_handle; #ifdef IPFIREWALL_USE_IP_STATEFUL Ipfirewall_state *fw_state; #endif Ipfirewall_rule *fw_rule; Ipfirewall_http *fw_http; Ipfirewall_url *fw_url; Ip_err ret = IPCOM_SUCCESS; suspend_handle = ipnet_suspend_stack(); if (suspend_handle == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_http_remove_filter :: Could not suspend stack"); return IPCOM_ERR_FAILED; } /* Verify that there are no rules that refer this HTTP filter */ #ifdef IPFIREWALL_USE_MAC_FILTER if (ret == IPCOM_SUCCESS) { for (fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_input); fw_rule != IP_NULL; fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule)) { if (fw_rule->inner.userdef == 1 && ipcom_strcmp((char *)fw_rule->inner.userdef_id, IPFIREWALL_HTTP_FILTER_ID) == 0 && ipcom_strcmp(fw_rule->inner.userdef_param, id) == 0) { IPCOM_LOG3(ERR, "ipfirewall_unregister_userdef() :: " "Cannot remove HTTP filter '%s' since MAC filter rule @%d:%d " "refers to it.", fw_rule->inner.userdef_param, fw_rule->inner.group_no, fw_rule->inner.rule_no); ret = IPCOM_ERR_FAILED; break; } } } if (ret == IPCOM_SUCCESS) { for (fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_output); fw_rule != IP_NULL; fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule)) { if (fw_rule->inner.userdef == 1 && ipcom_strcmp((char *)fw_rule->inner.userdef_id, IPFIREWALL_HTTP_FILTER_ID) == 0 && ipcom_strcmp(fw_rule->inner.userdef_param, id) == 0) { IPCOM_LOG3(ERR, "ipfirewall_unregister_userdef() :: " "Cannot remove HTTP filter '%s' since MAC filter rule @%d:%d " "refers to it.", fw_rule->inner.userdef_param, fw_rule->inner.group_no, fw_rule->inner.rule_no); ret = IPCOM_ERR_FAILED; break; } } } #endif /* #ifdef IPFIREWALL_USE_MAC_FILTER */ #ifdef IPFIREWALL_USE_IP_FILTER if (ret == IPCOM_SUCCESS) { for (fw_rule = IPCOM_LIST_FIRST(&ipfirewall->head_rule_input); fw_rule != IP_NULL; fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule)) { if (fw_rule->inner.userdef == 1 && ipcom_strcmp((char *)fw_rule->inner.userdef_id, IPFIREWALL_HTTP_FILTER_ID) == 0 && ipcom_strcmp(fw_rule->inner.userdef_param, id) == 0) { IPCOM_LOG3(ERR, "ipfirewall_unregister_userdef() :: " "Cannot remove HTTP filter '%s' since IP filter rule @%d:%d " "refers to it.", fw_rule->inner.userdef_param, fw_rule->inner.group_no, fw_rule->inner.rule_no); ret = IPCOM_ERR_FAILED; break; } } } if (ret == IPCOM_SUCCESS) { for (fw_rule = IPCOM_LIST_FIRST(&ipfirewall->head_rule_output); fw_rule != IP_NULL; fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule)) { if (fw_rule->inner.userdef == 1 && ipcom_strcmp((char *)fw_rule->inner.userdef_id, IPFIREWALL_HTTP_FILTER_ID) == 0 && ipcom_strcmp(fw_rule->inner.userdef_param, id) == 0) { IPCOM_LOG3(ERR, "ipfirewall_unregister_userdef() :: " "Cannot remove HTTP filter '%s' since IP filter rule @%d:%d " "refers to it.", fw_rule->inner.userdef_param, fw_rule->inner.group_no, fw_rule->inner.rule_no); ret = IPCOM_ERR_FAILED; break; } } } #endif /* #ifdef IPFIREWALL_USE_IP_FILTER */ #ifdef IPFIREWALL_USE_IP_STATEFUL if (ret == IPCOM_SUCCESS) { for (fw_state = IPCOM_LIST_FIRST(&ipfirewall->head_state); fw_state != IP_NULL; fw_state = IPCOM_LIST_NEXT(&ipfirewall->head_state)) { fw_rule=(Ipfirewall_rule *)fw_state->fw_rule; if ((fw_rule!=IP_NULL)&&(fw_rule->inner.userdef==1) &&(ipcom_strcmp((char *)fw_rule->inner.userdef_id,IPFIREWALL_HTTP_FILTER_ID)==0) &&(ipcom_strcmp(fw_rule->inner.userdef_param, id)==0)) { ipfirewall_clean_state(fw_state); } } } #endif /* IPFIREWALL_USE_IP_STATEFUL */ /* Find HTTP filter */ if (ret == IPCOM_SUCCESS) { fw_http = ipfirewall_http_find(id); if (fw_http != IP_NULL) { /* Clear URL list */ while((fw_url = IPCOM_LIST_LAST(&fw_http->head_url)) != IP_NULL) { ipcom_list_remove(&fw_url->list_url); if (fw_url->url != IP_NULL) ipcom_free(fw_url->url); /* Clear URL string */ ipcom_free(fw_url); } /* Remove from HTTP filter list */ ipcom_list_remove(&fw_http->list_http); ipcom_free(fw_http); } else { IPCOM_LOG1(ERR, "ipfirewall_http_add() :: HTTP filter '%s' not found", id); ret = IPCOM_ERR_NOT_FOUND; } } ipnet_resume_stack(suspend_handle); return ret; } #else int ipfirewall_userdef_http_empty_file; #endif /* #ifdef IPFIREWALL_USE_HTTP_FILTER */ /* **************************************************************************** * END OF FILE **************************************************************************** */