// Copyright 2010 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/mock_gssapi_library_posix.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace test { struct GssNameMockImpl { std::string name; gss_OID_desc name_type; static GssNameMockImpl* FromGssName(gss_name_t name) { return reinterpret_cast(name); } static gss_name_t ToGssName(GssNameMockImpl* name) { return reinterpret_cast(name); } }; } // namespace test namespace { // gss_OID helpers. // NOTE: gss_OID's do not own the data they point to, which should be static. void ClearOid(gss_OID dest) { if (!dest) return; dest->length = 0; dest->elements = nullptr; } void SetOid(gss_OID dest, const void* src, size_t length) { if (!dest) return; ClearOid(dest); if (!src) return; dest->length = length; if (length) dest->elements = const_cast(src); } void CopyOid(gss_OID dest, const gss_OID_desc* src) { if (!dest) return; ClearOid(dest); if (!src) return; SetOid(dest, src->elements, src->length); } // gss_buffer_t helpers. void ClearBuffer(gss_buffer_t dest) { if (!dest) return; dest->length = 0; if (dest->value) { delete[] reinterpret_cast(dest->value); dest->value = nullptr; } } void SetBuffer(gss_buffer_t dest, const void* src, size_t length) { if (!dest) return; ClearBuffer(dest); if (!src) return; dest->length = length; if (length) { dest->value = new char[length]; memcpy(dest->value, src, length); } } void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) { if (!dest) return; ClearBuffer(dest); if (!src) return; SetBuffer(dest, src->value, src->length); } std::string BufferToString(const gss_buffer_t src) { std::string dest; if (!src) return dest; const char* string = reinterpret_cast(src->value); dest.assign(string, src->length); return dest; } void BufferFromString(const std::string& src, gss_buffer_t dest) { if (!dest) return; SetBuffer(dest, src.c_str(), src.length()); } // gss_name_t helpers. void ClearName(gss_name_t dest) { if (!dest) return; auto* name = test::GssNameMockImpl::FromGssName(dest); name->name.clear(); ClearOid(&name->name_type); } void SetName(gss_name_t dest, const void* src, size_t length) { if (!dest) return; ClearName(dest); if (!src) return; auto* name = test::GssNameMockImpl::FromGssName(dest); name->name.assign(reinterpret_cast(src), length); } gss_name_t NameFromString(const std::string& src) { gss_name_t dest = test::GssNameMockImpl::ToGssName( new test::GssNameMockImpl{"", {0, nullptr}}); SetName(dest, src.c_str(), src.length()); return dest; } } // namespace namespace test { GssContextMockImpl::GssContextMockImpl() : lifetime_rec(0), ctx_flags(0), locally_initiated(0), open(0) { ClearOid(&mech_type); } GssContextMockImpl::GssContextMockImpl(const GssContextMockImpl& other) : src_name(other.src_name), targ_name(other.targ_name), lifetime_rec(other.lifetime_rec), ctx_flags(other.ctx_flags), locally_initiated(other.locally_initiated), open(other.open) { CopyOid(&mech_type, &other.mech_type); } GssContextMockImpl::GssContextMockImpl(const char* src_name_in, const char* targ_name_in, OM_uint32 lifetime_rec_in, const gss_OID_desc& mech_type_in, OM_uint32 ctx_flags_in, int locally_initiated_in, int open_in) : src_name(src_name_in ? src_name_in : ""), targ_name(targ_name_in ? targ_name_in : ""), lifetime_rec(lifetime_rec_in), ctx_flags(ctx_flags_in), locally_initiated(locally_initiated_in), open(open_in) { CopyOid(&mech_type, &mech_type_in); } GssContextMockImpl::~GssContextMockImpl() { ClearOid(&mech_type); } void GssContextMockImpl::Assign( const GssContextMockImpl& other) { if (&other == this) return; src_name = other.src_name; targ_name = other.targ_name; lifetime_rec = other.lifetime_rec; CopyOid(&mech_type, &other.mech_type); ctx_flags = other.ctx_flags; locally_initiated = other.locally_initiated; open = other.open; } MockGSSAPILibrary::SecurityContextQuery::SecurityContextQuery() : expected_package(), response_code(0), minor_response_code(0), context_info() { expected_input_token.length = 0; expected_input_token.value = nullptr; output_token.length = 0; output_token.value = nullptr; } MockGSSAPILibrary::SecurityContextQuery::SecurityContextQuery( const std::string& in_expected_package, OM_uint32 in_response_code, OM_uint32 in_minor_response_code, const test::GssContextMockImpl& in_context_info, const char* in_expected_input_token, const char* in_output_token) : expected_package(in_expected_package), response_code(in_response_code), minor_response_code(in_minor_response_code), context_info(in_context_info) { if (in_expected_input_token) { expected_input_token.length = strlen(in_expected_input_token); expected_input_token.value = const_cast(in_expected_input_token); } else { expected_input_token.length = 0; expected_input_token.value = nullptr; } if (in_output_token) { output_token.length = strlen(in_output_token); output_token.value = const_cast(in_output_token); } else { output_token.length = 0; output_token.value = nullptr; } } MockGSSAPILibrary::SecurityContextQuery::SecurityContextQuery( const SecurityContextQuery& other) = default; MockGSSAPILibrary::SecurityContextQuery::~SecurityContextQuery() = default; MockGSSAPILibrary::MockGSSAPILibrary() = default; MockGSSAPILibrary::~MockGSSAPILibrary() = default; void MockGSSAPILibrary::ExpectSecurityContext( const std::string& expected_package, OM_uint32 response_code, OM_uint32 minor_response_code, const GssContextMockImpl& context_info, const gss_buffer_desc& expected_input_token, const gss_buffer_desc& output_token) { SecurityContextQuery security_query; security_query.expected_package = expected_package; security_query.response_code = response_code; security_query.minor_response_code = minor_response_code; security_query.context_info.Assign(context_info); security_query.expected_input_token = expected_input_token; security_query.output_token = output_token; expected_security_queries_.push_back(security_query); } bool MockGSSAPILibrary::Init(const NetLogWithSource&) { return true; } // These methods match the ones in the GSSAPI library. OM_uint32 MockGSSAPILibrary::import_name( OM_uint32* minor_status, const gss_buffer_t input_name_buffer, const gss_OID input_name_type, gss_name_t* output_name) { if (minor_status) *minor_status = 0; if (!output_name) return GSS_S_BAD_NAME; if (!input_name_buffer) return GSS_S_CALL_BAD_STRUCTURE; if (!input_name_type) return GSS_S_BAD_NAMETYPE; GssNameMockImpl* output = new GssNameMockImpl; if (output == nullptr) return GSS_S_FAILURE; output->name_type.length = 0; output->name_type.elements = nullptr; // Save the data. output->name = BufferToString(input_name_buffer); CopyOid(&output->name_type, input_name_type); *output_name = test::GssNameMockImpl::ToGssName(output); return GSS_S_COMPLETE; } OM_uint32 MockGSSAPILibrary::release_name( OM_uint32* minor_status, gss_name_t* input_name) { if (minor_status) *minor_status = 0; if (!input_name) return GSS_S_BAD_NAME; if (!*input_name) return GSS_S_COMPLETE; GssNameMockImpl* name = GssNameMockImpl::FromGssName(*input_name); ClearName(*input_name); delete name; *input_name = GSS_C_NO_NAME; return GSS_S_COMPLETE; } OM_uint32 MockGSSAPILibrary::release_buffer( OM_uint32* minor_status, gss_buffer_t buffer) { if (minor_status) *minor_status = 0; if (!buffer) return GSS_S_BAD_NAME; ClearBuffer(buffer); return GSS_S_COMPLETE; } OM_uint32 MockGSSAPILibrary::display_name( OM_uint32* minor_status, const gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID* output_name_type) { if (minor_status) *minor_status = 0; if (!input_name) return GSS_S_BAD_NAME; if (!output_name_buffer) return GSS_S_CALL_BAD_STRUCTURE; if (!output_name_type) return GSS_S_CALL_BAD_STRUCTURE; GssNameMockImpl* internal_name = GssNameMockImpl::FromGssName(input_name); std::string name = internal_name->name; BufferFromString(name, output_name_buffer); if (output_name_type) { *output_name_type = internal_name ? &internal_name->name_type : GSS_C_NO_OID; } return GSS_S_COMPLETE; } OM_uint32 MockGSSAPILibrary::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) { OM_uint32 rv = GSS_S_COMPLETE; *minor_status = 0; std::string msg; switch (static_cast(status_value)) { case DisplayStatusSpecials::MultiLine: msg = base::StringPrintf("Line %u for status %u", ++*message_context, status_value); if (*message_context >= 5u) *message_context = 0u; break; case DisplayStatusSpecials::InfiniteLines: msg = base::StringPrintf("Line %u for status %u", ++*message_context, status_value); break; case DisplayStatusSpecials::Fail: rv = GSS_S_BAD_MECH; msg = "You should not see this"; EXPECT_EQ(*message_context, 0u); break; case DisplayStatusSpecials::EmptyMessage: EXPECT_EQ(*message_context, 0u); break; case DisplayStatusSpecials::UninitalizedBuffer: EXPECT_EQ(*message_context, 0u); return GSS_S_COMPLETE; case DisplayStatusSpecials::InvalidUtf8: msg = "\xff\xff\xff"; EXPECT_EQ(*message_context, 0u); break; default: msg = base::StringPrintf("Value: %u, Type %u", status_value, status_type); EXPECT_EQ(*message_context, 0u); } BufferFromString(msg, status_string); return rv; } OM_uint32 MockGSSAPILibrary::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) { if (minor_status) *minor_status = 0; if (!context_handle) return GSS_S_CALL_BAD_STRUCTURE; GssContextMockImpl** internal_context_handle = reinterpret_cast(context_handle); // Create it if necessary. if (!*internal_context_handle) { *internal_context_handle = new GssContextMockImpl; } EXPECT_TRUE(*internal_context_handle); GssContextMockImpl& context = **internal_context_handle; if (expected_security_queries_.empty()) { return GSS_S_UNAVAILABLE; } SecurityContextQuery security_query = expected_security_queries_.front(); expected_security_queries_.pop_front(); EXPECT_EQ(std::string("Negotiate"), security_query.expected_package); OM_uint32 major_status = security_query.response_code; if (minor_status) *minor_status = security_query.minor_response_code; context.src_name = security_query.context_info.src_name; context.targ_name = security_query.context_info.targ_name; context.lifetime_rec = security_query.context_info.lifetime_rec; CopyOid(&context.mech_type, &security_query.context_info.mech_type); context.ctx_flags = security_query.context_info.ctx_flags; context.locally_initiated = security_query.context_info.locally_initiated; context.open = security_query.context_info.open; if (!input_token) { EXPECT_FALSE(security_query.expected_input_token.length); } else { EXPECT_EQ(input_token->length, security_query.expected_input_token.length); if (input_token->length) { EXPECT_EQ(0, memcmp(input_token->value, security_query.expected_input_token.value, input_token->length)); } } CopyBuffer(output_token, &security_query.output_token); if (actual_mech_type) CopyOid(*actual_mech_type, mech_type); if (ret_flags) *ret_flags = req_flags; return major_status; } OM_uint32 MockGSSAPILibrary::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) { if (minor_status) *minor_status = 0; ADD_FAILURE(); return GSS_S_UNAVAILABLE; } OM_uint32 MockGSSAPILibrary::delete_sec_context( OM_uint32* minor_status, gss_ctx_id_t* context_handle, gss_buffer_t output_token) { if (minor_status) *minor_status = 0; if (!context_handle) return GSS_S_CALL_BAD_STRUCTURE; GssContextMockImpl** internal_context_handle = reinterpret_cast(context_handle); if (*internal_context_handle) { delete *internal_context_handle; *internal_context_handle = nullptr; } return GSS_S_COMPLETE; } OM_uint32 MockGSSAPILibrary::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) { if (minor_status) *minor_status = 0; if (!context_handle) return GSS_S_CALL_BAD_STRUCTURE; GssContextMockImpl* internal_context_ptr = reinterpret_cast(context_handle); GssContextMockImpl& context = *internal_context_ptr; if (src_name) *src_name = NameFromString(context.src_name); if (targ_name) *targ_name = NameFromString(context.targ_name); if (lifetime_rec) *lifetime_rec = context.lifetime_rec; if (mech_type) CopyOid(*mech_type, &context.mech_type); if (ctx_flags) *ctx_flags = context.ctx_flags; if (locally_initiated) *locally_initiated = context.locally_initiated; if (open) *open = context.open; return GSS_S_COMPLETE; } const std::string& MockGSSAPILibrary::GetLibraryNameForTesting() { return library_name_; } } // namespace test } // namespace net