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