1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h"
6 
7 #include <cstddef>
8 #include <memory>
9 #include <string>
10 
11 #include "url/gurl.h"
12 #include "url/origin.h"
13 #include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
14 #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
15 #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
16 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
17 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
18 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
19 #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
20 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
21 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
22 #include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
23 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
24 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
25 
26 namespace quic {
27 namespace test {
28 namespace {
29 
30 using testing::_;
31 using testing::AnyNumber;
32 using testing::DoAll;
33 using testing::HasSubstr;
34 using testing::Return;
35 using testing::SaveArg;
36 
37 constexpr char kTestOrigin[] = "https://test-origin.test";
38 constexpr char kTestOriginClientIndication[] =
39     "\0\0"                      // key (0x0000, origin)
40     "\0\x18"                    // length
41     "https://test-origin.test"  // value
42     "\0\x01"                    // key (0x0001, path)
43     "\0\x05"                    // length
44     "/test";                    // value
GetTestOrigin()45 const url::Origin GetTestOrigin() {
46   return url::Origin::Create(GURL(kTestOrigin));
47 }
GetTestOriginClientIndication()48 const std::string GetTestOriginClientIndication() {
49   return std::string(kTestOriginClientIndication,
50                      sizeof(kTestOriginClientIndication) - 1);
51 }
52 
GetVersions()53 ParsedQuicVersionVector GetVersions() {
54   return {DefaultVersionForQuicTransport()};
55 }
56 
57 class QuicTransportServerSessionTest : public QuicTest {
58  public:
QuicTransportServerSessionTest()59   QuicTransportServerSessionTest()
60       : connection_(&helper_,
61                     &alarm_factory_,
62                     Perspective::IS_SERVER,
63                     GetVersions()),
64         crypto_config_(QuicCryptoServerConfig::TESTING,
65                        QuicRandom::GetInstance(),
66                        crypto_test_utils::ProofSourceForTesting(),
67                        KeyExchangeSource::Default()),
68         compressed_certs_cache_(
69             QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
70     QuicEnableVersion(DefaultVersionForQuicTransport());
71     connection_.AdvanceTime(QuicTime::Delta::FromSeconds(100000));
72     crypto_test_utils::SetupCryptoServerConfigForTest(
73         helper_.GetClock(), helper_.GetRandomGenerator(), &crypto_config_);
74     session_ = std::make_unique<QuicTransportServerSession>(
75         &connection_, nullptr, DefaultQuicConfig(), GetVersions(),
76         &crypto_config_, &compressed_certs_cache_, &visitor_);
77     session_->Initialize();
78     crypto_stream_ = session_->GetMutableCryptoStream();
79     ON_CALL(visitor_, ProcessPath(_))
80         .WillByDefault(DoAll(SaveArg<0>(&path_), Return(true)));
81   }
82 
Connect()83   void Connect() {
84     crypto_test_utils::FakeClientOptions options;
85     options.only_tls_versions = true;
86     crypto_test_utils::HandshakeWithFakeClient(
87         &helper_, &alarm_factory_, &connection_, crypto_stream_,
88         QuicServerId("test.example.com", 443), options, QuicTransportAlpn());
89   }
90 
ReceiveIndication(quiche::QuicheStringPiece indication)91   void ReceiveIndication(quiche::QuicheStringPiece indication) {
92     QUIC_LOG(INFO) << "Receiving indication: "
93                    << quiche::QuicheTextUtils::HexDump(indication);
94     constexpr size_t kChunkSize = 1024;
95     // Shard the indication, since some of the tests cause it to not fit into a
96     // single frame.
97     for (size_t i = 0; i < indication.size(); i += kChunkSize) {
98       QuicStreamFrame frame(ClientIndicationStream(), /*fin=*/false, i,
99                             indication.substr(i, i + kChunkSize));
100       session_->OnStreamFrame(frame);
101     }
102     session_->OnStreamFrame(QuicStreamFrame(ClientIndicationStream(),
103                                             /*fin=*/true, indication.size(),
104                                             quiche::QuicheStringPiece()));
105   }
106 
ReceiveIndicationWithPath(quiche::QuicheStringPiece path)107   void ReceiveIndicationWithPath(quiche::QuicheStringPiece path) {
108     constexpr char kTestOriginClientIndicationPrefix[] =
109         "\0\0"                      // key (0x0000, origin)
110         "\0\x18"                    // length
111         "https://test-origin.test"  // value
112         "\0\x01";                   // key (0x0001, path)
113     std::string indication{kTestOriginClientIndicationPrefix,
114                            sizeof(kTestOriginClientIndicationPrefix) - 1};
115     indication.push_back(static_cast<char>(path.size() >> 8));
116     indication.push_back(static_cast<char>(path.size() & 0xff));
117     indication += std::string{path};
118     ReceiveIndication(indication);
119   }
120 
121  protected:
122   MockAlarmFactory alarm_factory_;
123   MockQuicConnectionHelper helper_;
124 
125   PacketSavingConnection connection_;
126   QuicCryptoServerConfig crypto_config_;
127   std::unique_ptr<QuicTransportServerSession> session_;
128   QuicCompressedCertsCache compressed_certs_cache_;
129   testing::NiceMock<MockServerVisitor> visitor_;
130   QuicCryptoServerStreamBase* crypto_stream_;
131   GURL path_;
132 };
133 
TEST_F(QuicTransportServerSessionTest,SuccessfulHandshake)134 TEST_F(QuicTransportServerSessionTest, SuccessfulHandshake) {
135   Connect();
136 
137   url::Origin origin;
138   EXPECT_CALL(visitor_, CheckOrigin(_))
139       .WillOnce(DoAll(SaveArg<0>(&origin), Return(true)));
140   ReceiveIndication(GetTestOriginClientIndication());
141   EXPECT_TRUE(session_->IsSessionReady());
142   EXPECT_EQ(origin, GetTestOrigin());
143   EXPECT_EQ(path_.path(), "/test");
144 }
145 
TEST_F(QuicTransportServerSessionTest,PiecewiseClientIndication)146 TEST_F(QuicTransportServerSessionTest, PiecewiseClientIndication) {
147   Connect();
148   size_t i = 0;
149   for (; i < sizeof(kTestOriginClientIndication) - 2; i++) {
150     QuicStreamFrame frame(
151         ClientIndicationStream(), false, i,
152         quiche::QuicheStringPiece(&kTestOriginClientIndication[i], 1));
153     session_->OnStreamFrame(frame);
154   }
155 
156   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
157   QuicStreamFrame last_frame(
158       ClientIndicationStream(), true, i,
159       quiche::QuicheStringPiece(&kTestOriginClientIndication[i], 1));
160   session_->OnStreamFrame(last_frame);
161   EXPECT_TRUE(session_->IsSessionReady());
162 }
163 
TEST_F(QuicTransportServerSessionTest,OriginRejected)164 TEST_F(QuicTransportServerSessionTest, OriginRejected) {
165   Connect();
166   EXPECT_CALL(connection_,
167               CloseConnection(_, HasSubstr("Origin check failed"), _));
168   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(false));
169   ReceiveIndication(GetTestOriginClientIndication());
170   EXPECT_FALSE(session_->IsSessionReady());
171 }
172 
MakeUnknownField(quiche::QuicheStringPiece payload)173 std::string MakeUnknownField(quiche::QuicheStringPiece payload) {
174   std::string buffer;
175   buffer.resize(payload.size() + 4);
176   QuicDataWriter writer(buffer.size(), &buffer[0]);
177   EXPECT_TRUE(writer.WriteUInt16(0xffff));
178   EXPECT_TRUE(writer.WriteUInt16(payload.size()));
179   EXPECT_TRUE(writer.WriteStringPiece(payload));
180   EXPECT_EQ(writer.remaining(), 0u);
181   return buffer;
182 }
183 
TEST_F(QuicTransportServerSessionTest,SkipUnusedFields)184 TEST_F(QuicTransportServerSessionTest, SkipUnusedFields) {
185   Connect();
186   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
187   ReceiveIndication(GetTestOriginClientIndication() +
188                     MakeUnknownField("foobar"));
189   EXPECT_TRUE(session_->IsSessionReady());
190 }
191 
TEST_F(QuicTransportServerSessionTest,SkipLongUnusedFields)192 TEST_F(QuicTransportServerSessionTest, SkipLongUnusedFields) {
193   const size_t bytes =
194       ClientIndicationMaxSize() - GetTestOriginClientIndication().size() - 4;
195   Connect();
196   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
197   ReceiveIndication(GetTestOriginClientIndication() +
198                     MakeUnknownField(std::string(bytes, 'a')));
199   EXPECT_TRUE(session_->IsSessionReady());
200 }
201 
TEST_F(QuicTransportServerSessionTest,ClientIndicationTooLong)202 TEST_F(QuicTransportServerSessionTest, ClientIndicationTooLong) {
203   Connect();
204   EXPECT_CALL(
205       connection_,
206       CloseConnection(_, HasSubstr("Client indication size exceeds"), _))
207       .Times(AnyNumber());
208   ReceiveIndication(GetTestOriginClientIndication() +
209                     MakeUnknownField(std::string(65534, 'a')));
210   EXPECT_FALSE(session_->IsSessionReady());
211 }
212 
TEST_F(QuicTransportServerSessionTest,NoOrigin)213 TEST_F(QuicTransportServerSessionTest, NoOrigin) {
214   Connect();
215   EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
216   ReceiveIndication(MakeUnknownField("foobar"));
217   EXPECT_FALSE(session_->IsSessionReady());
218 }
219 
TEST_F(QuicTransportServerSessionTest,EmptyClientIndication)220 TEST_F(QuicTransportServerSessionTest, EmptyClientIndication) {
221   Connect();
222   EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
223   ReceiveIndication("");
224   EXPECT_FALSE(session_->IsSessionReady());
225 }
226 
TEST_F(QuicTransportServerSessionTest,MalformedIndicationHeader)227 TEST_F(QuicTransportServerSessionTest, MalformedIndicationHeader) {
228   Connect();
229   EXPECT_CALL(connection_,
230               CloseConnection(_, HasSubstr("Expected 16-bit key"), _));
231   ReceiveIndication("\xff");
232   EXPECT_FALSE(session_->IsSessionReady());
233 }
234 
TEST_F(QuicTransportServerSessionTest,FieldTooShort)235 TEST_F(QuicTransportServerSessionTest, FieldTooShort) {
236   Connect();
237   EXPECT_CALL(
238       connection_,
239       CloseConnection(_, HasSubstr("Failed to read value for key 257"), _));
240   ReceiveIndication("\x01\x01\x01\x01");
241   EXPECT_FALSE(session_->IsSessionReady());
242 }
243 
TEST_F(QuicTransportServerSessionTest,InvalidOrigin)244 TEST_F(QuicTransportServerSessionTest, InvalidOrigin) {
245   const std::string kEmptyOriginIndication(4, '\0');
246   Connect();
247   EXPECT_CALL(
248       connection_,
249       CloseConnection(_, HasSubstr("Unable to parse the specified origin"), _));
250   ReceiveIndication(kEmptyOriginIndication);
251   EXPECT_FALSE(session_->IsSessionReady());
252 }
253 
TEST_F(QuicTransportServerSessionTest,PathWithQuery)254 TEST_F(QuicTransportServerSessionTest, PathWithQuery) {
255   Connect();
256   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
257   ReceiveIndicationWithPath("/test?foo=bar");
258   EXPECT_TRUE(session_->IsSessionReady());
259   EXPECT_EQ(path_.path(), "/test");
260   EXPECT_EQ(path_.query(), "foo=bar");
261 }
262 
TEST_F(QuicTransportServerSessionTest,PathNormalization)263 TEST_F(QuicTransportServerSessionTest, PathNormalization) {
264   Connect();
265   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
266   ReceiveIndicationWithPath("/foo/../bar");
267   EXPECT_TRUE(session_->IsSessionReady());
268   EXPECT_EQ(path_.path(), "/bar");
269 }
270 
TEST_F(QuicTransportServerSessionTest,EmptyPath)271 TEST_F(QuicTransportServerSessionTest, EmptyPath) {
272   Connect();
273   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
274   EXPECT_CALL(connection_,
275               CloseConnection(_, HasSubstr("Path must begin with a '/'"), _));
276   ReceiveIndicationWithPath("");
277   EXPECT_FALSE(session_->IsSessionReady());
278 }
279 
TEST_F(QuicTransportServerSessionTest,UnprefixedPath)280 TEST_F(QuicTransportServerSessionTest, UnprefixedPath) {
281   Connect();
282   EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
283   EXPECT_CALL(connection_,
284               CloseConnection(_, HasSubstr("Path must begin with a '/'"), _));
285   ReceiveIndicationWithPath("test");
286   EXPECT_FALSE(session_->IsSessionReady());
287 }
288 
289 }  // namespace
290 }  // namespace test
291 }  // namespace quic
292