Add new HTTP2 frame type SecurityFrame for security-related data. Also add new setting to negotiate whether the SecurityFrame type is supported.

PiperOrigin-RevId: 693054809
pull/37142/head
Alisha Nanda 3 weeks ago committed by Copybara-Service
parent 86a68b4000
commit e37d3848a1
  1. 2
      BUILD
  2. 2
      CMakeLists.txt
  3. 1
      Makefile
  4. 2
      Package.swift
  5. 4
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 2
      gRPC-C++.podspec
  9. 3
      gRPC-Core.podspec
  10. 2
      grpc.gemspec
  11. 2
      include/grpc/impl/channel_arg_names.h
  12. 2
      package.xml
  13. 35
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  14. 20
      src/core/ext/transport/chttp2/transport/frame.cc
  15. 12
      src/core/ext/transport/chttp2/transport/frame.h
  16. 82
      src/core/ext/transport/chttp2/transport/frame_security.cc
  17. 44
      src/core/ext/transport/chttp2/transport/frame_security.h
  18. 9
      src/core/ext/transport/chttp2/transport/http2_settings.cc
  19. 10
      src/core/ext/transport/chttp2/transport/http2_settings.h
  20. 3
      src/core/ext/transport/chttp2/transport/internal.h
  21. 1
      src/core/ext/transport/chttp2/transport/legacy_frame.h
  22. 22
      src/core/ext/transport/chttp2/transport/parsing.cc
  23. 1
      src/python/grpcio/grpc_core_dependencies.py
  24. 37
      test/core/bad_client/tests/simple_request.cc
  25. 5
      test/core/end2end/fuzzers/fuzzer_input.proto
  26. 3
      test/core/end2end/fuzzers/network_input.cc
  27. 4
      test/core/transport/chttp2/frame_test.cc
  28. 53
      test/core/transport/chttp2/http2_settings_test.cc
  29. 2
      tools/doxygen/Doxyfile.c++.internal
  30. 2
      tools/doxygen/Doxyfile.core.internal

@ -4701,6 +4701,7 @@ grpc_cc_library(
"//src/core:ext/transport/chttp2/transport/frame_goaway.cc",
"//src/core:ext/transport/chttp2/transport/frame_ping.cc",
"//src/core:ext/transport/chttp2/transport/frame_rst_stream.cc",
"//src/core:ext/transport/chttp2/transport/frame_security.cc",
"//src/core:ext/transport/chttp2/transport/frame_settings.cc",
"//src/core:ext/transport/chttp2/transport/frame_window_update.cc",
"//src/core:ext/transport/chttp2/transport/parsing.cc",
@ -4715,6 +4716,7 @@ grpc_cc_library(
"//src/core:ext/transport/chttp2/transport/frame_goaway.h",
"//src/core:ext/transport/chttp2/transport/frame_ping.h",
"//src/core:ext/transport/chttp2/transport/frame_rst_stream.h",
"//src/core:ext/transport/chttp2/transport/frame_security.h",
"//src/core:ext/transport/chttp2/transport/frame_settings.h",
"//src/core:ext/transport/chttp2/transport/frame_window_update.h",
"//src/core:ext/transport/chttp2/transport/internal.h",

2
CMakeLists.txt generated

@ -2317,6 +2317,7 @@ add_library(grpc
src/core/ext/transport/chttp2/transport/frame_goaway.cc
src/core/ext/transport/chttp2/transport/frame_ping.cc
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
src/core/ext/transport/chttp2/transport/frame_security.cc
src/core/ext/transport/chttp2/transport/frame_settings.cc
src/core/ext/transport/chttp2/transport/frame_window_update.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@ -3428,6 +3429,7 @@ add_library(grpc_unsecure
src/core/ext/transport/chttp2/transport/frame_goaway.cc
src/core/ext/transport/chttp2/transport/frame_ping.cc
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
src/core/ext/transport/chttp2/transport/frame_security.cc
src/core/ext/transport/chttp2/transport/frame_settings.cc
src/core/ext/transport/chttp2/transport/frame_window_update.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc

1
Makefile generated

@ -720,6 +720,7 @@ LIBGRPC_SRC = \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
src/core/ext/transport/chttp2/transport/frame_ping.cc \
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \
src/core/ext/transport/chttp2/transport/frame_security.cc \
src/core/ext/transport/chttp2/transport/frame_settings.cc \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder.cc \

2
Package.swift generated

@ -229,6 +229,8 @@ let package = Package(
"src/core/ext/transport/chttp2/transport/frame_ping.h",
"src/core/ext/transport/chttp2/transport/frame_rst_stream.cc",
"src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
"src/core/ext/transport/chttp2/transport/frame_security.cc",
"src/core/ext/transport/chttp2/transport/frame_security.h",
"src/core/ext/transport/chttp2/transport/frame_settings.cc",
"src/core/ext/transport/chttp2/transport/frame_settings.h",
"src/core/ext/transport/chttp2/transport/frame_window_update.cc",

@ -277,6 +277,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_goaway.h
- src/core/ext/transport/chttp2/transport/frame_ping.h
- src/core/ext/transport/chttp2/transport/frame_rst_stream.h
- src/core/ext/transport/chttp2/transport/frame_security.h
- src/core/ext/transport/chttp2/transport/frame_settings.h
- src/core/ext/transport/chttp2/transport/frame_window_update.h
- src/core/ext/transport/chttp2/transport/hpack_constants.h
@ -1332,6 +1333,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_goaway.cc
- src/core/ext/transport/chttp2/transport/frame_ping.cc
- src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
- src/core/ext/transport/chttp2/transport/frame_security.cc
- src/core/ext/transport/chttp2/transport/frame_settings.cc
- src/core/ext/transport/chttp2/transport/frame_window_update.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@ -2313,6 +2315,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_goaway.h
- src/core/ext/transport/chttp2/transport/frame_ping.h
- src/core/ext/transport/chttp2/transport/frame_rst_stream.h
- src/core/ext/transport/chttp2/transport/frame_security.h
- src/core/ext/transport/chttp2/transport/frame_settings.h
- src/core/ext/transport/chttp2/transport/frame_window_update.h
- src/core/ext/transport/chttp2/transport/hpack_constants.h
@ -2796,6 +2799,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_goaway.cc
- src/core/ext/transport/chttp2/transport/frame_ping.cc
- src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
- src/core/ext/transport/chttp2/transport/frame_security.cc
- src/core/ext/transport/chttp2/transport/frame_settings.cc
- src/core/ext/transport/chttp2/transport/frame_window_update.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder.cc

1
config.m4 generated

@ -95,6 +95,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
src/core/ext/transport/chttp2/transport/frame_ping.cc \
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \
src/core/ext/transport/chttp2/transport/frame_security.cc \
src/core/ext/transport/chttp2/transport/frame_settings.cc \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder.cc \

1
config.w32 generated

@ -60,6 +60,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_ping.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_rst_stream.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_security.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_settings.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.cc " +

2
gRPC-C++.podspec generated

@ -325,6 +325,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.h',
'src/core/ext/transport/chttp2/transport/frame_security.h',
'src/core/ext/transport/chttp2/transport/frame_settings.h',
'src/core/ext/transport/chttp2/transport/frame_window_update.h',
'src/core/ext/transport/chttp2/transport/hpack_constants.h',
@ -1615,6 +1616,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.h',
'src/core/ext/transport/chttp2/transport/frame_security.h',
'src/core/ext/transport/chttp2/transport/frame_settings.h',
'src/core/ext/transport/chttp2/transport/frame_window_update.h',
'src/core/ext/transport/chttp2/transport/hpack_constants.h',

3
gRPC-Core.podspec generated

@ -349,6 +349,8 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/frame_ping.h',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.h',
'src/core/ext/transport/chttp2/transport/frame_security.cc',
'src/core/ext/transport/chttp2/transport/frame_security.h',
'src/core/ext/transport/chttp2/transport/frame_settings.cc',
'src/core/ext/transport/chttp2/transport/frame_settings.h',
'src/core/ext/transport/chttp2/transport/frame_window_update.cc',
@ -2467,6 +2469,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.h',
'src/core/ext/transport/chttp2/transport/frame_security.h',
'src/core/ext/transport/chttp2/transport/frame_settings.h',
'src/core/ext/transport/chttp2/transport/frame_window_update.h',
'src/core/ext/transport/chttp2/transport/hpack_constants.h',

2
grpc.gemspec generated

@ -235,6 +235,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/transport/chttp2/transport/frame_ping.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_security.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_security.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_window_update.cc )

@ -400,6 +400,8 @@
"grpc.max_allowed_incoming_connections"
/** Configure per-channel or per-server stats plugins. */
#define GRPC_ARG_EXPERIMENTAL_STATS_PLUGINS "grpc.experimental.stats_plugins"
/** If non-zero, allow security frames to be sent and received. */
#define GRPC_ARG_SECURITY_FRAME_ALLOWED "grpc.security_frame_allowed"
/** \} */
#endif /* GRPC_IMPL_CHANNEL_ARG_NAMES_H */

2
package.xml generated

@ -217,6 +217,8 @@
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_ping.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_rst_stream.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_rst_stream.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_security.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_security.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_settings.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_settings.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_window_update.cc" role="src" />

@ -60,6 +60,7 @@
#include "src/core/ext/transport/chttp2/transport/frame_data.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
#include "src/core/ext/transport/chttp2/transport/frame_security.h"
#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
@ -549,6 +550,9 @@ static void read_channel_args(grpc_chttp2_transport* t,
t->settings.mutable_local().SetPreferredReceiveCryptoMessageSize(INT_MAX);
}
t->settings.mutable_local().SetAllowSecurityFrame(
channel_args.GetBool(GRPC_ARG_SECURITY_FRAME_ALLOWED).value_or(false));
t->ping_on_rst_stream_percent = grpc_core::Clamp(
channel_args.GetInt(GRPC_ARG_HTTP2_PING_ON_RST_STREAM_PERCENT)
.value_or(1),
@ -592,8 +596,19 @@ void grpc_chttp2_transport::WriteSecurityFrameLocked(
if (data == nullptr) {
return;
}
// TODO(alishananda): create security frame and append to qbuf, initiate write
grpc_core::Crash("unreachable");
if (!settings.peer().allow_security_frame()) {
close_transport_locked(
this,
grpc_error_set_int(
GRPC_ERROR_CREATE("Unexpected SECURITY frame scheduled for write"),
grpc_core::StatusIntProperty::kRpcStatus,
GRPC_STATUS_FAILED_PRECONDITION));
}
grpc_core::SliceBuffer security_frame;
grpc_chttp2_security_frame_create(data->c_slice_buffer(), data->Length(),
security_frame.c_slice_buffer());
grpc_slice_buffer_move_into(security_frame.c_slice_buffer(), &qbuf);
grpc_chttp2_initiate_write(this, GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE);
}
using grpc_event_engine::experimental::QueryExtension;
@ -638,13 +653,15 @@ grpc_chttp2_transport::grpc_chttp2_transport(
}
}
transport_framing_endpoint_extension = QueryExtension<
grpc_core::TransportFramingEndpointExtension>(
grpc_event_engine::experimental::grpc_get_wrapped_event_engine_endpoint(
ep.get()));
if (transport_framing_endpoint_extension != nullptr) {
transport_framing_endpoint_extension->SetSendFrameCallback(
[this](grpc_core::SliceBuffer* data) { WriteSecurityFrame(data); });
if (channel_args.GetBool(GRPC_ARG_SECURITY_FRAME_ALLOWED).value_or(false)) {
transport_framing_endpoint_extension = QueryExtension<
grpc_core::TransportFramingEndpointExtension>(
grpc_event_engine::experimental::grpc_get_wrapped_event_engine_endpoint(
ep.get()));
if (transport_framing_endpoint_extension != nullptr) {
transport_framing_endpoint_extension->SetSendFrameCallback(
[this](grpc_core::SliceBuffer* data) { WriteSecurityFrame(data); });
}
}
CHECK(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) ==

@ -38,6 +38,7 @@ constexpr uint8_t kFrameTypePing = 6;
constexpr uint8_t kFrameTypeGoaway = 7;
constexpr uint8_t kFrameTypeWindowUpdate = 8;
constexpr uint8_t kFrameTypePushPromise = 5;
constexpr uint8_t kFrameTypeSecurity = 200;
constexpr uint8_t kFlagEndStream = 1;
constexpr uint8_t kFlagAck = 1;
@ -122,6 +123,7 @@ class SerializeExtraBytesRequired {
size_t operator()(const Http2PingFrame&) { return 8; }
size_t operator()(const Http2GoawayFrame&) { return 8; }
size_t operator()(const Http2WindowUpdateFrame&) { return 4; }
size_t operator()(const Http2SecurityFrame&) { return 0; }
size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); }
};
@ -218,6 +220,15 @@ class SerializeHeaderAndPayload {
out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
}
void operator()(Http2SecurityFrame& frame) {
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
Http2FrameHeader{static_cast<uint32_t>(frame.payload.Length()),
kFrameTypeSecurity, 0, 0}
.Serialize(hdr.begin());
out_.AppendIndexed(Slice(std::move(hdr)));
out_.TakeAndAppend(frame.payload);
}
void operator()(Http2UnknownFrame&) { Crash("unreachable"); }
private:
@ -415,6 +426,11 @@ absl::StatusOr<Http2WindowUpdateFrame> ParseWindowUpdateFrame(
return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)};
}
absl::StatusOr<Http2SecurityFrame> ParseSecurityFrame(
const Http2FrameHeader& /*hdr*/, SliceBuffer& payload) {
return Http2SecurityFrame{std::move(payload)};
}
} // namespace
void Http2FrameHeader::Serialize(uint8_t* output) const {
@ -447,6 +463,8 @@ std::string Http2FrameTypeString(uint8_t frame_type) {
return "WINDOW_UPDATE";
case kFrameTypePing:
return "PING";
case kFrameTypeSecurity:
return "SECURITY";
}
return absl::StrCat("UNKNOWN(", frame_type, ")");
}
@ -495,6 +513,8 @@ absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr,
return absl::InternalError(
"push promise not supported (and SETTINGS_ENABLE_PUSH explicitly "
"disabled).");
case kFrameTypeSecurity:
return ParseSecurityFrame(hdr, payload);
default:
return Http2UnknownFrame{};
}

@ -151,6 +151,15 @@ struct Http2WindowUpdateFrame {
}
};
// Security-related frame
struct Http2SecurityFrame {
SliceBuffer payload;
bool operator==(const Http2SecurityFrame& other) const {
return payload.JoinIntoString() == other.payload.JoinIntoString();
}
};
// Type of frame was unknown (and should be ignored)
struct Http2UnknownFrame {
bool operator==(const Http2UnknownFrame&) const { return true; }
@ -164,7 +173,8 @@ struct Http2UnknownFrame {
using Http2Frame =
absl::variant<Http2DataFrame, Http2HeaderFrame, Http2ContinuationFrame,
Http2RstStreamFrame, Http2SettingsFrame, Http2PingFrame,
Http2GoawayFrame, Http2WindowUpdateFrame, Http2UnknownFrame>;
Http2GoawayFrame, Http2WindowUpdateFrame, Http2SecurityFrame,
Http2UnknownFrame>;
///////////////////////////////////////////////////////////////////////////////
// Frame header

@ -0,0 +1,82 @@
//
// Copyright 2024 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "src/core/ext/transport/chttp2/transport/frame_security.h"
#include <cstddef>
#include <cstdint>
#include "absl/status/status.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/event_engine_shims/endpoint.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/transport_framing_endpoint_extension.h"
absl::Status grpc_chttp2_security_frame_parser_parse(void* parser,
grpc_chttp2_transport* t,
grpc_chttp2_stream* /*s*/,
const grpc_slice& slice,
int is_last) {
// Ignore frames from non-EventEngine endpoints.
if (t->transport_framing_endpoint_extension == nullptr) {
return absl::OkStatus();
}
grpc_chttp2_security_frame_parser* p =
static_cast<grpc_chttp2_security_frame_parser*>(parser);
p->payload.Append(grpc_core::Slice(slice));
if (is_last) {
// Send security frame payload to endpoint.
t->transport_framing_endpoint_extension->ReceiveFrame(
std::move(p->payload));
}
return absl::OkStatus();
}
absl::Status grpc_chttp2_security_frame_parser_begin_frame(
grpc_chttp2_security_frame_parser* parser) {
parser->payload.Clear();
return absl::OkStatus();
}
void grpc_chttp2_security_frame_create(grpc_slice_buffer* payload,
uint32_t length,
grpc_slice_buffer* frame) {
// does this frame need padding for security?
// do we need to worry about max frame size? it's 16 bytes
grpc_slice hdr;
uint8_t* p;
static const size_t header_size = 9;
hdr = GRPC_SLICE_MALLOC(header_size);
p = GRPC_SLICE_START_PTR(hdr);
*p++ = static_cast<uint8_t>(length >> 16);
*p++ = static_cast<uint8_t>(length >> 8);
*p++ = static_cast<uint8_t>(length);
*p++ = GRPC_CHTTP2_FRAME_SECURITY;
*p++ = 0; // no flags
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
grpc_slice_buffer_add(frame, hdr);
grpc_slice_buffer_move_first_no_ref(payload, payload->length, frame);
}

@ -0,0 +1,44 @@
//
// Copyright 2024 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H
#include <grpc/slice.h>
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/transport_framing_endpoint_extension.h"
struct grpc_chttp2_security_frame_parser {
grpc_core::SliceBuffer payload;
};
void grpc_chttp2_security_frame_create(grpc_slice_buffer* payload,
uint32_t length,
grpc_slice_buffer* frame);
absl::Status grpc_chttp2_security_frame_parser_begin_frame(
grpc_chttp2_security_frame_parser* parser);
grpc_error_handle grpc_chttp2_security_frame_parser_parse(
void* parser, grpc_chttp2_transport* t, grpc_chttp2_stream* s,
const grpc_slice& slice, int is_last);
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H

@ -58,6 +58,9 @@ void Http2Settings::Diff(
cb(kGrpcPreferredReceiveCryptoFrameSizeWireId,
preferred_receive_crypto_message_size_);
}
if (allow_security_frame_ != old.allow_security_frame_) {
cb(kGrpcAllowSecurityFrameWireId, allow_security_frame_);
}
}
std::string Http2Settings::WireIdToName(uint16_t wire_id) {
@ -78,6 +81,8 @@ std::string Http2Settings::WireIdToName(uint16_t wire_id) {
return std::string(allow_true_binary_metadata_name());
case kGrpcPreferredReceiveCryptoFrameSizeWireId:
return std::string(preferred_receive_crypto_message_size_name());
case kGrpcAllowSecurityFrameWireId:
return std::string(allow_security_frame_name());
default:
return absl::StrCat("UNKNOWN (", wire_id, ")");
}
@ -119,6 +124,10 @@ grpc_http2_error_code Http2Settings::Apply(uint16_t key, uint32_t value) {
Clamp(value, min_preferred_receive_crypto_message_size(),
max_preferred_receive_crypto_message_size());
break;
case kGrpcAllowSecurityFrameWireId:
if (value > 1) return GRPC_HTTP2_PROTOCOL_ERROR;
allow_security_frame_ = value != 0;
break;
}
return GRPC_HTTP2_NO_ERROR;
}

@ -42,6 +42,7 @@ class Http2Settings {
kMaxHeaderListSizeWireId = 6,
kGrpcAllowTrueBinaryMetadataWireId = 65027,
kGrpcPreferredReceiveCryptoFrameSizeWireId = 65028,
kGrpcAllowSecurityFrameWireId = 65029,
};
void Diff(bool is_first_send, const Http2Settings& old,
@ -60,6 +61,7 @@ class Http2Settings {
bool allow_true_binary_metadata() const {
return allow_true_binary_metadata_;
}
bool allow_security_frame() const { return allow_security_frame_; }
void SetHeaderTableSize(uint32_t x) { header_table_size_ = x; }
void SetMaxConcurrentStreams(uint32_t x) { max_concurrent_streams_ = x; }
@ -79,6 +81,7 @@ class Http2Settings {
Clamp(x, min_preferred_receive_crypto_message_size(),
max_preferred_receive_crypto_message_size());
}
void SetAllowSecurityFrame(bool x) { allow_security_frame_ = x; }
static absl::string_view header_table_size_name() {
return "HEADER_TABLE_SIZE";
@ -100,6 +103,9 @@ class Http2Settings {
static absl::string_view preferred_receive_crypto_message_size_name() {
return "GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE";
}
static absl::string_view allow_security_frame_name() {
return "GRPC_ALLOW_SECURITY_FRAME";
}
static uint32_t max_initial_window_size() { return 2147483647u; }
static uint32_t max_max_frame_size() { return 16777215u; }
@ -120,7 +126,8 @@ class Http2Settings {
preferred_receive_crypto_message_size_ ==
rhs.preferred_receive_crypto_message_size_ &&
enable_push_ == rhs.enable_push_ &&
allow_true_binary_metadata_ == rhs.allow_true_binary_metadata_;
allow_true_binary_metadata_ == rhs.allow_true_binary_metadata_ &&
allow_security_frame_ == rhs.allow_security_frame_;
}
bool operator!=(const Http2Settings& rhs) const { return !operator==(rhs); }
@ -134,6 +141,7 @@ class Http2Settings {
uint32_t preferred_receive_crypto_message_size_ = 0u;
bool enable_push_ = true;
bool allow_true_binary_metadata_ = false;
bool allow_security_frame_ = false;
};
class Http2SettingsManager {

@ -45,6 +45,7 @@
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
#include "src/core/ext/transport/chttp2/transport/frame_security.h"
#include "src/core/ext/transport/chttp2/transport/frame_settings.h"
#include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
@ -405,6 +406,8 @@ struct grpc_chttp2_transport final : public grpc_core::FilterStackTransport,
} simple;
/// parser for goaway frames
grpc_chttp2_goaway_parser goaway_parser;
// parser for secure frames
grpc_chttp2_security_frame_parser security_frame_parser;
grpc_core::chttp2::TransportFlowControl flow_control;
/// initial window change. This is tracked as we parse settings frames from

@ -33,6 +33,7 @@ typedef struct grpc_chttp2_transport grpc_chttp2_transport;
#define GRPC_CHTTP2_FRAME_PING 6
#define GRPC_CHTTP2_FRAME_GOAWAY 7
#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
#define GRPC_CHTTP2_FRAME_SECURITY 200
#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1
#define GRPC_CHTTP2_FLAG_ACK 1

@ -47,6 +47,7 @@
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
#include "src/core/ext/transport/chttp2/transport/frame_security.h"
#include "src/core/ext/transport/chttp2/transport/frame_settings.h"
#include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
@ -88,6 +89,7 @@ static grpc_error_handle init_window_update_frame_parser(
grpc_chttp2_transport* t);
static grpc_error_handle init_ping_parser(grpc_chttp2_transport* t);
static grpc_error_handle init_goaway_parser(grpc_chttp2_transport* t);
static grpc_error_handle init_security_frame_parser(grpc_chttp2_transport* t);
static grpc_error_handle init_non_header_skip_frame_parser(
grpc_chttp2_transport* t);
@ -194,6 +196,8 @@ std::string FrameTypeString(uint8_t frame_type, uint8_t flags) {
return MakeFrameTypeString("GOAWAY", flags, {});
case GRPC_CHTTP2_FRAME_WINDOW_UPDATE:
return MakeFrameTypeString("WINDOW_UPDATE", flags, {});
case GRPC_CHTTP2_FRAME_SECURITY:
return MakeFrameTypeString("SECURITY", flags, {});
default:
return MakeFrameTypeString(
absl::StrCat("UNKNOWN_FRAME_TYPE_", static_cast<int>(frame_type)),
@ -451,6 +455,14 @@ static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t,
return init_ping_parser(t);
case GRPC_CHTTP2_FRAME_GOAWAY:
return init_goaway_parser(t);
case GRPC_CHTTP2_FRAME_SECURITY:
if (!t->settings.peer().allow_security_frame()) {
if (GRPC_TRACE_FLAG_ENABLED(http)) {
LOG(ERROR) << "Security frame received but not allowed, ignoring";
}
return init_non_header_skip_frame_parser(t);
}
return init_security_frame_parser(t);
default:
GRPC_TRACE_LOG(http, ERROR)
<< "Unknown frame type "
@ -882,6 +894,16 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
return absl::OkStatus();
}
static grpc_error_handle init_security_frame_parser(grpc_chttp2_transport* t) {
grpc_error_handle err =
grpc_chttp2_security_frame_parser_begin_frame(&t->security_frame_parser);
if (!err.ok()) return err;
t->parser = grpc_chttp2_transport::Parser{
"security_frame", grpc_chttp2_security_frame_parser_parse,
&t->security_frame_parser};
return absl::OkStatus();
}
static grpc_error_handle parse_frame_slice(grpc_chttp2_transport* t,
const grpc_slice& slice,
int is_last) {

@ -69,6 +69,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/transport/chttp2/transport/frame_goaway.cc',
'src/core/ext/transport/chttp2/transport/frame_ping.cc',
'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc',
'src/core/ext/transport/chttp2/transport/frame_security.cc',
'src/core/ext/transport/chttp2/transport/frame_settings.cc',
'src/core/ext/transport/chttp2/transport/frame_window_update.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder.cc',

@ -39,6 +39,26 @@
"\x10\x02te\x08trailers" \
"\x10\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)"
#define ONE_SETTING_HDR \
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \
"\x00\x00\x06\x04\x00\x00\x00\x00\x00" /* settings frame */
#define USUAL_HDR \
"\x00\x00\xc9\x01\x04\x00\x00\x00\x01" /* headers: generated from \
simple_request.headers in this \
directory */ \
"\x10\x05:path\x08/foo/bar" \
"\x10\x07:scheme\x04http" \
"\x10\x07:method\x04POST" \
"\x10\x0a:authority\x09localhost" \
"\x10\x0c" \
"content-type\x10" \
"application/grpc" \
"\x10\x14grpc-accept-encoding\x15" \
"deflate,identity,gzip" \
"\x10\x02te\x08trailers" \
"\x10\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)"
#define PFX_STR_UNUSUAL \
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \
"\x00\x00\x00\x04\x00\x00\x00\x00\x00" /* settings frame */ \
@ -219,7 +239,6 @@ int main(int argc, char** argv) {
"\x00\x00\x05\x00\x00\x00\x00\x00\x01"
"\x34\x00\x00\x00\x00",
0);
// push a data frame with bad flags
GRPC_RUN_BAD_CLIENT_TEST(verifier, nullptr,
PFX_STR "\x00\x00\x00\x00\x02\x00\x00\x00\x01", 0);
@ -235,6 +254,22 @@ int main(int argc, char** argv) {
"\x00\x00\x04\x08\x00\x00\x00\x00\x01"
"\x00\x00\x00\x00",
0);
// push a valid secure frame with payload "hello" and setting
// `allow_security_frame` enabled, frame should be parsed
GRPC_RUN_BAD_CLIENT_TEST(
verifier, nullptr,
ONE_SETTING_HDR
"\xFE\x05\x00\x00\x00\x01" USUAL_HDR
"\x00\x00\x05\xC8\x00\x00\x00\x00\x00\x68\x65\x6C\x6C\x6F",
0);
// push a valid secure frame with payload "hello" and setting
// `allow_security_frame` disabled, frame should be ignored
GRPC_RUN_BAD_CLIENT_TEST(
VerifyRpcDoesNotGetCanceled, nullptr,
ONE_SETTING_HDR
"\xFE\x05\x00\x00\x00\x00" USUAL_HDR
"\x00\x00\x05\xC8\x00\x00\x00\x00\x00\x68\x65\x6C\x6C\x6F",
0);
// push a short goaway
GRPC_RUN_BAD_CLIENT_TEST(failure_verifier, nullptr,
PFX_STR "\x00\x00\x04\x07\x00\x00\x00\x00\x00", 0);

@ -121,6 +121,10 @@ message H2WindowUpdateFrame {
uint32 increment = 2;
}
message H2SecurityFrame {
bytes payload = 1;
}
message H2ClientPrefix {}
message ChaoticGoodServerFragment {
@ -202,6 +206,7 @@ message InputSegment {
uint32 repeated_zeros = 12;
ChaoticGoodFrame chaotic_good = 13;
FakeTransportFrame fake_transport_frame = 14;
H2SecurityFrame security_frame = 15;
}
}

@ -326,6 +326,9 @@ grpc_slice SliceFromSegment(const fuzzer_input::InputSegment& segment) {
segment.window_update().stream_id(),
segment.window_update().increment(),
});
case fuzzer_input::InputSegment::kSecurityFrame:
return SliceFromH2Frame(Http2SecurityFrame{
SliceBufferFromBytes(segment.security_frame().payload())});
case fuzzer_input::InputSegment::kClientPrefix:
return grpc_slice_from_static_string("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
case fuzzer_input::InputSegment::kRepeatedZeros: {

@ -162,6 +162,8 @@ TEST(Frame, Serialization) {
0x0b, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2WindowUpdateFrame{1, 0x12345678}),
ByteVec(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78));
EXPECT_EQ(Serialize(Http2SecurityFrame{SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 200, 0, 0, 0, 0, 0, 'h', 'e', 'l', 'l', 'o'));
}
TEST(Frame, Parse) {
@ -215,6 +217,8 @@ TEST(Frame, Parse) {
Slice::FromCopiedString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78),
Http2Frame(Http2WindowUpdateFrame{1, 0x12345678}));
EXPECT_EQ(ParseFrame(0, 0, 5, 200, 0, 0, 0, 0, 0, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2SecurityFrame{SliceBufferFromString("hello")}));
}
TEST(Frame, ParsePadded) {

@ -29,6 +29,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) {
settings.SetMaxHeaderListSize(6);
settings.SetAllowTrueBinaryMetadata(true);
settings.SetPreferredReceiveCryptoMessageSize(77777);
settings.SetAllowSecurityFrame(true);
EXPECT_EQ(settings.header_table_size(), 1u);
EXPECT_EQ(settings.enable_push(), true);
EXPECT_EQ(settings.max_concurrent_streams(), 3u);
@ -37,6 +38,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) {
EXPECT_EQ(settings.max_header_list_size(), 6u);
EXPECT_EQ(settings.allow_true_binary_metadata(), true);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 77777u);
EXPECT_EQ(settings.allow_security_frame(), true);
settings.SetHeaderTableSize(10);
settings.SetEnablePush(false);
settings.SetMaxConcurrentStreams(30);
@ -45,6 +47,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) {
settings.SetMaxHeaderListSize(60);
settings.SetAllowTrueBinaryMetadata(false);
settings.SetPreferredReceiveCryptoMessageSize(70000);
settings.SetAllowSecurityFrame(false);
EXPECT_EQ(settings.header_table_size(), 10u);
EXPECT_EQ(settings.enable_push(), false);
EXPECT_EQ(settings.max_concurrent_streams(), 30u);
@ -53,6 +56,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) {
EXPECT_EQ(settings.max_header_list_size(), 60u);
EXPECT_EQ(settings.allow_true_binary_metadata(), false);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 70000u);
EXPECT_EQ(settings.allow_security_frame(), false);
}
TEST(Http2SettingsTest, InitialWindowSizeLimits) {
@ -269,6 +273,32 @@ TEST(Http2SettingsTest, DiffOnSettingsWithEightValuesSet) {
KeyValue{65027, 1}, KeyValue{65028, 77777}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithNineValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
settings1.SetMaxFrameSize(50000);
settings1.SetMaxHeaderListSize(6);
settings1.SetAllowTrueBinaryMetadata(true);
settings1.SetPreferredReceiveCryptoMessageSize(77777);
settings1.SetAllowSecurityFrame(true);
EXPECT_THAT(
Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1},
KeyValue{65028, 77777}, KeyValue{65029, 1}));
EXPECT_THAT(
Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1},
KeyValue{65028, 77777}, KeyValue{65029, 1}));
}
TEST(Http2SettingsTest, ChangingHeaderTableSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
@ -358,6 +388,17 @@ TEST(Http2SettingsTest,
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingAllowSecurityFrameChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetAllowSecurityFrame(true);
EXPECT_NE(settings1, settings2);
settings2.SetAllowSecurityFrame(true);
EXPECT_EQ(settings1, settings2);
settings2.SetAllowSecurityFrame(false);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, WireIdToNameWorks) {
EXPECT_EQ(Http2Settings::WireIdToName(1), "HEADER_TABLE_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(2), "ENABLE_PUSH");
@ -369,7 +410,8 @@ TEST(Http2SettingsTest, WireIdToNameWorks) {
"GRPC_ALLOW_TRUE_BINARY_METADATA");
EXPECT_EQ(Http2Settings::WireIdToName(65028),
"GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(65029), "UNKNOWN (65029)");
EXPECT_EQ(Http2Settings::WireIdToName(65029), "GRPC_ALLOW_SECURITY_FRAME");
EXPECT_EQ(Http2Settings::WireIdToName(65030), "UNKNOWN (65030)");
}
TEST(Http2SettingsTest, ApplyHeaderTableSizeWorks) {
@ -442,6 +484,15 @@ TEST(Http2SettingsTest, ApplyPreferredReceiveCryptoMessageSizeWorks) {
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
}
TEST(Http2SettingsTest, ApplyAllowSecurityFrameWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(65029, 0), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.allow_security_frame(), false);
EXPECT_EQ(settings.Apply(65029, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.allow_security_frame(), true);
EXPECT_EQ(settings.Apply(65029, 2), GRPC_HTTP2_PROTOCOL_ERROR);
}
namespace {
MATCHER_P(SettingsFrame, settings, "") {
if (!arg.has_value()) {

@ -1199,6 +1199,8 @@ src/core/ext/transport/chttp2/transport/frame_ping.cc \
src/core/ext/transport/chttp2/transport/frame_ping.h \
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \
src/core/ext/transport/chttp2/transport/frame_rst_stream.h \
src/core/ext/transport/chttp2/transport/frame_security.cc \
src/core/ext/transport/chttp2/transport/frame_security.h \
src/core/ext/transport/chttp2/transport/frame_settings.cc \
src/core/ext/transport/chttp2/transport/frame_settings.h \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \

@ -1006,6 +1006,8 @@ src/core/ext/transport/chttp2/transport/frame_ping.cc \
src/core/ext/transport/chttp2/transport/frame_ping.h \
src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \
src/core/ext/transport/chttp2/transport/frame_rst_stream.h \
src/core/ext/transport/chttp2/transport/frame_security.cc \
src/core/ext/transport/chttp2/transport/frame_security.h \
src/core/ext/transport/chttp2/transport/frame_settings.cc \
src/core/ext/transport/chttp2/transport/frame_settings.h \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \

Loading…
Cancel
Save