mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-02 02:06:09 +03:00
373 lines
14 KiB
C
373 lines
14 KiB
C
|
// Copyright 2012 The Chromium Authors
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#ifndef NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
|
||
|
#define NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
|
||
|
|
||
|
#include <string>
|
||
|
|
||
|
#include "base/gtest_prod_util.h"
|
||
|
#include "base/memory/raw_ptr.h"
|
||
|
#include "base/native_library.h"
|
||
|
#include "base/strings/string_piece_forward.h"
|
||
|
#include "base/values.h"
|
||
|
#include "build/build_config.h"
|
||
|
#include "net/base/completion_once_callback.h"
|
||
|
#include "net/base/net_export.h"
|
||
|
#include "net/http/http_auth.h"
|
||
|
#include "net/http/http_auth_mechanism.h"
|
||
|
|
||
|
#if BUILDFLAG(IS_APPLE)
|
||
|
#include <GSS/gssapi.h>
|
||
|
#elif BUILDFLAG(IS_FREEBSD)
|
||
|
#include <gssapi/gssapi.h>
|
||
|
#else
|
||
|
#include <gssapi.h>
|
||
|
#endif
|
||
|
|
||
|
namespace net {
|
||
|
|
||
|
class HttpAuthChallengeTokenizer;
|
||
|
|
||
|
// Mechanism OID for GSSAPI. We always use SPNEGO.
|
||
|
NET_EXPORT_PRIVATE extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC;
|
||
|
|
||
|
// GSSAPILibrary is introduced so unit tests can mock the calls to the GSSAPI
|
||
|
// library. The default implementation attempts to load one of the standard
|
||
|
// GSSAPI library implementations, then simply passes the arguments on to
|
||
|
// that implementation.
|
||
|
class NET_EXPORT_PRIVATE GSSAPILibrary {
|
||
|
public:
|
||
|
virtual ~GSSAPILibrary() = default;
|
||
|
|
||
|
// Initializes the library, including any necessary dynamic libraries.
|
||
|
// This is done separately from construction (which happens at startup time)
|
||
|
// in order to delay work until the class is actually needed.
|
||
|
virtual bool Init(const NetLogWithSource& net_log) = 0;
|
||
|
|
||
|
// These methods match the ones in the GSSAPI library.
|
||
|
virtual OM_uint32 import_name(
|
||
|
OM_uint32* minor_status,
|
||
|
const gss_buffer_t input_name_buffer,
|
||
|
const gss_OID input_name_type,
|
||
|
gss_name_t* output_name) = 0;
|
||
|
virtual OM_uint32 release_name(
|
||
|
OM_uint32* minor_status,
|
||
|
gss_name_t* input_name) = 0;
|
||
|
virtual OM_uint32 release_buffer(
|
||
|
OM_uint32* minor_status,
|
||
|
gss_buffer_t buffer) = 0;
|
||
|
virtual OM_uint32 display_name(
|
||
|
OM_uint32* minor_status,
|
||
|
const gss_name_t input_name,
|
||
|
gss_buffer_t output_name_buffer,
|
||
|
gss_OID* output_name_type) = 0;
|
||
|
virtual OM_uint32 display_status(
|
||
|
OM_uint32* minor_status,
|
||
|
OM_uint32 status_value,
|
||
|
int status_type,
|
||
|
const gss_OID mech_type,
|
||
|
OM_uint32* message_contex,
|
||
|
gss_buffer_t status_string) = 0;
|
||
|
virtual OM_uint32 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) = 0;
|
||
|
virtual OM_uint32 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) = 0;
|
||
|
virtual OM_uint32 delete_sec_context(
|
||
|
OM_uint32* minor_status,
|
||
|
gss_ctx_id_t* context_handle,
|
||
|
gss_buffer_t output_token) = 0;
|
||
|
virtual OM_uint32 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) = 0;
|
||
|
virtual const std::string& GetLibraryNameForTesting() = 0;
|
||
|
};
|
||
|
|
||
|
// GSSAPISharedLibrary class is defined here so that unit tests can access it.
|
||
|
class NET_EXPORT_PRIVATE GSSAPISharedLibrary : public GSSAPILibrary {
|
||
|
public:
|
||
|
// If |gssapi_library_name| is empty, hard-coded default library names are
|
||
|
// used.
|
||
|
explicit GSSAPISharedLibrary(const std::string& gssapi_library_name);
|
||
|
~GSSAPISharedLibrary() override;
|
||
|
|
||
|
// GSSAPILibrary methods:
|
||
|
bool Init(const NetLogWithSource& net_log) override;
|
||
|
OM_uint32 import_name(OM_uint32* minor_status,
|
||
|
const gss_buffer_t input_name_buffer,
|
||
|
const gss_OID input_name_type,
|
||
|
gss_name_t* output_name) override;
|
||
|
OM_uint32 release_name(OM_uint32* minor_status,
|
||
|
gss_name_t* input_name) override;
|
||
|
OM_uint32 release_buffer(OM_uint32* minor_status,
|
||
|
gss_buffer_t buffer) override;
|
||
|
OM_uint32 display_name(OM_uint32* minor_status,
|
||
|
const gss_name_t input_name,
|
||
|
gss_buffer_t output_name_buffer,
|
||
|
gss_OID* output_name_type) override;
|
||
|
OM_uint32 display_status(OM_uint32* minor_status,
|
||
|
OM_uint32 status_value,
|
||
|
int status_type,
|
||
|
const gss_OID mech_type,
|
||
|
OM_uint32* message_contex,
|
||
|
gss_buffer_t status_string) override;
|
||
|
OM_uint32 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) override;
|
||
|
OM_uint32 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) override;
|
||
|
OM_uint32 delete_sec_context(OM_uint32* minor_status,
|
||
|
gss_ctx_id_t* context_handle,
|
||
|
gss_buffer_t output_token) override;
|
||
|
OM_uint32 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) override;
|
||
|
const std::string& GetLibraryNameForTesting() override;
|
||
|
|
||
|
private:
|
||
|
FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup);
|
||
|
|
||
|
bool InitImpl(const NetLogWithSource& net_log);
|
||
|
// Finds a usable dynamic library for GSSAPI and loads it. The criteria are:
|
||
|
// 1. The library must exist.
|
||
|
// 2. The library must export the functions we need.
|
||
|
base::NativeLibrary LoadSharedLibrary(const NetLogWithSource& net_log);
|
||
|
bool BindMethods(base::NativeLibrary lib,
|
||
|
base::StringPiece library_name,
|
||
|
const NetLogWithSource& net_log);
|
||
|
|
||
|
bool initialized_ = false;
|
||
|
|
||
|
std::string gssapi_library_name_;
|
||
|
// Need some way to invalidate the library.
|
||
|
base::NativeLibrary gssapi_library_ = nullptr;
|
||
|
|
||
|
// Function pointers
|
||
|
decltype(&gss_import_name) import_name_ = nullptr;
|
||
|
decltype(&gss_release_name) release_name_ = nullptr;
|
||
|
decltype(&gss_release_buffer) release_buffer_ = nullptr;
|
||
|
decltype(&gss_display_name) display_name_ = nullptr;
|
||
|
decltype(&gss_display_status) display_status_ = nullptr;
|
||
|
decltype(&gss_init_sec_context) init_sec_context_ = nullptr;
|
||
|
decltype(&gss_wrap_size_limit) wrap_size_limit_ = nullptr;
|
||
|
decltype(&gss_delete_sec_context) delete_sec_context_ = nullptr;
|
||
|
decltype(&gss_inquire_context) inquire_context_ = nullptr;
|
||
|
};
|
||
|
|
||
|
// ScopedSecurityContext releases a gss_ctx_id_t when it goes out of
|
||
|
// scope.
|
||
|
class ScopedSecurityContext {
|
||
|
public:
|
||
|
explicit ScopedSecurityContext(GSSAPILibrary* gssapi_lib);
|
||
|
|
||
|
ScopedSecurityContext(const ScopedSecurityContext&) = delete;
|
||
|
ScopedSecurityContext& operator=(const ScopedSecurityContext&) = delete;
|
||
|
|
||
|
~ScopedSecurityContext();
|
||
|
|
||
|
gss_ctx_id_t get() const { return security_context_; }
|
||
|
gss_ctx_id_t* receive() { return &security_context_; }
|
||
|
|
||
|
private:
|
||
|
gss_ctx_id_t security_context_ = GSS_C_NO_CONTEXT;
|
||
|
raw_ptr<GSSAPILibrary> gssapi_lib_;
|
||
|
};
|
||
|
|
||
|
|
||
|
// TODO(ahendrickson): Share code with HttpAuthSSPI.
|
||
|
class NET_EXPORT_PRIVATE HttpAuthGSSAPI : public HttpAuthMechanism {
|
||
|
public:
|
||
|
HttpAuthGSSAPI(GSSAPILibrary* library,
|
||
|
const gss_OID gss_oid);
|
||
|
~HttpAuthGSSAPI() override;
|
||
|
|
||
|
// HttpAuthMechanism implementation:
|
||
|
bool Init(const NetLogWithSource& net_log) override;
|
||
|
bool NeedsIdentity() const override;
|
||
|
bool AllowsExplicitCredentials() const override;
|
||
|
HttpAuth::AuthorizationResult ParseChallenge(
|
||
|
HttpAuthChallengeTokenizer* tok) override;
|
||
|
int GenerateAuthToken(const AuthCredentials* credentials,
|
||
|
const std::string& spn,
|
||
|
const std::string& channel_bindings,
|
||
|
std::string* auth_token,
|
||
|
const NetLogWithSource& net_log,
|
||
|
CompletionOnceCallback callback) override;
|
||
|
void SetDelegation(HttpAuth::DelegationType delegation_type) override;
|
||
|
|
||
|
private:
|
||
|
int GetNextSecurityToken(const std::string& spn,
|
||
|
const std::string& channel_bindings,
|
||
|
gss_buffer_t in_token,
|
||
|
gss_buffer_t out_token,
|
||
|
const NetLogWithSource& net_log);
|
||
|
|
||
|
gss_OID gss_oid_;
|
||
|
raw_ptr<GSSAPILibrary> library_;
|
||
|
std::string decoded_server_auth_token_;
|
||
|
ScopedSecurityContext scoped_sec_context_;
|
||
|
HttpAuth::DelegationType delegation_type_ = HttpAuth::DelegationType::kNone;
|
||
|
};
|
||
|
|
||
|
// Diagnostics
|
||
|
|
||
|
// GetGssStatusCodeValue constructs a base::Value::Dict containing a status code
|
||
|
// and a message.
|
||
|
//
|
||
|
// {
|
||
|
// "status" : <status value as a number>,
|
||
|
// "message": [
|
||
|
// <list of strings explaining what that number means>
|
||
|
// ]
|
||
|
// }
|
||
|
//
|
||
|
// Messages are looked up via gss_display_status() exposed by |gssapi_lib|. The
|
||
|
// type of status code should be indicated by setting |status_code_type| to
|
||
|
// either |GSS_C_MECH_CODE| or |GSS_C_GSS_CODE|.
|
||
|
//
|
||
|
// Mechanism specific codes aren't unique, so the mechanism needs to be
|
||
|
// identified to look up messages if |status_code_type| is |GSS_C_MECH_CODE|.
|
||
|
// Since no mechanism OIDs are passed in, mechanism specific status codes will
|
||
|
// likely not have messages.
|
||
|
NET_EXPORT_PRIVATE base::Value::Dict GetGssStatusCodeValue(
|
||
|
GSSAPILibrary* gssapi_lib,
|
||
|
OM_uint32 status,
|
||
|
OM_uint32 status_code_type);
|
||
|
|
||
|
// Given major and minor GSSAPI status codes, returns a base::Value::Dict
|
||
|
// encapsulating the codes as well as their meanings as expanded via
|
||
|
// gss_display_status().
|
||
|
//
|
||
|
// The base::Value::Dict has the following structure:
|
||
|
// {
|
||
|
// "function": <name of GSSAPI function that returned the error>
|
||
|
// "major_status": {
|
||
|
// "status" : <status value as a number>,
|
||
|
// "message": [
|
||
|
// <list of strings hopefully explaining what that number means>
|
||
|
// ]
|
||
|
// },
|
||
|
// "minor_status": {
|
||
|
// "status" : <status value as a number>,
|
||
|
// "message": [
|
||
|
// <list of strings hopefully explaining what that number means>
|
||
|
// ]
|
||
|
// }
|
||
|
// }
|
||
|
//
|
||
|
// Passing nullptr to |gssapi_lib| will skip the message lookups. Thus the
|
||
|
// returned value will be missing the "message" fields. The same is true if the
|
||
|
// message lookup failed for some reason, or if the lookups succeeded but
|
||
|
// yielded an empty message.
|
||
|
NET_EXPORT_PRIVATE base::Value::Dict GetGssStatusValue(
|
||
|
GSSAPILibrary* gssapi_lib,
|
||
|
base::StringPiece method,
|
||
|
OM_uint32 major_status,
|
||
|
OM_uint32 minor_status);
|
||
|
|
||
|
// OidToValue returns a base::Value::Dict representing an OID. The structure of
|
||
|
// the value is:
|
||
|
// {
|
||
|
// "oid": <symbolic name of OID if it is known>
|
||
|
// "length": <length in bytes of serialized OID>,
|
||
|
// "bytes": <hexdump of up to 1024 bytes of serialized OID>
|
||
|
// }
|
||
|
NET_EXPORT_PRIVATE base::Value::Dict OidToValue(const gss_OID oid);
|
||
|
|
||
|
// GetDisplayNameValue returns a base::Value::Dict representing a gss_name_t. It
|
||
|
// invokes |gss_display_name()| via |gssapi_lib| to determine the display name
|
||
|
// associated with |gss_name|.
|
||
|
//
|
||
|
// The structure of the returned value is:
|
||
|
// {
|
||
|
// "gss_name": <display name as returned by gss_display_name()>,
|
||
|
// "type": <OID indicating type. See OidToValue() for structure of this
|
||
|
// field>
|
||
|
// }
|
||
|
//
|
||
|
// If the lookup failed, then the structure is:
|
||
|
// {
|
||
|
// "error": <error. See GetGssStatusValue() for structure.>
|
||
|
// }
|
||
|
//
|
||
|
// Note that |gss_name_t| is platform dependent. If |gss_display_name| fails,
|
||
|
// there's no good value to display in its stead.
|
||
|
NET_EXPORT_PRIVATE base::Value::Dict GetDisplayNameValue(
|
||
|
GSSAPILibrary* gssapi_lib,
|
||
|
const gss_name_t gss_name);
|
||
|
|
||
|
// GetContextStateAsValue returns a base::Value::Dict that describes the state
|
||
|
// of a GSSAPI context. The structure of the value is:
|
||
|
//
|
||
|
// {
|
||
|
// "source": {
|
||
|
// "name": <GSSAPI principal name of source (e.g. the user)>,
|
||
|
// "type": <OID of name type>
|
||
|
// },
|
||
|
// "target": {
|
||
|
// "name": <GSSAPI principal name of target (e.g. the server)>,
|
||
|
// "type": <OID of name type>
|
||
|
// },
|
||
|
// "lifetime": <Lifetime of the negotiated context in seconds.>,
|
||
|
// "mechanism": <OID of negotiated mechanism>,
|
||
|
// "flags": <Context flags. See documentation for gss_inquire_context for
|
||
|
// flag values>
|
||
|
// "open": <True if the context has finished the handshake>
|
||
|
// }
|
||
|
//
|
||
|
// If the inquiry fails, the following is returned:
|
||
|
// {
|
||
|
// "error": <error. See GetGssStatusValue() for structure.>
|
||
|
// }
|
||
|
NET_EXPORT_PRIVATE base::Value::Dict GetContextStateAsValue(
|
||
|
GSSAPILibrary* gssapi_lib,
|
||
|
const gss_ctx_id_t context_handle);
|
||
|
} // namespace net
|
||
|
|
||
|
#endif // NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
|