1 #include "quiche/http2/adapter/header_validator.h"
2
3 #include <array>
4
5 #include "absl/strings/escaping.h"
6 #include "absl/strings/numbers.h"
7 #include "quiche/http2/http2_constants.h"
8 #include "quiche/common/platform/api/quiche_logging.h"
9
10 namespace http2 {
11 namespace adapter {
12
13 namespace {
14
15 const absl::string_view kHttp2HeaderNameAllowedChars =
16 "!#$%&\'*+-.0123456789"
17 "^_`abcdefghijklmnopqrstuvwxyz|~";
18
19 const absl::string_view kHttp2HeaderValueAllowedChars =
20 "\t "
21 "!\"#$%&'()*+,-./"
22 "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
23 "abcdefghijklmnopqrstuvwxyz{|}~";
24
25 const absl::string_view kHttp2StatusValueAllowedChars = "0123456789";
26
27 const absl::string_view kValidAuthorityChars =
28 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~%!$&'()["
29 "]*+,;=:";
30
31 using CharMap = std::array<bool, 256>;
32
BuildValidCharMap(absl::string_view valid_chars)33 CharMap BuildValidCharMap(absl::string_view valid_chars) {
34 CharMap map;
35 map.fill(false);
36 for (char c : valid_chars) {
37 // Cast to uint8_t, guaranteed to have 8 bits. A char may have more, leading
38 // to possible indices above 256.
39 map[static_cast<uint8_t>(c)] = true;
40 }
41 return map;
42 }
AllowObsText(CharMap map)43 CharMap AllowObsText(CharMap map) {
44 // Characters above 0x80 are allowed in header field values as `obs-text` in
45 // RFC 7230.
46 for (uint8_t c = 0xff; c >= 0x80; --c) {
47 map[c] = true;
48 }
49 return map;
50 }
51
AllCharsInMap(absl::string_view str,const CharMap & map)52 bool AllCharsInMap(absl::string_view str, const CharMap& map) {
53 for (char c : str) {
54 if (!map[static_cast<uint8_t>(c)]) {
55 return false;
56 }
57 }
58 return true;
59 }
60
IsValidHeaderName(absl::string_view name)61 bool IsValidHeaderName(absl::string_view name) {
62 static const CharMap valid_chars =
63 BuildValidCharMap(kHttp2HeaderNameAllowedChars);
64 return AllCharsInMap(name, valid_chars);
65 }
66
IsValidStatus(absl::string_view status)67 bool IsValidStatus(absl::string_view status) {
68 static const CharMap valid_chars =
69 BuildValidCharMap(kHttp2StatusValueAllowedChars);
70 return AllCharsInMap(status, valid_chars);
71 }
72
ValidateRequestHeaders(const std::vector<std::string> & pseudo_headers,absl::optional<std::string> & authority,absl::string_view method,absl::string_view path,bool allow_extended_connect)73 bool ValidateRequestHeaders(const std::vector<std::string>& pseudo_headers,
74 absl::optional<std::string>& authority,
75 absl::string_view method, absl::string_view path,
76 bool allow_extended_connect) {
77 QUICHE_VLOG(2) << "Request pseudo-headers: ["
78 << absl::StrJoin(pseudo_headers, ", ")
79 << "], allow_extended_connect: " << allow_extended_connect
80 << ", authority: "
81 << (authority ? authority.value() : "<nullopt>")
82 << ", method: " << method << ", path: " << path;
83 if (method == "CONNECT") {
84 if (allow_extended_connect) {
85 // See RFC 8441.
86 static const std::vector<std::string>* kExtendedConnectHeaders =
87 new std::vector<std::string>(
88 {":authority", ":method", ":path", ":protocol", ":scheme"});
89 if (pseudo_headers == *kExtendedConnectHeaders) {
90 return true;
91 }
92 }
93 // See RFC 7540 Section 8.3.
94 static const std::vector<std::string>* kConnectHeaders =
95 new std::vector<std::string>({":authority", ":method"});
96 return authority.has_value() && !authority.value().empty() &&
97 pseudo_headers == *kConnectHeaders;
98 }
99
100 if (path.empty()) {
101 return false;
102 }
103 if (path == "*") {
104 if (method != "OPTIONS") {
105 return false;
106 }
107 } else if (path[0] != '/') {
108 return false;
109 }
110
111 static const std::vector<std::string>* kRequiredHeaders =
112 new std::vector<std::string>(
113 {":authority", ":method", ":path", ":scheme"});
114 return pseudo_headers == *kRequiredHeaders;
115 }
116
ValidateRequestTrailers(const std::vector<std::string> & pseudo_headers)117 bool ValidateRequestTrailers(const std::vector<std::string>& pseudo_headers) {
118 return pseudo_headers.empty();
119 }
120
ValidateResponseHeaders(const std::vector<std::string> & pseudo_headers)121 bool ValidateResponseHeaders(const std::vector<std::string>& pseudo_headers) {
122 static const std::vector<std::string>* kRequiredHeaders =
123 new std::vector<std::string>({":status"});
124 return pseudo_headers == *kRequiredHeaders;
125 }
126
ValidateResponseTrailers(const std::vector<std::string> & pseudo_headers)127 bool ValidateResponseTrailers(const std::vector<std::string>& pseudo_headers) {
128 return pseudo_headers.empty();
129 }
130
131 } // namespace
132
StartHeaderBlock()133 void HeaderValidator::StartHeaderBlock() {
134 HeaderValidatorBase::StartHeaderBlock();
135 pseudo_headers_.clear();
136 method_.clear();
137 path_.clear();
138 authority_ = absl::nullopt;
139 }
140
ValidateSingleHeader(absl::string_view key,absl::string_view value)141 HeaderValidator::HeaderStatus HeaderValidator::ValidateSingleHeader(
142 absl::string_view key, absl::string_view value) {
143 if (key.empty()) {
144 return HEADER_FIELD_INVALID;
145 }
146 if (max_field_size_.has_value() &&
147 key.size() + value.size() > max_field_size_.value()) {
148 QUICHE_VLOG(2) << "Header field size is " << key.size() + value.size()
149 << ", exceeds max size of " << max_field_size_.value();
150 return HEADER_FIELD_TOO_LONG;
151 }
152 const absl::string_view validated_key = key[0] == ':' ? key.substr(1) : key;
153 if (!IsValidHeaderName(validated_key)) {
154 QUICHE_VLOG(2) << "invalid chars in header name: ["
155 << absl::CEscape(validated_key) << "]";
156 return HEADER_FIELD_INVALID;
157 }
158 if (!IsValidHeaderValue(value, obs_text_option_)) {
159 QUICHE_VLOG(2) << "invalid chars in header value: [" << absl::CEscape(value)
160 << "]";
161 return HEADER_FIELD_INVALID;
162 }
163 if (key[0] == ':') {
164 if (key == ":status") {
165 if (value.size() != 3 || !IsValidStatus(value)) {
166 QUICHE_VLOG(2) << "malformed status value: [" << absl::CEscape(value)
167 << "]";
168 return HEADER_FIELD_INVALID;
169 }
170 if (value == "101") {
171 // Switching protocols is not allowed on a HTTP/2 stream.
172 return HEADER_FIELD_INVALID;
173 }
174 status_ = std::string(value);
175 } else if (key == ":method") {
176 method_ = std::string(value);
177 } else if (key == ":authority" && !ValidateAndSetAuthority(value)) {
178 return HEADER_FIELD_INVALID;
179 } else if (key == ":path") {
180 if (value.empty()) {
181 // For now, reject an empty path regardless of scheme.
182 return HEADER_FIELD_INVALID;
183 }
184 path_ = std::string(value);
185 }
186 pseudo_headers_.push_back(std::string(key));
187 } else if (key == "host") {
188 if (!status_.empty()) {
189 // Response headers can contain "Host".
190 } else {
191 if (!authority_.has_value()) {
192 pseudo_headers_.push_back(std::string(":authority"));
193 }
194 if (!ValidateAndSetAuthority(value)) {
195 return HEADER_FIELD_INVALID;
196 }
197 }
198 } else if (key == "content-length") {
199 const ContentLengthStatus status = HandleContentLength(value);
200 switch (status) {
201 case CONTENT_LENGTH_ERROR:
202 return HEADER_FIELD_INVALID;
203 case CONTENT_LENGTH_SKIP:
204 return HEADER_SKIP;
205 case CONTENT_LENGTH_OK:
206 return HEADER_OK;
207 default:
208 return HEADER_FIELD_INVALID;
209 }
210 } else if (key == "te" && value != "trailers") {
211 return HEADER_FIELD_INVALID;
212 } else if (key == "upgrade" || GetInvalidHttp2HeaderSet().contains(key)) {
213 // TODO(b/78024822): Remove the "upgrade" here once it's added to
214 // GetInvalidHttp2HeaderSet().
215 return HEADER_FIELD_INVALID;
216 }
217 return HEADER_OK;
218 }
219
220 // Returns true if all required pseudoheaders and no extra pseudoheaders are
221 // present for the given header type.
FinishHeaderBlock(HeaderType type)222 bool HeaderValidator::FinishHeaderBlock(HeaderType type) {
223 std::sort(pseudo_headers_.begin(), pseudo_headers_.end());
224 switch (type) {
225 case HeaderType::REQUEST:
226 return ValidateRequestHeaders(pseudo_headers_, authority_, method_, path_,
227 allow_extended_connect_);
228 case HeaderType::REQUEST_TRAILER:
229 return ValidateRequestTrailers(pseudo_headers_);
230 case HeaderType::RESPONSE_100:
231 case HeaderType::RESPONSE:
232 return ValidateResponseHeaders(pseudo_headers_);
233 case HeaderType::RESPONSE_TRAILER:
234 return ValidateResponseTrailers(pseudo_headers_);
235 }
236 return false;
237 }
238
IsValidHeaderValue(absl::string_view value,ObsTextOption option)239 bool HeaderValidator::IsValidHeaderValue(absl::string_view value,
240 ObsTextOption option) {
241 static const CharMap valid_chars =
242 BuildValidCharMap(kHttp2HeaderValueAllowedChars);
243 static const CharMap valid_chars_with_obs_text =
244 AllowObsText(BuildValidCharMap(kHttp2HeaderValueAllowedChars));
245 return AllCharsInMap(value, option == ObsTextOption::kAllow
246 ? valid_chars_with_obs_text
247 : valid_chars);
248 }
249
IsValidAuthority(absl::string_view authority)250 bool HeaderValidator::IsValidAuthority(absl::string_view authority) {
251 static const CharMap valid_chars = BuildValidCharMap(kValidAuthorityChars);
252 return AllCharsInMap(authority, valid_chars);
253 }
254
HandleContentLength(absl::string_view value)255 HeaderValidator::ContentLengthStatus HeaderValidator::HandleContentLength(
256 absl::string_view value) {
257 if (value.empty()) {
258 return CONTENT_LENGTH_ERROR;
259 }
260
261 if (status_ == "204" && value != "0") {
262 // There should be no body in a "204 No Content" response.
263 return CONTENT_LENGTH_ERROR;
264 }
265 if (!status_.empty() && status_[0] == '1' && value != "0") {
266 // There should also be no body in a 1xx response.
267 return CONTENT_LENGTH_ERROR;
268 }
269
270 size_t content_length = 0;
271 const bool valid = absl::SimpleAtoi(value, &content_length);
272 if (!valid) {
273 return CONTENT_LENGTH_ERROR;
274 }
275
276 if (content_length_.has_value()) {
277 return content_length == content_length_.value() ? CONTENT_LENGTH_SKIP
278 : CONTENT_LENGTH_ERROR;
279 }
280 content_length_ = content_length;
281 return CONTENT_LENGTH_OK;
282 }
283
284 // Returns whether `authority` contains only characters from the `host` ABNF
285 // from RFC 3986 section 3.2.2.
ValidateAndSetAuthority(absl::string_view authority)286 bool HeaderValidator::ValidateAndSetAuthority(absl::string_view authority) {
287 if (!IsValidAuthority(authority)) {
288 return false;
289 }
290 if (authority_.has_value() && authority != authority_.value()) {
291 return false;
292 }
293 authority_ = std::string(authority);
294 return true;
295 }
296
297 } // namespace adapter
298 } // namespace http2
299