/src/libhtp/htp/htp_parsers.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************** |
2 | | * Copyright (c) 2009-2010 Open Information Security Foundation |
3 | | * Copyright (c) 2010-2013 Qualys, Inc. |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions are |
8 | | * met: |
9 | | * |
10 | | * - Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | |
13 | | * - Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in the |
15 | | * documentation and/or other materials provided with the distribution. |
16 | | |
17 | | * - Neither the name of the Qualys, Inc. nor the names of its |
18 | | * contributors may be used to endorse or promote products derived from |
19 | | * this software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | | ***************************************************************************/ |
33 | | |
34 | | /** |
35 | | * @file |
36 | | * @author Ivan Ristic <ivanr@webkreator.com> |
37 | | */ |
38 | | |
39 | | #include "htp_config_auto.h" |
40 | | |
41 | | #include "htp_private.h" |
42 | | |
43 | | /** |
44 | | * Determines protocol number from a textual representation (i.e., "HTTP/1.1"). This |
45 | | * function will only understand a properly formatted protocol information. It does |
46 | | * not try to be flexible. |
47 | | * |
48 | | * @param[in] protocol |
49 | | * @return Protocol version or PROTOCOL_UNKNOWN. |
50 | | */ |
51 | 56.1k | int htp_parse_protocol(bstr *protocol) { |
52 | 56.1k | if (protocol == NULL) return HTP_PROTOCOL_INVALID; |
53 | | |
54 | | // TODO This function uses a very strict approach to parsing, whereas |
55 | | // browsers will typically be more flexible, allowing whitespace |
56 | | // before and after the forward slash, as well as allowing leading |
57 | | // zeroes in the numbers. We should be able to parse such malformed |
58 | | // content correctly (but emit a warning). |
59 | 56.1k | if (bstr_len(protocol) == 8) { |
60 | 10.1k | unsigned char *ptr = bstr_ptr(protocol); |
61 | 10.1k | if ((ptr[0] == 'H') && (ptr[1] == 'T') && (ptr[2] == 'T') && (ptr[3] == 'P') |
62 | 10.1k | && (ptr[4] == '/') && (ptr[6] == '.')) { |
63 | | // Check the version numbers |
64 | 2.94k | if (ptr[5] == '0') { |
65 | 575 | if (ptr[7] == '9') { |
66 | 201 | return HTP_PROTOCOL_0_9; |
67 | 201 | } |
68 | 2.36k | } else if (ptr[5] == '1') { |
69 | 1.90k | if (ptr[7] == '0') { |
70 | 323 | return HTP_PROTOCOL_1_0; |
71 | 1.58k | } else if (ptr[7] == '1') { |
72 | 1.30k | return HTP_PROTOCOL_1_1; |
73 | 1.30k | } |
74 | 1.90k | } |
75 | 2.94k | } |
76 | 10.1k | } |
77 | | |
78 | 54.3k | return HTP_PROTOCOL_INVALID; |
79 | 56.1k | } |
80 | | |
81 | | /** |
82 | | * Determines the numerical value of a response status given as a string. |
83 | | * |
84 | | * @param[in] status |
85 | | * @return Status code on success, or HTP_STATUS_INVALID on error. |
86 | | */ |
87 | 44.6k | int htp_parse_status(bstr *status) { |
88 | 44.6k | int64_t r = htp_parse_positive_integer_whitespace((unsigned char *) bstr_ptr(status), bstr_len(status), 10); |
89 | 44.6k | if (r >= HTP_VALID_STATUS_MIN && r <= HTP_VALID_STATUS_MAX) { |
90 | 43.5k | return (int)r; |
91 | 43.5k | } else { |
92 | 1.09k | return HTP_STATUS_INVALID; |
93 | 1.09k | } |
94 | 44.6k | } |
95 | | |
96 | | /** |
97 | | * Parses Digest Authorization request header. |
98 | | * |
99 | | * @param[in] connp |
100 | | * @param[in] auth_header |
101 | | */ |
102 | 0 | int htp_parse_authorization_digest(htp_connp_t *connp, htp_header_t *auth_header) { |
103 | | // Extract the username |
104 | 0 | int i = bstr_index_of_c(auth_header->value, "username="); |
105 | 0 | if (i == -1) return HTP_DECLINED; |
106 | | |
107 | 0 | unsigned char *data = bstr_ptr(auth_header->value); |
108 | 0 | size_t len = bstr_len(auth_header->value); |
109 | 0 | size_t pos = i + 9; |
110 | | |
111 | | // Ignore whitespace |
112 | 0 | while ((pos < len) && (isspace((int) data[pos]))) pos++; |
113 | 0 | if (pos == len) return HTP_DECLINED; |
114 | | |
115 | 0 | if (data[pos] != '"') return HTP_DECLINED; |
116 | | |
117 | 0 | return htp_extract_quoted_string_as_bstr(data + pos, len - pos, &(connp->in_tx->request_auth_username), NULL); |
118 | 0 | } |
119 | | |
120 | | /** |
121 | | * Parses Basic Authorization request header. |
122 | | * |
123 | | * @param[in] connp |
124 | | * @param[in] auth_header |
125 | | */ |
126 | 0 | int htp_parse_authorization_basic(htp_connp_t *connp, htp_header_t *auth_header) { |
127 | 0 | unsigned char *data = bstr_ptr(auth_header->value); |
128 | 0 | size_t len = bstr_len(auth_header->value); |
129 | 0 | size_t pos = 5; |
130 | | |
131 | | // Ignore whitespace |
132 | 0 | while ((pos < len) && (isspace((int) data[pos]))) pos++; |
133 | 0 | if (pos == len) return HTP_DECLINED; |
134 | | |
135 | | // Decode base64-encoded data |
136 | 0 | bstr *decoded = htp_base64_decode_mem(data + pos, len - pos); |
137 | 0 | if (decoded == NULL) return HTP_ERROR; |
138 | | |
139 | | // Now extract the username and password |
140 | 0 | int i = bstr_index_of_c(decoded, ":"); |
141 | 0 | if (i == -1) { |
142 | 0 | bstr_free(decoded); |
143 | 0 | return HTP_DECLINED; |
144 | 0 | } |
145 | | |
146 | 0 | connp->in_tx->request_auth_username = bstr_dup_ex(decoded, 0, i); |
147 | 0 | if (connp->in_tx->request_auth_username == NULL) { |
148 | 0 | bstr_free(decoded); |
149 | 0 | return HTP_ERROR; |
150 | 0 | } |
151 | | |
152 | 0 | connp->in_tx->request_auth_password = bstr_dup_ex(decoded, i + 1, bstr_len(decoded) - i - 1); |
153 | 0 | if (connp->in_tx->request_auth_password == NULL) { |
154 | 0 | bstr_free(decoded); |
155 | 0 | bstr_free(connp->in_tx->request_auth_username); |
156 | 0 | return HTP_ERROR; |
157 | 0 | } |
158 | | |
159 | 0 | bstr_free(decoded); |
160 | |
|
161 | 0 | return HTP_OK; |
162 | 0 | } |
163 | | |
164 | | /** |
165 | | * Parses Bearer Authorization request header. |
166 | | * |
167 | | * @param[in] connp |
168 | | * @param[in] auth_header |
169 | | */ |
170 | 0 | int htp_parse_authorization_bearer(htp_connp_t *connp, htp_header_t *auth_header) { |
171 | 0 | unsigned char *data = bstr_ptr(auth_header->value); |
172 | 0 | size_t len = bstr_len(auth_header->value); |
173 | 0 | size_t pos = 6; |
174 | | |
175 | | // Ignore whitespace |
176 | 0 | while ((pos < len) && (isspace((int) data[pos]))) pos++; |
177 | 0 | if (pos == len) return HTP_DECLINED; |
178 | | |
179 | | // There is nothing much else to check with Bearer auth so we just return |
180 | 0 | return HTP_OK; |
181 | 0 | } |
182 | | /** |
183 | | * Parses Authorization request header. |
184 | | * |
185 | | * @param[in] connp |
186 | | */ |
187 | 59.8k | int htp_parse_authorization(htp_connp_t *connp) { |
188 | 59.8k | htp_header_t *auth_header = htp_table_get_c(connp->in_tx->request_headers, "authorization"); |
189 | 59.8k | if (auth_header == NULL) { |
190 | 59.8k | connp->in_tx->request_auth_type = HTP_AUTH_NONE; |
191 | 59.8k | return HTP_OK; |
192 | 59.8k | } |
193 | | |
194 | | // TODO Need a flag to raise when failing to parse authentication headers. |
195 | | |
196 | 0 | if (bstr_begins_with_c_nocase(auth_header->value, "basic")) { |
197 | | // Basic authentication |
198 | 0 | connp->in_tx->request_auth_type = HTP_AUTH_BASIC; |
199 | 0 | return htp_parse_authorization_basic(connp, auth_header); |
200 | 0 | } else if (bstr_begins_with_c_nocase(auth_header->value, "digest")) { |
201 | | // Digest authentication |
202 | 0 | connp->in_tx->request_auth_type = HTP_AUTH_DIGEST; |
203 | 0 | return htp_parse_authorization_digest(connp, auth_header); |
204 | 0 | } else if (bstr_begins_with_c_nocase(auth_header->value, "bearer")) { |
205 | | // OAuth Bearer authentication |
206 | 0 | connp->in_tx->request_auth_type = HTP_AUTH_BEARER; |
207 | 0 | return htp_parse_authorization_bearer(connp, auth_header); |
208 | 0 | } else { |
209 | | // Unrecognized authentication method |
210 | 0 | connp->in_tx->request_auth_type = HTP_AUTH_UNRECOGNIZED; |
211 | 0 | } |
212 | | |
213 | 0 | return HTP_OK; |
214 | 0 | } |