Exemple #1
0
/**
 * Parses request headers.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_HEADERS(htp_connp_t *connp) {
    for (;;) {
        IN_COPY_BYTE_OR_RETURN(connp);

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            unsigned char *data;
            size_t len;

            htp_connp_req_consolidate_data(connp, &data, &len);

            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __FUNCTION__, data, len);
            #endif           

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, data, len)) {
                // Parse previous header, if any.
                if (connp->in_header != NULL) {
                    if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header),
                            bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->in_header);
                    connp->in_header = NULL;
                }

                htp_connp_req_clear_buffer(connp);

                // We've seen all the request headers.
                return htp_tx_state_request_headers(connp->in_tx);
            }

            htp_chomp(data, &len);

            // Check for header folding.
            if (htp_connp_is_line_folded(data, len) == 0) {
                // New header line.

                // Parse previous header, if any.
                if (connp->in_header != NULL) {
                    if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header),
                            bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->in_header);
                    connp->in_header = NULL;
                }

                IN_PEEK_NEXT(connp);

                if (htp_is_folding_char(connp->in_next_byte) == 0) {
                    // Because we know this header is not folded, we can process the buffer straight away.
                    if (connp->cfg->process_request_header(connp, data, len) != HTP_OK) return HTP_ERROR;
                } else {
                    // Keep the partial header data for parsing later.
                    connp->in_header = bstr_dup_mem(data, len);
                    if (connp->in_header == NULL) return HTP_ERROR;
                }
            } else {
                // Folding; check that there's a previous header line to add to.
                if (connp->in_header == NULL) {
                    // Invalid folding.

                    // Warn only once per transaction.
                    if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->in_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid request field folding");
                    }

                    // Keep the header data for parsing later.
                    connp->in_header = bstr_dup_mem(data, len);
                    if (connp->in_header == NULL) return HTP_ERROR;
                } else {
                    // Add to the existing header.                    
                    bstr *new_in_header = bstr_add_mem(connp->in_header, data, len);
                    if (new_in_header == NULL) return HTP_ERROR;
                    connp->in_header = new_in_header;
                }
            }

            htp_connp_req_clear_buffer(connp);
        }
    }

    return HTP_ERROR;
}
Exemple #2
0
/**
 * Parses response headers.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_RES_HEADERS(htp_connp_t *connp) {
    for (;;) {
        OUT_COPY_BYTE_OR_RETURN(connp);       
        
        // Have we reached the end of the line?
        if (connp->out_next_byte == LF) {
            unsigned char *data;
            size_t len;

            htp_connp_res_consolidate_data(connp, &data, &len);

            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __FUNCTION__, data, len);
            #endif

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, data, len)) {
                // Parse previous header, if any.
                if (connp->out_header != NULL) {
                    if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header),
                            bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->out_header);
                    connp->out_header = NULL;
                }
                
                htp_connp_res_clear_buffer(connp);               

                // We've seen all response headers.
                if (connp->out_tx->progress == HTP_RESPONSE_HEADERS) {
                    // Response headers.

                    // The next step is to determine if this response has a body.
                    connp->out_state = htp_connp_RES_BODY_DETERMINE;
                } else {
                    // Response trailer.

                    // Finalize sending raw trailer data.
                    htp_status_t rc = htp_connp_res_receiver_finalize_clear(connp);
                    if (rc != HTP_OK) return rc;

                    // Run hook response_TRAILER.
                    rc = htp_hook_run_all(connp->cfg->hook_response_trailer, connp);
                    if (rc != HTP_OK) return rc;

                    // The next step is to finalize this response.
                    connp->out_state = htp_connp_RES_FINALIZE;
                }

                return HTP_OK;
            }

            htp_chomp(data, &len);

            // Check for header folding.
            if (htp_connp_is_line_folded(data, len) == 0) {
                // New header line.

                // Parse previous header, if any.
                if (connp->out_header != NULL) {
                    if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header),
                            bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->out_header);
                    connp->out_header = NULL;
                }

                OUT_PEEK_NEXT(connp);

                if (htp_is_folding_char(connp->out_next_byte) == 0) {
                    // Because we know this header is not folded, we can process the buffer straight away.
                    if (connp->cfg->process_response_header(connp, data, len) != HTP_OK) return HTP_ERROR;
                } else {
                    // Keep the partial header data for parsing later.
                    connp->out_header = bstr_dup_mem(data, len);
                    if (connp->out_header == NULL) return HTP_ERROR;
                }
            } else {
                // Folding; check that there's a previous header line to add to.
                if (connp->out_header == NULL) {
                    // Invalid folding.

                    // Warn only once per transaction.
                    if (!(connp->out_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->out_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response field folding");
                    }

                    // Keep the header data for parsing later.
                    connp->out_header = bstr_dup_mem(data, len);
                    if (connp->out_header == NULL) return HTP_ERROR;
                } else {
                    // Add to the existing header.                    
                    bstr *new_out_header = bstr_add_mem(connp->out_header, data, len);
                    if (new_out_header == NULL) return HTP_ERROR;
                    connp->out_header = new_out_header;
                }
            }

            htp_connp_res_clear_buffer(connp);
        }
    }

    return HTP_ERROR;
}
Exemple #3
0
/**
 * Parses request headers.
 *
 * @param connp
 * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed.
 */
int htp_connp_REQ_HEADERS(htp_connp_t *connp) {
    for (;;) {
        IN_COPY_BYTE_OR_RETURN(connp);

        // Allocate structure to hold one header line
        if (connp->in_header_line == NULL) {
            connp->in_header_line = calloc(1, sizeof (htp_header_line_t));
            if (connp->in_header_line == NULL) return HTP_ERROR;
            connp->in_header_line->first_nul_offset = -1;
        }

        // Keep track of NUL bytes
        if (connp->in_next_byte == 0) {
            // Store the offset of the first NUL
            if (connp->in_header_line->has_nulls == 0) {
                connp->in_header_line->first_nul_offset = connp->in_line_len;
            }

            // Remember how many NULs there were
            connp->in_header_line->flags |= HTP_FIELD_NUL_BYTE;
            connp->in_header_line->has_nulls++;
        }

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __FUNCTION__, connp->in_line, connp->in_line_len);
            #endif

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, connp->in_line, connp->in_line_len)) {
                // Terminator line
                connp->in_tx->request_headers_sep = bstr_dup_mem((char *)connp->in_line, connp->in_line_len);
                if (connp->in_tx->request_headers_sep == NULL) {
                    return HTP_ERROR;
                }

                // Parse previous header, if any
                if (connp->in_header_line_index != -1) {
                    if (connp->cfg->process_request_header(connp) != HTP_OK) {
                        // Note: downstream responsible for error logging
                        return HTP_ERROR;
                    }

                    // Reset index
                    connp->in_header_line_index = -1;
                }

                // Cleanup
                free(connp->in_header_line);
                connp->in_line_len = 0;
                connp->in_header_line = NULL;

                // We've seen all request headers

                // Did this request arrive in multiple chunks?
                if (connp->in_chunk_count != connp->in_chunk_request_index) {
                    connp->in_tx->flags |= HTP_MULTI_PACKET_HEAD;
                }

                // Move onto the next processing phase
                if (connp->in_tx->progress == TX_PROGRESS_REQ_HEADERS) {
                    // Remember how many header lines there were before trailers
                    connp->in_tx->request_header_lines_no_trailers = list_size(connp->in_tx->request_header_lines);

                    // Run hook REQUEST_HEADERS_RAW
                    //if (connp->cfg->hook_request_headers_raw != NULL) {
                    //    htp_req_run_hook_request_headers_raw(connp, 0,
                    //        connp->in_tx->request_header_lines_no_trailers);
                    //}

                    // Determine if this request has a body                    
                    connp->in_state = htp_connp_REQ_CONNECT_CHECK;
                } else {
                    // Run hook REQUEST_HEADERS_RAW
                    //if ((connp->cfg->hook_request_headers_raw != NULL)
                    //    && (list_size(connp->in_tx->request_header_lines) > connp->in_tx->request_header_lines_no_trailers)) {
                    //    htp_req_run_hook_request_headers_raw(connp,
                    //        connp->in_tx->request_header_lines_no_trailers,
                    //        list_size(connp->in_tx->request_header_lines));
                    //}

                    // Run hook REQUEST_TRAILER
                    int rc = hook_run_all(connp->cfg->hook_request_trailer, connp);
                    if (rc != HOOK_OK) {
                        switch (rc) {
                            case HOOK_STOP:
                                return HTP_STOP;
                            case HOOK_ERROR:
                            case HOOK_DECLINED:
                            default:
                                htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
                                    "Request headers callback returned error (%d)", rc);
                                return HTP_ERROR;
                        }
                    }

                    // We've completed parsing this request
                    connp->in_state = htp_connp_REQ_IDLE;
                    connp->in_tx->progress = TX_PROGRESS_WAIT;
                }

                return HTP_OK;
            }

            // Prepare line for consumption
            int chomp_result = htp_chomp(connp->in_line, &connp->in_line_len);

            // Check for header folding
            if (htp_connp_is_line_folded(connp->in_line, connp->in_line_len) == 0) {
                // New header line

                // Parse previous header, if any
                if (connp->in_header_line_index != -1) {
                    if (connp->cfg->process_request_header(connp) != HTP_OK) {
                        // Note: downstream responsible for error logging
                        return HTP_ERROR;
                    }

                    // Reset index
                    connp->in_header_line_index = -1;
                }

                // Remember the index of the fist header line
                connp->in_header_line_index = connp->in_header_line_counter;
            } else {
                // Folding; check that there's a previous header line to add to
                if (connp->in_header_line_index == -1) {
                    if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->in_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
                            "Invalid request field folding");
                    }
                }
            }

            // Add the raw header line to the list
            connp->in_header_line->line = bstr_dup_mem((char *) connp->in_line, connp->in_line_len + chomp_result);
            if (connp->in_header_line->line == NULL) {
                return HTP_ERROR;
            }
            
            list_add(connp->in_tx->request_header_lines, connp->in_header_line);
            connp->in_header_line = NULL;

            // Cleanup for the next line
            connp->in_line_len = 0;
            if (connp->in_header_line_index == -1) {
                connp->in_header_line_index = connp->in_header_line_counter;
            }

            connp->in_header_line_counter++;
        }
    }
}
Exemple #4
0
TEST(UtilTest, IsLineFolded) {
    EXPECT_EQ(-1, htp_connp_is_line_folded((unsigned char*)"", 0));
    EXPECT_EQ(1, htp_connp_is_line_folded((unsigned char*)"\tline", 5));
    EXPECT_EQ(1, htp_connp_is_line_folded((unsigned char*)" line", 5));
    EXPECT_EQ(0, htp_connp_is_line_folded((unsigned char*)"line ", 5));
}
Exemple #5
0
/**
 * Parses response headers.
 *
 * @param connp
 * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed.
 */
int htp_connp_RES_HEADERS(htp_connp_t * connp) {
    for (;;) {
        OUT_COPY_BYTE_OR_RETURN(connp);

        if (connp->out_header_line == NULL) {
            connp->out_header_line = calloc(1, sizeof (htp_header_line_t));
            if (connp->out_header_line == NULL) return HTP_ERROR;
            connp->out_header_line->first_nul_offset = -1;
        }

        // Keep track of NUL bytes
        if (connp->out_next_byte == 0) {
            // Store the offset of the first NUL
            if (connp->out_header_line->has_nulls == 0) {
                connp->out_header_line->first_nul_offset = connp->out_line_len;
            }

            // Remember how many NULs there were
            connp->out_header_line->flags |= HTP_FIELD_NUL_BYTE;
            connp->out_header_line->has_nulls++;
        }

        // Have we reached the end of the line?
        if (connp->out_next_byte == LF) {
            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __FUNCTION__, connp->out_line, connp->out_line_len);
            #endif

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, connp->out_line, connp->out_line_len)) {
                // Terminator line
                connp->out_tx->response_headers_sep = bstr_dup_mem((char *)connp->out_line, connp->out_line_len);
                if (connp->out_tx->response_headers_sep == NULL) {
                    return HTP_ERROR;
                }

                // Parse previous header, if any
                if (connp->out_header_line_index != -1) {
                    // Only try to parse a header, but ignore
                    // any problems. That's what browsers do.
                    connp->cfg->process_response_header(connp);

                    // Reset index
                    connp->out_header_line_index = -1;
                }

                // Cleanup
                free(connp->out_header_line);
                connp->out_line_len = 0;
                connp->out_header_line = NULL;

                // We've seen all response headers
                if (connp->out_tx->progress == TX_PROGRESS_RES_HEADERS) {
                    // Determine if this response has a body
                    connp->out_state = htp_connp_RES_BODY_DETERMINE;
                } else {
                    // Run hook response_TRAILER
                    int rc = hook_run_all(connp->cfg->hook_response_trailer, connp);
                    if (rc != HOOK_OK) {
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
                            "Response trailer callback returned error (%d)", rc);
                        return HTP_ERROR;
                    }

                    // We've completed parsing this response
                    connp->out_state = htp_connp_RES_IDLE;
                }

                return HTP_OK;
            }

            // Prepare line for consumption
            int chomp_result = htp_chomp(connp->out_line, &connp->out_line_len);

            // Check for header folding
            if (htp_connp_is_line_folded(connp->out_line, connp->out_line_len) == 0) {
                // New header line

                // Parse previous header, if any
                if (connp->out_header_line_index != -1) {
                    // Only try to parse a header, but ignore
                    // any problems. That's what browsers do.
                    connp->cfg->process_response_header(connp);

                    // Reset index
                    connp->out_header_line_index = -1;
                }

                // Remember the index of the fist header line
                connp->out_header_line_index = connp->out_header_line_counter;
            } else {
                // Folding; check that there's a previous header line to add to
                if (connp->out_header_line_index == -1) {
                    if (!(connp->out_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->out_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response field folding");
                    }
                }
            }

            // Add the raw header line to the list
            connp->out_header_line->line = bstr_dup_mem((char *) connp->out_line, connp->out_line_len + chomp_result);
            if (connp->out_header_line->line == NULL) {
                return HTP_ERROR;
            }

            list_add(connp->out_tx->response_header_lines, connp->out_header_line);
            connp->out_header_line = NULL;

            // Cleanup for the next line
            connp->out_line_len = 0;
            if (connp->out_header_line_index == -1) {

                connp->out_header_line_index = connp->out_header_line_counter;
            }

            connp->out_header_line_counter++;
        }
    }
}
Exemple #6
0
/**
 * Parses request headers.
 *
 * @param connp
 * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed.
 */
int htp_connp_REQ_HEADERS(htp_connp_t *connp) {
    for (;;) {
        IN_COPY_BYTE_OR_RETURN(connp);

        if (connp->in_header_line == NULL) {
            connp->in_header_line = calloc(1, sizeof (htp_header_line_t));
            if (connp->in_header_line == NULL) return HTP_ERROR;
            connp->in_header_line->first_nul_offset = -1;
        }

        // Keep track of NUL bytes
        if (connp->in_next_byte == 0) {
            // Store the offset of the first NUL
            if (connp->in_header_line->has_nulls == 0) {
                connp->in_header_line->first_nul_offset = connp->in_line_len;
            }

            // Remember how many NULs there were
            connp->in_header_line->flags |= HTP_FIELD_NUL_BYTE;
            connp->in_header_line->has_nulls++;
        }

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __FUNCTION__, connp->in_line, connp->in_line_len);
            #endif

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, connp->in_line, connp->in_line_len)) {
                // Terminator line

                // Parse previous header, if any
                if (connp->in_header_line_index != -1) {
                    if (connp->cfg->process_request_header(connp) != HTP_OK) {
                        // Note: downstream responsible for error logging
                        return HTP_ERROR;
                    }

                    // Reset index
                    connp->in_header_line_index = -1;
                }

                // Cleanup
                free(connp->in_header_line);
                connp->in_line_len = 0;
                connp->in_header_line = NULL;                

                // We've seen all request headers
                if (connp->in_chunk_count != connp->in_chunk_request_index) {
                    connp->in_tx->flags |= HTP_MULTI_PACKET_HEAD;
                }

                // Move onto the next processing phase
                if (connp->in_tx->progress[0] == TX_PROGRESS_REQ_HEADERS) {
                    // Determine if this request has a body
                    //connp->in_state = htp_connp_REQ_BODY_DETERMINE;
                    connp->in_state = htp_connp_REQ_CONNECT_CHECK;
                } else {
                    // Run hook REQUEST_TRAILER
                    int rc = hook_run_all(connp->cfg->hook_request_trailer, connp);
                    if (rc != HOOK_OK) {
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
                            "Request trailer callback returned error (%d)", rc);
                        return HTP_ERROR;
                    }

                    // We've completed parsing this request
                    connp->in_state = htp_connp_REQ_IDLE;
                    connp->in_tx->progress[0] = TX_PROGRESS_WAIT;
                }

                return HTP_OK;
            }

            // Prepare line for consumption
            size_t raw_in_line_len = connp->in_line_len;
            htp_chomp(connp->in_line, &connp->in_line_len);

            // Check for header folding
            if (htp_connp_is_line_folded(connp->in_line, connp->in_line_len) == 0) {
                // New header line

                // Parse previous header, if any
                if (connp->in_header_line_index != -1) {
                    if (connp->cfg->process_request_header(connp) != HTP_OK) {
                        // Note: downstream responsible for error logging
                        return HTP_ERROR;
                    }

                    // Reset index
                    connp->in_header_line_index = -1;
                }

                // Remember the index of the fist header line
                connp->in_header_line_index = connp->in_header_line_counter;
            } else {
                // Folding; check that there's a previous header line to add to
                if (connp->in_header_line_index == -1) {
                    if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->in_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0,
                            "Invalid request field folding");
                    }
                }
            }

            // Add the raw header line to the list
            if (raw_in_line_len > connp->in_line_len) {
                if (raw_in_line_len - connp->in_line_len == 2 &&
                        connp->in_line[connp->in_line_len] == 0x0d &&
                        connp->in_line[connp->in_line_len + 1] == 0x0a) {
                    connp->in_header_line->terminators = NULL;
                } else {
                    connp->in_header_line->terminators =
                        bstr_memdup((char *) connp->in_line + connp->in_line_len,
                                raw_in_line_len - connp->in_line_len);
                    if (connp->in_header_line->terminators == NULL) {
                        return HTP_ERROR;
                    }
                }
            } else {
                connp->in_header_line->terminators = NULL;
            }

            connp->in_header_line->line = bstr_memdup((char *) connp->in_line, connp->in_line_len);
            if (connp->in_header_line->line == NULL) {
                return HTP_ERROR;
            }
            list_add(connp->in_tx->request_header_lines, connp->in_header_line);
            connp->in_header_line = NULL;

            // Cleanup for the next line
            connp->in_line_len = 0;
            if (connp->in_header_line_index == -1) {
                connp->in_header_line_index = connp->in_header_line_counter;
            }

            connp->in_header_line_counter++;
        }
    }
    return HTP_ERROR;
}