00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 //
00004 // This program is free software; you can redistribute it and/or modify it
00005 // under the terms of the GNU General Public License Version 2 as published
00006 // by the Free Software Foundation. You may not use, modify or distribute
00007 // this program under any other version of the GNU General Public License.
00008 //
00009 // This program is distributed in the hope that it will be useful, but
00010 // WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program; if not, write to the Free Software Foundation, Inc.,
00016 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 //--------------------------------------------------------------------------
00018 // http_msg_header.cc author Tom Peters <thopeter@cisco.com>
00019
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023
00024 #include "http_msg_header.h"
00025
00026 #include "decompress/file_decomp.h"
00027 #include "file_api/file_flows.h"
00028 #include "file_api/file_service.h"
00029 #include "http_api.h"
00030 #include "http_msg_request.h"
00031 #include "http_msg_body.h"
00032 #include "pub_sub/http_events.h"
00033 #include "sfip/sf_ip.h"
00034
00035 using namespace HttpEnums;
00036
00037 HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size,
00038 HttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_,
00039 const HttpParaList* params_) :
00040 HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
00041 {
00042 transaction->set_header(this, source_id);
00043 }
00044
00045 void HttpMsgHeader::publish()
00046 {
00047 HttpEvent http_event(this);
00048 if(source_id == SRC_CLIENT)
00049 {
00050 get_data_bus().publish(HTTP_REQUEST_HEADER_EVENT_KEY, http_event, flow);
00051 }
00052 else
00053 {
00054 get_data_bus().publish(HTTP_RESPONSE_HEADER_EVENT_KEY, http_event, flow);
00055 }
00056 }
00057
00058 const Field& HttpMsgHeader::get_true_ip()
00059 {
00060 if (true_ip.length() != STAT_NOT_COMPUTE)
00061 return true_ip;
00062
00063 const Field* header_to_use;
00064 const Field& xff = get_header_value_norm(HEAD_X_FORWARDED_FOR);
00065 if (xff.length() > 0)
00066 header_to_use = &xff;
00067 else
00068 {
00069 const Field& tcip = get_header_value_norm(HEAD_TRUE_CLIENT_IP);
00070 if (tcip.length() > 0)
00071 header_to_use = &tcip;
00072 else
00073 {
00074 true_ip.set(STAT_NOT_PRESENT);
00075 return true_ip;
00076 }
00077 }
00078
00079 // This is potentially a comma-separated list of IP addresses. Just take the first one in
00080 // the list. Since this is a normalized header field any whitespace will be an actual space.
00081 uint8_t* addr_str = new uint8_t[header_to_use->length()+1];
00082 int32_t length;
00083 for (length = 0; length < header_to_use->length(); length++)
00084 {
00085 if (is_sp_comma[header_to_use->start()[length]])
00086 break;
00087 addr_str[length] = header_to_use->start()[length];
00088 }
00089 addr_str[length] = '\0';
00090
00091 SfIp tmp_sfip;
00092 const SfIpRet status = tmp_sfip.set((char*)addr_str);
00093 delete[] addr_str;
00094 if (status != SFIP_SUCCESS)
00095 {
00096 true_ip.set(STAT_PROBLEMATIC);
00097 }
00098 else
00099 {
00100 const size_t addr_length = (tmp_sfip.is_ip6() ? 4 : 1);
00101 uint32_t* const addr_buf = new uint32_t[addr_length];
00102 memcpy(addr_buf, tmp_sfip.get_ptr(), addr_length * sizeof(uint32_t));
00103 true_ip.set(addr_length * sizeof(uint32_t), (uint8_t*)addr_buf, true);
00104 }
00105 return true_ip;
00106 }
00107
00108 void HttpMsgHeader::gen_events()
00109 {
00110 if ((get_header_count(HEAD_CONTENT_LENGTH) > 0) &&
00111 (get_header_count(HEAD_TRANSFER_ENCODING) > 0))
00112 {
00113 add_infraction(INF_BOTH_CL_AND_TE);
00114 create_event(EVENT_BOTH_CL_AND_TE);
00115 }
00116 // Content-Transfer-Encoding is a MIME header not sanctioned by HTTP. Which may not prevent
00117 // some clients from recognizing it and applying a decoding that Snort does not expect.
00118 if (get_header_count(HEAD_CONTENT_TRANSFER_ENCODING) > 0)
00119 {
00120 add_infraction(INF_CTE_HEADER);
00121 create_event(EVENT_CTE_HEADER);
00122 }
00123 }
00124
00125 void HttpMsgHeader::update_flow()
00126 {
00127 session_data->section_type[source_id] = SEC__NOT_COMPUTE;
00128
00129 // The following logic to determine body type is by no means the last word on this topic.
00130 if (tcp_close)
00131 {
00132 session_data->half_reset(source_id);
00133 session_data->type_expected[source_id] = SEC_ABORT;
00134 return;
00135 }
00136
00137 if ((source_id == SRC_SERVER) && ((status_code_num <= 199) || (status_code_num == 204) ||
00138 (status_code_num == 304)))
00139 {
00140 // No body allowed by RFC for these response codes. The message is over regardless of the
00141 // headers.
00142 if (get_header_count(HEAD_TRANSFER_ENCODING) > 0)
00143 {
00144 add_infraction(INF_BAD_CODE_BODY_HEADER);
00145 create_event(EVENT_BAD_CODE_BODY_HEADER);
00146 }
00147 if (get_header_count(HEAD_CONTENT_LENGTH) > 0)
00148 {
00149 if (norm_decimal_integer(get_header_value_norm(HEAD_CONTENT_LENGTH)) > 0)
00150 {
00151 add_infraction(INF_BAD_CODE_BODY_HEADER);
00152 create_event(EVENT_BAD_CODE_BODY_HEADER);
00153 }
00154 }
00155 session_data->half_reset(SRC_SERVER);
00156 return;
00157 }
00158
00159 if ((source_id == SRC_SERVER) && (transaction->get_request() != nullptr) &&
00160 (transaction->get_request()->get_method_id() == METH_HEAD))
00161 {
00162 // No body allowed by RFC for response to HEAD method
00163 session_data->half_reset(SRC_SERVER);
00164 return;
00165 }
00166
00167 const Field& te_header = get_header_value_norm(HEAD_TRANSFER_ENCODING);
00168 if ((te_header.length() > 0) && (version_id == VERS_1_0))
00169 {
00170 // HTTP 1.0 should not be chunked and many browsers will ignore the TE header
00171 add_infraction(INF_CHUNKED_ONE_POINT_ZERO);
00172 create_event(EVENT_CHUNKED_ONE_POINT_ZERO);
00173 }
00174 if ((te_header.length() > 0) && (version_id != VERS_1_0))
00175 {
00176 // If there is a Transfer-Encoding header, it should be "chunked" without any other
00177 // encodings being listed. The RFC allows other encodings to come before chunked but
00178 // no one does this in real life.
00179 const int CHUNKED_SIZE = 7;
00180 bool is_chunked = false;
00181
00182 if ((te_header.length() == CHUNKED_SIZE) &&
00183 !memcmp(te_header.start(), "chunked", CHUNKED_SIZE))
00184 {
00185 is_chunked = true;
00186 }
00187 else if ((te_header.length() > CHUNKED_SIZE) &&
00188 !memcmp(te_header.start() + (te_header.length() - (CHUNKED_SIZE+1)),
00189 ",chunked", CHUNKED_SIZE+1))
00190 {
00191 add_infraction(INF_PADDED_TE_HEADER);
00192 create_event(EVENT_PADDED_TE_HEADER);
00193 is_chunked = true;
00194 }
00195
00196 if (is_chunked)
00197 {
00198 session_data->type_expected[source_id] = SEC_BODY_CHUNK;
00199 HttpModule::increment_peg_counts(PEG_CHUNKED);
00200 prepare_body();
00201 return;
00202 }
00203 else
00204 {
00205 add_infraction(INF_BAD_TE_HEADER);
00206 create_event(EVENT_BAD_TE_HEADER);
00207 }
00208 }
00209
00210 // else because Transfer-Encoding header negates Content-Length header even if something was
00211 // wrong with Transfer-Encoding header. However a Transfer-Encoding header in a 1.0 message
00212 // does not negate the Content-Length header.
00213 // FIXIT-L the following can be zero, need an alert for empty CL header value
00214 else if (get_header_value_norm(HEAD_CONTENT_LENGTH).length() > 0)
00215 {
00216 const int64_t content_length =
00217 norm_decimal_integer(get_header_value_norm(HEAD_CONTENT_LENGTH));
00218 if (content_length > 0)
00219 {
00220 // Regular body
00221 session_data->type_expected[source_id] = SEC_BODY_CL;
00222 session_data->data_length[source_id] = content_length;
00223 prepare_body();
00224 return;
00225 }
00226 else if (content_length == 0)
00227 {
00228 // No body
00229 if (get_header_count(HEAD_EXPECT) > 0)
00230 {
00231 add_infraction(INF_EXPECT_WITHOUT_BODY_CL0);
00232 create_event(EVENT_EXPECT_WITHOUT_BODY);
00233 }
00234 session_data->half_reset(source_id);
00235 return;
00236 }
00237 else
00238 {
00239 add_infraction(INF_BAD_CONTENT_LENGTH);
00240 create_event(EVENT_BAD_CONTENT_LENGTH);
00241 // Treat as if there was no Content-Length header (drop through)
00242 }
00243 }
00244
00245 if (source_id == SRC_CLIENT)
00246 {
00247 // No body
00248 if ((method_id == METH_POST) || (method_id == METH_PUT))
00249 {
00250 // Despite the name of this event, we assume for parsing purposes that this POST or PUT
00251 // does not have a body rather than running to connection close. Obviously that is just
00252 // an assumption.
00253 add_infraction(INF_POST_WO_BODY);
00254 create_event(EVENT_UNBOUNDED_POST);
00255 }
00256 if (get_header_count(HEAD_EXPECT) > 0)
00257 {
00258 add_infraction(INF_EXPECT_WITHOUT_BODY_NO_CL);
00259 create_event(EVENT_EXPECT_WITHOUT_BODY);
00260 }
00261 session_data->half_reset(source_id);
00262 return;
00263 }
00264 else
00265 {
00266 // Old-style response body runs to connection close
00267 session_data->type_expected[source_id] = SEC_BODY_OLD;
00268 prepare_body();
00269 return;
00270 }
00271 }
00272
00273 // Common activities of preparing for upcoming regular body or chunked body
00274 void HttpMsgHeader::prepare_body()
00275 {
00276 session_data->body_octets[source_id] = 0;
00277 const int64_t& depth = (source_id == SRC_CLIENT) ? params->request_depth :
00278 params->response_depth;
00279 session_data->detect_depth_remaining[source_id] = (depth != -1) ? depth : INT64_MAX;
00280 if (session_data->detect_depth_remaining[source_id] > 0)
00281 {
00282 // Depth must be positive because first body section must actually go to detection in order
00283 // to be the detection section
00284 detection_section = false;
00285 }
00286 setup_file_processing();
00287 setup_encoding_decompression();
00288 setup_utf_decoding();
00289 setup_pdf_swf_decompression();
00290 update_depth();
00291 if (source_id == SRC_CLIENT)
00292 {
00293 HttpModule::increment_peg_counts(PEG_REQUEST_BODY);
00294 }
00295 }
00296
00297 void HttpMsgHeader::setup_file_processing()
00298 {
00299 // FIXIT-M Bidirectional file processing is problematic so we don't do it. When the library
00300 // fully supports it remove the outer if statement that prevents it from being done.
00301 if (session_data->file_depth_remaining[1-source_id] <= 0)
00302 {
00303 if ((session_data->file_depth_remaining[source_id] = FileService::get_max_file_depth())
00304 < 0)
00305 {
00306 session_data->file_depth_remaining[source_id] = 0;
00307 return;
00308 }
00309
00310 // Do we meet all the conditions for MIME file processing?
00311 if (source_id == SRC_CLIENT)
00312 {
00313 const Field& content_type = get_header_value_raw(HEAD_CONTENT_TYPE);
00314 if (content_type.length() > 0)
00315 {
00316 if (boundary_present(content_type))
00317 {
00318 session_data->mime_state[source_id] =
00319 new MimeSession(&decode_conf, &mime_conf);
00320 // Show file processing the Content-Type header as if it were regular data.
00321 // This will enable it to find the boundary string.
00322 // FIXIT-L develop a proper interface for passing the boundary string.
00323 // This interface is a leftover from when OHI pushed whole messages through
00324 // this interface.
00325 session_data->mime_state[source_id]->process_mime_data(flow,
00326 content_type.start(), content_type.length(), true,
00327 SNORT_FILE_POSITION_UNKNOWN);
00328 session_data->mime_state[source_id]->process_mime_data(flow,
00329 (const uint8_t*)"\r\n", 2, true, SNORT_FILE_POSITION_UNKNOWN);
00330 }
00331 }
00332 }
00333
00334 // Otherwise do regular file processing
00335 if (session_data->mime_state[source_id] == nullptr)
00336 {
00337 FileFlows* file_flows = FileFlows::get_file_flows(flow);
00338 if (!file_flows)
00339 session_data->file_depth_remaining[source_id] = 0;
00340 }
00341 }
00342 else
00343 {
00344 session_data->file_depth_remaining[source_id] = 0;
00345 }
00346 }
00347
00348 void HttpMsgHeader::setup_encoding_decompression()
00349 {
00350 if (!params->unzip)
00351 return;
00352
00353 CompressId& compression = session_data->compression[source_id];
00354
00355 // Search the Content-Encoding header to find the type of compression used. We detect and alert
00356 // on multiple layers of compression but we only decompress the outermost layer. Thus the last
00357 // encoding in the Content-Encoding header is the one we use. If we don't recognize or support
00358 // the last encoding we won't do anything.
00359
00360 const Field& norm_content_encoding = get_header_value_norm(HEAD_CONTENT_ENCODING);
00361 int32_t cont_offset = 0;
00362 while (norm_content_encoding.length() > cont_offset)
00363 {
00364 const Contentcoding content_code = (Contentcoding)get_next_code(norm_content_encoding,
00365 cont_offset, HttpMsgHeadShared::content_code_list);
00366 if ((compression != CMP_NONE) && (content_code != CONTENTCODE_IDENTITY))
00367 {
00368 add_infraction(INF_STACKED_ENCODINGS);
00369 create_event(EVENT_STACKED_ENCODINGS);
00370 compression = CMP_NONE;
00371 }
00372 switch (content_code)
00373 {
00374 case CONTENTCODE_GZIP:
00375 case CONTENTCODE_X_GZIP:
00376 compression = CMP_GZIP;
00377 break;
00378 case CONTENTCODE_DEFLATE:
00379 compression = CMP_DEFLATE;
00380 break;
00381 case CONTENTCODE_COMPRESS:
00382 case CONTENTCODE_EXI:
00383 case CONTENTCODE_PACK200_GZIP:
00384 case CONTENTCODE_X_COMPRESS:
00385 add_infraction(INF_UNSUPPORTED_ENCODING);
00386 create_event(EVENT_UNSUPPORTED_ENCODING);
00387 break;
00388 case CONTENTCODE_IDENTITY:
00389 break;
00390 case CONTENTCODE_CHUNKED:
00391 add_infraction(INF_CONTENT_ENCODING_CHUNKED);
00392 create_event(EVENT_CONTENT_ENCODING_CHUNKED);
00393 break;
00394 case CONTENTCODE__OTHER:
00395 add_infraction(INF_UNKNOWN_ENCODING);
00396 create_event(EVENT_UNKNOWN_ENCODING);
00397 break;
00398 }
00399 }
00400
00401 if (compression == CMP_NONE)
00402 return;
00403
00404 session_data->compress_stream[source_id] = new z_stream;
00405 session_data->compress_stream[source_id]->zalloc = Z_NULL;
00406 session_data->compress_stream[source_id]->zfree = Z_NULL;
00407 session_data->compress_stream[source_id]->next_in = Z_NULL;
00408 session_data->compress_stream[source_id]->avail_in = 0;
00409 const int window_bits = (compression == CMP_GZIP) ? GZIP_WINDOW_BITS : DEFLATE_WINDOW_BITS;
00410 if (inflateInit2(session_data->compress_stream[source_id], window_bits) != Z_OK)
00411 {
00412 session_data->compression[source_id] = CMP_NONE;
00413 delete session_data->compress_stream[source_id];
00414 session_data->compress_stream[source_id] = nullptr;
00415 }
00416 }
00417
00418 void HttpMsgHeader::setup_utf_decoding()
00419 {
00420 if (!params->normalize_utf || source_id == SRC_CLIENT )
00421 return;
00422
00423 Field last_token;
00424 CharsetCode charset_code;
00425
00426 const Field& norm_content_type = get_header_value_norm(HEAD_CONTENT_TYPE);
00427 if (norm_content_type.length() <= 0)
00428 return;
00429
00430 get_last_token(norm_content_type, last_token, ';');
00431
00432 // No semicolon in the Content-Type header
00433 if ( last_token.length() == norm_content_type.length() )
00434 {
00435 if (SnortStrnStr((const char*)norm_content_type.start(), norm_content_type.length(),
00436 "text"))
00437 {
00438 charset_code = CHARSET_UNKNOWN;
00439 }
00440 else
00441 return;
00442 }
00443 else
00444 {
00445 charset_code = (CharsetCode)str_to_code(last_token.start(), last_token.length(),
00446 HttpMsgHeadShared::charset_code_list);
00447
00448 if( charset_code == CHARSET_OTHER )
00449 {
00450 charset_code = (CharsetCode)substr_to_code(last_token.start(), last_token.length(),
00451 HttpMsgHeadShared::charset_code_opt_list);
00452
00453 if ( charset_code != CHARSET_UNKNOWN )
00454 return;
00455 }
00456 else if ( charset_code == CHARSET_UTF7 )
00457 {
00458 add_infraction(INF_UTF7);
00459 create_event(EVENT_UTF7);
00460 }
00461 }
00462
00463 session_data->utf_state = new UtfDecodeSession();
00464 session_data->utf_state->set_decode_utf_state_charset(charset_code);
00465 }
00466
00467 void HttpMsgHeader::setup_pdf_swf_decompression()
00468 {
00469 if (source_id == SRC_CLIENT || (!params->decompress_pdf && !params->decompress_swf))
00470 return;
00471
00472 session_data->fd_state = File_Decomp_New();
00473 session_data->fd_state->Modes =
00474 (params->decompress_pdf ? FILE_PDF_DEFL_BIT : 0) |
00475 (params->decompress_swf ? (FILE_SWF_ZLIB_BIT | FILE_SWF_LZMA_BIT) : 0);
00476 session_data->fd_state->Alert_Callback = HttpMsgBody::fd_event_callback;
00477 session_data->fd_state->Alert_Context = &session_data->fd_alert_context;
00478 session_data->fd_state->Compr_Depth = 0;
00479 session_data->fd_state->Decompr_Depth = 0;
00480
00481 (void)File_Decomp_Init(session_data->fd_state);
00482 }
00483
00484 #ifdef REG_TEST
00485 void HttpMsgHeader::print_section(FILE* output)
00486 {
00487 HttpMsgSection::print_section_title(output, "header");
00488 HttpMsgHeadShared::print_headers(output);
00489 get_classic_buffer(HTTP_BUFFER_COOKIE, 0, 0).print(output,
00490 HttpApi::classic_buffer_names[HTTP_BUFFER_COOKIE-1]);
00491 get_classic_buffer(HTTP_BUFFER_HEADER, 0, 0).print(output,
00492 HttpApi::classic_buffer_names[HTTP_BUFFER_HEADER-1]);
00493 get_classic_buffer(HTTP_BUFFER_RAW_COOKIE, 0, 0).print(output,
00494 HttpApi::classic_buffer_names[HTTP_BUFFER_RAW_COOKIE-1]);
00495 get_classic_buffer(HTTP_BUFFER_RAW_HEADER, 0, 0).print(output,
00496 HttpApi::classic_buffer_names[HTTP_BUFFER_RAW_HEADER-1]);
00497 HttpMsgSection::print_section_wrapup(output);
00498 }
00499 #endif
00500
END OF CODE