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