• 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/http/http_auth_gssapi_posix.h"
11 
12 #include <limits>
13 #include <string>
14 #include <string_view>
15 
16 #include "base/base64.h"
17 #include "base/compiler_specific.h"
18 #include "base/feature_list.h"
19 #include "base/files/file_path.h"
20 #include "base/format_macros.h"
21 #include "base/logging.h"
22 #include "base/memory/raw_ptr.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/threading/thread_restrictions.h"
27 #include "base/values.h"
28 #include "build/build_config.h"
29 #include "net/base/features.h"
30 #include "net/base/net_errors.h"
31 #include "net/http/http_auth.h"
32 #include "net/http/http_auth_gssapi_posix.h"
33 #include "net/http/http_auth_multi_round_parse.h"
34 #include "net/log/net_log_event_type.h"
35 #include "net/log/net_log_values.h"
36 #include "net/log/net_log_with_source.h"
37 #include "net/net_buildflags.h"
38 
39 namespace net {
40 
41 using DelegationType = HttpAuth::DelegationType;
42 
43 // Exported mechanism for GSSAPI. We always use SPNEGO:
44 
45 // iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
46 gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = {
47   6,
48   const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
49 };
50 
51 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
52     &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
53 
DelegationTypeToFlag(DelegationType delegation_type)54 OM_uint32 DelegationTypeToFlag(DelegationType delegation_type) {
55   switch (delegation_type) {
56     case DelegationType::kNone:
57       return 0;
58     case DelegationType::kByKdcPolicy:
59       return GSS_C_DELEG_POLICY_FLAG;
60     case DelegationType::kUnconstrained:
61       return GSS_C_DELEG_FLAG;
62   }
63 }
64 
65 // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
66 class ScopedBuffer {
67  public:
ScopedBuffer(gss_buffer_t buffer,GSSAPILibrary * gssapi_lib)68   ScopedBuffer(gss_buffer_t buffer, GSSAPILibrary* gssapi_lib)
69       : buffer_(buffer), gssapi_lib_(gssapi_lib) {
70     DCHECK(gssapi_lib_);
71   }
72 
73   ScopedBuffer(const ScopedBuffer&) = delete;
74   ScopedBuffer& operator=(const ScopedBuffer&) = delete;
75 
~ScopedBuffer()76   ~ScopedBuffer() {
77     if (buffer_ != GSS_C_NO_BUFFER) {
78       OM_uint32 minor_status = 0;
79       OM_uint32 major_status =
80           gssapi_lib_->release_buffer(&minor_status, buffer_);
81       DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
82           << "Problem releasing buffer. major=" << major_status
83           << ", minor=" << minor_status;
84       buffer_ = GSS_C_NO_BUFFER;
85     }
86   }
87 
88  private:
89   gss_buffer_t buffer_;
90   raw_ptr<GSSAPILibrary> gssapi_lib_;
91 };
92 
93 // ScopedName releases a gss_name_t when it goes out of scope.
94 class ScopedName {
95  public:
ScopedName(gss_name_t name,GSSAPILibrary * gssapi_lib)96   ScopedName(gss_name_t name, GSSAPILibrary* gssapi_lib)
97       : name_(name), gssapi_lib_(gssapi_lib) {
98     DCHECK(gssapi_lib_);
99   }
100 
101   ScopedName(const ScopedName&) = delete;
102   ScopedName& operator=(const ScopedName&) = delete;
103 
~ScopedName()104   ~ScopedName() {
105     if (name_ != GSS_C_NO_NAME) {
106       OM_uint32 minor_status = 0;
107       OM_uint32 major_status = gssapi_lib_->release_name(&minor_status, &name_);
108       if (major_status != GSS_S_COMPLETE) {
109         DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
110             << "Problem releasing name. "
111             << GetGssStatusValue(nullptr, "gss_release_name", major_status,
112                                  minor_status);
113       }
114       name_ = GSS_C_NO_NAME;
115     }
116   }
117 
118  private:
119   gss_name_t name_;
120   raw_ptr<GSSAPILibrary> gssapi_lib_;
121 };
122 
OidEquals(const gss_OID left,const gss_OID right)123 bool OidEquals(const gss_OID left, const gss_OID right) {
124   if (left->length != right->length)
125     return false;
126   return 0 == memcmp(left->elements, right->elements, right->length);
127 }
128 
GetGssStatusCodeValue(GSSAPILibrary * gssapi_lib,OM_uint32 status,OM_uint32 status_code_type)129 base::Value::Dict GetGssStatusCodeValue(GSSAPILibrary* gssapi_lib,
130                                         OM_uint32 status,
131                                         OM_uint32 status_code_type) {
132   base::Value::Dict rv;
133 
134   rv.Set("status", static_cast<int>(status));
135 
136   // Message lookups aren't performed if there's no library or if the status
137   // indicates success.
138   if (!gssapi_lib || status == GSS_S_COMPLETE)
139     return rv;
140 
141   // gss_display_status() can potentially return multiple strings by sending
142   // each string on successive invocations. State is maintained across these
143   // invocations in a caller supplied OM_uint32.  After each successful call,
144   // the context is set to a non-zero value that should be passed as a message
145   // context to each successive gss_display_status() call.  The initial and
146   // terminal values of this context storage is 0.
147   OM_uint32 message_context = 0;
148 
149   // To account for the off chance that gss_display_status() misbehaves and gets
150   // into an infinite loop, we'll artificially limit the number of iterations to
151   // |kMaxDisplayIterations|. This limit is arbitrary.
152   constexpr size_t kMaxDisplayIterations = 8;
153   size_t iterations = 0;
154 
155   // In addition, each message string is again arbitrarily limited to
156   // |kMaxMsgLength|. There's no real documented limit to work with here.
157   constexpr size_t kMaxMsgLength = 4096;
158 
159   base::Value::List messages;
160   do {
161     gss_buffer_desc_struct message_buffer = GSS_C_EMPTY_BUFFER;
162     ScopedBuffer message_buffer_releaser(&message_buffer, gssapi_lib);
163 
164     OM_uint32 minor_status = 0;
165     OM_uint32 major_status = gssapi_lib->display_status(
166         &minor_status, status, status_code_type, GSS_C_NO_OID, &message_context,
167         &message_buffer);
168 
169     if (major_status != GSS_S_COMPLETE || message_buffer.length == 0 ||
170         !message_buffer.value) {
171       continue;
172     }
173 
174     std::string_view message_string{
175         static_cast<const char*>(message_buffer.value),
176         std::min(kMaxMsgLength, message_buffer.length)};
177 
178     // The returned string is almost assuredly ASCII, but be defensive.
179     if (!base::IsStringUTF8(message_string))
180       continue;
181 
182     messages.Append(message_string);
183   } while (message_context != 0 && ++iterations < kMaxDisplayIterations);
184 
185   if (!messages.empty())
186     rv.Set("message", std::move(messages));
187   return rv;
188 }
189 
GetGssStatusValue(GSSAPILibrary * gssapi_lib,std::string_view method,OM_uint32 major_status,OM_uint32 minor_status)190 base::Value::Dict GetGssStatusValue(GSSAPILibrary* gssapi_lib,
191                                     std::string_view method,
192                                     OM_uint32 major_status,
193                                     OM_uint32 minor_status) {
194   base::Value::Dict params;
195   params.Set("function", method);
196   params.Set("major_status",
197              GetGssStatusCodeValue(gssapi_lib, major_status, GSS_C_GSS_CODE));
198   params.Set("minor_status",
199              GetGssStatusCodeValue(gssapi_lib, minor_status, GSS_C_MECH_CODE));
200   return params;
201 }
202 
OidToValue(gss_OID oid)203 base::Value::Dict OidToValue(gss_OID oid) {
204   base::Value::Dict params;
205 
206   if (!oid || oid->length == 0) {
207     params.Set("oid", "<Empty OID>");
208     return params;
209   }
210 
211   params.Set("length", static_cast<int>(oid->length));
212   if (!oid->elements)
213     return params;
214 
215   // Cap OID content at arbitrary limit 1k.
216   constexpr OM_uint32 kMaxOidDataSize = 1024;
217   params.Set("bytes", NetLogBinaryValue(oid->elements, std::min(kMaxOidDataSize,
218                                                                 oid->length)));
219 
220   // Based on RFC 2744 Appendix A. Hardcoding the OIDs in the list below to
221   // avoid having a static dependency on the library.
222   static const struct {
223     const char* symbolic_name;
224     const gss_OID_desc oid_desc;
225   } kWellKnownOIDs[] = {
226       {"GSS_C_NT_USER_NAME",
227        {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")}},
228       {"GSS_C_NT_MACHINE_UID_NAME",
229        {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")}},
230       {"GSS_C_NT_STRING_UID_NAME",
231        {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")}},
232       {"GSS_C_NT_HOSTBASED_SERVICE_X",
233        {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x02")}},
234       {"GSS_C_NT_HOSTBASED_SERVICE",
235        {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")}},
236       {"GSS_C_NT_ANONYMOUS", {6, const_cast<char*>("\x2b\x06\01\x05\x06\x03")}},
237       {"GSS_C_NT_EXPORT_NAME",
238        {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x04")}}};
239 
240   for (auto& well_known_oid : kWellKnownOIDs) {
241     if (OidEquals(oid, const_cast<const gss_OID>(&well_known_oid.oid_desc)))
242       params.Set("oid", well_known_oid.symbolic_name);
243   }
244 
245   return params;
246 }
247 
GetDisplayNameValue(GSSAPILibrary * gssapi_lib,const gss_name_t gss_name)248 base::Value::Dict GetDisplayNameValue(GSSAPILibrary* gssapi_lib,
249                                       const gss_name_t gss_name) {
250   OM_uint32 major_status = 0;
251   OM_uint32 minor_status = 0;
252   gss_buffer_desc_struct name = GSS_C_EMPTY_BUFFER;
253   gss_OID name_type = GSS_C_NO_OID;
254 
255   base::Value::Dict rv;
256   major_status =
257       gssapi_lib->display_name(&minor_status, gss_name, &name, &name_type);
258   ScopedBuffer scoped_output_name(&name, gssapi_lib);
259   if (major_status != GSS_S_COMPLETE) {
260     rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_display_name",
261                                       major_status, minor_status));
262     return rv;
263   }
264   auto name_string =
265       std::string_view(reinterpret_cast<const char*>(name.value), name.length);
266   rv.Set("name", base::IsStringUTF8(name_string)
267                      ? NetLogStringValue(name_string)
268                      : NetLogBinaryValue(name.value, name.length));
269   rv.Set("type", OidToValue(name_type));
270   return rv;
271 }
272 
ContextFlagsToValue(OM_uint32 flags)273 base::Value::Dict ContextFlagsToValue(OM_uint32 flags) {
274   base::Value::Dict rv;
275   rv.Set("value", base::StringPrintf("0x%08x", flags));
276   rv.Set("delegated", (flags & GSS_C_DELEG_FLAG) == GSS_C_DELEG_FLAG);
277   rv.Set("mutual", (flags & GSS_C_MUTUAL_FLAG) == GSS_C_MUTUAL_FLAG);
278   return rv;
279 }
280 
GetContextStateAsValue(GSSAPILibrary * gssapi_lib,const gss_ctx_id_t context_handle)281 base::Value::Dict GetContextStateAsValue(GSSAPILibrary* gssapi_lib,
282                                          const gss_ctx_id_t context_handle) {
283   base::Value::Dict rv;
284   if (context_handle == GSS_C_NO_CONTEXT) {
285     rv.Set("error", GetGssStatusValue(nullptr, "<none>", GSS_S_NO_CONTEXT, 0));
286     return rv;
287   }
288 
289   OM_uint32 major_status = 0;
290   OM_uint32 minor_status = 0;
291   gss_name_t src_name = GSS_C_NO_NAME;
292   gss_name_t targ_name = GSS_C_NO_NAME;
293   OM_uint32 lifetime_rec = 0;
294   gss_OID mech_type = GSS_C_NO_OID;
295   OM_uint32 ctx_flags = 0;
296   int locally_initiated = 0;
297   int open = 0;
298   major_status = gssapi_lib->inquire_context(&minor_status,
299                                              context_handle,
300                                              &src_name,
301                                              &targ_name,
302                                              &lifetime_rec,
303                                              &mech_type,
304                                              &ctx_flags,
305                                              &locally_initiated,
306                                              &open);
307   if (major_status != GSS_S_COMPLETE) {
308     rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_inquire_context",
309                                       major_status, minor_status));
310     return rv;
311   }
312   ScopedName scoped_src_name(src_name, gssapi_lib);
313   ScopedName scoped_targ_name(targ_name, gssapi_lib);
314 
315   rv.Set("source", GetDisplayNameValue(gssapi_lib, src_name));
316   rv.Set("target", GetDisplayNameValue(gssapi_lib, targ_name));
317   // lifetime_rec is a uint32, while base::Value only takes ints. On 32 bit
318   // platforms uint32 doesn't fit on an int.
319   rv.Set("lifetime", base::NumberToString(lifetime_rec));
320   rv.Set("mechanism", OidToValue(mech_type));
321   rv.Set("flags", ContextFlagsToValue(ctx_flags));
322   rv.Set("open", !!open);
323   return rv;
324 }
325 
326 namespace {
327 
328 // Return a NetLog value for the result of loading a library.
LibraryLoadResultParams(std::string_view library_name,std::string_view load_result)329 base::Value::Dict LibraryLoadResultParams(std::string_view library_name,
330                                           std::string_view load_result) {
331   base::Value::Dict params;
332   params.Set("library_name", library_name);
333   if (!load_result.empty())
334     params.Set("load_result", load_result);
335   return params;
336 }
337 
338 }  // namespace
339 
GSSAPISharedLibrary(const std::string & gssapi_library_name)340 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name)
341     : gssapi_library_name_(gssapi_library_name) {}
342 
~GSSAPISharedLibrary()343 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
344   if (gssapi_library_) {
345     base::UnloadNativeLibrary(gssapi_library_);
346     gssapi_library_ = nullptr;
347   }
348 }
349 
Init(const NetLogWithSource & net_log)350 bool GSSAPISharedLibrary::Init(const NetLogWithSource& net_log) {
351   if (!initialized_)
352     InitImpl(net_log);
353   return initialized_;
354 }
355 
InitImpl(const NetLogWithSource & net_log)356 bool GSSAPISharedLibrary::InitImpl(const NetLogWithSource& net_log) {
357   DCHECK(!initialized_);
358   gssapi_library_ = LoadSharedLibrary(net_log);
359   if (gssapi_library_ == nullptr)
360     return false;
361   initialized_ = true;
362   return true;
363 }
364 
LoadSharedLibrary(const NetLogWithSource & net_log)365 base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary(
366     const NetLogWithSource& net_log) {
367   const char* const* library_names;
368   size_t num_lib_names;
369   const char* user_specified_library[1];
370   if (!gssapi_library_name_.empty()) {
371     user_specified_library[0] = gssapi_library_name_.c_str();
372     library_names = user_specified_library;
373     num_lib_names = 1;
374   } else {
375     static const char* const kDefaultLibraryNames[] = {
376 #if BUILDFLAG(IS_APPLE)
377       "/System/Library/Frameworks/GSS.framework/GSS"
378 #elif BUILDFLAG(IS_OPENBSD)
379       "libgssapi.so"  // Heimdal - OpenBSD
380 #else
381       "libgssapi_krb5.so.2",  // MIT Kerberos - FC, Suse10, Debian
382       "libgssapi.so.4",       // Heimdal - Suse10, MDK
383       "libgssapi.so.2",       // Heimdal - Gentoo
384       "libgssapi.so.1"        // Heimdal - Suse9, CITI - FC, MDK, Suse10
385 #endif
386     };
387     library_names = kDefaultLibraryNames;
388     num_lib_names = std::size(kDefaultLibraryNames);
389   }
390 
391   net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_LOAD);
392 
393   // There has to be at least one candidate.
394   DCHECK_NE(0u, num_lib_names);
395 
396   const char* library_name = nullptr;
397   base::NativeLibraryLoadError load_error;
398 
399   for (size_t i = 0; i < num_lib_names; ++i) {
400     load_error = base::NativeLibraryLoadError();
401     library_name = library_names[i];
402     base::FilePath file_path(library_name);
403 
404     // TODO(asanka): Move library loading to a separate thread.
405     //               http://crbug.com/66702
406     base::ScopedAllowBlocking scoped_allow_blocking_temporarily;
407     base::NativeLibrary lib = base::LoadNativeLibrary(file_path, &load_error);
408     if (lib) {
409       if (BindMethods(lib, library_name, net_log)) {
410         net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
411           return LibraryLoadResultParams(library_name, "");
412         });
413         return lib;
414       }
415       base::UnloadNativeLibrary(lib);
416     }
417   }
418 
419   // If loading failed, then log the result of the final attempt. Doing so
420   // is specially important on platforms where there's only one possible
421   // library. Doing so also always logs the failure when the GSSAPI library
422   // name is explicitly specified.
423   net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
424     return LibraryLoadResultParams(library_name, load_error.ToString());
425   });
426   return nullptr;
427 }
428 
429 namespace {
430 
BindFailureParams(std::string_view library_name,std::string_view method)431 base::Value::Dict BindFailureParams(std::string_view library_name,
432                                     std::string_view method) {
433   base::Value::Dict params;
434   params.Set("library_name", library_name);
435   params.Set("method", method);
436   return params;
437 }
438 
BindUntypedMethod(base::NativeLibrary lib,std::string_view library_name,const char * method,const NetLogWithSource & net_log)439 void* BindUntypedMethod(base::NativeLibrary lib,
440                         std::string_view library_name,
441                         const char* method,
442                         const NetLogWithSource& net_log) {
443   void* ptr = base::GetFunctionPointerFromNativeLibrary(lib, method);
444   if (ptr == nullptr) {
445     net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_BIND_FAILED,
446                      [&] { return BindFailureParams(library_name, method); });
447   }
448   return ptr;
449 }
450 
451 template <typename T>
BindMethod(base::NativeLibrary lib,std::string_view library_name,const char * method,T * receiver,const NetLogWithSource & net_log)452 bool BindMethod(base::NativeLibrary lib,
453                 std::string_view library_name,
454                 const char* method,
455                 T* receiver,
456                 const NetLogWithSource& net_log) {
457   *receiver = reinterpret_cast<T>(
458       BindUntypedMethod(lib, library_name, method, net_log));
459   return *receiver != nullptr;
460 }
461 
462 }  // namespace
463 
BindMethods(base::NativeLibrary lib,std::string_view name,const NetLogWithSource & net_log)464 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib,
465                                       std::string_view name,
466                                       const NetLogWithSource& net_log) {
467   bool ok = true;
468   // It's unlikely for BindMethods() to fail if LoadNativeLibrary() succeeded. A
469   // failure in this function indicates an interoperability issue whose
470   // diagnosis requires knowing all the methods that are missing. Hence |ok| is
471   // updated in a manner that prevents short-circuiting the BindGssMethod()
472   // invocations.
473   ok &= BindMethod(lib, name, "gss_delete_sec_context", &delete_sec_context_,
474                    net_log);
475   ok &= BindMethod(lib, name, "gss_display_name", &display_name_, net_log);
476   ok &= BindMethod(lib, name, "gss_display_status", &display_status_, net_log);
477   ok &= BindMethod(lib, name, "gss_import_name", &import_name_, net_log);
478   ok &= BindMethod(lib, name, "gss_init_sec_context", &init_sec_context_,
479                    net_log);
480   ok &=
481       BindMethod(lib, name, "gss_inquire_context", &inquire_context_, net_log);
482   ok &= BindMethod(lib, name, "gss_release_buffer", &release_buffer_, net_log);
483   ok &= BindMethod(lib, name, "gss_release_name", &release_name_, net_log);
484   ok &=
485       BindMethod(lib, name, "gss_wrap_size_limit", &wrap_size_limit_, net_log);
486 
487   if (ok) [[likely]] {
488     return true;
489   }
490 
491   delete_sec_context_ = nullptr;
492   display_name_ = nullptr;
493   display_status_ = nullptr;
494   import_name_ = nullptr;
495   init_sec_context_ = nullptr;
496   inquire_context_ = nullptr;
497   release_buffer_ = nullptr;
498   release_name_ = nullptr;
499   wrap_size_limit_ = nullptr;
500   return false;
501 }
502 
import_name(OM_uint32 * minor_status,const gss_buffer_t input_name_buffer,const gss_OID input_name_type,gss_name_t * output_name)503 OM_uint32 GSSAPISharedLibrary::import_name(
504     OM_uint32* minor_status,
505     const gss_buffer_t input_name_buffer,
506     const gss_OID input_name_type,
507     gss_name_t* output_name) {
508   DCHECK(initialized_);
509   return import_name_(minor_status, input_name_buffer, input_name_type,
510                       output_name);
511 }
512 
release_name(OM_uint32 * minor_status,gss_name_t * input_name)513 OM_uint32 GSSAPISharedLibrary::release_name(
514     OM_uint32* minor_status,
515     gss_name_t* input_name) {
516   DCHECK(initialized_);
517   return release_name_(minor_status, input_name);
518 }
519 
release_buffer(OM_uint32 * minor_status,gss_buffer_t buffer)520 OM_uint32 GSSAPISharedLibrary::release_buffer(
521     OM_uint32* minor_status,
522     gss_buffer_t buffer) {
523   DCHECK(initialized_);
524   return release_buffer_(minor_status, buffer);
525 }
526 
display_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)527 OM_uint32 GSSAPISharedLibrary::display_name(
528     OM_uint32* minor_status,
529     const gss_name_t input_name,
530     gss_buffer_t output_name_buffer,
531     gss_OID* output_name_type) {
532   DCHECK(initialized_);
533   return display_name_(minor_status,
534                        input_name,
535                        output_name_buffer,
536                        output_name_type);
537 }
538 
display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,const gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)539 OM_uint32 GSSAPISharedLibrary::display_status(
540     OM_uint32* minor_status,
541     OM_uint32 status_value,
542     int status_type,
543     const gss_OID mech_type,
544     OM_uint32* message_context,
545     gss_buffer_t status_string) {
546   DCHECK(initialized_);
547   return display_status_(minor_status, status_value, status_type, mech_type,
548                          message_context, status_string);
549 }
550 
init_sec_context(OM_uint32 * minor_status,const gss_cred_id_t initiator_cred_handle,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)551 OM_uint32 GSSAPISharedLibrary::init_sec_context(
552     OM_uint32* minor_status,
553     const gss_cred_id_t initiator_cred_handle,
554     gss_ctx_id_t* context_handle,
555     const gss_name_t target_name,
556     const gss_OID mech_type,
557     OM_uint32 req_flags,
558     OM_uint32 time_req,
559     const gss_channel_bindings_t input_chan_bindings,
560     const gss_buffer_t input_token,
561     gss_OID* actual_mech_type,
562     gss_buffer_t output_token,
563     OM_uint32* ret_flags,
564     OM_uint32* time_rec) {
565   DCHECK(initialized_);
566   return init_sec_context_(minor_status,
567                            initiator_cred_handle,
568                            context_handle,
569                            target_name,
570                            mech_type,
571                            req_flags,
572                            time_req,
573                            input_chan_bindings,
574                            input_token,
575                            actual_mech_type,
576                            output_token,
577                            ret_flags,
578                            time_rec);
579 }
580 
wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)581 OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
582     OM_uint32* minor_status,
583     const gss_ctx_id_t context_handle,
584     int conf_req_flag,
585     gss_qop_t qop_req,
586     OM_uint32 req_output_size,
587     OM_uint32* max_input_size) {
588   DCHECK(initialized_);
589   return wrap_size_limit_(minor_status,
590                           context_handle,
591                           conf_req_flag,
592                           qop_req,
593                           req_output_size,
594                           max_input_size);
595 }
596 
delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)597 OM_uint32 GSSAPISharedLibrary::delete_sec_context(
598     OM_uint32* minor_status,
599     gss_ctx_id_t* context_handle,
600     gss_buffer_t output_token) {
601   // This is called from the owner class' destructor, even if
602   // Init() is not called, so we can't assume |initialized_|
603   // is set.
604   if (!initialized_)
605     return 0;
606   return delete_sec_context_(minor_status,
607                              context_handle,
608                              output_token);
609 }
610 
inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * open)611 OM_uint32 GSSAPISharedLibrary::inquire_context(
612     OM_uint32* minor_status,
613     const gss_ctx_id_t context_handle,
614     gss_name_t* src_name,
615     gss_name_t* targ_name,
616     OM_uint32* lifetime_rec,
617     gss_OID* mech_type,
618     OM_uint32* ctx_flags,
619     int* locally_initiated,
620     int* open) {
621   DCHECK(initialized_);
622   return inquire_context_(minor_status,
623                           context_handle,
624                           src_name,
625                           targ_name,
626                           lifetime_rec,
627                           mech_type,
628                           ctx_flags,
629                           locally_initiated,
630                           open);
631 }
632 
GetLibraryNameForTesting()633 const std::string& GSSAPISharedLibrary::GetLibraryNameForTesting() {
634   return gssapi_library_name_;
635 }
636 
ScopedSecurityContext(GSSAPILibrary * gssapi_lib)637 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
638     : security_context_(GSS_C_NO_CONTEXT),
639       gssapi_lib_(gssapi_lib) {
640   DCHECK(gssapi_lib_);
641 }
642 
~ScopedSecurityContext()643 ScopedSecurityContext::~ScopedSecurityContext() {
644   if (security_context_ != GSS_C_NO_CONTEXT) {
645     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
646     OM_uint32 minor_status = 0;
647     OM_uint32 major_status = gssapi_lib_->delete_sec_context(
648         &minor_status, &security_context_, &output_token);
649     DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
650         << "Problem releasing security_context. "
651         << GetGssStatusValue(gssapi_lib_, "delete_sec_context", major_status,
652                              minor_status);
653     security_context_ = GSS_C_NO_CONTEXT;
654   }
655 }
656 
HttpAuthGSSAPI(GSSAPILibrary * library,gss_OID gss_oid)657 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, gss_OID gss_oid)
658     : gss_oid_(gss_oid), library_(library), scoped_sec_context_(library) {
659   DCHECK(library_);
660 }
661 
662 HttpAuthGSSAPI::~HttpAuthGSSAPI() = default;
663 
Init(const NetLogWithSource & net_log)664 bool HttpAuthGSSAPI::Init(const NetLogWithSource& net_log) {
665   if (!library_)
666     return false;
667   return library_->Init(net_log);
668 }
669 
NeedsIdentity() const670 bool HttpAuthGSSAPI::NeedsIdentity() const {
671   return decoded_server_auth_token_.empty();
672 }
673 
AllowsExplicitCredentials() const674 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
675 #if BUILDFLAG(IS_CHROMEOS)
676   if (base::FeatureList::IsEnabled(features::kKerberosInBrowserRedirect)) {
677     return true;
678   } else {
679     return false;
680   }
681 #else
682   return false;
683 #endif
684 }
685 
SetDelegation(DelegationType delegation_type)686 void HttpAuthGSSAPI::SetDelegation(DelegationType delegation_type) {
687   delegation_type_ = delegation_type;
688 }
689 
ParseChallenge(HttpAuthChallengeTokenizer * tok)690 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
691     HttpAuthChallengeTokenizer* tok) {
692   if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) {
693     return ParseFirstRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok);
694   }
695   std::string encoded_auth_token;
696   return ParseLaterRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok,
697                                   &encoded_auth_token,
698                                   &decoded_server_auth_token_);
699 }
700 
GenerateAuthToken(const AuthCredentials * credentials,const std::string & spn,const std::string & channel_bindings,std::string * auth_token,const NetLogWithSource & net_log,CompletionOnceCallback)701 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
702                                       const std::string& spn,
703                                       const std::string& channel_bindings,
704                                       std::string* auth_token,
705                                       const NetLogWithSource& net_log,
706                                       CompletionOnceCallback /*callback*/) {
707   DCHECK(auth_token);
708 
709   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
710   input_token.length = decoded_server_auth_token_.length();
711   input_token.value = (input_token.length > 0)
712                           ? const_cast<char*>(decoded_server_auth_token_.data())
713                           : nullptr;
714   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
715   ScopedBuffer scoped_output_token(&output_token, library_);
716   int rv = GetNextSecurityToken(spn, channel_bindings, &input_token,
717                                 &output_token, net_log);
718   if (rv != OK)
719     return rv;
720 
721   // Base64 encode data in output buffer and prepend the scheme.
722   std::string encode_input(static_cast<char*>(output_token.value),
723                            output_token.length);
724   std::string encode_output = base::Base64Encode(encode_input);
725   *auth_token = "Negotiate " + encode_output;
726   return OK;
727 }
728 
729 namespace {
730 
731 // GSSAPI status codes consist of a calling error (essentially, a programmer
732 // bug), a routine error (defined by the RFC), and supplementary information,
733 // all bitwise-or'ed together in different regions of the 32 bit return value.
734 // This means a simple switch on the return codes is not sufficient.
735 
MapImportNameStatusToError(OM_uint32 major_status)736 int MapImportNameStatusToError(OM_uint32 major_status) {
737   if (major_status == GSS_S_COMPLETE)
738     return OK;
739   if (GSS_CALLING_ERROR(major_status) != 0)
740     return ERR_UNEXPECTED;
741   OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
742   switch (routine_error) {
743     case GSS_S_FAILURE:
744       // Looking at the MIT Kerberos implementation, this typically is returned
745       // when memory allocation fails. However, the API does not guarantee
746       // that this is the case, so using ERR_UNEXPECTED rather than
747       // ERR_OUT_OF_MEMORY.
748       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
749     case GSS_S_BAD_NAME:
750     case GSS_S_BAD_NAMETYPE:
751       return ERR_MALFORMED_IDENTITY;
752     case GSS_S_DEFECTIVE_TOKEN:
753       // Not mentioned in the API, but part of code.
754       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
755     case GSS_S_BAD_MECH:
756       return ERR_UNSUPPORTED_AUTH_SCHEME;
757     default:
758       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
759   }
760 }
761 
MapInitSecContextStatusToError(OM_uint32 major_status)762 int MapInitSecContextStatusToError(OM_uint32 major_status) {
763   // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
764   // other code just checks if major_status is equivalent to it to indicate
765   // that there are no other errors included.
766   if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
767     return OK;
768   if (GSS_CALLING_ERROR(major_status) != 0)
769     return ERR_UNEXPECTED;
770   OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
771   switch (routine_status) {
772     case GSS_S_DEFECTIVE_TOKEN:
773       return ERR_INVALID_RESPONSE;
774     case GSS_S_DEFECTIVE_CREDENTIAL:
775       // Not expected since this implementation uses the default credential.
776       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
777     case GSS_S_BAD_SIG:
778       // Probably won't happen, but it's a bad response.
779       return ERR_INVALID_RESPONSE;
780     case GSS_S_NO_CRED:
781       return ERR_INVALID_AUTH_CREDENTIALS;
782     case GSS_S_CREDENTIALS_EXPIRED:
783       return ERR_INVALID_AUTH_CREDENTIALS;
784     case GSS_S_BAD_BINDINGS:
785       // This only happens with mutual authentication.
786       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
787     case GSS_S_NO_CONTEXT:
788       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
789     case GSS_S_BAD_NAMETYPE:
790       return ERR_UNSUPPORTED_AUTH_SCHEME;
791     case GSS_S_BAD_NAME:
792       return ERR_UNSUPPORTED_AUTH_SCHEME;
793     case GSS_S_BAD_MECH:
794       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
795     case GSS_S_FAILURE:
796       // This should be an "Unexpected Security Status" according to the
797       // GSSAPI documentation, but it's typically used to indicate that
798       // credentials are not correctly set up on a user machine, such
799       // as a missing credential cache or hitting this after calling
800       // kdestroy.
801       // TODO(cbentzel): Use minor code for even better mapping?
802       return ERR_MISSING_AUTH_CREDENTIALS;
803     default:
804       if (routine_status != 0)
805         return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
806       break;
807   }
808   OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
809   // Replays could indicate an attack.
810   if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
811                              GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
812     return ERR_INVALID_RESPONSE;
813 
814   // At this point, every documented status has been checked.
815   return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
816 }
817 
ImportNameErrorParams(GSSAPILibrary * library,std::string_view spn,OM_uint32 major_status,OM_uint32 minor_status)818 base::Value::Dict ImportNameErrorParams(GSSAPILibrary* library,
819                                         std::string_view spn,
820                                         OM_uint32 major_status,
821                                         OM_uint32 minor_status) {
822   base::Value::Dict params;
823   params.Set("spn", spn);
824   if (major_status != GSS_S_COMPLETE)
825     params.Set("status", GetGssStatusValue(library, "import_name", major_status,
826                                            minor_status));
827   return params;
828 }
829 
InitSecContextErrorParams(GSSAPILibrary * library,gss_ctx_id_t context,OM_uint32 major_status,OM_uint32 minor_status)830 base::Value::Dict InitSecContextErrorParams(GSSAPILibrary* library,
831                                             gss_ctx_id_t context,
832                                             OM_uint32 major_status,
833                                             OM_uint32 minor_status) {
834   base::Value::Dict params;
835   if (major_status != GSS_S_COMPLETE)
836     params.Set("status", GetGssStatusValue(library, "gss_init_sec_context",
837                                            major_status, minor_status));
838   if (context != GSS_C_NO_CONTEXT)
839     params.Set("context", GetContextStateAsValue(library, context));
840   return params;
841 }
842 
843 }  // anonymous namespace
844 
GetNextSecurityToken(const std::string & spn,const std::string & channel_bindings,gss_buffer_t in_token,gss_buffer_t out_token,const NetLogWithSource & net_log)845 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
846                                          const std::string& channel_bindings,
847                                          gss_buffer_t in_token,
848                                          gss_buffer_t out_token,
849                                          const NetLogWithSource& net_log) {
850   // GSSAPI header files, to this day, require OIDs passed in as non-const
851   // pointers. Rather than const casting, let's just leave this as non-const.
852   // Even if the OID pointer is const, the inner |elements| pointer is still
853   // non-const.
854   static gss_OID_desc kGSS_C_NT_HOSTBASED_SERVICE = {
855       10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
856 
857   // Create a name for the principal
858   // TODO(cbentzel): Just do this on the first pass?
859   std::string spn_principal = spn;
860   gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
861   spn_buffer.value = const_cast<char*>(spn_principal.c_str());
862   spn_buffer.length = spn_principal.size() + 1;
863   OM_uint32 minor_status = 0;
864   gss_name_t principal_name = GSS_C_NO_NAME;
865 
866   OM_uint32 major_status =
867       library_->import_name(&minor_status, &spn_buffer,
868                             &kGSS_C_NT_HOSTBASED_SERVICE, &principal_name);
869   net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_IMPORT_NAME, [&] {
870     return ImportNameErrorParams(library_, spn, major_status, minor_status);
871   });
872   int rv = MapImportNameStatusToError(major_status);
873   if (rv != OK)
874     return rv;
875   ScopedName scoped_name(principal_name, library_);
876 
877   // Continue creating a security context.
878   net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX);
879   major_status = library_->init_sec_context(
880       &minor_status, GSS_C_NO_CREDENTIAL, scoped_sec_context_.receive(),
881       principal_name, gss_oid_, DelegationTypeToFlag(delegation_type_),
882       GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, in_token,
883       nullptr,  // actual_mech_type
884       out_token,
885       nullptr,  // ret flags
886       nullptr);
887   net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] {
888     return InitSecContextErrorParams(library_, scoped_sec_context_.get(),
889                                      major_status, minor_status);
890   });
891   return MapInitSecContextStatusToError(major_status);
892 }
893 
894 }  // namespace net
895