123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/http/http_auth_gssapi_posix.h"
- #include <limits>
- #include <string>
- #include "base/base64.h"
- #include "base/compiler_specific.h"
- #include "base/files/file_path.h"
- #include "base/format_macros.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/values.h"
- #include "build/build_config.h"
- #include "net/base/net_errors.h"
- #include "net/http/http_auth.h"
- #include "net/http/http_auth_gssapi_posix.h"
- #include "net/http/http_auth_multi_round_parse.h"
- #include "net/log/net_log_event_type.h"
- #include "net/log/net_log_values.h"
- #include "net/log/net_log_with_source.h"
- #include "net/net_buildflags.h"
- namespace net {
- using DelegationType = HttpAuth::DelegationType;
- // Exported mechanism for GSSAPI. We always use SPNEGO:
- // iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
- gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = {
- 6,
- const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
- };
- gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
- &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
- OM_uint32 DelegationTypeToFlag(DelegationType delegation_type) {
- switch (delegation_type) {
- case DelegationType::kNone:
- return 0;
- case DelegationType::kByKdcPolicy:
- return GSS_C_DELEG_POLICY_FLAG;
- case DelegationType::kUnconstrained:
- return GSS_C_DELEG_FLAG;
- }
- }
- // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
- class ScopedBuffer {
- public:
- ScopedBuffer(gss_buffer_t buffer, GSSAPILibrary* gssapi_lib)
- : buffer_(buffer), gssapi_lib_(gssapi_lib) {
- DCHECK(gssapi_lib_);
- }
- ScopedBuffer(const ScopedBuffer&) = delete;
- ScopedBuffer& operator=(const ScopedBuffer&) = delete;
- ~ScopedBuffer() {
- if (buffer_ != GSS_C_NO_BUFFER) {
- OM_uint32 minor_status = 0;
- OM_uint32 major_status =
- gssapi_lib_->release_buffer(&minor_status, buffer_);
- DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
- << "Problem releasing buffer. major=" << major_status
- << ", minor=" << minor_status;
- buffer_ = GSS_C_NO_BUFFER;
- }
- }
- private:
- gss_buffer_t buffer_;
- raw_ptr<GSSAPILibrary> gssapi_lib_;
- };
- // ScopedName releases a gss_name_t when it goes out of scope.
- class ScopedName {
- public:
- ScopedName(gss_name_t name, GSSAPILibrary* gssapi_lib)
- : name_(name), gssapi_lib_(gssapi_lib) {
- DCHECK(gssapi_lib_);
- }
- ScopedName(const ScopedName&) = delete;
- ScopedName& operator=(const ScopedName&) = delete;
- ~ScopedName() {
- if (name_ != GSS_C_NO_NAME) {
- OM_uint32 minor_status = 0;
- OM_uint32 major_status = gssapi_lib_->release_name(&minor_status, &name_);
- if (major_status != GSS_S_COMPLETE) {
- DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
- << "Problem releasing name. "
- << GetGssStatusValue(nullptr, "gss_release_name", major_status,
- minor_status);
- }
- name_ = GSS_C_NO_NAME;
- }
- }
- private:
- gss_name_t name_;
- raw_ptr<GSSAPILibrary> gssapi_lib_;
- };
- bool OidEquals(const gss_OID left, const gss_OID right) {
- if (left->length != right->length)
- return false;
- return 0 == memcmp(left->elements, right->elements, right->length);
- }
- base::Value::Dict GetGssStatusCodeValue(GSSAPILibrary* gssapi_lib,
- OM_uint32 status,
- OM_uint32 status_code_type) {
- base::Value::Dict rv;
- rv.Set("status", static_cast<int>(status));
- // Message lookups aren't performed if there's no library or if the status
- // indicates success.
- if (!gssapi_lib || status == GSS_S_COMPLETE)
- return rv;
- // gss_display_status() can potentially return multiple strings by sending
- // each string on successive invocations. State is maintained across these
- // invocations in a caller supplied OM_uint32. After each successful call,
- // the context is set to a non-zero value that should be passed as a message
- // context to each successive gss_display_status() call. The initial and
- // terminal values of this context storage is 0.
- OM_uint32 message_context = 0;
- // To account for the off chance that gss_display_status() misbehaves and gets
- // into an infinite loop, we'll artificially limit the number of iterations to
- // |kMaxDisplayIterations|. This limit is arbitrary.
- constexpr size_t kMaxDisplayIterations = 8;
- size_t iterations = 0;
- // In addition, each message string is again arbitrarily limited to
- // |kMaxMsgLength|. There's no real documented limit to work with here.
- constexpr size_t kMaxMsgLength = 4096;
- base::Value::List messages;
- do {
- gss_buffer_desc_struct message_buffer = GSS_C_EMPTY_BUFFER;
- ScopedBuffer message_buffer_releaser(&message_buffer, gssapi_lib);
- OM_uint32 minor_status = 0;
- OM_uint32 major_status = gssapi_lib->display_status(
- &minor_status, status, status_code_type, GSS_C_NO_OID, &message_context,
- &message_buffer);
- if (major_status != GSS_S_COMPLETE || message_buffer.length == 0 ||
- !message_buffer.value) {
- continue;
- }
- base::StringPiece message_string{
- static_cast<const char*>(message_buffer.value),
- std::min(kMaxMsgLength, message_buffer.length)};
- // The returned string is almost assuredly ASCII, but be defensive.
- if (!base::IsStringUTF8(message_string))
- continue;
- messages.Append(message_string);
- } while (message_context != 0 && ++iterations < kMaxDisplayIterations);
- if (!messages.empty())
- rv.Set("message", std::move(messages));
- return rv;
- }
- base::Value::Dict GetGssStatusValue(GSSAPILibrary* gssapi_lib,
- base::StringPiece method,
- OM_uint32 major_status,
- OM_uint32 minor_status) {
- base::Value::Dict params;
- params.Set("function", method);
- params.Set("major_status",
- GetGssStatusCodeValue(gssapi_lib, major_status, GSS_C_GSS_CODE));
- params.Set("minor_status",
- GetGssStatusCodeValue(gssapi_lib, minor_status, GSS_C_MECH_CODE));
- return params;
- }
- base::Value::Dict OidToValue(gss_OID oid) {
- base::Value::Dict params;
- if (!oid || oid->length == 0) {
- params.Set("oid", "<Empty OID>");
- return params;
- }
- params.Set("length", static_cast<int>(oid->length));
- if (!oid->elements)
- return params;
- // Cap OID content at arbitrary limit 1k.
- constexpr OM_uint32 kMaxOidDataSize = 1024;
- params.Set("bytes", NetLogBinaryValue(oid->elements, std::min(kMaxOidDataSize,
- oid->length)));
- // Based on RFC 2744 Appendix A. Hardcoding the OIDs in the list below to
- // avoid having a static dependency on the library.
- static const struct {
- const char* symbolic_name;
- const gss_OID_desc oid_desc;
- } kWellKnownOIDs[] = {
- {"GSS_C_NT_USER_NAME",
- {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")}},
- {"GSS_C_NT_MACHINE_UID_NAME",
- {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")}},
- {"GSS_C_NT_STRING_UID_NAME",
- {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")}},
- {"GSS_C_NT_HOSTBASED_SERVICE_X",
- {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x02")}},
- {"GSS_C_NT_HOSTBASED_SERVICE",
- {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")}},
- {"GSS_C_NT_ANONYMOUS", {6, const_cast<char*>("\x2b\x06\01\x05\x06\x03")}},
- {"GSS_C_NT_EXPORT_NAME",
- {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x04")}}};
- for (auto& well_known_oid : kWellKnownOIDs) {
- if (OidEquals(oid, const_cast<const gss_OID>(&well_known_oid.oid_desc)))
- params.Set("oid", well_known_oid.symbolic_name);
- }
- return params;
- }
- base::Value::Dict GetDisplayNameValue(GSSAPILibrary* gssapi_lib,
- const gss_name_t gss_name) {
- OM_uint32 major_status = 0;
- OM_uint32 minor_status = 0;
- gss_buffer_desc_struct name = GSS_C_EMPTY_BUFFER;
- gss_OID name_type = GSS_C_NO_OID;
- base::Value::Dict rv;
- major_status =
- gssapi_lib->display_name(&minor_status, gss_name, &name, &name_type);
- ScopedBuffer scoped_output_name(&name, gssapi_lib);
- if (major_status != GSS_S_COMPLETE) {
- rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_display_name",
- major_status, minor_status));
- return rv;
- }
- auto name_string =
- base::StringPiece(reinterpret_cast<const char*>(name.value), name.length);
- rv.Set("name", base::IsStringUTF8(name_string)
- ? NetLogStringValue(name_string)
- : NetLogBinaryValue(name.value, name.length));
- rv.Set("type", OidToValue(name_type));
- return rv;
- }
- base::Value::Dict ContextFlagsToValue(OM_uint32 flags) {
- base::Value::Dict rv;
- rv.Set("value", base::StringPrintf("0x%08x", flags));
- rv.Set("delegated", (flags & GSS_C_DELEG_FLAG) == GSS_C_DELEG_FLAG);
- rv.Set("mutual", (flags & GSS_C_MUTUAL_FLAG) == GSS_C_MUTUAL_FLAG);
- return rv;
- }
- base::Value GetContextStateAsValue(GSSAPILibrary* gssapi_lib,
- const gss_ctx_id_t context_handle) {
- base::Value::Dict rv;
- if (context_handle == GSS_C_NO_CONTEXT) {
- rv.Set("error", GetGssStatusValue(nullptr, "<none>", GSS_S_NO_CONTEXT, 0));
- return base::Value(std::move(rv));
- }
- OM_uint32 major_status = 0;
- OM_uint32 minor_status = 0;
- gss_name_t src_name = GSS_C_NO_NAME;
- gss_name_t targ_name = GSS_C_NO_NAME;
- OM_uint32 lifetime_rec = 0;
- gss_OID mech_type = GSS_C_NO_OID;
- OM_uint32 ctx_flags = 0;
- int locally_initiated = 0;
- int open = 0;
- major_status = gssapi_lib->inquire_context(&minor_status,
- context_handle,
- &src_name,
- &targ_name,
- &lifetime_rec,
- &mech_type,
- &ctx_flags,
- &locally_initiated,
- &open);
- if (major_status != GSS_S_COMPLETE) {
- rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_inquire_context",
- major_status, minor_status));
- return base::Value(std::move(rv));
- }
- ScopedName scoped_src_name(src_name, gssapi_lib);
- ScopedName scoped_targ_name(targ_name, gssapi_lib);
- rv.Set("source", GetDisplayNameValue(gssapi_lib, src_name));
- rv.Set("target", GetDisplayNameValue(gssapi_lib, targ_name));
- // lifetime_rec is a uint32, while base::Value only takes ints. On 32 bit
- // platforms uint32 doesn't fit on an int.
- rv.Set("lifetime", base::NumberToString(lifetime_rec));
- rv.Set("mechanism", OidToValue(mech_type));
- rv.Set("flags", ContextFlagsToValue(ctx_flags));
- rv.Set("open", !!open);
- return base::Value(std::move(rv));
- }
- namespace {
- // Return a NetLog value for the result of loading a library.
- base::Value LibraryLoadResultParams(base::StringPiece library_name,
- base::StringPiece load_result) {
- base::Value::Dict params;
- params.Set("library_name", library_name);
- if (!load_result.empty())
- params.Set("load_result", load_result);
- return base::Value(std::move(params));
- }
- } // namespace
- GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name)
- : gssapi_library_name_(gssapi_library_name) {}
- GSSAPISharedLibrary::~GSSAPISharedLibrary() {
- if (gssapi_library_) {
- base::UnloadNativeLibrary(gssapi_library_);
- gssapi_library_ = nullptr;
- }
- }
- bool GSSAPISharedLibrary::Init(const NetLogWithSource& net_log) {
- if (!initialized_)
- InitImpl(net_log);
- return initialized_;
- }
- bool GSSAPISharedLibrary::InitImpl(const NetLogWithSource& net_log) {
- DCHECK(!initialized_);
- gssapi_library_ = LoadSharedLibrary(net_log);
- if (gssapi_library_ == nullptr)
- return false;
- initialized_ = true;
- return true;
- }
- base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary(
- const NetLogWithSource& net_log) {
- const char* const* library_names;
- size_t num_lib_names;
- const char* user_specified_library[1];
- if (!gssapi_library_name_.empty()) {
- user_specified_library[0] = gssapi_library_name_.c_str();
- library_names = user_specified_library;
- num_lib_names = 1;
- } else {
- static const char* const kDefaultLibraryNames[] = {
- #if BUILDFLAG(IS_APPLE)
- "/System/Library/Frameworks/GSS.framework/GSS"
- #elif BUILDFLAG(IS_OPENBSD)
- "libgssapi.so" // Heimdal - OpenBSD
- #else
- "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian
- "libgssapi.so.4", // Heimdal - Suse10, MDK
- "libgssapi.so.2", // Heimdal - Gentoo
- "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10
- #endif
- };
- library_names = kDefaultLibraryNames;
- num_lib_names = std::size(kDefaultLibraryNames);
- }
- net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_LOAD);
- // There has to be at least one candidate.
- DCHECK_NE(0u, num_lib_names);
- const char* library_name = nullptr;
- base::NativeLibraryLoadError load_error;
- for (size_t i = 0; i < num_lib_names; ++i) {
- load_error = base::NativeLibraryLoadError();
- library_name = library_names[i];
- base::FilePath file_path(library_name);
- // TODO(asanka): Move library loading to a separate thread.
- // http://crbug.com/66702
- base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily;
- base::NativeLibrary lib = base::LoadNativeLibrary(file_path, &load_error);
- if (lib) {
- if (BindMethods(lib, library_name, net_log)) {
- net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
- return LibraryLoadResultParams(library_name, "");
- });
- return lib;
- }
- base::UnloadNativeLibrary(lib);
- }
- }
- // If loading failed, then log the result of the final attempt. Doing so
- // is specially important on platforms where there's only one possible
- // library. Doing so also always logs the failure when the GSSAPI library
- // name is explicitly specified.
- net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
- return LibraryLoadResultParams(library_name, load_error.ToString());
- });
- return nullptr;
- }
- namespace {
- base::Value BindFailureParams(base::StringPiece library_name,
- base::StringPiece method) {
- base::Value::Dict params;
- params.Set("library_name", library_name);
- params.Set("method", method);
- return base::Value(std::move(params));
- }
- void* BindUntypedMethod(base::NativeLibrary lib,
- base::StringPiece library_name,
- base::StringPiece method,
- const NetLogWithSource& net_log) {
- void* ptr = base::GetFunctionPointerFromNativeLibrary(lib, method);
- if (ptr == nullptr) {
- net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_BIND_FAILED,
- [&] { return BindFailureParams(library_name, method); });
- }
- return ptr;
- }
- template <typename T>
- bool BindMethod(base::NativeLibrary lib,
- base::StringPiece library_name,
- base::StringPiece method,
- T* receiver,
- const NetLogWithSource& net_log) {
- *receiver = reinterpret_cast<T>(
- BindUntypedMethod(lib, library_name, method, net_log));
- return *receiver != nullptr;
- }
- } // namespace
- bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib,
- base::StringPiece name,
- const NetLogWithSource& net_log) {
- bool ok = true;
- // It's unlikely for BindMethods() to fail if LoadNativeLibrary() succeeded. A
- // failure in this function indicates an interoperability issue whose
- // diagnosis requires knowing all the methods that are missing. Hence |ok| is
- // updated in a manner that prevents short-circuiting the BindGssMethod()
- // invocations.
- ok &= BindMethod(lib, name, "gss_delete_sec_context", &delete_sec_context_,
- net_log);
- ok &= BindMethod(lib, name, "gss_display_name", &display_name_, net_log);
- ok &= BindMethod(lib, name, "gss_display_status", &display_status_, net_log);
- ok &= BindMethod(lib, name, "gss_import_name", &import_name_, net_log);
- ok &= BindMethod(lib, name, "gss_init_sec_context", &init_sec_context_,
- net_log);
- ok &=
- BindMethod(lib, name, "gss_inquire_context", &inquire_context_, net_log);
- ok &= BindMethod(lib, name, "gss_release_buffer", &release_buffer_, net_log);
- ok &= BindMethod(lib, name, "gss_release_name", &release_name_, net_log);
- ok &=
- BindMethod(lib, name, "gss_wrap_size_limit", &wrap_size_limit_, net_log);
- if (LIKELY(ok))
- return true;
- delete_sec_context_ = nullptr;
- display_name_ = nullptr;
- display_status_ = nullptr;
- import_name_ = nullptr;
- init_sec_context_ = nullptr;
- inquire_context_ = nullptr;
- release_buffer_ = nullptr;
- release_name_ = nullptr;
- wrap_size_limit_ = nullptr;
- return false;
- }
- OM_uint32 GSSAPISharedLibrary::import_name(
- OM_uint32* minor_status,
- const gss_buffer_t input_name_buffer,
- const gss_OID input_name_type,
- gss_name_t* output_name) {
- DCHECK(initialized_);
- return import_name_(minor_status, input_name_buffer, input_name_type,
- output_name);
- }
- OM_uint32 GSSAPISharedLibrary::release_name(
- OM_uint32* minor_status,
- gss_name_t* input_name) {
- DCHECK(initialized_);
- return release_name_(minor_status, input_name);
- }
- OM_uint32 GSSAPISharedLibrary::release_buffer(
- OM_uint32* minor_status,
- gss_buffer_t buffer) {
- DCHECK(initialized_);
- return release_buffer_(minor_status, buffer);
- }
- OM_uint32 GSSAPISharedLibrary::display_name(
- OM_uint32* minor_status,
- const gss_name_t input_name,
- gss_buffer_t output_name_buffer,
- gss_OID* output_name_type) {
- DCHECK(initialized_);
- return display_name_(minor_status,
- input_name,
- output_name_buffer,
- output_name_type);
- }
- OM_uint32 GSSAPISharedLibrary::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) {
- DCHECK(initialized_);
- return display_status_(minor_status, status_value, status_type, mech_type,
- message_context, status_string);
- }
- OM_uint32 GSSAPISharedLibrary::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) {
- DCHECK(initialized_);
- return init_sec_context_(minor_status,
- initiator_cred_handle,
- context_handle,
- target_name,
- mech_type,
- req_flags,
- time_req,
- input_chan_bindings,
- input_token,
- actual_mech_type,
- output_token,
- ret_flags,
- time_rec);
- }
- OM_uint32 GSSAPISharedLibrary::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) {
- DCHECK(initialized_);
- return wrap_size_limit_(minor_status,
- context_handle,
- conf_req_flag,
- qop_req,
- req_output_size,
- max_input_size);
- }
- OM_uint32 GSSAPISharedLibrary::delete_sec_context(
- OM_uint32* minor_status,
- gss_ctx_id_t* context_handle,
- gss_buffer_t output_token) {
- // This is called from the owner class' destructor, even if
- // Init() is not called, so we can't assume |initialized_|
- // is set.
- if (!initialized_)
- return 0;
- return delete_sec_context_(minor_status,
- context_handle,
- output_token);
- }
- OM_uint32 GSSAPISharedLibrary::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) {
- DCHECK(initialized_);
- return inquire_context_(minor_status,
- context_handle,
- src_name,
- targ_name,
- lifetime_rec,
- mech_type,
- ctx_flags,
- locally_initiated,
- open);
- }
- const std::string& GSSAPISharedLibrary::GetLibraryNameForTesting() {
- return gssapi_library_name_;
- }
- ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
- : security_context_(GSS_C_NO_CONTEXT),
- gssapi_lib_(gssapi_lib) {
- DCHECK(gssapi_lib_);
- }
- ScopedSecurityContext::~ScopedSecurityContext() {
- if (security_context_ != GSS_C_NO_CONTEXT) {
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
- OM_uint32 minor_status = 0;
- OM_uint32 major_status = gssapi_lib_->delete_sec_context(
- &minor_status, &security_context_, &output_token);
- DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
- << "Problem releasing security_context. "
- << GetGssStatusValue(gssapi_lib_, "delete_sec_context", major_status,
- minor_status);
- security_context_ = GSS_C_NO_CONTEXT;
- }
- }
- HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, gss_OID gss_oid)
- : gss_oid_(gss_oid), library_(library), scoped_sec_context_(library) {
- DCHECK(library_);
- }
- HttpAuthGSSAPI::~HttpAuthGSSAPI() = default;
- bool HttpAuthGSSAPI::Init(const NetLogWithSource& net_log) {
- if (!library_)
- return false;
- return library_->Init(net_log);
- }
- bool HttpAuthGSSAPI::NeedsIdentity() const {
- return decoded_server_auth_token_.empty();
- }
- bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
- return false;
- }
- void HttpAuthGSSAPI::SetDelegation(DelegationType delegation_type) {
- delegation_type_ = delegation_type;
- }
- HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
- HttpAuthChallengeTokenizer* tok) {
- if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) {
- return net::ParseFirstRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok);
- }
- std::string encoded_auth_token;
- return net::ParseLaterRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok,
- &encoded_auth_token,
- &decoded_server_auth_token_);
- }
- int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
- const std::string& spn,
- const std::string& channel_bindings,
- std::string* auth_token,
- const NetLogWithSource& net_log,
- CompletionOnceCallback /*callback*/) {
- DCHECK(auth_token);
- gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
- input_token.length = decoded_server_auth_token_.length();
- input_token.value = (input_token.length > 0)
- ? const_cast<char*>(decoded_server_auth_token_.data())
- : nullptr;
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
- ScopedBuffer scoped_output_token(&output_token, library_);
- int rv = GetNextSecurityToken(spn, channel_bindings, &input_token,
- &output_token, net_log);
- if (rv != OK)
- return rv;
- // Base64 encode data in output buffer and prepend the scheme.
- std::string encode_input(static_cast<char*>(output_token.value),
- output_token.length);
- std::string encode_output;
- base::Base64Encode(encode_input, &encode_output);
- *auth_token = "Negotiate " + encode_output;
- return OK;
- }
- namespace {
- // GSSAPI status codes consist of a calling error (essentially, a programmer
- // bug), a routine error (defined by the RFC), and supplementary information,
- // all bitwise-or'ed together in different regions of the 32 bit return value.
- // This means a simple switch on the return codes is not sufficient.
- int MapImportNameStatusToError(OM_uint32 major_status) {
- if (major_status == GSS_S_COMPLETE)
- return OK;
- if (GSS_CALLING_ERROR(major_status) != 0)
- return ERR_UNEXPECTED;
- OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
- switch (routine_error) {
- case GSS_S_FAILURE:
- // Looking at the MIT Kerberos implementation, this typically is returned
- // when memory allocation fails. However, the API does not guarantee
- // that this is the case, so using ERR_UNEXPECTED rather than
- // ERR_OUT_OF_MEMORY.
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_BAD_NAME:
- case GSS_S_BAD_NAMETYPE:
- return ERR_MALFORMED_IDENTITY;
- case GSS_S_DEFECTIVE_TOKEN:
- // Not mentioned in the API, but part of code.
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_BAD_MECH:
- return ERR_UNSUPPORTED_AUTH_SCHEME;
- default:
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
- }
- }
- int MapInitSecContextStatusToError(OM_uint32 major_status) {
- // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
- // other code just checks if major_status is equivalent to it to indicate
- // that there are no other errors included.
- if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
- return OK;
- if (GSS_CALLING_ERROR(major_status) != 0)
- return ERR_UNEXPECTED;
- OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
- switch (routine_status) {
- case GSS_S_DEFECTIVE_TOKEN:
- return ERR_INVALID_RESPONSE;
- case GSS_S_DEFECTIVE_CREDENTIAL:
- // Not expected since this implementation uses the default credential.
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_BAD_SIG:
- // Probably won't happen, but it's a bad response.
- return ERR_INVALID_RESPONSE;
- case GSS_S_NO_CRED:
- return ERR_INVALID_AUTH_CREDENTIALS;
- case GSS_S_CREDENTIALS_EXPIRED:
- return ERR_INVALID_AUTH_CREDENTIALS;
- case GSS_S_BAD_BINDINGS:
- // This only happens with mutual authentication.
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_NO_CONTEXT:
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_BAD_NAMETYPE:
- return ERR_UNSUPPORTED_AUTH_SCHEME;
- case GSS_S_BAD_NAME:
- return ERR_UNSUPPORTED_AUTH_SCHEME;
- case GSS_S_BAD_MECH:
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
- case GSS_S_FAILURE:
- // This should be an "Unexpected Security Status" according to the
- // GSSAPI documentation, but it's typically used to indicate that
- // credentials are not correctly set up on a user machine, such
- // as a missing credential cache or hitting this after calling
- // kdestroy.
- // TODO(cbentzel): Use minor code for even better mapping?
- return ERR_MISSING_AUTH_CREDENTIALS;
- default:
- if (routine_status != 0)
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
- break;
- }
- OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
- // Replays could indicate an attack.
- if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
- GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
- return ERR_INVALID_RESPONSE;
- // At this point, every documented status has been checked.
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
- }
- base::Value ImportNameErrorParams(GSSAPILibrary* library,
- base::StringPiece spn,
- OM_uint32 major_status,
- OM_uint32 minor_status) {
- base::Value::Dict params;
- params.Set("spn", spn);
- if (major_status != GSS_S_COMPLETE)
- params.Set("status", GetGssStatusValue(library, "import_name", major_status,
- minor_status));
- return base::Value(std::move(params));
- }
- base::Value InitSecContextErrorParams(GSSAPILibrary* library,
- gss_ctx_id_t context,
- OM_uint32 major_status,
- OM_uint32 minor_status) {
- base::Value::Dict params;
- if (major_status != GSS_S_COMPLETE)
- params.Set("status", GetGssStatusValue(library, "gss_init_sec_context",
- major_status, minor_status));
- if (context != GSS_C_NO_CONTEXT)
- params.Set("context", GetContextStateAsValue(library, context));
- return base::Value(std::move(params));
- }
- } // anonymous namespace
- int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
- const std::string& channel_bindings,
- gss_buffer_t in_token,
- gss_buffer_t out_token,
- const NetLogWithSource& net_log) {
- // GSSAPI header files, to this day, require OIDs passed in as non-const
- // pointers. Rather than const casting, let's just leave this as non-const.
- // Even if the OID pointer is const, the inner |elements| pointer is still
- // non-const.
- static gss_OID_desc kGSS_C_NT_HOSTBASED_SERVICE = {
- 10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
- // Create a name for the principal
- // TODO(cbentzel): Just do this on the first pass?
- std::string spn_principal = spn;
- gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
- spn_buffer.value = const_cast<char*>(spn_principal.c_str());
- spn_buffer.length = spn_principal.size() + 1;
- OM_uint32 minor_status = 0;
- gss_name_t principal_name = GSS_C_NO_NAME;
- OM_uint32 major_status =
- library_->import_name(&minor_status, &spn_buffer,
- &kGSS_C_NT_HOSTBASED_SERVICE, &principal_name);
- net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_IMPORT_NAME, [&] {
- return ImportNameErrorParams(library_, spn, major_status, minor_status);
- });
- int rv = MapImportNameStatusToError(major_status);
- if (rv != OK)
- return rv;
- ScopedName scoped_name(principal_name, library_);
- // Continue creating a security context.
- net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX);
- major_status = library_->init_sec_context(
- &minor_status, GSS_C_NO_CREDENTIAL, scoped_sec_context_.receive(),
- principal_name, gss_oid_, DelegationTypeToFlag(delegation_type_),
- GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, in_token,
- nullptr, // actual_mech_type
- out_token,
- nullptr, // ret flags
- nullptr);
- net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] {
- return InitSecContextErrorParams(library_, scoped_sec_context_.get(),
- major_status, minor_status);
- });
- return MapInitSecContextStatusToError(major_status);
- }
- } // namespace net
|