예제 #1
0
/**
**  Inspect an HTTP server response packet to determine the state.
**  
**  We inspect this packet and determine whether we are in the beginning
**  of a response header or if we are looking at payload.  We limit the
**  amount of inspection done on responses by only inspecting the HTTP header
**  and some payload.  If the whole packet is a payload, then we just ignore
**  it, since we inspected the previous header and payload.
**  
**  We limit the amount of the payload by adjusting the Server structure
**  members, header and header size.
**  
**  @param Server      the server structure
**  @param data        pointer to the beginning of payload
**  @param dsize       the size of the payload
**  @param flow_depth  the amount of header and payload to inspect
**  
**  @return integer
**  
**  @retval HI_INVALID_ARG invalid argument
**  @retval HI_SUCCESS     function success
*/
static int IsHttpServerData(HI_SESSION *Session, Packet *p, HttpSessionData *sd)
{
    const u_char *start;
    const u_char *end;
    const u_char *ptr;
    int len;
    uint32_t seq_num = 0;
    HI_SERVER *Server; 
    HTTPINSPECT_CONF *ServerConf;

    ServerConf = Session->server_conf;
    if(!ServerConf)
        return HI_INVALID_ARG;

    Server = &(Session->server);

    clearHttpRespBuffer(Server);
    /* 
    ** HTTP:Server-Side-Session-Performance-Optimization
    ** This drops Server->Client packets which are not part of the 
    ** HTTP Response header. It can miss part of the response header 
    ** if the header is sent as multiple packets.
    */
    if(!(p->data))
    {
        return HI_INVALID_ARG;
    }

    seq_num = GET_PKT_SEQ(p);

    /*
    **  Let's set up the data pointers.
    */
    Server->response.header_raw      = p->data;
    Server->response.header_raw_size = p->dsize;

    start = p->data;
    end = p->data + p->dsize;
    ptr = start;

    ptr = MovePastDelims(start,end,ptr);

    len = end - ptr;
    if ( len > 4 )
    {
        if(!IsHttpVersion(&ptr, end))
        {
            p->packet_flags |= PKT_HTTP_DECODE;
            ApplyFlowDepth(ServerConf, p, sd, 0, 0, seq_num);
            return HI_SUCCESS;
        }
        else
        {
            if(ServerConf->server_flow_depth > 0)
            {
                sd->resp_state.is_max_seq = 1;
                sd->resp_state.max_seq = seq_num + ServerConf->server_flow_depth;
            }
            p->packet_flags |= PKT_HTTP_DECODE;
            ApplyFlowDepth(ServerConf, p, sd, 0, 1, seq_num);
            return HI_SUCCESS;
        }
    }
    else
    {
        return HI_SUCCESS;
    }


    return HI_SUCCESS;
}
예제 #2
0
/**
**  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.)
**  
**  @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_SUCCESS       found the next non-whitespace
**  @retval HI_OUT_OF_BOUNDS whitespace to the end of the buffer
**  @retval URI_END          delimiter found, end of URI
**  @retval NO_URI
*/
static int NextNonWhiteSpace(HI_SESSION *Session, u_char *start, 
        u_char *end, u_char **ptr, URI_PTR *uri_ptr)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    u_char **start_sp;
    u_char **end_sp;

    /*
    **  Reset the identifier, because we've just seen another space.  We
    **  should only see the identifier immediately after a space followed
    **  by a delimiter.
    */
    if(uri_ptr->ident)
    {
        if(ServerConf->non_strict)
        {
            /*
            **  In non-strict mode it is ok to see spaces after the
            **  "identifier", so we just increment the ptr and return.
            */
            (*ptr)++;
            return HI_SUCCESS;
        }
        else
        {
            /*
            **  This means that we've already seen a space and a version
            **  identifier, and now that we've seen another space, we know
            **  that this can't be the URI so we just bail out with no
            **  URI.
            */
            return NO_URI;
        }
    }

    uri_ptr->ident = NULL;

    /*
    **  We only check for one here, because both should be set if one
    **  is.
    */
    if(uri_ptr->first_sp_end)
    {
        /*
        **  If the second space has been set, then this means that we have   
        **  seen a third space, which we shouldn't see in the URI so we
        **  are now done and know there is no URI in this packet.
        */
        if(uri_ptr->second_sp_end)
        {
            return NO_URI;
        }

        /*
        **  Since we've seen the second space, we need to update the uri ptr
        **  to the end of the first space, since the URI cannot be before the
        **  first space.
        */
        uri_ptr->uri = uri_ptr->first_sp_end;

        uri_ptr->second_sp_start = *ptr;
        uri_ptr->second_sp_end = NULL;

        start_sp = &uri_ptr->second_sp_start;
        end_sp = &uri_ptr->second_sp_end;
    }
    else
    {
        /*
        **  This means that there is whitespace at the beginning of the line
        **  and we unset the URI so we can set it later if need be.
        **
        **  This is mainly so we handle data that is all spaces correctly.
        **  
        **  In the normal case where we've seen text and then the first space,
        **  we leave the uri ptr pointing at the beginning of the data, and
        **  set the uri end after we've determined where to put it.
        */
        if(start == *ptr)
            uri_ptr->uri = NULL;


        uri_ptr->first_sp_start = *ptr;
        uri_ptr->first_sp_end = NULL;

        start_sp = &uri_ptr->first_sp_start;
        end_sp = &uri_ptr->first_sp_end;
    }

    while(hi_util_in_bounds(start, end, *ptr))
    {
        /*
        **  Check for whitespace
        */
        if(**ptr == ' ')
        {
            (*ptr)++;
            continue;
        }
        else if((**ptr == '\t'))
        {
            if(ServerConf->apache_whitespace.on)
            {
                if(hi_eo_generate_event(Session, 
                                        ServerConf->apache_whitespace.alert))
                {
                    hi_eo_client_event_log(Session, HI_EO_CLIENT_APACHE_WS,
                                           NULL, NULL);
                }

                (*ptr)++;
                continue;
            }
            else
            {
                return NO_URI;
            }
        }
        else
        {
            /*
            **  This sets the sp_end for whatever space delimiter we are on,
            **  whether that is the first space or the second space.
            */
            *end_sp = *ptr;

            if(!IsHttpVersion(ptr, end))
            {
                /*
                **  This is the default method and what we've been doing
                **  since the start of development.
                */
                if(uri_ptr->second_sp_start)
                {
                    /*
                    **  There is no HTTP version indentifier at the beginning
                    **  of the second space, and this means that there is no
                    **  URI.
                    */
                    if(ServerConf->non_strict)
                    {
                        /*
                        **  In non-strict mode, we must assume the URI is
                        **  between the first and second space, so now
                        **  that we've seen the second space that's the
                        **  identifier.
                        */
                        uri_ptr->ident  = *end_sp;
                        uri_ptr->uri_end = *start_sp;
                    
                        return HI_SUCCESS;
                    }
                    else
                    {
                        /*
                        **  Since we are in strict mode here, it means that
                        **  we haven't seen a valid identifier, so there was
                        **  no URI.
                        */
                        return NO_URI;
                    }
                }

                /*
                **  RESET NECESSARY URI_PTRs HERE.  This is the place where
                **  the uri is updated.  It can only happen once, so do it
                **  right here.
                **
                **  When we get here it means that we have found the end of
                **  the FIRST whitespace, and that there was no delimiter,
                **  so we reset the uri pointers and other related 
                **  pointers.
                */
                uri_ptr->uri      = *end_sp;
                uri_ptr->uri_end  = end;
                uri_ptr->norm     = NULL;
                uri_ptr->last_dir = NULL;
                uri_ptr->param    = NULL;
                uri_ptr->proxy    = NULL;
            }
            else
            {
                /*
                **  Means we found the HTTP version identifier and we reset
                **  the uri_end pointer to point to the beginning of the
                **  whitespace detected.
                **
                **  This works for both "uri_is_here HTTP/1.0" and
                **  "METHOD uri_is_here HTTP/1.0", so it works when the
                **  identifier is after either the first or the second
                **  whitespace.
                */
                uri_ptr->ident   = *end_sp;
                uri_ptr->uri_end = *start_sp;
            }

            /*
            **  We found a non-whitespace char
            */
            return HI_SUCCESS;
        }
    }

    /*
    **  This is the case where we've seen text and found a whitespace until
    **  the end of the buffer.  In that case, we set the uri_end to the
    **  beginning of the whitespace.
    */
    uri_ptr->uri_end = *start_sp;

    return HI_OUT_OF_BOUNDS;
}
예제 #3
0
int HttpResponseInspection(HI_SESSION *Session, Packet *p, const unsigned char *data,
        int dsize, HttpSessionData *sd)
{
    HTTPINSPECT_CONF *ServerConf;
    URI_PTR stat_code_ptr;
    URI_PTR stat_msg_ptr;
    HEADER_PTR header_ptr;
    URI_PTR body_ptr;
    HI_SERVER *Server;

    const u_char *start;
    const u_char *end;
    const u_char *ptr;
    int len;
    int iRet = 0;
    int resp_header_size = 0;
    /* Refers to the stream reassembled packets when reassembly is turned on.
     * Refers to all packets when reassembly is turned off.
     */
    int not_stream_insert = 1;
#ifdef ZLIB
    int parse_cont_encoding = 1;
    int status;
#endif
    int expected_pkt = 0;
    int alt_dsize;
    uint32_t seq_num = 0;

    if (!Session || !p || !data || (dsize == 0))
        return HI_INVALID_ARG;

    ServerConf = Session->server_conf;
    if(!ServerConf)
        return HI_INVALID_ARG;


    Server = &(Session->server);
    clearHttpRespBuffer(Server);

    seq_num = GET_PKT_SEQ(p);

    if ( (sd != NULL) )
    {
        /* If the previously inspected packet in this session identified as a body
         * and if the packets are stream inserted wait for reassembled */
        if (sd->resp_state.inspect_reassembled)
        {
            if(p->packet_flags & PKT_STREAM_INSERT)
            {
#ifdef ZLIB
                parse_cont_encoding = 0;
#endif
                not_stream_insert = 0;
            }
        }
        /* If this packet is the next expected packet to be inspected and is out of sequence
         * clear out the resp state*/
#ifdef ZLIB
        if(( sd->decomp_state && sd->decomp_state->decompress_data) && parse_cont_encoding)
        {
            if( sd->resp_state.next_seq &&
                    (seq_num == sd->resp_state.next_seq) )
            {
                sd->resp_state.next_seq = seq_num + p->dsize;
                expected_pkt = 1;
            }
            else
            {
                ResetGzipState(sd->decomp_state);
                ResetRespState(&(sd->resp_state));
            }
        }
        else
#endif
        if(sd->resp_state.inspect_body && not_stream_insert)
        {
            /* If the server flow depth is 0 then we need to check if the packet
             * is in sequence
             */
            if(!ServerConf->server_flow_depth)
            {
                if( sd->resp_state.next_seq &&
                        (seq_num == sd->resp_state.next_seq) )
                {
                    sd->resp_state.next_seq = seq_num + p->dsize;
                    expected_pkt = 1;
                }
                else
                {
#ifdef ZLIB
                    ResetGzipState(sd->decomp_state);
#endif
                    ResetRespState(&(sd->resp_state));
                }
            }
            else 
            {
                /*Check if the sequence number of the packet is within the allowed
                 * flow_depth
                 */
                if( (sd->resp_state.is_max_seq) && 
                        SEQ_LT(seq_num, (sd->resp_state.max_seq)))
                {
                    expected_pkt = 1;
                }
                else
                {
#ifdef ZLIB
                    ResetGzipState(sd->decomp_state);
#endif
                    ResetRespState(&(sd->resp_state));
                }
            }

        }
    }


    memset(&stat_code_ptr, 0x00, sizeof(URI_PTR));
    memset(&stat_msg_ptr, 0x00, sizeof(URI_PTR));
    memset(&header_ptr, 0x00, sizeof(HEADER_PTR));
    memset(&body_ptr, 0x00, sizeof(URI_PTR));

    start = data;
    end = data + dsize;
    ptr = start;

    /* moving past the CRLF */

    while(hi_util_in_bounds(start, end, ptr))
    {
        if(*ptr < 0x21)
        {
            if(*ptr < 0x0E && *ptr > 0x08)
            {
                ptr++;
                continue;
            }
            else
            {
                if(*ptr == 0x20)
                {
                    ptr++;
                    continue;
                }
            }
        }

        break;
    }

    /*after doing this we need to basically check for version, status code and status message*/

    len = end - ptr;
    if ( len > 4 )
    {
        if(!IsHttpVersion(&ptr, end))
        { 
            if(expected_pkt)
            {
                ptr = start;
                p->packet_flags |= PKT_HTTP_DECODE;
            }
            else
            {
                p->packet_flags |= PKT_HTTP_DECODE;
                ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 0, seq_num);
                if ( not_stream_insert && (sd != NULL))
                {
#ifdef ZLIB
                    ResetGzipState(sd->decomp_state);
#endif
                    ResetRespState(&(sd->resp_state));
                }
                CLR_SERVER_HEADER(Server);
                return HI_SUCCESS;
            }
        }
        else
        {
            p->packet_flags |= PKT_HTTP_DECODE;
            /* This is a next expected packet to be decompressed but the packet is a
             * valid HTTP response. So the gzip decompression ends here */
            if(expected_pkt)
            {
                expected_pkt = 0;
#ifdef ZLIB
                ResetGzipState(sd->decomp_state);
#endif
                ResetRespState(&(sd->resp_state));
            }
            while(hi_util_in_bounds(start, end, ptr))
            {
                if (isspace((int)*ptr))
                    break;
                ptr++;
            }

        }
    }
    else if (!expected_pkt)
    {
        return HI_SUCCESS;
    }

    /*If this is the next expected packet to be decompressed, send this packet 
     * decompression */

    if (expected_pkt)
    {
        if (hi_util_in_bounds(start, end, ptr))
        {
            iRet = hi_server_inspect_body(Session, sd, ptr, end, &body_ptr);
        }
    }
    else
    {
        iRet = hi_server_extract_status_code(Session, start,ptr,end , &stat_code_ptr);

        if ( iRet == STAT_END )
        {
            Server->response.status_code = stat_code_ptr.uri;
            Server->response.status_code_size = stat_code_ptr.uri_end - stat_code_ptr.uri;
            if ( (int)Server->response.status_code_size <= 0)
            {
                CLR_SERVER_STAT(Server);
            }
            else
            {
                iRet = hi_server_extract_status_msg(start, stat_code_ptr.uri_end , 
                        end, &stat_msg_ptr);
    
                if ( stat_msg_ptr.uri )
                {
                    Server->response.status_msg = stat_msg_ptr.uri;
                    Server->response.status_msg_size = stat_msg_ptr.uri_end - stat_msg_ptr.uri;
                    if ((int)Server->response.status_msg_size <= 0)
                    {
                        CLR_SERVER_STAT(Server);
                    }
                    else
                    {
#ifdef ZLIB
                        ptr =  hi_server_extract_header(Session, ServerConf, &header_ptr,
                                            stat_msg_ptr.uri_end , end, parse_cont_encoding, sd );
#else
                        /* We dont need the content-encoding header when zlib is not enabled */
                        ptr =  hi_server_extract_header(Session, ServerConf, &header_ptr,
                                    stat_msg_ptr.uri_end , end, 0, sd );
#endif
                    }
                }
                else
                {
                    CLR_SERVER_STAT(Server);
                }
            }
     
            if (header_ptr.header.uri)
            {
                Server->response.header_raw = header_ptr.header.uri;
                Server->response.header_raw_size = 
                    header_ptr.header.uri_end - header_ptr.header.uri;
                if(!Server->response.header_raw_size)
                {
                    CLR_SERVER_HEADER(Server);
                }
                else
                {
                    resp_header_size = (header_ptr.header.uri_end - p->data);
                    hi_stats.resp_headers++;
                    Server->response.header_norm = header_ptr.header.uri;
                    if (header_ptr.cookie.cookie)
                    {
                        hi_stats.resp_cookies++;
                        Server->response.cookie.cookie = header_ptr.cookie.cookie;
                        Server->response.cookie.cookie_end = header_ptr.cookie.cookie_end;
                        Server->response.cookie.next = header_ptr.cookie.next;
                    }
                    else
                    {
                        Server->response.cookie.cookie = NULL;
                        Server->response.cookie.cookie_end = NULL;
                        Server->response.cookie.next = NULL;
                    }
                    if (sd != NULL)
                    {
#ifdef ZLIB
                        if( header_ptr.content_encoding.compress_fmt )
                        {
                            hi_stats.gzip_pkts++;

                            /* We've got gzip data - grab buffer from mempool and attach
                             * to session data if server is configured to do so */
                            if (sd->decomp_state == NULL)
                                SetGzipBuffers(sd, Session);

                            if (sd->decomp_state != NULL)
                            {
                                sd->decomp_state->decompress_data = 1;
                                sd->decomp_state->compress_fmt =
                                                            header_ptr.content_encoding.compress_fmt;
                            }

                        }
                        else
#endif
                        {
                            sd->resp_state.inspect_body = 1;
                        }

                        sd->resp_state.last_pkt_contlen = header_ptr.content_len.len;
                        if(ServerConf->server_flow_depth == -1)
                            sd->resp_state.is_max_seq = 0;
                        else
                        {
                            sd->resp_state.is_max_seq = 1;
                            sd->resp_state.max_seq = seq_num +
                                        (header_ptr.header.uri_end - start)+ ServerConf->server_flow_depth;
                        }

                        if (p->packet_flags & PKT_STREAM_INSERT)
                        {
                            if(header_ptr.content_len.cont_len_start && ((end - (header_ptr.header.uri_end)) >= header_ptr.content_len.len))
                            {
                                /* change this when the api is fixed to flush correctly */
                                //stream_api->response_flush_stream(p);
                                expected_pkt = 1;
                            }
                            else
                                sd->resp_state.inspect_reassembled = 1;

                        }
                        else
                        {
                            if(p->packet_flags & PKT_REBUILT_STREAM)
                                sd->resp_state.inspect_reassembled = 1;

                            expected_pkt = 1;
                        }
                        if(expected_pkt)
                        {
                            sd->resp_state.next_seq = seq_num + p->dsize;

                            if(hi_util_in_bounds(start, end, header_ptr.header.uri_end))
                            {
                                iRet = hi_server_inspect_body(Session, sd, header_ptr.header.uri_end,
                                                                end, &body_ptr);
                            }
                        }
                    }
                }
            }
            else
            {
                CLR_SERVER_HEADER(Server);

            }
        }
        else
        {
            CLR_SERVER_STAT(Server);
        }
    }

    if( body_ptr.uri )
    {
        Server->response.body = body_ptr.uri;
        Server->response.body_size = body_ptr.uri_end - body_ptr.uri;
        if( Server->response.body_size > 0)
        {
            if ( Server->response.body_size < sizeof(DecodeBuffer.data) )
            {
                alt_dsize = Server->response.body_size;
            }
            else
            {
                alt_dsize = sizeof(DecodeBuffer.data);
            }
#ifdef ZLIB
            if(sd->decomp_state && sd->decomp_state->decompress_data)
            {
                status = SafeMemcpy(DecodeBuffer.data, Server->response.body,
                                            alt_dsize, DecodeBuffer.data, DecodeBuffer.data + sizeof(DecodeBuffer.data));
                if( status != SAFEMEM_SUCCESS  )
                    return HI_MEM_ALLOC_FAIL;
                p->data_flags |= DATA_FLAGS_GZIP;
                SetAltDecode(p, alt_dsize);
                SetDetectLimit(p, alt_dsize);
            }
            else
#endif
            {
                if(sd->resp_state.last_pkt_chunked)
                {
                    p->data_flags |= DATA_FLAGS_RESP_BODY;
                    SetAltDecode(p, alt_dsize);
                    SetDetectLimit(p, alt_dsize);
                }
                else
                {
                    p->data_flags |= DATA_FLAGS_RESP_BODY;
                    p->packet_flags |= PKT_HTTP_RESP_BODY;
                    SetDetectLimit(p, (alt_dsize + resp_header_size));
                }
            }

            if (get_decode_utf_state_charset(&(sd->utf_state)) != CHARSET_DEFAULT)
            {
                if ( Server->response.body_size < sizeof(DecodeBuffer.data) )
                {
                    alt_dsize = Server->response.body_size;
                }           
                else
                {
                    alt_dsize = sizeof(DecodeBuffer.data);
                }
                SetDetectLimit(p, alt_dsize);
                SetAltDecode(p, alt_dsize);
            }
        }
        
    }
    else
    {
        /* There is no body to the HTTP response.
         * In this case we need to inspect the entire HTTP response header.
         */
        ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 1, seq_num);
    }

    return HI_SUCCESS;
}