📄 hi_client.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_client.c**** @author Daniel Roelker <droelker@sourcefire.com>**** @brief Main file for all the client functions and inspection** flow.****** The job of the client module is to analyze and inspect the HTTP** protocol, finding where the various fields begin and end. This must** be accomplished in a stateful and stateless manner.**** While the fields are being determined, we also do checks for ** normalization, so we don't normalize fields that don't need it.**** Currently, the only fields we check for this is the URI and the** parameter fields.** ** NOTES:** - 3.8.03: Initial development. DJR** - 2.4.05: Added tab_uri_delimiter config option. AJM.*/#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <sys/types.h>#include "hi_ui_config.h"#include "hi_si.h"#include "hi_mi.h"#include "hi_client.h"#include "hi_eo_log.h"#include "hi_util.h"#include "hi_util_hbm.h"#include "hi_return_codes.h"#include "util.h"/* These numbers were chosen to avoid conflicting with * the return codes in hi_return_codes.h */ #define URI_END 99#define POST_END 100#define NO_URI 101#define INVALID_HEX_VAL -1/**** This structure holds pointers to the different sections of an HTTP** request. We need to track where whitespace begins and ends, so we** can evaluate the placement of the URI correctly.**** For example,**** GET / HTTP/1.0** ^ ^ ** start end**** The end space pointers are set to NULL if there is space until the end** of the buffer.*/typedef struct s_URI_PTR{ const u_char *uri; /* the beginning of the URI */ const u_char *uri_end; /* the end of the URI */ const u_char *norm; /* ptr to first normalization occurence */ const u_char *ident; /* ptr to beginning of the HTTP identifier */ const u_char *first_sp_start; /* beginning of first space delimiter */ const u_char *first_sp_end; /* end of first space delimiter */ const u_char *second_sp_start; /* beginning of second space delimiter */ const u_char *second_sp_end; /* end of second space delimiter */ const u_char *param; /* '?' (beginning of parameter field) */ const u_char *delimiter; /* HTTP URI delimiter (\r\n\) */ const u_char *last_dir; /* ptr to last dir, so we catch long dirs */ const u_char *proxy; /* ptr to the absolute URI */} URI_PTR;typedef struct s_HEADER_PTR{ URI_PTR header; COOKIE_PTR cookie;} HEADER_PTR;/**** This makes passing function arguments much more readable and easier** to follow.*/typedef int (*LOOKUP_FCN)(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);/*** The lookup table contains functions for different HTTP delimiters ** (like whitespace and the HTTP delimiter \r and \n).*/static LOOKUP_FCN lookup_table[256];static int hex_lookup[256];static int NextNonWhiteSpace(HI_SESSION *Session, const u_char *start, const u_char *end, const u_char **ptr, URI_PTR *uri_ptr);/*** NAME** CheckChunkEncoding::*//**** This routine checks for chunk encoding anomalies in an HTTP client request** packet.** ** We convert potential chunk lengths and test them against the user-defined** max chunk length. We log events on any chunk lengths that are over this** defined chunk lengths.** ** Chunks are skipped to save time when the chunk is contained in the packet.** ** We assume coming into this function that we are pointed at the beginning** of what may be a chunk length. That's why the iCheckChunk var is set** to 1.** ** @param Session pointer to the Session construct** @param start pointer to where to beginning of buffer** @param end pointer to the end of buffer** ** @return integer** ** @retval HI_SUCCESS function successful** @retval HI_INVALID_ARG invalid argument*/static int CheckChunkEncoding(HI_SESSION *Session, const u_char *start, const u_char *end){ u_int iChunkLen = 0; int iChunkChars = 0; int iCheckChunk = 1; const u_char *ptr; const u_char *jump_ptr; if(!start || !end) return HI_INVALID_ARG; ptr = start; while(hi_util_in_bounds(start, end, ptr)) { if(*ptr == '\n' || *ptr == ' ' || *ptr == '\t') { if(iCheckChunk && iChunkLen != 0) { if(Session->server_conf->chunk_length < iChunkLen && hi_eo_generate_event(Session, HI_EO_CLIENT_LARGE_CHUNK)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_LARGE_CHUNK, NULL, NULL); } jump_ptr = ptr + iChunkLen; if(jump_ptr <= ptr) { break; } if(hi_util_in_bounds(start, end, jump_ptr)) { ptr = jump_ptr; } else { /* ** Chunk too large for packet, so we bail */ break; } } /* ** If we've already evaluated the chunk, or we have a valid delimiter ** for handling new chunks, we reset and starting evaluating possible ** chunk lengths. */ if(iCheckChunk || *ptr == '\n') { iCheckChunk = 1; iChunkLen = 0; iChunkChars = 0; } ptr++; continue; } if(iCheckChunk) { if(hex_lookup[*ptr] == INVALID_HEX_VAL) { if(*ptr == '\r') { ptr++; if(!hi_util_in_bounds(start, end, ptr)) break; if(*ptr == '\n') continue; } else if(*ptr == ';') { /* ** This is where we skip through the chunk name=value ** field. */ ptr++; while(hi_util_in_bounds(start, end, ptr)) { if(*ptr == '\n') break; ptr++; } continue; } iCheckChunk = 0; iChunkLen = 0; iChunkChars = 0; } else { if(iChunkChars >= 8) { iCheckChunk = 0; iChunkLen = 0; iChunkChars = 0; } else { iChunkLen <<= 4; iChunkLen |= (unsigned int)hex_lookup[*ptr]; iChunkChars++; } } } ptr++; } return HI_SUCCESS;}/*** NAME** FindPipelineReq::*//**** Catch multiple requests per packet, by returning pointer to after the** end of the request header if there is another request.** ** There are 4 types of "valid" delimiters that we look for. They are:** "\r\n\r\n"** "\r\n\n"** "\n\r\n"** "\n\n"** The only patterns that we really only need to look for are:** "\n\r\n"** "\n\n"** The reason being that these two patterns are suffixes of the other ** patterns. So once we find those, we are all good.** ** @param Session pointer to the session** @param start pointer to the start of text** @param end pointer to the end of text** ** @return pointer** ** @retval NULL Did not find pipeline request** @retval !NULL Found another possible request.*/static INLINE const u_char *FindPipelineReq(HI_SESSION *Session, const u_char *start, const u_char *end){ const u_char *p; u_char *offset; if(!start || !end) return NULL; p = start; offset = (u_char*)p; /* ** We say end - 6 because we need at least six bytes to verify that ** there is an end to the URI and still a request afterwards. To be ** exact, we should only subtract 1, but we are not interested in a ** 1 byte method, uri, etc. ** ** a.k.a there needs to be data after the initial request to inspect ** to make it worth our while. */ while(p < (end - 6)) { if(*p == '\n') { if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len) && ((p - offset) >= Session->server_conf->max_hdr_len)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL); } p++; offset = (u_char*)p; if(*p < 0x0E) { if(*p == '\r') { p++; if(*p == '\n') { return ++p; } } else if(*p == '\n') { return ++p; } } } p++; } /* Never observed an end-of-field. Maybe it's not there, but the header is long anyway: */ if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len) && ((p - start) >= Session->server_conf->max_hdr_len)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL); } return NULL;}/*** NAME** IsHttpVersion::*//**** This checks that there is a version following a space with in an HTTP** packet.** ** This function gets called when a whitespace area has ended, and we want** to know if a version identifier is followed directly after. So we look** for the rfc standard "HTTP/" and report appropriately. We also need** to make sure that the function succeeds given an end of buffer, so for** instance if the buffer ends like " HTT", we still assume that this is** a valid version identifier because of TCP segmentation.** ** We also check for the 0.9 standard of GET URI\r\n. When we see a \r or** a \n, then we just return with the pointer still pointing to that char.** The reason is because on the next loop, we'll do the evaluation that** we normally do and finish up processing there.** ** @param start pointer to the start of the version identifier** @param end pointer to the end of the buffer (could be the end of the** data section, or just to the beginning of the delimiter.** ** @return integer** ** @retval 1 this is an HTTP version identifier** @retval 0 this is not an HTTP identifier, or bad parameters*/static int IsHttpVersion(const u_char **ptr, const u_char *end){ static u_char s_acHttpDelimiter[] = "HTTP/"; static int s_iHttpDelimiterLen = 5; int len; int iCtr; if(*ptr >= end) { return 0; } len = end - *ptr; if(len > s_iHttpDelimiterLen) { len = s_iHttpDelimiterLen; } /* ** This is where we check for the defunct method again. This method ** allows a request of "GET /index.html \r[\n]". So we need to ** check validate this as a legal identifier. */ if(**ptr == '\n' || **ptr == '\r') { /* ** We don't increment the pointer because we check for a legal ** identifier in the delimiter checking. Read the comments for ** setting the defunct variable in these functions. */ return 1; } for(iCtr = 0; iCtr < len; iCtr++) { if(s_acHttpDelimiter[iCtr] != (u_char)toupper((int)**ptr)) { return 0; } (*ptr)++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -