summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-04 17:58:08 +0000
committerahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-04 17:58:08 +0000
commit8372717950d8e6e9d0ee2b2134e233923b077c30 (patch)
tree1240690a8539cd54eca9f77b2ac0043121f7c9d4 /net/http
parentd227c44279f0a5811e51b40f3d399bf4111c1aaf (diff)
downloadchromium_src-8372717950d8e6e9d0ee2b2134e233923b077c30.zip
chromium_src-8372717950d8e6e9d0ee2b2134e233923b077c30.tar.gz
chromium_src-8372717950d8e6e9d0ee2b2134e233923b077c30.tar.bz2
Preliminary support for GSSAPI (Linux and Mac OS X).
Second CL. BUG=33033. TEST=None. Review URL: http://codereview.chromium.org/1736009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48945 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_auth_gssapi_posix.cc454
-rw-r--r--net/http/http_auth_gssapi_posix.h196
-rw-r--r--net/http/http_auth_gssapi_posix_unittest.cc25
3 files changed, 675 insertions, 0 deletions
diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc
new file mode 100644
index 0000000..7b6faa1
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix.cc
@@ -0,0 +1,454 @@
+// Copyright (c) 2010 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 "base/base64.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+
+namespace {
+
+gssapi::gss_OID_desc LOCAL_GSS_C_NT_HOSTBASED_SERVICE_VAL = {
+ 10,
+ const_cast<char *>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
+};
+
+gssapi::gss_OID LOCAL_GSS_C_NT_HOSTBASED_SERVICE =
+ &LOCAL_GSS_C_NT_HOSTBASED_SERVICE_VAL;
+
+} // namespace
+
+namespace net {
+
+GSSAPISharedLibrary::GSSAPISharedLibrary()
+ : initialized_(false),
+ gssapi_library_(NULL),
+ import_name_(NULL),
+ release_name_(NULL),
+ release_buffer_(NULL),
+ display_status_(NULL),
+ init_sec_context_(NULL),
+ wrap_size_limit_(NULL) {
+}
+
+GSSAPISharedLibrary::~GSSAPISharedLibrary() {
+ if (gssapi_library_) {
+ base::UnloadNativeLibrary(gssapi_library_);
+ gssapi_library_ = NULL;
+ }
+}
+
+bool GSSAPISharedLibrary::Init() {
+ if (!initialized_)
+ InitImpl();
+ return initialized_;
+}
+
+bool GSSAPISharedLibrary::InitImpl() {
+ DCHECK(!initialized_);
+ gssapi_library_ = LoadSharedObject();
+ if (gssapi_library_ == NULL)
+ return false;
+ if (!BindMethods())
+ return false;
+ initialized_ = true;
+ return true;
+}
+
+base::NativeLibrary GSSAPISharedLibrary::LoadSharedObject() {
+ static const char* kLibraryNames[] = {
+#if defined(OS_MACOSX)
+ "libgssapi_krb5.dylib" // MIT Kerberos
+#else
+ "libgssapi_krb5.so.2", // MIT Kerberos
+ "libgssapi.so.4", // Heimdal
+ "libgssapi.so.1" // Heimdal
+#endif
+ };
+ static size_t num_lib_names = arraysize(kLibraryNames);
+
+ for (size_t i = 0; i < num_lib_names; ++i) {
+ const char* library_name = kLibraryNames[i];
+ FilePath file_path(library_name);
+ base::NativeLibrary lib = base::LoadNativeLibrary(file_path);
+ if (lib)
+ return lib;
+ }
+ return NULL;
+}
+
+template <typename T>
+bool FindAndBind(base::NativeLibrary library, const char* name, T* t) {
+ void* func = base::GetFunctionPointerFromNativeLibrary(library, name);
+ if (func == NULL)
+ return false;
+ *t = reinterpret_cast<T>(func);
+ return true;
+}
+
+bool GSSAPISharedLibrary::BindMethods() {
+ DCHECK(gssapi_library_ != NULL);
+ if (!FindAndBind(gssapi_library_, "gss_import_name", &import_name_))
+ return false;
+ if (!FindAndBind(gssapi_library_, "gss_release_name", &release_name_))
+ return false;
+ if (!FindAndBind(gssapi_library_, "gss_release_buffer", &release_buffer_))
+ return false;
+ if (!FindAndBind(gssapi_library_, "gss_display_status", &display_status_))
+ return false;
+ if (!FindAndBind(gssapi_library_, "gss_init_sec_context", &init_sec_context_))
+ return false;
+ if (!FindAndBind(gssapi_library_, "gss_wrap_size_limit", &wrap_size_limit_))
+ return false;
+ return true;
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::import_name(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_buffer_t input_name_buffer,
+ const gssapi::gss_OID input_name_type,
+ gssapi::gss_name_t* output_name) {
+ DCHECK(initialized_);
+ return import_name_(minor_status, input_name_buffer, input_name_type,
+ output_name);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::release_name(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_name_t* input_name) {
+ DCHECK(initialized_);
+ return release_name_(minor_status, input_name);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::release_buffer(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_buffer_t buffer) {
+ DCHECK(initialized_);
+ return release_buffer_(minor_status, buffer);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::display_status(
+ gssapi::OM_uint32* minor_status,
+ gssapi::OM_uint32 status_value,
+ int status_type,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32* message_context,
+ gssapi::gss_buffer_t status_string) {
+ DCHECK(initialized_);
+ return display_status_(minor_status, status_value, status_type, mech_type,
+ message_context, status_string);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::init_sec_context(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_cred_id_t initiator_cred_handle,
+ gssapi::gss_ctx_id_t* context_handle,
+ const gssapi::gss_name_t target_name,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32 req_flags,
+ gssapi::OM_uint32 time_req,
+ const gssapi::gss_channel_bindings_t input_chan_bindings,
+ const gssapi::gss_buffer_t input_token,
+ gssapi::gss_OID* actual_mech_type,
+ gssapi::gss_buffer_t output_token,
+ gssapi::OM_uint32* ret_flags,
+ gssapi::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);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gssapi::gss_qop_t qop_req,
+ gssapi::OM_uint32 req_output_size,
+ gssapi::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);
+}
+
+GSSAPILibrary* GSSAPILibrary::GetDefault() {
+ return Singleton<GSSAPISharedLibrary>::get();
+}
+
+namespace {
+
+std::string DisplayStatus(gssapi::OM_uint32 major_status,
+ gssapi::OM_uint32 minor_status) {
+ if (major_status == GSS_S_COMPLETE)
+ return "OK";
+ return StringPrintf("0x%08x 0x%08x", major_status, minor_status);
+}
+
+std::string DisplayCode(GSSAPILibrary* gssapi_lib,
+ gssapi::OM_uint32 status,
+ gssapi::OM_uint32 status_code_type) {
+ const int kMaxDisplayIterations = 8;
+ // msg_ctx needs to be outside the loop because it is invoked multiple times.
+ gssapi::OM_uint32 msg_ctx = 0;
+ std::string rv = StringPrintf("(0x%08X)", status);
+
+ // This loop should continue iterating until msg_ctx is 0 after the first
+ // iteration. To be cautious and prevent an infinite loop, it stops after
+ // a finite number of iterations as well.
+ for (int i = 0; i < kMaxDisplayIterations; ++i) {
+ gssapi::OM_uint32 min_stat;
+ gssapi::gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
+ gssapi_lib->display_status(&min_stat, status, status_code_type,
+ GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ rv += StringPrintf(" %s", static_cast<char *>(msg.value));
+ gssapi_lib->release_buffer(&min_stat, &msg);
+ if (!msg_ctx)
+ break;
+ }
+ return rv;
+}
+
+std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
+ gssapi::OM_uint32 major_status,
+ gssapi::OM_uint32 minor_status) {
+ if (major_status == GSS_S_COMPLETE)
+ return "OK";
+ std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
+ std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
+ return StringPrintf("Major: %s | Minor: %s", major.c_str(), minor.c_str());
+}
+
+// ScopedName releases a gssapi::gss_name_t when it goes out of scope.
+class ScopedName {
+ public:
+ ScopedName(gssapi::gss_name_t name,
+ GSSAPILibrary* gssapi_lib)
+ : name_(name),
+ gssapi_lib_(gssapi_lib) {
+ DCHECK(gssapi_lib_);
+ }
+
+ ~ScopedName() {
+ if (name_ != GSS_C_NO_NAME) {
+ gssapi::OM_uint32 minor_status = 0;
+ gssapi::OM_uint32 major_status =
+ gssapi_lib_->release_name(&minor_status, &name_);
+ if (major_status != GSS_S_COMPLETE) {
+ LOG(WARNING) << "Problem releasing name. "
+ << DisplayStatus(major_status, minor_status);
+ }
+ name_ = GSS_C_NO_NAME;
+ }
+ }
+
+ private:
+ gssapi::gss_name_t name_;
+ GSSAPILibrary* gssapi_lib_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedName);
+};
+
+// ScopedBuffer releases a gssapi::gss_buffer_t when it goes out of scope.
+class ScopedBuffer {
+ public:
+ ScopedBuffer(gssapi::gss_buffer_t buffer,
+ GSSAPILibrary* gssapi_lib)
+ : buffer_(buffer),
+ gssapi_lib_(gssapi_lib) {
+ DCHECK(gssapi_lib_);
+ }
+
+ ~ScopedBuffer() {
+ if (buffer_ != GSS_C_NO_BUFFER) {
+ gssapi::OM_uint32 minor_status = 0;
+ gssapi::OM_uint32 major_status =
+ gssapi_lib_->release_buffer(&minor_status, buffer_);
+ if (major_status != GSS_S_COMPLETE) {
+ LOG(WARNING) << "Problem releasing buffer. "
+ << DisplayStatus(major_status, minor_status);
+ }
+ buffer_ = GSS_C_NO_BUFFER;
+ }
+ }
+
+ private:
+ gssapi::gss_buffer_t buffer_;
+ GSSAPILibrary* gssapi_lib_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
+};
+
+} // namespace
+
+ HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
+ const std::string& scheme,
+ gssapi::gss_OID gss_oid)
+ : scheme_(scheme),
+ gss_oid_(gss_oid),
+ library_(library),
+ sec_context_(NULL) {
+}
+
+HttpAuthGSSAPI::~HttpAuthGSSAPI() {
+}
+
+bool HttpAuthGSSAPI::NeedsIdentity() const {
+ return decoded_server_auth_token_.empty();
+}
+
+bool HttpAuthGSSAPI::IsFinalRound() const {
+ return !NeedsIdentity();
+}
+
+bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
+ // Verify the challenge's auth-scheme.
+ if (!tok->valid() ||
+ !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
+ return false;
+
+ tok->set_expect_base64_token(true);
+ if (!tok->GetNext()) {
+ decoded_server_auth_token_.clear();
+ return true;
+ }
+
+ std::string encoded_auth_token = tok->value();
+ std::string decoded_auth_token;
+ bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
+ if (!base64_rv) {
+ LOG(ERROR) << "Base64 decoding of auth token failed.";
+ return false;
+ }
+ decoded_server_auth_token_ = decoded_auth_token;
+ return true;
+}
+
+int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
+ const std::wstring* password,
+ const std::wstring& spn,
+ const HttpRequestInfo* request,
+ const ProxyInfo* proxy,
+ std::string* out_credentials) {
+ DCHECK(library_);
+ DCHECK((username == NULL) == (password == NULL));
+
+ library_->Init();
+
+ if (!IsFinalRound()) {
+ int rv = OnFirstRound(username, password);
+ if (rv != OK)
+ return rv;
+ }
+
+ gssapi::gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ input_token.length = decoded_server_auth_token_.length();
+ input_token.value = const_cast<char *>(decoded_server_auth_token_.data());
+ gssapi::gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int rv = GetNextSecurityToken(spn, &input_token, &output_token);
+ 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;
+ bool ok = base::Base64Encode(encode_input, &encode_output);
+ gssapi::OM_uint32 minor_status = 0;
+ library_->release_buffer(&minor_status, &output_token);
+ if (!ok)
+ return ERR_UNEXPECTED;
+ *out_credentials = scheme_ + " " + encode_output;
+ return OK;
+}
+
+int HttpAuthGSSAPI::OnFirstRound(const std::wstring* username,
+ const std::wstring* password) {
+ // TODO(cbentzel): Acquire credentials?
+ DCHECK((username == NULL) == (password == NULL));
+ username_.clear();
+ password_.clear();
+ if (username) {
+ username_ = *username;
+ password_ = *password;
+ }
+ return OK;
+}
+
+int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn,
+ gssapi::gss_buffer_t in_token,
+ gssapi::gss_buffer_t out_token) {
+ // Create a name for the principal
+ // TODO(cbentzel): Should this be username@spn? What about domain?
+ // TODO(cbentzel): Just do this on the first pass?
+ const GURL spn_url(WideToASCII(spn));
+ std::string spn_principal = GetHostAndPort(spn_url);
+ gssapi::gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
+ spn_buffer.value = const_cast<char *>(spn_principal.data());
+ spn_buffer.length = spn_principal.size() + 1;
+ gssapi::OM_uint32 minor_status = 0;
+ gssapi::gss_name_t principal_name;
+ gssapi::OM_uint32 major_status = library_->import_name(
+ &minor_status,
+ &spn_buffer,
+ LOCAL_GSS_C_NT_HOSTBASED_SERVICE,
+ &principal_name);
+ if (major_status != GSS_S_COMPLETE) {
+ LOG(WARNING) << "Problem importing name. "
+ << DisplayExtendedStatus(library_,
+ major_status,
+ minor_status);
+ return ERR_UNEXPECTED;
+ }
+ ScopedName scoped_name(principal_name, library_);
+
+ // Create a security context.
+ gssapi::OM_uint32 req_flags = 0;
+ major_status = library_->init_sec_context(
+ &minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &sec_context_,
+ principal_name,
+ gss_oid_,
+ req_flags,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ in_token,
+ NULL, // actual_mech_type
+ out_token,
+ NULL, // ret flags
+ NULL);
+ if (major_status != GSS_S_COMPLETE &&
+ major_status != GSS_S_CONTINUE_NEEDED) {
+ LOG(WARNING) << "Problem initializing context. "
+ << DisplayExtendedStatus(library_,
+ major_status,
+ minor_status);
+ return ERR_UNEXPECTED;
+ }
+
+ return (major_status != GSS_S_COMPLETE) ? ERR_IO_PENDING : OK;
+}
+
+} // namespace net
+
diff --git a/net/http/http_auth_gssapi_posix.h b/net/http/http_auth_gssapi_posix.h
new file mode 100644
index 0000000..0eacbf3
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix.h
@@ -0,0 +1,196 @@
+// Copyright (c) 2010 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.
+
+#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/native_library.h"
+#include "net/http/http_auth.h"
+
+#define GSS_USE_FUNCTION_POINTERS
+#include "net/third_party/gssapi/gssapi.h"
+
+class GURL;
+
+namespace net {
+
+class HttpRequestInfo;
+class ProxyInfo;
+
+// 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 GSSAPILibrary {
+ public:
+ virtual ~GSSAPILibrary() {}
+
+ // Initializes the library, including any necessary dynamic libraries.
+ virtual bool Init() = 0;
+
+ // These methods match the ones in the GSSAPI library.
+ virtual gssapi::OM_uint32 import_name(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_buffer_t input_name_buffer,
+ const gssapi::gss_OID input_name_type,
+ gssapi::gss_name_t* output_name) = 0;
+ virtual gssapi::OM_uint32 release_name(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_name_t* input_name) = 0;
+ virtual gssapi::OM_uint32 release_buffer(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_buffer_t buffer) = 0;
+ virtual gssapi::OM_uint32 display_status(
+ gssapi::OM_uint32* minor_status,
+ gssapi::OM_uint32 status_value,
+ int status_type,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32* message_contex,
+ gssapi::gss_buffer_t status_string) = 0;
+ virtual gssapi::OM_uint32 init_sec_context(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_cred_id_t initiator_cred_handle,
+ gssapi::gss_ctx_id_t* context_handle,
+ const gssapi::gss_name_t target_name,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32 req_flags,
+ gssapi::OM_uint32 time_req,
+ const gssapi::gss_channel_bindings_t input_chan_bindings,
+ const gssapi::gss_buffer_t input_token,
+ gssapi::gss_OID* actual_mech_type,
+ gssapi::gss_buffer_t output_token,
+ gssapi::OM_uint32* ret_flags,
+ gssapi::OM_uint32* time_rec) = 0;
+ virtual gssapi::OM_uint32 wrap_size_limit(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gssapi::gss_qop_t qop_req,
+ gssapi::OM_uint32 req_output_size,
+ gssapi::OM_uint32* max_input_size) = 0;
+
+ // Get the default GSSPILibrary instance. The object returned is a singleton
+ // instance, and the caller should not delete it.
+ static GSSAPILibrary* GetDefault();
+};
+
+// GSSAPISharedLibrary class is defined here so that unit tests can access it.
+class GSSAPISharedLibrary : public GSSAPILibrary {
+ public:
+ GSSAPISharedLibrary();
+ virtual ~GSSAPISharedLibrary();
+
+ // GSSAPILibrary methods:
+ virtual bool Init();
+ virtual gssapi::OM_uint32 import_name(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_buffer_t input_name_buffer,
+ const gssapi::gss_OID input_name_type,
+ gssapi::gss_name_t* output_name);
+ virtual gssapi::OM_uint32 release_name(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_name_t* input_name);
+ virtual gssapi::OM_uint32 release_buffer(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_buffer_t buffer);
+ virtual gssapi::OM_uint32 display_status(
+ gssapi::OM_uint32* minor_status,
+ gssapi::OM_uint32 status_value,
+ int status_type,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32* message_contex,
+ gssapi::gss_buffer_t status_string);
+ virtual gssapi::OM_uint32 init_sec_context(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_cred_id_t initiator_cred_handle,
+ gssapi::gss_ctx_id_t* context_handle,
+ const gssapi::gss_name_t target_name,
+ const gssapi::gss_OID mech_type,
+ gssapi::OM_uint32 req_flags,
+ gssapi::OM_uint32 time_req,
+ const gssapi::gss_channel_bindings_t input_chan_bindings,
+ const gssapi::gss_buffer_t input_token,
+ gssapi::gss_OID* actual_mech_type,
+ gssapi::gss_buffer_t output_token,
+ gssapi::OM_uint32* ret_flags,
+ gssapi::OM_uint32* time_rec);
+ virtual gssapi::OM_uint32 wrap_size_limit(
+ gssapi::OM_uint32* minor_status,
+ const gssapi::gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gssapi::gss_qop_t qop_req,
+ gssapi::OM_uint32 req_output_size,
+ gssapi::OM_uint32* max_input_size);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup);
+
+ bool InitImpl();
+ static base::NativeLibrary LoadSharedObject();
+ bool BindMethods();
+
+ bool initialized_;
+
+ // Need some way to invalidate the library.
+ base::NativeLibrary gssapi_library_;
+
+ // Function pointers
+ gssapi::gss_import_name_type import_name_;
+ gssapi::gss_release_name_type release_name_;
+ gssapi::gss_release_buffer_type release_buffer_;
+ gssapi::gss_display_status_type display_status_;
+ gssapi::gss_init_sec_context_type init_sec_context_;
+ gssapi::gss_wrap_size_limit_type wrap_size_limit_;
+};
+
+// TODO(cbentzel): Share code with HttpAuthSSPI.
+class HttpAuthGSSAPI {
+ public:
+ HttpAuthGSSAPI(GSSAPILibrary* library,
+ const std::string& scheme,
+ const gssapi::gss_OID gss_oid);
+ ~HttpAuthGSSAPI();
+
+ bool NeedsIdentity() const;
+ bool IsFinalRound() const;
+
+ bool ParseChallenge(HttpAuth::ChallengeTokenizer* tok);
+
+ // Generates an authentication token.
+ // The return value is an error code. If it's not |OK|, the value of
+ // |*auth_token| is unspecified.
+ // |spn| is the Service Principal Name of the server that the token is
+ // being generated for.
+ // If this is the first round of a multiple round scheme, credentials are
+ // obtained using |*username| and |*password|. If |username| and |password|
+ // are NULL, the default credentials are used instead.
+ int GenerateAuthToken(const std::wstring* username,
+ const std::wstring* password,
+ const std::wstring& spn,
+ const HttpRequestInfo* request,
+ const ProxyInfo* proxy,
+ std::string* out_credentials);
+
+ private:
+ int OnFirstRound(const std::wstring* username,
+ const std::wstring* password);
+ int GetNextSecurityToken(const std::wstring& spn,
+ gssapi::gss_buffer_t in_token,
+ gssapi::gss_buffer_t out_token);
+
+ std::string scheme_;
+ std::wstring username_;
+ std::wstring password_;
+ gssapi::gss_OID gss_oid_;
+ GSSAPILibrary* library_;
+ std::string decoded_server_auth_token_;
+ gssapi::gss_ctx_id_t sec_context_;
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
diff --git a/net/http/http_auth_gssapi_posix_unittest.cc b/net/http/http_auth_gssapi_posix_unittest.cc
new file mode 100644
index 0000000..f7a010c
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix_unittest.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2010 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 "base/native_library.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup) {
+ // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
+ // libraries we expect, and also whether or not they have the interface
+ // functions we want.
+ base::NativeLibrary lib = GSSAPISharedLibrary::LoadSharedObject();
+ bool has_library = (lib != NULL);
+ if (has_library) {
+ base::UnloadNativeLibrary(lib);
+ }
+ GSSAPILibrary* gssapi = GSSAPILibrary::GetDefault();
+ EXPECT_EQ(has_library, gssapi->Init());
+}
+
+} // namespace net