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_request.cc author Tom Peters <thopeter@cisco.com>
00019
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023
00024 #include "http_msg_request.h"
00025
00026 #include "http_api.h"
00027
00028 using namespace HttpEnums;
00029
00030 HttpMsgRequest::HttpMsgRequest(const uint8_t* buffer, const uint16_t buf_size,
00031 HttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_,
00032 const HttpParaList* params_) :
00033 HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
00034 {
00035 transaction->set_request(this);
00036 }
00037
00038 void HttpMsgRequest::parse_start_line()
00039 {
00040 // Version field
00041 if ((start_line.length() < 10) || !is_sp_tab[start_line.start()[start_line.length()-9]] ||
00042 memcmp(start_line.start() + start_line.length() - 8, "HTTP/", 5))
00043 {
00044 // Something is wrong with this message. Check for lower case letters in HTTP-name.
00045 if ((start_line.length() >= 10) && is_sp_tab[start_line.start()[start_line.length()-9]] &&
00046 http_name_nocase_ok(start_line.start() + start_line.length() - 8))
00047 {
00048 add_infraction(INF_VERSION_NOT_UPPERCASE);
00049 create_event(EVENT_VERSION_NOT_UPPERCASE);
00050 }
00051 // Check for version 0.9 request.
00052 else if (handle_zero_nine())
00053 {
00054 return;
00055 }
00056 // Just a plain old bad request
00057 else
00058 {
00059 add_infraction(INF_BAD_REQ_LINE);
00060 transaction->get_events(source_id)->generate_misformatted_http(start_line.start(),
00061 start_line.length());
00062 return;
00063 }
00064 }
00065
00066 version.set(8, start_line.start() + (start_line.length() - 8));
00067 derive_version_id();
00068
00069 HttpModule::increment_peg_counts(PEG_REQUEST);
00070
00071 // The splitter guarantees there will be a non-whitespace at octet 1 and a whitespace within
00072 // octets 2-81. The following algorithm uses those assumptions.
00073
00074 int32_t first_space; // first whitespace in request line
00075 for (first_space = 1; !is_sp_tab[start_line.start()[first_space]]; first_space++);
00076
00077 int32_t first_end; // last whitespace in first clump of whitespace
00078 for (first_end = first_space+1; is_sp_tab[start_line.start()[first_end]]; first_end++);
00079 first_end--;
00080
00081 int32_t last_begin; // first whitespace in clump of whitespace before version
00082 for (last_begin = start_line.length() - 10; is_sp_tab[start_line.start()[last_begin]];
00083 last_begin--);
00084 last_begin++;
00085
00086 method.set(first_space, start_line.start());
00087 method_id = (MethodId)str_to_code(method.start(), method.length(), method_list);
00088
00089 switch (method_id)
00090 {
00091 case METH_GET: HttpModule::increment_peg_counts(PEG_GET); break;
00092 case METH_HEAD: HttpModule::increment_peg_counts(PEG_HEAD); break;
00093 case METH_POST: HttpModule::increment_peg_counts(PEG_POST); break;
00094 case METH_PUT: HttpModule::increment_peg_counts(PEG_PUT); break;
00095 case METH_DELETE: HttpModule::increment_peg_counts(PEG_DELETE); break;
00096 case METH_CONNECT: HttpModule::increment_peg_counts(PEG_CONNECT); break;
00097 case METH_OPTIONS: HttpModule::increment_peg_counts(PEG_OPTIONS); break;
00098 case METH_TRACE: HttpModule::increment_peg_counts(PEG_TRACE); break;
00099 default: HttpModule::increment_peg_counts(PEG_OTHER_METHOD); break;
00100 }
00101
00102 if (first_end < last_begin)
00103 {
00104 uri = new HttpUri(start_line.start() + first_end + 1, last_begin - first_end - 1,
00105 method_id, params->uri_param, transaction->get_infractions(source_id),
00106 transaction->get_events(source_id));
00107 }
00108 else
00109 {
00110 add_infraction(INF_NO_URI);
00111 create_event(EVENT_URI_MISSING);
00112 }
00113 }
00114
00115 bool HttpMsgRequest::http_name_nocase_ok(const uint8_t* start)
00116 {
00117 return ((start[0] == 'H') || (start[0] == 'h')) &&
00118 ((start[1] == 'T') || (start[1] == 't')) &&
00119 ((start[2] == 'T') || (start[2] == 't')) &&
00120 ((start[3] == 'P') || (start[3] == 'p')) &&
00121 (start[4] == '/');
00122 }
00123
00124 bool HttpMsgRequest::handle_zero_nine()
00125 {
00126 // 0.9 request line is supposed to be "GET <URI>\r\n"
00127 if ((start_line.length() >= 3) &&
00128 !memcmp(start_line.start(), "GET", 3) &&
00129 ((start_line.length() == 3) || is_sp_tab[start_line.start()[3]]))
00130 {
00131 add_infraction(INF_ZERO_NINE_REQ);
00132 create_event(EVENT_SIMPLE_REQUEST);
00133 method.set(3, start_line.start());
00134 method_id = METH_GET;
00135 version_id = VERS_0_9;
00136
00137 // Eliminate the clump of whitespace following GET and possible clump of whitespace at the
00138 // end and whatever is left is assumed to be the URI
00139 int32_t uri_begin;
00140 for (uri_begin = 4; (uri_begin < start_line.length()) &&
00141 is_sp_tab[start_line.start()[uri_begin]]; uri_begin++);
00142 if (uri_begin < start_line.length())
00143 {
00144 int32_t uri_end;
00145 for (uri_end = start_line.length() - 1; is_sp_tab[start_line.start()[uri_end]];
00146 uri_end--);
00147 uri = new HttpUri(start_line.start() + uri_begin, uri_end - uri_begin + 1, method_id,
00148 params->uri_param, transaction->get_infractions(source_id),
00149 transaction->get_events(source_id));
00150 }
00151 else
00152 {
00153 add_infraction(INF_NO_URI);
00154 create_event(EVENT_URI_MISSING);
00155 }
00156 return true;
00157 }
00158 return false;
00159 }
00160
00161 const Field& HttpMsgRequest::get_uri()
00162 {
00163 if (uri != nullptr)
00164 {
00165 return uri->get_uri();
00166 }
00167 return Field::FIELD_NULL;
00168 }
00169
00170 const Field& HttpMsgRequest::get_uri_norm_classic()
00171 {
00172 if (uri != nullptr)
00173 {
00174 return uri->get_norm_classic();
00175 }
00176 return Field::FIELD_NULL;
00177 }
00178
00179 void HttpMsgRequest::gen_events()
00180 {
00181 if (*transaction->get_infractions(source_id) & INF_BAD_REQ_LINE)
00182 return;
00183
00184 const bool zero_nine = *transaction->get_infractions(source_id) & INF_ZERO_NINE_REQ;
00185
00186 if ((start_line.start()[method.length()] == '\t') ||
00187 (!zero_nine && (start_line.start()[start_line.length() - 9] == '\t')))
00188 {
00189 add_infraction(INF_REQUEST_TAB);
00190 create_event(EVENT_APACHE_WS);
00191 }
00192
00193 // Look for white space issues in and around the URI.
00194 // Supposed to be <method><space><URI><space><version> or 0.9 format GET<space><URI>
00195 const int32_t version_start = !zero_nine ? start_line.length() - 9 : start_line.length();
00196 for (int32_t k = method.length() + 1; k < version_start; k++)
00197 {
00198 if (is_sp_tab[start_line.start()[k]])
00199 {
00200 if (uri && (uri->get_uri().start() <= start_line.start() + k) &&
00201 (start_line.start() + k < uri->get_uri().start() + uri->get_uri().length()))
00202 {
00203 // white space inside the URI is not allowed
00204 if (start_line.start()[k] == ' ')
00205 {
00206 add_infraction(INF_URI_SPACE);
00207 create_event(EVENT_UNESCAPED_SPACE_URI);
00208 }
00209 }
00210 else
00211 {
00212 // extra white space before or after the URI
00213 add_infraction(INF_REQUEST_WS);
00214 create_event(EVENT_IMPROPER_WS);
00215 if (start_line.start()[k] == '\t')
00216 {
00217 // which is also a tab
00218 add_infraction(INF_REQUEST_TAB);
00219 create_event(EVENT_APACHE_WS);
00220 }
00221 }
00222 }
00223 }
00224
00225 if (method_id == METH__OTHER)
00226 create_event(EVENT_UNKNOWN_METHOD);
00227
00228 if (session_data->zero_nine_expected != 0)
00229 {
00230 // Previous 0.9 request on this connection should have been the last request message
00231 add_infraction(INF_ZERO_NINE_CONTINUE);
00232 create_event(EVENT_ZERO_NINE_CONTINUE);
00233 }
00234 else if (zero_nine && (trans_num != 1))
00235 {
00236 // Switched to 0.9 request after previously sending non-0.9 request on this connection
00237 add_infraction(INF_ZERO_NINE_NOT_FIRST);
00238 create_event(EVENT_ZERO_NINE_NOT_FIRST);
00239 }
00240 }
00241
00242 void HttpMsgRequest::update_flow()
00243 {
00244 if (*transaction->get_infractions(source_id) & INF_BAD_REQ_LINE)
00245 {
00246 session_data->half_reset(source_id);
00247 session_data->type_expected[source_id] = SEC_ABORT;
00248 }
00249 else if (*transaction->get_infractions(source_id) & INF_ZERO_NINE_REQ)
00250 {
00251 session_data->half_reset(source_id);
00252 // There can only be one 0.9 response per connection because it ends the S2C connection. Do
00253 // not allow a pipelined request to overwrite a previous 0.9 setup.
00254 if (session_data->zero_nine_expected == 0)
00255 {
00256 // FIXIT-L Add a configuration option to not do this. This would support an HTTP server
00257 // that responds to a 0.9 GET request with a full-blown 1.0 or 1.1 response with status
00258 // line and headers.
00259 session_data->zero_nine_expected = trans_num;
00260 }
00261 }
00262 else
00263 {
00264 session_data->type_expected[source_id] = SEC_HEADER;
00265 session_data->version_id[source_id] = version_id;
00266 session_data->method_id = method_id;
00267 }
00268 session_data->section_type[source_id] = SEC__NOT_COMPUTE;
00269 }
00270
00271 #ifdef REG_TEST
00272
00273 void HttpMsgRequest::print_section(FILE* output)
00274 {
00275 HttpMsgSection::print_section_title(output, "request line");
00276 fprintf(output, "Version ID: %d\n", version_id);
00277 fprintf(output, "Method ID: %d\n", method_id);
00278 if (uri != nullptr)
00279 {
00280 uri->get_uri().print(output, "URI");
00281 fprintf(output, "URI Type: %d\n", uri->get_uri_type());
00282 uri->get_scheme().print(output, "Scheme");
00283 uri->get_authority().print(output, "Authority");
00284 uri->get_host().print(output, "Host Name");
00285 uri->get_norm_host().print(output, "Normalized Host Name");
00286 uri->get_port().print(output, "Port");
00287 uri->get_abs_path().print(output, "Absolute Path");
00288 uri->get_path().print(output, "Path");
00289 uri->get_norm_path().print(output, "Normalized Path");
00290 uri->get_query().print(output, "Query");
00291 uri->get_norm_query().print(output, "Normalized Query");
00292 uri->get_fragment().print(output, "Fragment");
00293 uri->get_norm_fragment().print(output, "Normalized Fragment");
00294 }
00295 get_classic_buffer(HTTP_BUFFER_METHOD, 0, 0).print(output,
00296 HttpApi::classic_buffer_names[HTTP_BUFFER_METHOD-1]);
00297 get_classic_buffer(HTTP_BUFFER_RAW_URI, 0, 0).print(output,
00298 HttpApi::classic_buffer_names[HTTP_BUFFER_RAW_URI-1]);
00299 get_classic_buffer(HTTP_BUFFER_URI, 0, 0).print(output,
00300 HttpApi::classic_buffer_names[HTTP_BUFFER_URI-1]);
00301 get_classic_buffer(HTTP_BUFFER_VERSION, 0, 0).print(output,
00302 HttpApi::classic_buffer_names[HTTP_BUFFER_VERSION-1]);
00303 get_classic_buffer(HTTP_BUFFER_RAW_REQUEST, 0, 0).print(output,
00304 HttpApi::classic_buffer_names[HTTP_BUFFER_RAW_REQUEST-1]);
00305 HttpMsgSection::print_section_wrapup(output);
00306 }
00307
00308 #endif
00309
END OF CODE