blob: e492bd99425afb84200a1878598c1d7bf5214adc [file] [log] [blame]
// 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/mock_gssapi_library_posix.h"
#include "base/logging.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<GssNameMockImpl*>(name);
}
static gss_name_t ToGssName(GssNameMockImpl* name) {
return reinterpret_cast<gss_name_t>(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<void*>(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<char*>(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<char*>(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<const char*>(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<char*>(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<char*>(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<DisplayStatusSpecials>(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<test::GssContextMockImpl**>(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<GssContextMockImpl**>(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<GssContextMockImpl*>(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