Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2625)

Unified Diff: net/http/http_auth_gssapi_posix.cc

Issue 2268002: Implemented mid-level Negotiate protocol for Posix. (Closed)
Patch Set: Merged with trunk. Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http/http_auth_gssapi_posix.h ('k') | net/http/http_auth_gssapi_posix_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/http_auth_gssapi_posix.cc
diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7c428e6456cea71826c2bd51afcfa7424104efe3
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix.cc
@@ -0,0 +1,499 @@
+// 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 <string>
+
+#include "base/base64.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/http/http_auth.h"
+
+namespace net {
+
+gssapi::gss_OID_desc GSS_C_NT_USER_NAME = {
+ 10,
+ (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"
+};
+gssapi::gss_OID_desc GSS_C_NT_MACHINE_UID_NAME = {
+ 10,
+ (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"
+};
+gssapi::gss_OID_desc GSS_C_NT_STRING_UID_NAME = {
+ 10,
+ (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"
+};
+gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X = {
+ 6,
+ (void *) "\x2b\x06\x01\x05\x06\x02"
+};
+gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE = {
+ 10,
+ (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"
+};
+gssapi::gss_OID_desc GSS_C_NT_ANONYMOUS = {
+ 6,
+ (void *) "\x2b\x06\01\x05\x06\x03"
+};
+gssapi::gss_OID_desc GSS_C_NT_EXPORT_NAME = {
+ 6,
+ (void *) "\x2b\x06\x01\x05\x06\x04"
+};
+
+GSSAPISharedLibrary::GSSAPISharedLibrary()
+ : initialized_(false),
+ cached_initialize_value_(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_) {
+ cached_initialize_value_ = InitImpl();
+ initialized_ = true;
+ }
+ return cached_initialize_value_;
+}
+
+bool GSSAPISharedLibrary::InitImpl() {
+ DCHECK(!initialized_);
+ gssapi_library_ = LoadSharedObject();
+ if (gssapi_library_ == NULL)
+ return false;
+ if (!BindMethods())
+ return false;
+ return true;
+}
+
+const char* GSSAPISharedLibrary::kLibraryNames[] = {
+ "libgssapi_krb5.so.2",
+ "libgssapi.so.4",
+ "libgssapi.so.1",
+};
+
+base::NativeLibrary GSSAPISharedLibrary::LoadSharedObject() {
+ for (unsigned int i = 0; i < arraysize(kLibraryNames); ++i) {
+ FilePath file_path(kLibraryNames[i]);
+ 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) {
+ string16 error_message = base::GetLibraryErrorMessage();
+ LOG(WARNING) << "dlsym failed when finding function pointer for " << name
+ << ": " << error_message;
+ 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(cached_initialize_value_);
+ if (!import_name_)
+ return gssapi::OM_uint32(-1);
+ 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(cached_initialize_value_);
+ if (!release_name_)
+ return gssapi::OM_uint32(-1);
+ return release_name_(minor_status, input_name);
+}
+
+gssapi::OM_uint32 GSSAPISharedLibrary::release_buffer(
+ gssapi::OM_uint32* minor_status,
+ gssapi::gss_buffer_t buffer) {
+ DCHECK(cached_initialize_value_);
+ if (!release_buffer_)
+ return gssapi::OM_uint32(-1);
+ 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(cached_initialize_value_);
+ if (!display_status_)
+ return gssapi::OM_uint32(-1);
+ status_string->length = 0;
+ status_string->value = NULL;
+ 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(cached_initialize_value_);
+ if (!init_sec_context_)
+ return gssapi::OM_uint32(-1);
+ 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,
+ reinterpret_cast<gssapi::gss_buffer_t>(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(cached_initialize_value_);
+ if (!wrap_size_limit_)
+ return gssapi::OM_uint32(-1);
+ 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("%x %x", major_status, minor_status);
+}
+
+static const int kMaxDisplayIterations = 8;
+
+std::string DisplayCode(GSSAPILibrary* gssapi_lib,
+ gssapi::OM_uint32 status,
+ gssapi::OM_uint32 status_code_type) {
+ // 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(const std::string& scheme,
+ gssapi::gss_OID gss_oid,
+ GSSAPILibrary* library)
+ : 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;
+}
+
+
+static uint8 kHostbasedServiceOidElements[] = {
+ 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x01, 0x04};
+static gssapi::gss_OID_desc kHostbasedServiceOidDesc = {
+ arraysize(kHostbasedServiceOidElements),
+ static_cast<void *>(&kHostbasedServiceOidElements)};
+
+int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
+ const std::wstring* password,
+ const std::wstring& origin,
+ 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 = static_cast<void *>(const_cast<char *>(
+ decoded_server_auth_token_.c_str()));
+ gssapi::gss_buffer_desc output_token1 = GSS_C_EMPTY_BUFFER;
+ gssapi::gss_buffer_t output_token = &output_token1;
+ int rv = GetNextSecurityToken(origin, &input_token, &output_token);
+ if (rv != OK)
+ return rv;
+ ScopedBuffer sb_out_token(output_token, library_);
+
+ // 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);
+ if (!ok)
+ return rv;
+ *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_ = L"";
+ password_ = L"";
+ if (username) {
+ username_ = *username;
+ password_ = *password;
+ }
+ return OK;
+}
+
+int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& origin,
+ gssapi::gss_buffer_t in_token,
+ gssapi::gss_buffer_t* out_token) {
+ // Create a name for the principal
+ // TODO(cbentzel): Should this be username@origin? What about domain?
+ // TODO(cbentzel): Just do this on the first pass?
+ const GURL orig(WideToASCII(origin));
+ std::string origin_principal = GetHostAndPort(orig);
+ gssapi::gss_buffer_desc origin_buffer = GSS_C_EMPTY_BUFFER;
+ origin_buffer.value = static_cast<void *>(const_cast<char *>(
+ origin_principal.c_str()));
+ origin_buffer.length = origin_principal.size() + 1;
+ gssapi::OM_uint32 minor_status_import_name = 0;
+ gssapi::gss_name_t principal_name;
+ gssapi::OM_uint32 major_status_import_name = library_->import_name(
+ &minor_status_import_name,
+ &origin_buffer,
+ &kHostbasedServiceOidDesc,
+ &principal_name);
+ if (major_status_import_name != GSS_S_COMPLETE) {
+ LOG(WARNING) << "Problem importing name. "
+ << DisplayExtendedStatus(library_,
+ major_status_import_name,
+ minor_status_import_name);
+ return ERR_UNEXPECTED;
+ }
+ ScopedName scoped_name(principal_name, library_);
+
+ // Create a security context.
+ gssapi::OM_uint32 minor_status_sec_context;
+ gssapi::OM_uint32 req_flags = 0;
+ gssapi::OM_uint32 major_status_sec_context = library_->init_sec_context(
+ &minor_status_sec_context,
+ 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_sec_context != GSS_S_COMPLETE &&
+ major_status_sec_context != GSS_S_CONTINUE_NEEDED) {
+ LOG(WARNING) << "Problem initializing context. "
+ << DisplayExtendedStatus(library_,
+ major_status_sec_context,
+ minor_status_sec_context);
+ return ERR_UNEXPECTED;
+ }
+
+ return (major_status_sec_context != GSS_S_COMPLETE) ? ERR_IO_PENDING : OK;
+}
+
+} // namespace net
+
« no previous file with comments | « net/http/http_auth_gssapi_posix.h ('k') | net/http/http_auth_gssapi_posix_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698