📄 hi_norm.c
字号:
/**************************************************************************** * * Copyright (C) 2003-2008 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ /**** @file hi_norm.c** ** @author Daniel Roelker <droelker@sourcefire.com** ** @brief Contains normalization skeleton for server and client** normalization routines.** ** This file contains the core routines to normalize the different fields** within the HTTP protocol. We currently only support client URI** normalization, but the hooks are here to easily add other routines.** ** NOTES:** - Initial development. DJR*/#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <sys/types.h>#include "hi_client_norm.h"#include "hi_eo.h"#include "hi_eo_events.h"#include "hi_eo_log.h"#include "hi_ui_iis_unicode_map.h"#include "hi_return_codes.h"#include "hi_si.h"#include "hi_util.h"#include "hi_util_xmalloc.h"#define MAX_DIRS 2048#define NO_HEX_VAL -1#define BASE36_VAL -2#define HEX_VAL 1/**** This define checks for negative return codes, since we have multiple** reasons to error. This just cuts the return code checks, especially** as we add more errors.*/#define GET_ERR 0x80000000#define END_OF_BUFFER -1#define DOUBLE_ENCODING -2#define DIR_TRAV -2#define NON_ASCII_CHAR 0xfftypedef struct s_URI_NORM_STATE{ u_char *abs_uri; u_char *param; /* ** Directory tracking */ u_char *dir_track[MAX_DIRS]; u_int dir_count;} URI_NORM_STATE;typedef int (*DECODE_FUNC)(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_NORM_STATE *);static int hex_lookup[256];static int valid_lookup[256];/*** NAME** GetPtr::*//**** This routine is for getting bytes in the U decode.** ** This checks the current bounds and checking for the double decoding.** This routine differs from the other Get routines because it returns** other values than just END_OF_BUFFER and the char.** ** We also return DOUBLE_ENCODING if there is a % and double decoding** is turned on.** ** When using this function it is important to note that it increments** the buffer before checking the bounds. So, if you call this function** in a loop and don't check for END_OF_BUFFER being returned, then ** you are going to overwrite the buffer. If I put the check in, you** would just be in an never-ending loop. So just use this correctly.** ** @param ServerConf the server configuration** @param start the start of the URI** @param end the end of the URI** @param ptr the current pointer into the URI** ** @return integer** ** @retval END_OF_BUFFER the end of the buffer has been reached.** @retval DOUBLE_ENCODING a percent was found and double decoding is on** @retval <= 0xff an ASCII char */static int GetPtr(HI_SESSION *Session, const u_char *start, const u_char *end, const u_char **ptr, URI_NORM_STATE *norm_state){ HTTPINSPECT_CONF *ServerConf = Session->server_conf; (*ptr)++; if(!hi_util_in_bounds(start, end, *ptr)) return END_OF_BUFFER; if(ServerConf->double_decoding.on && **ptr == '%') return DOUBLE_ENCODING; return (int)**ptr;}/*** NAME** UDecode::*//**** Handles the single decode for %U encoding.** ** This routine receives the ptr pointing to the u. We check the bounds** and continue with processing. %u encoding works by specifying the** exact codepoint to be used. For example, %u002f would be /. So this** all seems fine. BUT, the problem is that IIS maps multiple codepoints** to ASCII characters. So, %u2044 also maps to /. So this is what we** need to handle here.** ** This routine only handles the single encoding. For double decoding,** %u is handled in DoubleDecode(). It's the same routine, with just** the GetByte function different.** ** We use a get_byte function to get the bytes, so we can use this** routine for PercentDecode and for DoubleDecode.**** @param ServerConf the server configuration** @param start the start of the URI** @param end the end of the URI** @param ptr the current pointer into the URI** @param get_byte the function pointer to get bytes.** ** @return integer** ** @retval END_OF_BUFFER we are at the end of the buffer** @retval DOUBLE_ENCODING this U encoding is possible double encoded** @retval NON_ASCII_CHAR return this char for non-ascii or bad decodes** @retval iChar this is the char that we decoded.*/static int UDecode(HI_SESSION *Session, const u_char *start, const u_char *end, const u_char **ptr, DECODE_FUNC get_byte, URI_NORM_STATE *norm_state){ HTTPINSPECT_CONF *ServerConf = Session->server_conf; int iByte; int iNorm; int iCtr; iNorm = 0; hi_stats.unicode++; for(iCtr = 0; iCtr < 4; iCtr++) { iByte = get_byte(Session, start, end, ptr, norm_state); if(iByte & GET_ERR) return iByte; if(valid_lookup[(u_char)iByte] < 0) { hi_stats.non_ascii++; return NON_ASCII_CHAR; } iNorm <<= 4; iNorm = (iNorm | (hex_lookup[(u_char)iByte])); } /* ** If the decoded codepoint is greater than a single byte value, ** then we return a NON_ASCII_CHAR. */ if(iNorm > 0xff) { /* ** We check here for IIS codepoints that map to ASCII chars. */ if(ServerConf->iis_unicode.on && iNorm <= 0xffff) { iNorm = ServerConf->iis_unicode_map[iNorm]; if(iNorm == HI_UI_NON_ASCII_CODEPOINT) { hi_stats.non_ascii++; iNorm = NON_ASCII_CHAR; } if(hi_eo_generate_event(Session, ServerConf->iis_unicode.alert) && !norm_state->param) { hi_eo_client_event_log(Session, HI_EO_CLIENT_IIS_UNICODE, NULL, NULL); } } else { hi_stats.non_ascii++; return NON_ASCII_CHAR; } } /* ** Check if we alert on this encoding */ if(hi_eo_generate_event(Session, ServerConf->u_encoding.alert) && !norm_state->param) { hi_eo_client_event_log(Session, HI_EO_CLIENT_U_ENCODE, NULL, NULL); } return iNorm;}/*** NAME** PercentDecode::*//**** This is the first level of decoding, and deals with ASCII, U, and** double decoding.**** This function is the main decoding function. It handles all the ASCII** encoding and the U encoding, and tells us when there is a double** encoding.** ** We use the GetPtr() routine to get the bytes for us. This routine** checks for DOUBLE_ENCODING and tells us about it if it finds something,** so we can reset the ptrs and run it through the double decoding** routine.** ** The philosophy behind this routine is that if we run out of buffer** we return such, the only other thing we return besides the decodes** char is a NON_ASCII_CHAR in the case that we try and decode something** like %tt. This is no good, so we return a place holder.** ** @param ServerConf the server configuration** @param start the start of the URI** @param end the end of the URI** @param ptr the current pointer into the URI** ** @return integer** ** @retval END_OF_BUFFER We've hit the end of buffer while decoding.** @retval NON_ASCII_CHAR Invalid hex encoding, so we return a placeholder.** @retval char return the valid char** ** @see GetPtr()*/static int PercentDecode(HI_SESSION *Session, const u_char *start, const u_char *end, const u_char **ptr, URI_NORM_STATE *norm_state){ HTTPINSPECT_CONF *ServerConf = Session->server_conf; int iByte; const u_char *orig_ptr; int iNorm; orig_ptr = *ptr; iByte = GetPtr(Session, start, end, ptr, norm_state); if(iByte & GET_ERR) { if(iByte == END_OF_BUFFER) return END_OF_BUFFER; if(iByte == DOUBLE_ENCODING) { *ptr = orig_ptr; return (int)**ptr; } } /* ** Initialize the normalization byte */ iNorm = 0; /* ** hex values */ if(valid_lookup[(u_char)iByte] < 0) { /* ** Check for %u encoding. ** ** The u-encoding loop always returns something. */ if(ServerConf->u_encoding.on && (toupper(iByte) == 'U')) { iNorm = UDecode(Session, start, end, ptr, GetPtr, norm_state); /* ** We have to handle the double meaning of END_OF_BUFFER ** when using the GetPtr() function. */ if(iNorm & GET_ERR) { if(iNorm == END_OF_BUFFER) { /* ** We have reached the end of the buffer while ** processing a U encoding. */ return END_OF_BUFFER; } if(iNorm == DOUBLE_ENCODING) { *ptr = orig_ptr; return (int)**ptr; } } return iNorm; } else if(!ServerConf->base36.on || valid_lookup[(u_char)iByte] != BASE36_VAL) { hi_stats.non_ascii++; return NON_ASCII_CHAR; } /* ** The logic above dictates that if we get to this point, we ** have a valid base36 encoding, so let's log the event. */ hi_stats.base36++; if(hi_eo_generate_event(Session, ServerConf->base36.alert) && !norm_state->param) { hi_eo_client_event_log(Session, HI_EO_CLIENT_BASE36, NULL, NULL); } } iNorm = (hex_lookup[(u_char)iByte]<<4); iByte = GetPtr(Session, start, end, ptr, norm_state); if(iByte & GET_ERR) { if(iByte == END_OF_BUFFER) return END_OF_BUFFER; if(iByte == DOUBLE_ENCODING) { *ptr = orig_ptr; return (int)**ptr; } } if(valid_lookup[(u_char)iByte] < 0) { if(!ServerConf->base36.on || valid_lookup[(u_char)iByte] != BASE36_VAL) { hi_stats.non_ascii++; return NON_ASCII_CHAR; } /* ** Once again, we know we have a valid base36 encoding, let's alert ** if possible. */ hi_stats.base36++; if(hi_eo_generate_event(Session, ServerConf->base36.alert) && !norm_state->param) { hi_eo_client_event_log(Session, HI_EO_CLIENT_BASE36, NULL, NULL); } } iNorm = (iNorm | (hex_lookup[(u_char)iByte])) & 0xff; if(hi_eo_generate_event(Session,ServerConf->ascii.alert) && !norm_state->param) { hi_eo_client_event_log(Session, HI_EO_CLIENT_ASCII, NULL, NULL); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -