Bug #9262
openhttp2: stream progress does not follow the header block's completion; firewall decisions at request_headers wait forever or decide early
Description
Description¶
The HTTP/2 stream progress follows the kind of the last frame, not the completion of the parts of the message. Any HEADERS frame without END_STREAM puts the stream in request_headers, and it stays there after END_HEADERS completed the header block until a DATA frame or END_STREAM arrives, which may never happen.
In firewall mode, logic that waits while the transaction is at a hook, because that hook's data can still arrive, therefore misbehaves. An LTE allow-list on the request headers (accept:flow http2:stream:<request_headers on http.host) stays pending for a stream that sends a complete header block and no DATA: a request to a denied host is never dropped, and with DATA it is dropped one packet late, after the request headers were delivered.
HTTP/1 progress has the expected meaning: a state is entered when the previous part is complete and left when its own part is complete.
Steps to reproduce¶
Firewall rule, plus accept:hook rules for the other states, with an HTTP/2 app default policy of drop:flow:
accept:flow,alert http2:stream:<request_headers any any -> any any (http.host; content:"good.example"; sid:101;)
Send an HTTP/2 POST to evil.example: one HEADERS frame with END_HEADERS and without END_STREAM, then no DATA.
Expected behaviour¶
The flow is dropped by the request_headers default policy on the HEADERS packet.
Actual behaviour¶
The flow is never dropped.
Possible fix¶
Let the stream progress follow completion: HEADERS without END_HEADERS to request_headers; END_HEADERS without END_STREAM to request_data; END_STREAM to closed once the header block is complete. Related: #9217.
No data to display