• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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