Example #1
0
static INLINE int hi_server_extract_status_code(HI_SESSION *Session, const u_char *start, const u_char *ptr, 
        const u_char *end, URI_PTR *result)
{
    int iRet = HI_SUCCESS;
    SkipBlankSpace(start,end,&ptr);

    result->uri = ptr;

    while(  hi_util_in_bounds(start, end, ptr) )
    {
        if(isdigit((int)*ptr))
        {
            SkipDigits(start, end, &ptr);
            if (  hi_util_in_bounds(start, end, ptr) )
            {
                if(isspace((int)*ptr))
                {
                    result->uri_end = ptr;
                    iRet = STAT_END;
                    return iRet;
                }
                else
                {
                    iRet = HI_NONFATAL_ERR;
                    return iRet;
                }

            }
            else
            {
                iRet = HI_OUT_OF_BOUNDS;
                return iRet;
            }

        }
        else
        {

            if(hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_STATCODE))
            {
                hi_eo_server_event_log(Session, HI_EO_SERVER_INVALID_STATCODE, NULL, NULL);
            }
            ptr++;
        }
    }

    iRet = HI_OUT_OF_BOUNDS;

    return iRet;
}
Example #2
0
/**
**  Set the ub_ptr and update the URI_NORM_STATE.
**
**  The main point of this function is to take care of the details in
**  updating the directory stack and setting the buffer pointer to the
**  last directory.
**
**  @param norm_state pointer to the normalization state struct
**  @param ub_ptr     double pointer to the normalized buffer
**
**  @return integer
**
**  @retval HI_SUCCESS function successful
**
**  @see hi_norm_uri()
*/
static int DirTrav(HI_SESSION *Session, URI_NORM_STATE *norm_state,
                   u_char *ub_start,u_char **ub_ptr)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;

    hi_stats.dir_trav++;
    if(norm_state->dir_count)
    {
        *ub_ptr = norm_state->dir_track[norm_state->dir_count - 1];

        /*
        **  Check to make sure that we aren't at the beginning
        */
        if(norm_state->dir_count >= 1)
        {
            norm_state->dir_count--;
        }
    }
    else
    {
        /*
        **  This is a special case where there was no / seen before
        **  we see a /../.  When this happens, we just reset the ub_ptr
        **  back to the beginning of the norm buffer and let the slash
        **  get written on the next iteration of the loop.
        */
        *ub_ptr = ub_start;

        /*
        **  Let's put the alert here for webroot dir traversal.
        */
        if(hi_eo_generate_event(Session, ServerConf->webroot.alert) &&
           !norm_state->param)
        {
            hi_eo_client_event_log(Session, HI_EO_CLIENT_WEBROOT_DIR,
                                   NULL, NULL);
        }
    }

    return HI_SUCCESS;
}
Example #3
0
/**
**  This is the final GetByte routine.  The value that is returned from this
**  routine is the final decoded byte, and normalization can begin.  This
**  routine handles the double phase of decoding that IIS is fond of.
**
**  So to recap all the decoding up until this point.
**
**  The first phase is to call GetByte().  GetByte() returns the first stage
**  of decoding, which handles the UTF-8 decoding.  If we have decoded a
**  % of some type, then we head into DoubleDecode() if the ServerConf
**  allows it.
**
**  What returns from DoubleDecode is the final result.
**
**  @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 norm_state  the pointer to the URI norm state
**
**  @return integer
**
**  @retval END_OF_BUFFER  While decoding, the end of buffer was reached.
**  @retval char           The resultant decoded char.
**
**  @see DoubleDecode();
**  @see GetByte();
*/
static int GetDecodedByte(HI_SESSION *Session, const u_char *start,
                          const u_char *end, const u_char **ptr,
                          URI_NORM_STATE *norm_state, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iChar;

    iChar = GetByte(Session,start,end,ptr, norm_state,encodeType);
    if(iChar == END_OF_BUFFER)
        return END_OF_BUFFER;

    if(ServerConf->double_decoding.on && (u_char)iChar == '%')
    {
        iChar = DoubleDecode(Session,start,end,ptr,norm_state,encodeType);
    }

    /*
    **  Let's change '\' to '/' if possible
    */
    if(ServerConf->iis_backslash.on && (u_char)iChar == 0x5c)
    {
        if(hi_eo_generate_event(Session, ServerConf->iis_backslash.alert) &&
           !norm_state->param)
        {
            hi_eo_client_event_log(Session, HI_EO_CLIENT_IIS_BACKSLASH,
                                   NULL, NULL);
        }

        iChar = 0x2f;
    }

    if( (u_char)iChar == '+')
    {
        iChar = 0x20;
    }

    return iChar;
}
Example #4
0
/**
**  We check the directory length against the global config.
**  
**  @param Session pointer to the current session
**  @param uri_ptr pointer to the URI state
**  @param ptr     pointer to the current index in buffer
**  
**  @return integer
**  
**  @retval HI_SUCCESS
*/
static INLINE int CheckLongDir(HI_SESSION *Session, URI_PTR *uri_ptr, 
                               u_char *ptr)
{
    int iDirLen;

    /*
    **  Check for oversize directory
    */
    if(Session->server_conf->long_dir && uri_ptr->last_dir &&
       !uri_ptr->param)
    {
        iDirLen = ptr - uri_ptr->last_dir;

        if(iDirLen > Session->server_conf->long_dir &&
           hi_eo_generate_event(Session, HI_EO_CLIENT_OVERSIZE_DIR))
        {
            hi_eo_client_event_log(Session, HI_EO_CLIENT_OVERSIZE_DIR,
                                   NULL, NULL);
        }
    }

    return HI_SUCCESS;

}
Example #5
0
/**
**  This function inspects the normalized chars for any other processing
**  that we need to do, such as directory traversals.
**
**  The main things that we check for here are '/' and '?'.  There reason
**  for '/' is that we do directory traversals.  If it's a slash, we call
**  the routine that will normalize mutli-slashes, self-referential dirs,
**  and dir traversals.  We do all that processing here and call the
**  appropriate functions.
**
**  The '?' is so we can mark the parameter field, and check for oversize
**  directories one last time.  Once the parameter field is set, we don't
**  do any more oversize directory checks since we aren't in the url
**  any more.
**
**  @param Session      pointer to the current session
**  @param iChar        the char to inspect
**  @param norm_state   the normalization state
**  @param start        the start of the URI buffer
**  @param end          the end of the URI buffer
**  @param ptr          the address of the pointer index into the URI buffer
**  @param ub_start     the start of the norm buffer
**  @param ub_end       the end of the norm buffer
**  @param ub_ptr       the address of the pointer index into the norm buffer
**
**  @return integer
**
**  @retval END_OF_BUFFER    we've reached the end of the URI or norm buffer
**  @retval HI_NONFATAL_ERR  no special char, so just write the char and
**                           increment the ub_ptr.
**  @retval HI_SUCCESS       normalized the special char and already
**                           incremented the buffers.
*/
static inline int InspectUriChar(HI_SESSION *Session, int iChar,
                                 URI_NORM_STATE *norm_state,
                                 const u_char *start, const u_char *end, const u_char **ptr,
                                 u_char *ub_start, u_char *ub_end,
                                 u_char **ub_ptr, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iDir;

    /*
    **  Let's add absolute URI/proxy support everyone.
    */
    if(!norm_state->dir_count && (u_char)iChar == ':' &&
       hi_util_in_bounds(start, end, ((*ptr)+2)))
    {
        if(**ptr == '/' && *((*ptr)+1) == '/')
        {
            /*
            **  We've found absolute vodka.
            */
            if(!hi_util_in_bounds(ub_start, ub_end, ((*ub_ptr)+2)))
                return END_OF_BUFFER;

            /*
            **  Write the :
            */
            **ub_ptr = (u_char)iChar;
            (*ub_ptr)++;

            /*
            **  This increments us past the first slash, so at the next
            **  slash we will track a directory.
            **
            **  The reason we do this is so that an attacker can't trick
            **  us into normalizing a directory away that ended in a :.
            **  For instance, if we got a URL that was separated in by a
            **  packet boundary like this, and we were looking for the
            **  URL real_dir:/file.html:
            **    real_dir://obfuscate_dir/../file.html
            **  we would normalize it with proxy support to:
            **    /file.html
            **  because we never tracked the :// as a valid directory.  So
            **  even though this isn't the best solution, it is the best
            **  we can do given that we are working with stateless
            **  inspection.
            */
            (*ptr)++;

            return HI_SUCCESS;
        }
    }

    /*
    **  Now that we have the "true" byte, we check this byte for other
    **  types of normalization:
    **    -  directory traversals
    **    -  multiple slashes
    */
    if((u_char)iChar == '/')
    {
        /*
        **  First thing we do is check for a long directory.
        */
        CheckLongDir(Session, norm_state, *ub_ptr);

        iDir = DirNorm(Session, start, end, ptr, norm_state, encodeType);

        if(iDir == DIR_TRAV)
        {
            /*
            **  This is the case where we have a directory traversal.
            **
            **  The DirTrav function will reset the ub_ptr to the previous
            **  slash.  After that, we just continue through the loop because
            **  DirNorm has already set ptr to the slash, so we can just
            **  continue on.
            */
            DirTrav(Session,norm_state, ub_start, ub_ptr);
        }
        else
        {
            /*
            **  This is the case where we didn't have a directory traversal,
            **  and we are now just writing the char after the '/'.
            **
            **  We call DirSet, because all this function does is write a
            **  '/' into the buffer and increment the ub_ptr.  We then
            **  check the return code and return END_OF_BUFFER if
            **  needed.
            */
            DirSet(norm_state, ub_ptr);
            if(iDir == END_OF_BUFFER)
                return END_OF_BUFFER;

            /*
            **  We check the bounds before we write the next byte
            */
            if(!hi_util_in_bounds(ub_start, ub_end, *ub_ptr))
                return END_OF_BUFFER;

            /*
            **  Set the char to what we got in DirNorm()
            */
            /*
            **  Look for user-defined Non-Rfc chars.  If we find them
            **  then log an alert.
            */
            if(ServerConf->non_rfc_chars[(u_char)iDir])
            {
                if(hi_eo_generate_event(Session, HI_EO_CLIENT_NON_RFC_CHAR) &&
                   !norm_state->param)
                {
                    hi_eo_client_event_log(Session, HI_EO_CLIENT_NON_RFC_CHAR,
                                           NULL, NULL);
                }
            }

            **ub_ptr = (u_char)iDir;
            (*ub_ptr)++;
        }

        return HI_SUCCESS;
    }

    if((!byte_decoded && (u_char)iChar == '?'))
    {
        /*
        **  We assume that this is the beginning of the parameter field,
        **  and check for a long directory following.  Event though seeing
        **  a question mark does not guarantee the parameter field, thanks
        **  IIS.
        */
        CheckLongDir(Session, norm_state, *ub_ptr);
        norm_state->param = *ub_ptr;
    }

    /*
    **  This is neither char, so we just bail and let the loop finish
    **  for us.
    */
    return HI_NONFATAL_ERR;
}
Example #6
0
/**
**  The double decoding routine for IIS good times.
**
**  Coming into this function means that we just decoded a % or that
**  we just saw two percents in a row.  We know which state we are
**  in depending if the first char is a '%' or not.
**
**  In the IIS world, there are two decodes, but only some of the decode
**  options are valid.  All options are valid in the first decode
**  stage, but the second decode stage only supports:
**  -  %u encoding
**  -  ascii
**
**  Knowing this, we can decode appropriately.
**
**  @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 norm_state  the ptr to the URI norm state
**
**  @return integer
**
**  @retval NON_ASCII_CHAR  End of buffer reached while decoding
**  @retval char            The decoded char
*/
static int DoubleDecode(HI_SESSION *Session, const u_char *start,
                        const u_char *end, const u_char **ptr,
                        URI_NORM_STATE *norm_state, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iByte;
    int iNorm;

    *encodeType |= HTTP_ENCODE_TYPE__DOUBLE_ENCODE;

    /*
    **  We now know that we have seen a previous % and that we need to
    **  decode the remaining bytes.  We are in one of multiple cases:
    **
    **  -  %25xxxx
    **  -  %%xx%xx
    **  -  %u0025xxxx
    **  -  etc.
    **
    **  But, the one common factor is that they each started out with a
    **  % encoding of some type.
    **
    **  So now we just get the remaining bytes and do the processing
    **  ourselves in this routine.
    */
    iByte = GetByte(Session, start, end, ptr, norm_state, encodeType);
    if(iByte == END_OF_BUFFER)
        return NON_ASCII_CHAR;

    if(valid_lookup[(u_char)iByte] < 0)
    {
        if(ServerConf->u_encoding.on && (toupper(iByte) == 'U'))
        {
            iNorm = UDecode(Session, start, end, ptr, GetByte, norm_state, encodeType);

            if(iNorm == END_OF_BUFFER)
            {
                /*
                **  We have reached the end of the buffer while
                **  processing a U encoding.  We keep the current
                **  pointer and return a NON_ASCII char for the
                **  bad encoding.
                */
                return NON_ASCII_CHAR;
            }

            return iNorm;
        }

        return iByte;
    }

    iNorm = (hex_lookup[(u_char)iByte]<<4);

    iByte = GetByte(Session, start, end, ptr, norm_state, encodeType);
    if(iByte == END_OF_BUFFER)
        return NON_ASCII_CHAR;

    if(valid_lookup[(u_char)iByte] < 0)
    {
        return iByte;
    }

    iNorm = (iNorm | (hex_lookup[(u_char)iByte])) & 0xff;

    if(hi_eo_generate_event(Session, ServerConf->double_decoding.alert) &&
       (norm_state->param == NULL))
    {
        hi_eo_client_event_log(Session, HI_EO_CLIENT_DOUBLE_DECODE,
                               NULL, NULL);
    }
    byte_decoded = true;

    return iNorm;
}
Example #7
0
/*
**  Decode the UTF-8 sequences and check for valid codepoints via the
**  Unicode standard and the IIS standard.
**
**  We decode up to 3 bytes of UTF-8 because that's all I've been able to
**  get to work on various servers, so let's reduce some false positives.
**  So we decode valid UTF-8 sequences and then check the value.  If the
**  value is ASCII, then it's decoded to that.  Otherwise, if iis_unicode
**  is turned on, we will check the unicode codemap for valid IIS mappings.
**  If a mapping turns up, then we return the mapped ASCII.
**
**  @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 NON_ASCII_CHAR  Reached end of buffer while decoding
**  @retval char            return the decoded or badly decoded char
**
**  @see GetByte()
**  @see UnicodeDecode()
*/
static int UTF8Decode(HI_SESSION *Session, const u_char *start,
                      const u_char *end, const u_char **ptr, int iFirst,
                      URI_NORM_STATE *norm_state, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iBareByte;
    int iNorm;
    int iNumBytes;
    int iCtr;
    int iByte;

    /*
    **  Right now we support up to 3 byte unicode sequences.  We can add
    **  more if any of the HTTP servers support more.
    */
    if((iFirst & 0xe0) == 0xc0)
    {
        iNumBytes = 1;
        iNorm = iFirst & 0x1f;
    }
    else if((iFirst & 0xf0) == 0xe0)
    {
        iNumBytes = 2;
        iNorm = iFirst & 0x0f;
    }
    else
    {
        *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
        hi_stats.non_ascii++;
        /*
        **  This means that we have an invalid first sequence byte for
        **  a unicode sequence.  So we just return the byte and move on.
        */
        return iFirst;
    }

    /*
    **  This is the main loop for UTF-8 decoding.  We check for the only
    **  valid sequence after the first byte whish is 0x80.  Otherwise,
    **  it was invalid and we setnd a NON_ASCII_CHAR and continue on
    **  with our processing.
    */
    for(iCtr = 0; iCtr < iNumBytes; iCtr++)
    {
        iByte = GetChar(Session, start, end, ptr, &iBareByte, norm_state, encodeType);
        if(iByte == END_OF_BUFFER || iByte == NON_ASCII_CHAR || iBareByte)
            return NON_ASCII_CHAR;

        if((iByte & 0xc0) == 0x80)
        {
            iNorm <<= 6;
            iNorm |= (iByte & 0x3f);
        }
        else
        {
            *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
            hi_stats.non_ascii++;
            /*
            **  This means that we don't have a valid unicode sequence, so
            **  we just bail.
            */
            return NON_ASCII_CHAR;
        }
    }

    /*
    **  Check for unicode as ASCII and if there is not an ASCII char then
    **  we return the space holder char.
    */
    if(iNorm > 0x7f)
    {
        if(ServerConf->iis_unicode.on)
        {
            iNorm = ServerConf->iis_unicode_map[iNorm];

            if(iNorm == HI_UI_NON_ASCII_CODEPOINT)
            {
                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);
            }
            *encodeType |= HTTP_ENCODE_TYPE__IIS_UNICODE;

            hi_stats.unicode++;
            return iNorm;
        }
        else
        {
            *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
            hi_stats.non_ascii++;
            iNorm = NON_ASCII_CHAR;
        }
    }

    *encodeType |= HTTP_ENCODE_TYPE__UTF8_UNICODE;

    if(hi_eo_generate_event(Session, ServerConf->utf_8.alert) &&
       !norm_state->param)
    {
        hi_eo_client_event_log(Session, HI_EO_CLIENT_UTF_8,
                               NULL, NULL);
    }

    return iNorm;
}
Example #8
0
/**
**  Wrapper for PercentDecode() and handles the return values from
**  PercentDecode().
**
**  This really decodes the chars for UnicodeDecode().  If the char is
**  a percent then we process stuff, otherwise we just increment the
**  pointer and return.
**
**  @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 bare_byte   value for a non-ASCII char or a decoded non-ASCII char
**
**  @return integer
**
**  @retval END_OF_BUFFER   End of the buffer has been reached before decode.
**  @retval NON_ASCII_CHAR  End of buffer during decoding, return decoded char.
**  @retval char            return the valid decoded/undecoded char
**
**  @see PercentDecode()
**  @see GetByte()
*/
static int GetChar(HI_SESSION *Session, const u_char *start,
                   const u_char *end, const u_char **ptr, int *bare_byte,
                   URI_NORM_STATE *norm_state, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iNorm;

    if(!hi_util_in_bounds(start, end, *ptr))
        return END_OF_BUFFER;

    iNorm = (int)(**ptr);

    if(**ptr == '%' && ServerConf->ascii.on)
    {
        /*
        **  We go into percent encoding.
        */
        iNorm = PercentDecode(Session, start, end, ptr, norm_state, encodeType);

        /*
        **  If during the course of PercentDecode() we run into the end
        **  of the buffer, then we return early (WITHOUT INCREMENTING ptr)
        **  with a NON_ASCII_CHAR.
        */
        if(iNorm == END_OF_BUFFER)
            return NON_ASCII_CHAR;

        *bare_byte = 0;
    }
    else
    {
        if(ServerConf->bare_byte.on && (u_char)iNorm > 0x7f)
        {
            *encodeType |= HTTP_ENCODE_TYPE__BARE_BYTE;
            if(hi_eo_generate_event(Session, ServerConf->bare_byte.alert) &&
               !norm_state->param)
            {
                hi_eo_client_event_log(Session, HI_EO_CLIENT_BARE_BYTE,
                                       NULL, NULL);
            }

            /*
            **  Set the bare_byte flag
            */
            *bare_byte = 0;
        }
        else
        {
            /*
            **  Set the bare_byte flag negative.
            */
            *bare_byte = 1;
        }
    }

    /*
    **  Increment the buffer.
    */
    (*ptr)++;

    return iNorm;
}
Example #9
0
/**
**  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, uint16_t *encodeType)
{
    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, encodeType);
    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, encodeType);

            /*
            **  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)
                {
                    *encodeType |= HTTP_ENCODE_TYPE__DOUBLE_ENCODE;
                    *ptr = orig_ptr;
                    return (int)**ptr;
                }
            }

            return iNorm;
        }
        else
        {
            *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
            hi_stats.non_ascii++;
            return NON_ASCII_CHAR;
        }
    }

    iNorm = (hex_lookup[(u_char)iByte]<<4);
    iByte = GetPtr(Session, start, end, ptr, norm_state, encodeType);
    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)
    {
        *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
        hi_stats.non_ascii++;
        return NON_ASCII_CHAR;
    }

    iNorm = (iNorm | (hex_lookup[(u_char)iByte])) & 0xff;

    *encodeType |= HTTP_ENCODE_TYPE__ASCII;
    byte_decoded = true;

    if(hi_eo_generate_event(Session,ServerConf->ascii.alert) &&
       !norm_state->param)
    {
        hi_eo_client_event_log(Session, HI_EO_CLIENT_ASCII,
                               NULL, NULL);
    }

    return iNorm;
}
Example #10
0
/**
**  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, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iByte;
    int iNorm;
    int iCtr;

    iNorm = 0;
    *encodeType |= HTTP_ENCODE_TYPE__UENCODE;
    hi_stats.unicode++;

    for(iCtr = 0; iCtr < 4; iCtr++)
    {
        iByte = get_byte(Session, start, end, ptr, norm_state, encodeType);
        if(iByte & GET_ERR)
            return iByte;

        if(valid_lookup[(u_char)iByte] < 0)
        {
            *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
            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)
            {
                *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
                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);
            }
            *encodeType |= HTTP_ENCODE_TYPE__IIS_UNICODE;
        }
        else
        {
            *encodeType |= HTTP_ENCODE_TYPE__NONASCII;
            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);
    }

    byte_decoded = true;

    return iNorm;
}
Example #11
0
/**
**  Normalize the URI into the URI normalize buffer.
**
**  This is the routine that users call to normalize the URI.  It iterates
**  through the URI buffer decoding the next character and is then checked
**  for any directory problems before writing the decoded character into the
**  normalizing buffer.
**
**  We return the length of the normalized URI buffer in the variable,
**  uribuf_size.  This value is passed in as the max size of the normalization
**  buffer, which we then set in iMaxUriBufSize for later reference.
**
**  If there was some sort of problem during normalizing we set the normalized
**  URI buffer size to 0 and return HI_NONFATAL_ERR.
**
**  @param ServerConf   the pointer to the server configuration
**  @param uribuf       the pointer to the normalize uri buffer
**  @param uribuf_size  the size of the normalize buffer
**  @param uri          the pointer to the unnormalized uri buffer
**  @param uri_size     the size of the unnormalized uri buffer
**
**  @return integer
**
**  @retval HI_NONFATAL_ERR there was a problem during normalizing, the
**                          uribuf_size is also set to 0
**  @retval HI_SUCCESS      Normalizing the URI was successful
*/
int hi_norm_uri(HI_SESSION *Session, u_char *uribuf, int *uribuf_size,
                const u_char *uri, int uri_size, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf;
    int iChar;
    int iRet;
    int iMaxUriBufSize;
    URI_NORM_STATE norm_state;
    u_char *ub_ptr;
    const u_char *ptr;
    const u_char *start;
    const u_char *end;
    u_char *ub_start;
    u_char *ub_end;

    ServerConf = Session->server_conf;

    iMaxUriBufSize = *uribuf_size;

    start = uri;
    end   = uri + uri_size;
    ub_start = uribuf;
    ub_end   = uribuf + iMaxUriBufSize;

    ub_ptr = uribuf;
    ptr    = uri;

    /*
    **  Initialize the URI directory normalization state
    */
    norm_state.dir_count = 0;
    norm_state.param     = NULL;

    while(hi_util_in_bounds(ub_start, ub_end, ub_ptr))
    {
        byte_decoded = false;

        iChar = GetDecodedByte(Session, start, end, &ptr, &norm_state, encodeType);
        if(iChar == END_OF_BUFFER)
            break;

        /*
        **  Look for user-defined Non-Rfc chars.  If we find them
        **  then log an alert.
        */
        if(ServerConf->non_rfc_chars[(u_char)iChar])
        {
            if(hi_eo_generate_event(Session, HI_EO_CLIENT_NON_RFC_CHAR) &&
               !norm_state.param)
            {
                hi_eo_client_event_log(Session, HI_EO_CLIENT_NON_RFC_CHAR,
                                       NULL, NULL);
            }
        }

        iRet = InspectUriChar(Session, iChar, &norm_state, start, end, &ptr,
                              ub_start, ub_end, &ub_ptr, encodeType);
        if (iRet)
        {
            if(iRet == END_OF_BUFFER)
                break;

            /*
            **  This is the default case when we don't want anything to do with
            **  the char besides writing the value into the buffer.
            */
            *ub_ptr = (u_char)iChar;
            ub_ptr++;
        }
    }

    /*
    **  Now that we are done, let's make sure that we didn't just have a
    **  single large directory, with the rest in the next packet.
    */
    CheckLongDir(Session, &norm_state, ub_ptr);

    /*
    **  This means that we got to the end of the URI, so we set the length,
    **  check it, and move on.
    */
    *uribuf_size = ub_ptr - ub_start;

    if(*uribuf_size > uri_size || *uribuf_size < 1)
        return HI_NONFATAL_ERR;

    return HI_SUCCESS;
}
Example #12
0
static INLINE int hi_server_extract_body(
                        HI_SESSION *Session, HttpSessionData *sd,
                        const u_char *ptr, const u_char *end, URI_PTR *result)
{
    HTTPINSPECT_CONF *ServerConf;
    const u_char *start = ptr;
    int iRet = HI_SUCCESS;
    const u_char *post_end = end; 
    int chunk_size = 0;
    int chunk_read = 0;
    int bytes_to_read = 0;
    ServerConf = Session->server_conf;

    switch(ServerConf->server_flow_depth)
    {
        case -1: 
            result->uri = result->uri_end = NULL;
            return iRet;
        case 0:
            break;
        default:
            if(sd->resp_state.flow_depth_read < ServerConf->server_flow_depth)
            {
                bytes_to_read = ServerConf->server_flow_depth - sd->resp_state.flow_depth_read;
                if((end-ptr) > bytes_to_read )
                {
                    end = ptr + bytes_to_read;
                }
                sd->resp_state.flow_depth_read +=bytes_to_read;
            }
            else
            {
                result->uri = result->uri_end = NULL;
                return iRet;
            }
    }

/*    if( ServerConf->server_flow_depth && ((end - ptr) > ServerConf->server_flow_depth) )
    {
        end = ptr + ServerConf->server_flow_depth;
    }*/

    if (!(sd->resp_state.last_pkt_contlen))
    {
        if( ServerConf->chunk_length )
        {
            if(CheckChunkEncoding(Session, start, end, &post_end, (u_char *)DecodeBuffer.data, sizeof(DecodeBuffer.data),
                                    sd->resp_state.last_chunk_size, &chunk_size, &chunk_read ) == 1)
            {
                sd->resp_state.last_chunk_size = chunk_size;
                sd->resp_state.last_pkt_chunked = 1;
                result->uri = (u_char *)DecodeBuffer.data;
                result->uri_end = result->uri + chunk_read;
                return iRet;
            }
            else
            {
                if(!(sd->resp_state.last_pkt_chunked))
                {
                    if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN))
                    {
                        hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL);
                    }
                }
                else
                {
                    sd->resp_state.last_pkt_chunked = 0;
                    sd->resp_state.last_chunk_size = 0;
                }
                result->uri = start;
                result->uri_end = end;
            }
        }
        else
        {
            result->uri = start;
            result->uri_end = end;
            return iRet;
        }
    }

    result->uri = start;
    result->uri_end = end;

    return STAT_END;
}
Example #13
0
/**
**  The main function for dealing with multiple slashes, self-referential
**  directories, and directory traversals.
**
**  This routine does GetDecodedByte() while looking for directory foo.  It's
**  called every time that we see a slash in the main hi_norm_uri.  Most of
**  the time we just enter this loop, find a non-directory-foo char and
**  return that char.  hi_norm_uri() takes care of the directory state
**  updating and so forth.
**
**  But when we run into trouble with directories, this function takes care
**  of that.  We loop through multiple slashes until we get to the next
**  directory.  We also loop through self-referential directories until we
**  get to the next directory.  Then finally we deal with directory
**  traversals.
**
**  With directory traversals we do a kind of "look ahead".  We verify that
**  there is indeed a directory traversal, and then set the ptr back to
**  the beginning of the '/', so when we iterate through hi_norm_uri() we
**  catch it.
**
**  The return value for this function is usually the character after
**  the directory.  When there was a directory traversal, it returns the
**  value DIR_TRAV.  And when END_OF_BUFFER is returned, it means that we've
**  really hit the end of the buffer, or we were looping through multiple
**  slashes and self-referential directories until the end of the URI
**  buffer.
**
**  @param ServerConf   pointer to the Server configuration
**  @param start        pointer to the start of the URI buffer
**  @param end          pointer to the end of the URI buffer
**  @param ptr          pointer to the index in the URI buffer
**
**  @return integer
**
**  @retval END_OF_BUFFER   we've reached the end of buffer
**  @retval DIR_TRAV        we found a directory traversal
**  @retval char            return the next char after the directory
**
**  @see hi_norm_uri()
**  @see GetDecodedByte()
*/
static int DirNorm(HI_SESSION *Session, const u_char *start, const u_char *end,
                   const u_char **ptr, URI_NORM_STATE *norm_state, uint16_t *encodeType)
{
    HTTPINSPECT_CONF *ServerConf = Session->server_conf;
    int iChar;
    int iDir;
    const u_char *orig_ptr;
    const u_char *dir_ptr;
    // save the directory path here to check for unicode attack

    while((iChar = GetDecodedByte(Session, start, end, ptr, norm_state, encodeType)) !=
          END_OF_BUFFER)
    {
        orig_ptr = *ptr;

        /*
        **  This is kind of a short cut to get out of here as soon as we
        **  can.  If the character is over 0x2f then we know that is can't
        **  be either the '.' or the '/', so we break and return the
        **  char.
        */
        if((u_char)iChar < 0x30)
        {
            /*
            **  We check for multiple slashes.  If we find multiple slashes
            **  then we just continue on until we find something interesting.
            */
            if(ServerConf->multiple_slash.on && (u_char)iChar == '/')
            {
                hi_stats.slashes++;
                if(hi_eo_generate_event(Session,
                                        ServerConf->multiple_slash.alert) &&
                   !norm_state->param)
                {
                    hi_eo_client_event_log(Session,
                                           HI_EO_CLIENT_MULTI_SLASH,
                                           NULL, NULL);
                }

                continue;
            }
            /*
            **  This is where we start looking for self-referential dirs
            **  and directory traversals.
            */
            else if(ServerConf->directory.on && (u_char)iChar == '.' &&
                    !norm_state->param)
            {
                iDir = GetDecodedByte(Session,start,end,ptr,norm_state,encodeType);
                if(iDir != END_OF_BUFFER)
                {
                    if((u_char)iDir == '.')
                    {
                        /*
                        **  This sets the dir_ptr to the beginning of the
                        **  byte that may be a dir.  So if it is a slash,
                        **  we can get back to that slash and continue
                        **  processing.
                        */
                        dir_ptr = *ptr;

                        iDir = GetDecodedByte(Session,start,end,ptr,norm_state,encodeType);
                        if(iDir != END_OF_BUFFER)
                        {
                            if((u_char)iDir == '/')
                            {
                                hi_stats.self_ref++;
                                /*
                                **  We found a real live directory traversal
                                **  so we reset the pointer to before the
                                **  '/' and finish up after the return.
                                */
                                if(hi_eo_generate_event(Session,
                                             ServerConf->directory.alert) &&
                                   !norm_state->param)
                                {
                                    hi_eo_client_event_log(Session,
                                                         HI_EO_CLIENT_DIR_TRAV,
                                                         NULL, NULL);
                                }

                                *ptr = dir_ptr;
                                return DIR_TRAV;
                            }
                        }

                        *ptr = orig_ptr;
                        return iChar;
                    }
                    else if((u_char)iDir == '/')
                    {
                        /*
                        **  We got a self-referential directory traversal.
                        **
                        **  Keep processing until we stop seeing self
                        **  referential directories.
                        */
                        if(hi_eo_generate_event(Session,
                                                ServerConf->directory.alert) &&
                           !norm_state->param)
                        {
                            hi_eo_client_event_log(Session,
                                                   HI_EO_CLIENT_SELF_DIR_TRAV,
                                                   NULL, NULL);
                        }

                        continue;
                    }
                }

                /*
                **  This means that we saw '.' and then another char, so
                **  it was just a file/dir that started with a '.'.
                */
                *ptr = orig_ptr;
                return iChar;
            }
        }

        /*
        **  This is where we write the chars after the slash
        */
        return iChar;
    }

    return END_OF_BUFFER;
}
Example #14
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;
}
Example #15
0
/**
**  Find the URI and determine whether the URI needs to be normalized.
**  
**  This is a big step in stateless inspection, because we need to reliably
**  find the URI and when possible filter out non-URIs.  We do this using a
**  simple state machine that is based on characters found in the data
**  buffer.
**  
**  Another important aspect of the stateless inspection is the ability to
**  track and inspect pipelined requests.  It is VERY IMPORTANT to reset the
**  pipeline_req pointer, since we don't memset the whole structure.  This
**  pointer is reset in the hi_si_session_inspection() function.  Check there
**  for more details.
**  
**  Normalization is detected when we are looking at the packet for the URI.
**  We look for the following issues:
**      - ////
**      - /../
**      - /./
**      - non-ascii charss
**      - %
**      - \
**  When these things are seen we point to the first occurence in the URI, or
**  where we have to start normalizing.  If the URI is updated to a new
**  pointer, then the normalization pointer is reset and we start over.
**  Using this method should cut down the memcpy()s per URI, since most
**  URIs are not normalized.
**  
**  If this function returns HI_NONFATAL_ERR, we return out of mode_inspection
**  with an error and abort HttpInspect processing, and continue on with
**  any other processing we do.  The Session parameters that we use here are
**  reset in the next time that we do session_inspection, so we don't do
**  any initialization here.
**  
**  @param Session pointer to the HTTP session
**  @param data    pointer to the start of the packet payload
**  @param dsize   size of the payload
**  
**  @return integer
**  
**  @retval HI_INVALID_ARG  invalid argument
**  @retval HI_NONFATAL_ERR no URI detected
**  @retval HI_SUCCESS      URI detected and Session pointers updated
*/
static int StatelessInspection(HI_SESSION *Session, unsigned char *data,
        int dsize)
{
    HTTPINSPECT_CONF *ServerConf;
    HTTPINSPECT_CONF *ClientConf;
    HI_CLIENT *Client;
    URI_PTR uri_ptr;
    u_char *start;
    u_char *end;
    u_char *ptr;
    int iRet;

    if(!Session || !data || dsize < 1)
    {
        return HI_INVALID_ARG;
    }

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

    ClientConf = Session->client_conf;
    if(!ClientConf)
    {
        return HI_INVALID_ARG;
    }

    Client = &Session->client;

    memset(&uri_ptr, 0x00, sizeof(URI_PTR));

    /*
    **  We set the starting boundary depending on whether this request is
    **  a normal request or a pipeline request.  The end boundary is always
    **  the same whether it is a pipeline request or other.
    */
    if(Client->request.pipeline_req)
    {
        start = Client->request.pipeline_req;
    }
    else
    {
        start = data;
    }

    end   = data + dsize;

    ptr = start;

    /*
    **  Apache and IIS strike again . . . Thanks Kanatoko
    **    - Ignore CRLFs at the beginning of the request.
    */
    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;
    }

    uri_ptr.uri = ptr;
    uri_ptr.uri_end = end;

    /*
    **  This loop compares each char to an array of functions
    **  (one for each char) and calling that function if there is one.
    **  
    **  If there is no function, then we just increment the char ptr and
    **  continue processing.
    **
    **  If there is a function, we call that function and process.  It's
    **  important to note that the function that is called is responsible
    **  for incrementing the ptr to the next char to be inspected.  The
    **  loop does not increment the pointer when a function is called to
    **  allow the maximum flexibility to the functions.
    */
    while(hi_util_in_bounds(start, end, ptr))
    {
        if(lookup_table[*ptr])
        {
            if((iRet = (lookup_table[*ptr])(Session, start, end,
                            &ptr, &uri_ptr)))
            {
                if(iRet == URI_END)
                {
                    /*
                    **  You found a URI, let's break and check it out.
                    */
                    break;
                }
                else if(iRet == HI_OUT_OF_BOUNDS)
                {
                    /*
                    **  Means you've reached the end of the buffer.  THIS
                    **  DOESN'T MEAN YOU HAVEN'T FOUND A URI.
                    */
                    break;
                }
                else /* NO_URI */
                {
                    /*
                    **  Check for chunk encoding, because the delimiter can
                    **  also be a space, which would look like a pipeline request
                    **  to us if we don't do this first.
                    */
                    if(Session->server_conf->chunk_length)
                            CheckChunkEncoding(Session, start, end);

                    /*
                    **  We only inspect the packet for another pipeline
                    **  request if there wasn't a previous pipeline request.
                    **  The reason that we do this is because 
                    */  
                    if(!Client->request.pipeline_req)
                    {
                        /*
                        **  Just because there was no URI in the first part
                        **  the packet, doesn't mean that this isn't a
                        **  pipelined request that has been segmented.
                        */
                        if(!ServerConf->no_pipeline)
                        {
                            if((Client->request.pipeline_req =
                                FindPipelineReq(ptr, end)))
                            {
                                return HI_SUCCESS;
                            }
                        }
                    }

                    return HI_NONFATAL_ERR;
                }
            }
            else
            {
                /*
                **  This means that we found the next non-whitespace char
                **  and since we are already pointed there, so we just
                **  continue.
                */
                continue;
            }
        }

        ptr++;
    }

    /*
    **  If there is a pipelined request in this packet, we should always
    **  see the first space followed by text (which is the URI).  Without
    **  that first space, then we never get to the URI, so we should just
    **  return, since there is nothing else to inspect.
    */
    if(Client->request.pipeline_req)
    {
        if(uri_ptr.uri != uri_ptr.first_sp_end)
        {
            if(Session->server_conf->chunk_length)
                CheckChunkEncoding(Session, start, end);

            return HI_NONFATAL_ERR;
        }
    }

    /*
    **  We set the HI_CLIENT variables from the URI_PTR structure.  We also
    **  do error checking for the values in this routine as well.
    */
    if((iRet = SetClientVars(Client, &uri_ptr, dsize)))
    {
        return iRet;
    }

    /*
    **  One last check for an oversize directory.  This gets the long
    **  directory when there is a beginning slash and no other slashes
    **  until the end of the packet.
    **
    **  We do this check after we set the variables, just in case there
    **  was some errors while setting the variables.  This could save some
    **  false positives on a bad URI setting.
    */
    CheckLongDir(Session, &uri_ptr, ptr);

    /*
    **  Check for absolute URI and alert for proxy comm if necessary
    **
    **  NOTE:
    **    Also check ClientConf for proxy configuration so we don't
    **    alert on outbound requests from legitimate proxies.
    */
    if(uri_ptr.proxy && Session->global_conf->proxy_alert &&
       (!ServerConf->allow_proxy && !ClientConf->allow_proxy))
    {
        if(hi_eo_generate_event(Session, HI_EO_CLIENT_PROXY_USE))
        {
            hi_eo_client_event_log(Session, HI_EO_CLIENT_PROXY_USE, 
                    NULL, NULL);
        }
    }

    /*
    **  Find the next pipeline request, if one is there.  If we don't find
    **  a pipeline request, then we return NULL here, so this is always
    **  set to the correct value.
    */
    if(!ServerConf->no_pipeline)
    {
        Client->request.pipeline_req = FindPipelineReq(uri_ptr.delimiter, end);
    }
    else
    {
        Client->request.pipeline_req = NULL;
    }

    if(Session->server_conf->chunk_length)
        CheckChunkEncoding(Session, uri_ptr.delimiter, end);

    return HI_SUCCESS;
}
Example #16
0
/**
**  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' || *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;
}
Example #17
0
static INLINE int hi_server_decompress(HI_SESSION *Session, HttpSessionData *sd, const u_char *ptr, 
        const u_char *end, URI_PTR *result)
{
    const u_char *start = ptr;
    int rawbuf_size = end - ptr;
    int iRet = HI_SUCCESS;
    int zRet = HI_FATAL_ERR;
    int compr_depth, decompr_depth;
    int compr_bytes_read, decompr_bytes_read;
    int compr_avail, decompr_avail;
    int total_bytes_read = 0;
    int chunk_size = 0;
    int chunk_read = 0;

    u_char *compr_buffer;
    u_char *decompr_buffer;
    compr_depth = sd->decomp_state->compr_depth;
    decompr_depth = sd->decomp_state->decompr_depth;
    compr_bytes_read = sd->decomp_state->compr_bytes_read;
    decompr_bytes_read = sd->decomp_state->decompr_bytes_read;
    compr_buffer = sd->decomp_state->compr_buffer;
    decompr_buffer = sd->decomp_state->decompr_buffer;

    if(Session->server_conf->unlimited_decompress)
    {
        compr_avail = compr_depth;
        decompr_avail = decompr_depth;
    }
    else
    {
        compr_avail = compr_depth-compr_bytes_read;
        decompr_avail = decompr_depth - decompr_bytes_read;
    }

    /* Apply the server flow depth
     * If the server flow depth is set then we need to decompress only upto the 
     * server flow depth
     */
    switch ( Session->server_conf->server_flow_depth)
    {
        case -1:
            decompr_avail=0;
            break;
        case 0:
            break;
        default:
            if(sd->resp_state.flow_depth_read < Session->server_conf->server_flow_depth)
            {
                if(decompr_avail > (Session->server_conf->server_flow_depth - sd->resp_state.flow_depth_read))
                    decompr_avail = Session->server_conf->server_flow_depth - sd->resp_state.flow_depth_read;
            }
            else
            {
                decompr_avail = 0;
            }
            break;
    }

    if(compr_avail <=0 || decompr_avail <=0 ||
            (!compr_buffer) || (!decompr_buffer))
    {
        ResetGzipState(sd->decomp_state);
        ResetRespState(&(sd->resp_state));
        return iRet;
    }


    if(rawbuf_size < compr_avail)
    {
        compr_avail = rawbuf_size;
    }

    if(!(sd->resp_state.last_pkt_contlen))
    {
        if(CheckChunkEncoding(Session, start, end, NULL, compr_buffer, compr_avail, 
                    sd->resp_state.last_chunk_size, &chunk_size, &chunk_read ) == 1)
        {
            sd->resp_state.last_chunk_size = chunk_size;
            compr_avail = chunk_read;
            zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, &total_bytes_read,
                                    sd->decomp_state->compress_fmt);
        }
        else
        {
            /* No Content-Length or Transfer-Encoding : chunked */
            if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN))
            {
                hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL);
            }

            memcpy(compr_buffer, ptr, compr_avail);
            zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, 
                    &total_bytes_read, sd->decomp_state->compress_fmt);
        }
    }
    else
    {
        memcpy(compr_buffer, ptr, compr_avail);
        zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, 
                &total_bytes_read, sd->decomp_state->compress_fmt);
    }
    
    sd->decomp_state->compr_bytes_read += compr_avail;
    hi_stats.compr_bytes_read += compr_avail;

    if((zRet == HI_SUCCESS) || (zRet == HI_NONFATAL_ERR))
    {
        if(decompr_buffer)
        {
            result->uri = decompr_buffer;
            if ( total_bytes_read < decompr_avail )
            {
                result->uri_end = decompr_buffer + total_bytes_read;
                sd->decomp_state->decompr_bytes_read += total_bytes_read;
                sd->resp_state.flow_depth_read += total_bytes_read;
                hi_stats.decompr_bytes_read += total_bytes_read;
            }
            else
            {
                result->uri_end = decompr_buffer + decompr_avail;
                sd->decomp_state->decompr_bytes_read += decompr_avail;
                sd->resp_state.flow_depth_read += decompr_avail;
                hi_stats.decompr_bytes_read += decompr_avail;
            }
        }
    }
    else
    {
        ResetGzipState(sd->decomp_state);
        ResetRespState(&(sd->resp_state));
    }

    return iRet;


}