• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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/http/http_auth_gssapi_posix.h"
6 
7 #include <memory>
8 #include <string_view>
9 
10 #include "base/base_paths.h"
11 #include "base/check.h"
12 #include "base/functional/bind.h"
13 #include "base/json/json_reader.h"
14 #include "base/native_library.h"
15 #include "base/path_service.h"
16 #include "net/base/net_errors.h"
17 #include "net/http/http_auth_challenge_tokenizer.h"
18 #include "net/http/mock_gssapi_library_posix.h"
19 #include "net/log/net_log_with_source.h"
20 #include "net/log/test_net_log.h"
21 #include "net/log/test_net_log_util.h"
22 #include "net/net_buildflags.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 
25 namespace net {
26 
27 namespace {
28 
29 // gss_buffer_t helpers.
ClearBuffer(gss_buffer_t dest)30 void ClearBuffer(gss_buffer_t dest) {
31   if (!dest)
32     return;
33   dest->length = 0;
34   delete [] reinterpret_cast<char*>(dest->value);
35   dest->value = nullptr;
36 }
37 
SetBuffer(gss_buffer_t dest,const void * src,size_t length)38 void SetBuffer(gss_buffer_t dest, const void* src, size_t length) {
39   if (!dest)
40     return;
41   ClearBuffer(dest);
42   if (!src)
43     return;
44   dest->length = length;
45   if (length) {
46     dest->value = new char[length];
47     memcpy(dest->value, src, length);
48   }
49 }
50 
CopyBuffer(gss_buffer_t dest,const gss_buffer_t src)51 void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) {
52   if (!dest)
53     return;
54   ClearBuffer(dest);
55   if (!src)
56     return;
57   SetBuffer(dest, src->value, src->length);
58 }
59 
60 const char kInitialAuthResponse[] = "Mary had a little lamb";
61 
EstablishInitialContext(test::MockGSSAPILibrary * library)62 void EstablishInitialContext(test::MockGSSAPILibrary* library) {
63   test::GssContextMockImpl context_info(
64       "localhost",                         // Source name
65       "example.com",                       // Target name
66       23,                                  // Lifetime
67       *CHROME_GSS_SPNEGO_MECH_OID_DESC,    // Mechanism
68       0,                                   // Context flags
69       1,                                   // Locally initiated
70       0);                                  // Open
71   gss_buffer_desc in_buffer = {0, nullptr};
72   gss_buffer_desc out_buffer = {std::size(kInitialAuthResponse),
73                                 const_cast<char*>(kInitialAuthResponse)};
74   library->ExpectSecurityContext(
75       "Negotiate",
76       GSS_S_CONTINUE_NEEDED,
77       0,
78       context_info,
79       in_buffer,
80       out_buffer);
81 }
82 
UnexpectedCallback(int result)83 void UnexpectedCallback(int result) {
84   // At present getting tokens from gssapi is fully synchronous, so the callback
85   // should never be called.
86   ADD_FAILURE();
87 }
88 
89 }  // namespace
90 
TEST(HttpAuthGSSAPIPOSIXTest,GSSAPIStartup)91 TEST(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup) {
92   RecordingNetLogObserver net_log_observer;
93   // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
94   // libraries we expect, and also whether or not they have the interface
95   // functions we want.
96   auto gssapi = std::make_unique<GSSAPISharedLibrary>(std::string());
97   DCHECK(gssapi.get());
98   EXPECT_TRUE(
99       gssapi.get()->Init(NetLogWithSource::Make(NetLogSourceType::NONE)));
100 
101   // Should've logged a AUTH_LIBRARY_LOAD event, but not
102   // AUTH_LIBRARY_BIND_FAILED.
103   auto entries = net_log_observer.GetEntries();
104   auto offset = ExpectLogContainsSomewhere(
105       entries, 0u, NetLogEventType::AUTH_LIBRARY_LOAD, NetLogEventPhase::BEGIN);
106   offset = ExpectLogContainsSomewhereAfter(entries, offset,
107                                            NetLogEventType::AUTH_LIBRARY_LOAD,
108                                            NetLogEventPhase::END);
109   ASSERT_LT(offset, entries.size());
110 
111   const auto& entry = entries[offset];
112   EXPECT_NE("", GetStringValueFromParams(entry, "library_name"));
113 
114   // No load_result since it succeeded.
115   EXPECT_FALSE(GetOptionalStringValueFromParams(entry, "load_result"));
116 }
117 
TEST(HttpAuthGSSAPIPOSIXTest,CustomLibraryMissing)118 TEST(HttpAuthGSSAPIPOSIXTest, CustomLibraryMissing) {
119   RecordingNetLogObserver net_log_observer;
120 
121   auto gssapi =
122       std::make_unique<GSSAPISharedLibrary>("/this/library/does/not/exist");
123   EXPECT_FALSE(
124       gssapi.get()->Init(NetLogWithSource::Make(NetLogSourceType::NONE)));
125 
126   auto entries = net_log_observer.GetEntries();
127   auto offset = ExpectLogContainsSomewhere(
128       entries, 0, NetLogEventType::AUTH_LIBRARY_LOAD, NetLogEventPhase::END);
129   ASSERT_LT(offset, entries.size());
130 
131   const auto& entry = entries[offset];
132   EXPECT_NE("", GetStringValueFromParams(entry, "load_result"));
133 }
134 
TEST(HttpAuthGSSAPIPOSIXTest,CustomLibraryExists)135 TEST(HttpAuthGSSAPIPOSIXTest, CustomLibraryExists) {
136   RecordingNetLogObserver net_log_observer;
137   base::FilePath module;
138   ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &module));
139   auto basename = base::GetNativeLibraryName("test_gssapi");
140   module = module.AppendASCII(basename);
141   auto gssapi = std::make_unique<GSSAPISharedLibrary>(module.value());
142   EXPECT_TRUE(
143       gssapi.get()->Init(NetLogWithSource::Make(NetLogSourceType::NONE)));
144 
145   auto entries = net_log_observer.GetEntries();
146   auto offset = ExpectLogContainsSomewhere(
147       entries, 0, NetLogEventType::AUTH_LIBRARY_LOAD, NetLogEventPhase::END);
148   ASSERT_LT(offset, entries.size());
149 
150   const auto& entry = entries[offset];
151   EXPECT_FALSE(GetOptionalStringValueFromParams(entry, "load_result"));
152   EXPECT_EQ(module.AsUTF8Unsafe(),
153             GetStringValueFromParams(entry, "library_name"));
154 }
155 
TEST(HttpAuthGSSAPIPOSIXTest,CustomLibraryMethodsMissing)156 TEST(HttpAuthGSSAPIPOSIXTest, CustomLibraryMethodsMissing) {
157   RecordingNetLogObserver net_log_observer;
158   base::FilePath module;
159   ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &module));
160   auto basename = base::GetNativeLibraryName("test_badgssapi");
161   module = module.AppendASCII(basename);
162   auto gssapi = std::make_unique<GSSAPISharedLibrary>(module.value());
163 
164   // Are you here because this test mysteriously passed even though the library
165   // doesn't actually have all the methods we need? This could be because the
166   // test library (//net:test_badgssapi) inadvertently depends on a valid GSSAPI
167   // library. On macOS this can happen because it's pretty easy to end up
168   // depending on GSS.framework.
169   //
170   // To resolve this issue, make sure that //net:test_badgssapi target in
171   // //net/BUILD.gn should have an empty `deps` and an empty `libs`.
172   EXPECT_FALSE(
173       gssapi.get()->Init(NetLogWithSource::Make(NetLogSourceType::NONE)));
174 
175   auto entries = net_log_observer.GetEntries();
176   auto offset = ExpectLogContainsSomewhere(
177       entries, 0, NetLogEventType::AUTH_LIBRARY_BIND_FAILED,
178       NetLogEventPhase::NONE);
179   ASSERT_LT(offset, entries.size());
180 
181   const auto& entry = entries[offset];
182   EXPECT_EQ("gss_import_name", GetStringValueFromParams(entry, "method"));
183 }
184 
TEST(HttpAuthGSSAPIPOSIXTest,GSSAPICycle)185 TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) {
186   auto mock_library = std::make_unique<test::MockGSSAPILibrary>();
187   DCHECK(mock_library.get());
188   mock_library->Init(NetLogWithSource());
189   const char kAuthResponse[] = "Mary had a little lamb";
190   test::GssContextMockImpl context1(
191       "localhost",                         // Source name
192       "example.com",                       // Target name
193       23,                                  // Lifetime
194       *CHROME_GSS_SPNEGO_MECH_OID_DESC,    // Mechanism
195       0,                                   // Context flags
196       1,                                   // Locally initiated
197       0);                                  // Open
198   test::GssContextMockImpl context2(
199       "localhost",                         // Source name
200       "example.com",                       // Target name
201       23,                                  // Lifetime
202       *CHROME_GSS_SPNEGO_MECH_OID_DESC,    // Mechanism
203       0,                                   // Context flags
204       1,                                   // Locally initiated
205       1);                                  // Open
206   test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
207       test::MockGSSAPILibrary::SecurityContextQuery(
208           "Negotiate",            // Package name
209           GSS_S_CONTINUE_NEEDED,  // Major response code
210           0,                      // Minor response code
211           context1,               // Context
212           nullptr,                // Expected input token
213           kAuthResponse),         // Output token
214       test::MockGSSAPILibrary::SecurityContextQuery(
215           "Negotiate",     // Package name
216           GSS_S_COMPLETE,  // Major response code
217           0,               // Minor response code
218           context2,        // Context
219           kAuthResponse,   // Expected input token
220           kAuthResponse)   // Output token
221   };
222 
223   for (const auto& query : queries) {
224     mock_library->ExpectSecurityContext(
225         query.expected_package, query.response_code, query.minor_response_code,
226         query.context_info, query.expected_input_token, query.output_token);
227   }
228 
229   OM_uint32 major_status = 0;
230   OM_uint32 minor_status = 0;
231   gss_cred_id_t initiator_cred_handle = nullptr;
232   gss_ctx_id_t context_handle = nullptr;
233   gss_name_t target_name = nullptr;
234   gss_OID mech_type = nullptr;
235   OM_uint32 req_flags = 0;
236   OM_uint32 time_req = 25;
237   gss_channel_bindings_t input_chan_bindings = nullptr;
238   gss_buffer_desc input_token = {0, nullptr};
239   gss_OID actual_mech_type = nullptr;
240   gss_buffer_desc output_token = {0, nullptr};
241   OM_uint32 ret_flags = 0;
242   OM_uint32 time_rec = 0;
243   for (const auto& query : queries) {
244     major_status = mock_library->init_sec_context(&minor_status,
245                                                   initiator_cred_handle,
246                                                   &context_handle,
247                                                   target_name,
248                                                   mech_type,
249                                                   req_flags,
250                                                   time_req,
251                                                   input_chan_bindings,
252                                                   &input_token,
253                                                   &actual_mech_type,
254                                                   &output_token,
255                                                   &ret_flags,
256                                                   &time_rec);
257     EXPECT_EQ(query.response_code, major_status);
258     CopyBuffer(&input_token, &output_token);
259     ClearBuffer(&output_token);
260   }
261   ClearBuffer(&input_token);
262   major_status = mock_library->delete_sec_context(&minor_status,
263                                                   &context_handle,
264                                                   GSS_C_NO_BUFFER);
265   EXPECT_EQ(static_cast<OM_uint32>(GSS_S_COMPLETE), major_status);
266 }
267 
TEST(HttpAuthGSSAPITest,ParseChallenge_FirstRound)268 TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) {
269   // The first round should just consist of an unadorned "Negotiate" header.
270   test::MockGSSAPILibrary mock_library;
271   HttpAuthGSSAPI auth_gssapi(&mock_library, CHROME_GSS_SPNEGO_MECH_OID_DESC);
272   HttpAuthChallengeTokenizer challenge("Negotiate");
273   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
274             auth_gssapi.ParseChallenge(&challenge));
275 }
276 
TEST(HttpAuthGSSAPITest,ParseChallenge_TwoRounds)277 TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
278   RecordingNetLogObserver net_log_observer;
279   // The first round should just have "Negotiate", and the second round should
280   // have a valid base64 token associated with it.
281   test::MockGSSAPILibrary mock_library;
282   HttpAuthGSSAPI auth_gssapi(&mock_library, CHROME_GSS_SPNEGO_MECH_OID_DESC);
283   HttpAuthChallengeTokenizer first_challenge("Negotiate");
284   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
285             auth_gssapi.ParseChallenge(&first_challenge));
286 
287   // Generate an auth token and create another thing.
288   EstablishInitialContext(&mock_library);
289   std::string auth_token;
290   EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(
291                     nullptr, "HTTP/intranet.google.com", std::string(),
292                     &auth_token, NetLogWithSource::Make(NetLogSourceType::NONE),
293                     base::BindOnce(&UnexpectedCallback)));
294 
295   HttpAuthChallengeTokenizer second_challenge("Negotiate Zm9vYmFy");
296   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
297             auth_gssapi.ParseChallenge(&second_challenge));
298 
299   auto entries = net_log_observer.GetEntries();
300   auto offset = ExpectLogContainsSomewhere(
301       entries, 0, NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX,
302       NetLogEventPhase::END);
303   // There should be two of these.
304   offset = ExpectLogContainsSomewhere(
305       entries, offset, NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX,
306       NetLogEventPhase::END);
307   ASSERT_LT(offset, entries.size());
308   const std::string* source =
309       entries[offset].params.FindStringByDottedPath("context.source.name");
310   ASSERT_TRUE(source);
311   EXPECT_EQ("localhost", *source);
312 }
313 
TEST(HttpAuthGSSAPITest,ParseChallenge_UnexpectedTokenFirstRound)314 TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) {
315   // If the first round challenge has an additional authentication token, it
316   // should be treated as an invalid challenge from the server.
317   test::MockGSSAPILibrary mock_library;
318   HttpAuthGSSAPI auth_gssapi(&mock_library, CHROME_GSS_SPNEGO_MECH_OID_DESC);
319   HttpAuthChallengeTokenizer challenge("Negotiate Zm9vYmFy");
320   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
321             auth_gssapi.ParseChallenge(&challenge));
322 }
323 
TEST(HttpAuthGSSAPITest,ParseChallenge_MissingTokenSecondRound)324 TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
325   // If a later-round challenge is simply "Negotiate", it should be treated as
326   // an authentication challenge rejection from the server or proxy.
327   test::MockGSSAPILibrary mock_library;
328   HttpAuthGSSAPI auth_gssapi(&mock_library, CHROME_GSS_SPNEGO_MECH_OID_DESC);
329   HttpAuthChallengeTokenizer first_challenge("Negotiate");
330   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
331             auth_gssapi.ParseChallenge(&first_challenge));
332 
333   EstablishInitialContext(&mock_library);
334   std::string auth_token;
335   EXPECT_EQ(OK,
336             auth_gssapi.GenerateAuthToken(
337                 nullptr, "HTTP/intranet.google.com", std::string(), &auth_token,
338                 NetLogWithSource(), base::BindOnce(&UnexpectedCallback)));
339   HttpAuthChallengeTokenizer second_challenge("Negotiate");
340   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
341             auth_gssapi.ParseChallenge(&second_challenge));
342 }
343 
TEST(HttpAuthGSSAPITest,ParseChallenge_NonBase64EncodedToken)344 TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
345   // If a later-round challenge has an invalid base64 encoded token, it should
346   // be treated as an invalid challenge.
347   test::MockGSSAPILibrary mock_library;
348   HttpAuthGSSAPI auth_gssapi(&mock_library, CHROME_GSS_SPNEGO_MECH_OID_DESC);
349   HttpAuthChallengeTokenizer first_challenge("Negotiate");
350   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
351             auth_gssapi.ParseChallenge(&first_challenge));
352 
353   EstablishInitialContext(&mock_library);
354   std::string auth_token;
355   EXPECT_EQ(OK,
356             auth_gssapi.GenerateAuthToken(
357                 nullptr, "HTTP/intranet.google.com", std::string(), &auth_token,
358                 NetLogWithSource(), base::BindOnce(&UnexpectedCallback)));
359   HttpAuthChallengeTokenizer second_challenge("Negotiate =happyjoy=");
360   EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
361             auth_gssapi.ParseChallenge(&second_challenge));
362 }
363 
TEST(HttpAuthGSSAPITest,OidToValue_NIL)364 TEST(HttpAuthGSSAPITest, OidToValue_NIL) {
365   auto actual = OidToValue(GSS_C_NO_OID);
366   auto expected = base::JSONReader::Read(R"({ "oid": "<Empty OID>" })");
367   ASSERT_TRUE(expected.has_value());
368   EXPECT_EQ(actual, expected);
369 }
370 
TEST(HttpAuthGSSAPITest,OidToValue_Known)371 TEST(HttpAuthGSSAPITest, OidToValue_Known) {
372   gss_OID_desc known = {6, const_cast<char*>("\x2b\x06\01\x05\x06\x03")};
373 
374   auto actual = OidToValue(const_cast<const gss_OID>(&known));
375   auto expected = base::JSONReader::Read(R"(
376       {
377         "oid"   : "GSS_C_NT_ANONYMOUS",
378         "length": 6,
379         "bytes" : "KwYBBQYD"
380       }
381   )");
382   ASSERT_TRUE(expected.has_value());
383   EXPECT_EQ(actual, expected);
384 }
385 
TEST(HttpAuthGSSAPITest,OidToValue_Unknown)386 TEST(HttpAuthGSSAPITest, OidToValue_Unknown) {
387   gss_OID_desc unknown = {6, const_cast<char*>("\x2b\x06\01\x05\x06\x05")};
388   auto actual = OidToValue(const_cast<const gss_OID>(&unknown));
389   auto expected = base::JSONReader::Read(R"(
390       {
391         "length": 6,
392         "bytes" : "KwYBBQYF"
393       }
394   )");
395   ASSERT_TRUE(expected.has_value());
396   EXPECT_EQ(actual, expected);
397 }
398 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_NoLibrary)399 TEST(HttpAuthGSSAPITest, GetGssStatusValue_NoLibrary) {
400   auto actual = GetGssStatusValue(nullptr, "my_method", GSS_S_BAD_NAME, 1);
401   auto expected = base::JSONReader::Read(R"(
402       {
403         "function": "my_method",
404         "major_status": {
405           "status": 131072
406         },
407         "minor_status": {
408           "status": 1
409         }
410       }
411   )");
412   ASSERT_TRUE(expected.has_value());
413   EXPECT_EQ(actual, expected);
414 }
415 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_WithLibrary)416 TEST(HttpAuthGSSAPITest, GetGssStatusValue_WithLibrary) {
417   test::MockGSSAPILibrary library;
418   auto actual = GetGssStatusValue(&library, "my_method", GSS_S_BAD_NAME, 1);
419   auto expected = base::JSONReader::Read(R"(
420       {
421         "function": "my_method",
422         "major_status": {
423           "status": 131072,
424           "message": [ "Value: 131072, Type 1" ]
425         },
426         "minor_status": {
427           "status": 1,
428           "message": [ "Value: 1, Type 2" ]
429         }
430       }
431   )");
432   ASSERT_TRUE(expected.has_value());
433   EXPECT_EQ(actual, expected);
434 }
435 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_Multiline)436 TEST(HttpAuthGSSAPITest, GetGssStatusValue_Multiline) {
437   test::MockGSSAPILibrary library;
438   auto actual = GetGssStatusValue(
439       &library, "my_method",
440       static_cast<OM_uint32>(
441           test::MockGSSAPILibrary::DisplayStatusSpecials::MultiLine),
442       0);
443   auto expected = base::JSONReader::Read(R"(
444       {
445         "function": "my_method",
446         "major_status": {
447           "status": 128,
448           "message": [
449             "Line 1 for status 128",
450             "Line 2 for status 128",
451             "Line 3 for status 128",
452             "Line 4 for status 128",
453             "Line 5 for status 128"
454           ]
455         },
456         "minor_status": {
457           "status": 0
458         }
459       }
460   )");
461   ASSERT_TRUE(expected.has_value());
462   EXPECT_EQ(actual, expected);
463 }
464 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_InfiniteLines)465 TEST(HttpAuthGSSAPITest, GetGssStatusValue_InfiniteLines) {
466   test::MockGSSAPILibrary library;
467   auto actual = GetGssStatusValue(
468       &library, "my_method",
469       static_cast<OM_uint32>(
470           test::MockGSSAPILibrary::DisplayStatusSpecials::InfiniteLines),
471       0);
472   auto expected = base::JSONReader::Read(R"(
473       {
474         "function": "my_method",
475         "major_status": {
476           "status": 129,
477           "message": [
478             "Line 1 for status 129",
479             "Line 2 for status 129",
480             "Line 3 for status 129",
481             "Line 4 for status 129",
482             "Line 5 for status 129",
483             "Line 6 for status 129",
484             "Line 7 for status 129",
485             "Line 8 for status 129"
486           ]
487         },
488         "minor_status": {
489           "status": 0
490         }
491       }
492   )");
493   ASSERT_TRUE(expected.has_value());
494   EXPECT_EQ(actual, expected);
495 }
496 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_Failure)497 TEST(HttpAuthGSSAPITest, GetGssStatusValue_Failure) {
498   test::MockGSSAPILibrary library;
499   auto actual = GetGssStatusValue(
500       &library, "my_method",
501       static_cast<OM_uint32>(
502           test::MockGSSAPILibrary::DisplayStatusSpecials::Fail),
503       0);
504   auto expected = base::JSONReader::Read(R"(
505       {
506         "function": "my_method",
507         "major_status": {
508           "status": 130
509         },
510         "minor_status": {
511           "status": 0
512         }
513       }
514   )");
515   ASSERT_TRUE(expected.has_value());
516   EXPECT_EQ(actual, expected);
517 }
518 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_EmptyMessage)519 TEST(HttpAuthGSSAPITest, GetGssStatusValue_EmptyMessage) {
520   test::MockGSSAPILibrary library;
521   auto actual = GetGssStatusValue(
522       &library, "my_method",
523       static_cast<OM_uint32>(
524           test::MockGSSAPILibrary::DisplayStatusSpecials::EmptyMessage),
525       0);
526   auto expected = base::JSONReader::Read(R"(
527       {
528         "function": "my_method",
529         "major_status": {
530           "status": 131
531         },
532         "minor_status": {
533           "status": 0
534         }
535       }
536   )");
537   ASSERT_TRUE(expected.has_value());
538   EXPECT_EQ(actual, expected);
539 }
540 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_Misbehave)541 TEST(HttpAuthGSSAPITest, GetGssStatusValue_Misbehave) {
542   test::MockGSSAPILibrary library;
543   auto actual = GetGssStatusValue(
544       &library, "my_method",
545       static_cast<OM_uint32>(
546           test::MockGSSAPILibrary::DisplayStatusSpecials::UninitalizedBuffer),
547       0);
548   auto expected = base::JSONReader::Read(R"(
549       {
550         "function": "my_method",
551         "major_status": {
552           "status": 132
553         },
554         "minor_status": {
555           "status": 0
556         }
557       }
558   )");
559   ASSERT_TRUE(expected.has_value());
560   EXPECT_EQ(actual, expected);
561 }
562 
TEST(HttpAuthGSSAPITest,GetGssStatusValue_NotUtf8)563 TEST(HttpAuthGSSAPITest, GetGssStatusValue_NotUtf8) {
564   test::MockGSSAPILibrary library;
565   auto actual = GetGssStatusValue(
566       &library, "my_method",
567       static_cast<OM_uint32>(
568           test::MockGSSAPILibrary::DisplayStatusSpecials::InvalidUtf8),
569       0);
570   auto expected = base::JSONReader::Read(R"(
571       {
572         "function": "my_method",
573         "major_status": {
574           "status": 133
575         },
576         "minor_status": {
577           "status": 0
578         }
579       }
580   )");
581   ASSERT_TRUE(expected.has_value());
582   EXPECT_EQ(actual, expected);
583 }
584 
TEST(HttpAuthGSSAPITest,GetContextStateAsValue_ValidContext)585 TEST(HttpAuthGSSAPITest, GetContextStateAsValue_ValidContext) {
586   test::GssContextMockImpl context{"source_spn@somewhere",
587                                    "target_spn@somewhere.else",
588                                    /* lifetime_rec= */ 100,
589                                    *CHROME_GSS_SPNEGO_MECH_OID_DESC,
590                                    /* ctx_flags= */ 0,
591                                    /* locally_initiated= */ 1,
592                                    /* open= */ 0};
593   test::MockGSSAPILibrary library;
594   auto actual = GetContextStateAsValue(
595       &library, reinterpret_cast<const gss_ctx_id_t>(&context));
596   auto expected = base::JSONReader::Read(R"(
597       {
598         "source": {
599           "name": "source_spn@somewhere",
600           "type": {
601             "oid" : "<Empty OID>"
602           }
603         },
604         "target": {
605           "name": "target_spn@somewhere.else",
606           "type": {
607             "oid" : "<Empty OID>"
608           }
609         },
610         "lifetime": "100",
611         "mechanism": {
612           "oid": "<Empty OID>"
613         },
614         "flags": {
615           "value": "0x00000000",
616           "delegated": false,
617           "mutual": false
618         },
619         "open": false
620       }
621   )");
622   ASSERT_TRUE(expected.has_value());
623   EXPECT_EQ(actual, expected);
624 }
625 
TEST(HttpAuthGSSAPITest,GetContextStateAsValue_NoContext)626 TEST(HttpAuthGSSAPITest, GetContextStateAsValue_NoContext) {
627   test::MockGSSAPILibrary library;
628   auto actual = GetContextStateAsValue(&library, GSS_C_NO_CONTEXT);
629   auto expected = base::JSONReader::Read(R"(
630       {
631          "error": {
632             "function": "<none>",
633             "major_status": {
634                "status": 524288
635             },
636             "minor_status": {
637                "status": 0
638             }
639          }
640       }
641   )");
642   ASSERT_TRUE(expected.has_value());
643   EXPECT_EQ(actual, expected);
644 }
645 
646 }  // namespace net
647