⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hi_client.c

📁 snort-2.1.0入侵检测
💻 C
📖 第 1 页 / 共 3 页
字号:
/****  @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*/#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"#define URI_END  1#define NO_URI  -1#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{    u_char *uri;                /* the beginning of the URI */    u_char *uri_end;            /* the end of the URI */    u_char *norm;               /* ptr to first normalization occurence */    u_char *ident;              /* ptr to beginning of the HTTP identifier */    u_char *first_sp_start;     /* beginning of first space delimiter */    u_char *first_sp_end;       /* end of first space delimiter */    u_char *second_sp_start;    /* beginning of second space delimiter */    u_char *second_sp_end;      /* end of second space delimiter */    u_char *param;              /* '?' (beginning of parameter field) */    u_char *delimiter;          /* HTTP URI delimiter (\r\n\) */    u_char *last_dir;           /* ptr to last dir, so we catch long dirs */    u_char *proxy;              /* ptr to the absolute URI */}  URI_PTR;/****  This makes passing function arguments much more readable and easier**  to follow.*/typedef int (*LOOKUP_FCN)(HI_SESSION *, u_char *, u_char *, 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];/***  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, u_char *start, u_char *end){    u_int  iChunkLen   = 0;    int    iChunkChars = 0;    int    iCheckChunk = 1;    u_char *ptr;    u_char *jump_ptr;    if(!start || !end)        return HI_INVALID_ARG;    ptr = start;    while(hi_util_in_bounds(start, end, ptr))    {        if(*ptr == '\n')        {            if(iCheckChunk && iChunkLen != 0)            {                if(Session->server_conf->chunk_length < iChunkLen)                    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;                }            }            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 |= 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 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 u_char *FindPipelineReq(u_char *start, u_char *end){    u_char *p;    if(!start || !end)        return NULL;    p = start;    /*    **  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')        {            p++;            if(*p < 0x0E)            {                if(*p == '\r')                {                    p++;                    if(*p == '\n')                    {                        return ++p;                    }                }                else if(*p == '\n')                {                    return ++p;                }            }        }        p++;    }    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(u_char **ptr, 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)++;    }         /*    **  This means that we match all the chars that we could given the     **  remaining length so we should increment the pointer by that much    **  since we don't need to inspect this again.    */    (*ptr)++;    return 1;}/***  NAME**    find_rfc_delimiter::*//****  Check for standard RFC HTTP delimiter.****  If we find the delimiter, we return that URI_PTR structures should**  be checked, which bails us out of the loop.  If there isn't a RFC**  delimiter, then we bail with a no URI.  Otherwise, we check for out**  of bounds.****  @param ServerConf pointer to the server configuration**  @param start      pointer to the start of payload**  @param end        pointer to the end of the payload**  @param ptr        pointer to the pointer of the current index**  @param uri_ptr    pointer to the URI_PTR construct**  **  @return integer**  **  @retval HI_OUT_OF_BOUNDS **  @retval URI_END end of the URI is found, check URI_PTR.**  @retval NO_URI  malformed delimiter, no URI.*/static int find_rfc_delimiter(HI_SESSION *Session, u_char *start,         u_char *end, u_char **ptr, URI_PTR *uri_ptr){    if(*ptr == start || !uri_ptr->uri)        return NO_URI;    /*    **  This is important to catch the defunct way of getting URIs without    **  specifying "HTTP/major.minor\r\n\r\n".  This is a quick way for    **  us to tell if we are in that state.    **    **  We check for a legal identifier to deal with the case of    **  "some_of_the_uri_in segmented packet \r\n" in the defunct case.    **  Since we find a "valid" (still defunct) delimiter, we account for    **  it here, so that we don't set the uri_end to the delimiter.    **    **  NOTE:    **  We now assume that the defunct method is in effect and if there is    **  a valid identifier, then we don't update the uri_end because it's    **  already been set when the identifier was validated.    */    (*ptr)++;    if(!hi_util_in_bounds(start, end, *ptr))    {        return HI_OUT_OF_BOUNDS;    }            if(**ptr == '\n')    {        uri_ptr->delimiter = (*ptr)-1;        if(!uri_ptr->ident)            uri_ptr->uri_end = uri_ptr->delimiter;                return URI_END;    }    return NO_URI;}/***  NAME**    find_non_rfc_delimiter::*//****  Check for non standard delimiter '\n'.****  It now appears that apache and iis both take this non-standard **  delimiter.  So, we most likely will always look for it, but maybe**  give off a special alert or something.****  @param ServerConf pointer to the server configuration**  @param start      pointer to the start of payload**  @param end        pointer to the end of the payload**  @param ptr        pointer to the pointer of the current index**  @param uri_ptr    pointer to the URI_PTR construct  **  **  @return integer**  **  @retval URI_END delimiter found, end of URI**  @retval NO_URI  */static int find_non_rfc_delimiter(HI_SESSION *Session, u_char *start,        u_char *end, u_char **ptr, URI_PTR *uri_ptr){    HTTPINSPECT_CONF *ServerConf = Session->server_conf;    if(*ptr == start || !uri_ptr->uri)        return NO_URI;    /*    **  This is important to catch the defunct way of getting URIs without    **  specifying "HTTP/major.minor\r\n\r\n".  This is a quick way for    **  us to tell if we are in that state.    **    **  We check for a legal identifier to deal with the case of    **  "some_of_the_uri_in segmented packet \r\n" in the defunct case.    **  Since we find a "valid" (still defunct) delimiter, we account for    **  it here, so that we don't set the uri_end to the delimiter.    **    **  NOTE:    **  We now assume that the defunct method is in effect and if there is    **  a valid identifier, then we don't update the uri_end because it's    **  already been set when the identifier was validated.    */    if(ServerConf->iis_delimiter.on)    {        if(hi_eo_generate_event(Session, ServerConf->iis_delimiter.alert))        {            hi_eo_client_event_log(Session, HI_EO_CLIENT_IIS_DELIMITER,                                   NULL, NULL);        }        uri_ptr->delimiter = *ptr;                if(!uri_ptr->ident)            uri_ptr->uri_end = uri_ptr->delimiter;                return URI_END;    }    /*    **  This allows us to do something if the delimiter check is not turned    **  on.  Most likely this is worthy of an alert, IF it's not normal to    **  see these requests.    **    **  But for now, we always return true.    */    uri_ptr->delimiter = *ptr;    if(!uri_ptr->ident)        uri_ptr->uri_end = uri_ptr->delimiter;    return URI_END;}/***  NAME**    NextNonWhiteSpace::*//****  Update the URI_PTR fields spaces, find the next non-white space char,**  and validate the HTTP version identifier after the spaces.**  **  This is the main part of the URI algorithm.  This verifies that there**  isn't too many spaces in the data to be a URI, it checks that after the**  second space that there is an HTTP identifier or otherwise it's no good.**  Also, if we've found an identifier after the first whitespace, and**  find another whitespace, there is no URI.**  **  The uri and uri_end pointers are updated in this function depending**  on what space we are at, and if the space was followed by the HTTP**  identifier.  (NOTE:  the HTTP delimiter is no longer "HTTP/", but**  can also be "\r\n", "\n", or "\r".  This is the defunct method, and**  we deal with it in the IsHttpVersion and delimiter functions.)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -