Skip to content

Commit

Permalink
Unauthenticated handlers (microsoft#962)
Browse files Browse the repository at this point in the history
  • Loading branch information
jumaffre authored Mar 19, 2020
1 parent 66a70b8 commit 99f7521
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 219 deletions.
19 changes: 19 additions & 0 deletions sphinx/source/schemas/LOG_record_anonymous_params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"id": {
"maximum": 18446744073709551615,
"minimum": 0,
"type": "number"
},
"msg": {
"type": "string"
}
},
"required": [
"id",
"msg"
],
"title": "LOG_record_anonymous/params",
"type": "object"
}
5 changes: 5 additions & 0 deletions sphinx/source/schemas/LOG_record_anonymous_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "LOG_record_anonymous/result",
"type": "boolean"
}
1 change: 1 addition & 0 deletions sphinx/source/users/rpc_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The API can also be retrieved from a running service using the `listMethods`_ an
"LOG_get",
"LOG_get_pub",
"LOG_record",
"LOG_record_anonymous",
"LOG_record_prefix_cert",
"LOG_record_pub",
"getCommit",
Expand Down
23 changes: 23 additions & 0 deletions src/apps/logging/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace ccfapp
static constexpr auto LOG_GET_PUBLIC = "LOG_get_pub";

static constexpr auto LOG_RECORD_PREFIX_CERT = "LOG_record_prefix_cert";
static constexpr auto LOG_RECORD_ANONYMOUS_CALLER = "LOG_record_anonymous";
};

// SNIPPET: table_definition
Expand Down Expand Up @@ -195,6 +196,22 @@ namespace ccfapp
};
// SNIPPET_END: log_record_prefix_cert

auto log_record_anonymous =
[this](Store::Tx& tx, nlohmann::json&& params) {
const auto in = params.get<LoggingRecord::In>();

if (in.msg.empty())
{
return make_error(
HTTP_STATUS_BAD_REQUEST, "Cannot record an empty log message");
}

const auto log_line = fmt::format("Anonymous: {}", in.msg);
auto view = tx.get_view(records);
view->put(in.id, log_line);
return make_success(true);
};

install(Procs::LOG_RECORD, json_adapter(record), Write)
.set_auto_schema<LoggingRecord::In, bool>();
// SNIPPET_START: install_get
Expand All @@ -211,6 +228,12 @@ namespace ccfapp
.set_result_schema(get_public_result_schema);

install(Procs::LOG_RECORD_PREFIX_CERT, log_record_prefix_cert, Write);
install(
Procs::LOG_RECORD_ANONYMOUS_CALLER,
json_adapter(log_record_anonymous),
Write)
.set_auto_schema<LoggingRecord::In, bool>()
.set_require_client_identity(false);

nwt.signatures.set_global_hook([this, &notifier](
kv::Version version,
Expand Down
7 changes: 4 additions & 3 deletions src/enclave/rpccontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace enclave
{
size_t client_session_id = InvalidSessionId;
std::vector<uint8_t> caller_cert = {};
bool is_forwarded = false;
bool is_forwarding = false;

//
// Only set in the case of a forwarded RPC
Expand All @@ -32,7 +32,7 @@ namespace enclave
caller_id(caller_id_)
{}
};
std::optional<Forwarded> fwd = std::nullopt;
std::optional<Forwarded> original_caller = std::nullopt;

// Constructor used for non-forwarded RPC
SessionContext(
Expand All @@ -46,7 +46,8 @@ namespace enclave
size_t fwd_session_id_,
ccf::CallerId caller_id_,
const std::vector<uint8_t>& caller_cert_ = {}) :
fwd(std::make_optional<Forwarded>(fwd_session_id_, caller_id_)),
original_caller(
std::make_optional<Forwarded>(fwd_session_id_, caller_id_)),
caller_cert(caller_cert_)
{}
};
Expand Down
2 changes: 1 addition & 1 deletion src/node/rpc/forwarder.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ namespace ccf
}

if (!send_forwarded_response(
ctx->session->fwd->client_session_id,
ctx->session->original_caller->client_session_id,
from_node,
fwd_handler->process_forwarded(ctx)))
{
Expand Down
150 changes: 68 additions & 82 deletions src/node/rpc/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ namespace ccf
handlers.set_history(history);
}

std::optional<nlohmann::json> forward_or_redirect_json(
std::optional<std::vector<uint8_t>> forward_or_redirect_json(
std::shared_ptr<enclave::RpcContext> ctx)
{
if (cmd_forwarder && !ctx->session->fwd.has_value())
if (cmd_forwarder && !ctx->session->original_caller.has_value())
{
return std::nullopt;
}
Expand Down Expand Up @@ -218,66 +218,25 @@ namespace ccf

Store::Tx tx;

// Retrieve id of caller
std::optional<CallerId> caller_id;
if (ctx->is_create_request)
{
caller_id = INVALID_ID;
}
else
{
caller_id = handlers.valid_caller(tx, ctx->session->caller_cert);
}

if (!caller_id.has_value())
{
ctx->set_response_status(HTTP_STATUS_FORBIDDEN);
ctx->set_response_body(invalid_caller_error_message());
return ctx->serialise_response();
}

const auto signed_request = ctx->get_signed_request();
if (signed_request.has_value())
{
if (
!ctx->is_create_request &&
!verify_client_signature(
ctx->session->caller_cert,
caller_id.value(),
signed_request.value()))
{
set_response_unauthorized(ctx);
return ctx->serialise_response();
}

// Client signature is only recorded on the primary
if (
consensus == nullptr || consensus->is_primary() ||
ctx->is_create_request)
{
record_client_signature(
tx, caller_id.value(), signed_request.value());
}
}
auto caller_id = handlers.get_caller_id(tx, ctx->session->caller_cert);

if (consensus != nullptr && consensus->type() == ConsensusType::PBFT)
{
auto rep = process_if_local_node_rpc(ctx, tx, caller_id.value());
auto rep = process_if_local_node_rpc(ctx, tx, caller_id);
if (rep.has_value())
{
return rep.value();
return rep;
}
kv::TxHistory::RequestID reqid;

update_history();
reqid = {caller_id.value(),
ctx->session->client_session_id,
ctx->get_request_index()};
reqid = {
caller_id, ctx->session->client_session_id, ctx->get_request_index()};
if (history)
{
if (!history->add_request(
reqid,
caller_id.value(),
caller_id,
ctx->session->caller_cert,
ctx->get_serialised_request()))
{
Expand All @@ -302,7 +261,7 @@ namespace ccf
}
else
{
auto rep = process_command(ctx, tx, caller_id.value());
auto rep = process_command(ctx, tx, caller_id);

// If necessary, forward the RPC to the current primary
if (!rep.has_value())
Expand All @@ -314,7 +273,7 @@ namespace ccf
if (
primary_id != NoNode && cmd_forwarder &&
cmd_forwarder->forward_command(
ctx, primary_id, caller_id.value(), get_cert_to_forward(ctx)))
ctx, primary_id, caller_id, get_cert_to_forward(ctx)))
{
// Indicate that the RPC has been forwarded to primary
LOG_DEBUG_FMT("RPC forwarded to primary {}", primary_id);
Expand Down Expand Up @@ -360,13 +319,14 @@ namespace ccf
auto req_view = tx.get_view(*pbft_requests_map);
req_view->put(
0,
{ctx->session->fwd.value().caller_id,
{ctx->session->original_caller.value().caller_id,
ctx->session->caller_cert,
ctx->get_serialised_request(),
ctx->pbft_raw});
}

auto rep = process_command(ctx, tx, ctx->session->fwd->caller_id);
auto rep =
process_command(ctx, tx, ctx->session->original_caller->caller_id);

version = tx.get_version();

Expand Down Expand Up @@ -395,31 +355,18 @@ namespace ccf
std::vector<uint8_t> process_forwarded(
std::shared_ptr<enclave::RpcContext> ctx) override
{
if (!ctx->session->fwd.has_value())
if (!ctx->session->original_caller.has_value())
{
throw std::logic_error(
"Processing forwarded command with unitialised forwarded context");
}

Store::Tx tx;

if (!lookup_forwarded_caller_cert(ctx, tx))
{
ctx->set_response_status(HTTP_STATUS_FORBIDDEN);
ctx->set_response_body(invalid_caller_error_message());
return ctx->serialise_response();
}
update_consensus();

// Store client signature. It is assumed that the forwarder node has
// already verified the client signature.
const auto signed_request = ctx->get_signed_request();
if (signed_request.has_value())
{
record_client_signature(
tx, ctx->session->fwd->caller_id, signed_request.value());
}
Store::Tx tx;

auto rep = process_command(ctx, tx, ctx->session->fwd->caller_id);
auto rep =
process_command(ctx, tx, ctx->session->original_caller->caller_id);
if (!rep.has_value())
{
// This should never be called when process_command is called with a
Expand All @@ -430,7 +377,7 @@ namespace ccf
return rep.value();
}

std::optional<nlohmann::json> process_if_local_node_rpc(
std::optional<std::vector<uint8_t>> process_if_local_node_rpc(
std::shared_ptr<enclave::RpcContext> ctx,
Store::Tx& tx,
CallerId caller_id)
Expand Down Expand Up @@ -459,27 +406,66 @@ namespace ccf
return ctx->serialise_response();
}

if (
handler->require_client_signature &&
!ctx->get_signed_request().has_value())
if (handler->require_client_identity && handlers.has_certs())
{
// Only if handler requires client identity.
// If a request is forwarded, check that the caller is known. Otherwise,
// only check that the caller id is valid.
if (
(ctx->session->original_caller.has_value() &&
!lookup_forwarded_caller_cert(ctx, tx)) ||
caller_id == INVALID_ID)
{
ctx->set_response_status(HTTP_STATUS_FORBIDDEN);
ctx->set_response_body(invalid_caller_error_message());
return ctx->serialise_response();
}
}

bool is_primary = (consensus == nullptr) || consensus->is_primary() ||
ctx->is_create_request;

const auto signed_request = ctx->get_signed_request();
if (handler->require_client_signature && !signed_request.has_value())
{
set_response_unauthorized(
ctx, fmt::format("'{}' RPC must be signed", method));
return ctx->serialise_response();
}

update_history();
// By default, signed requests are verified and recorded, even on
// handlers that do not require client signatures
if (signed_request.has_value())
{
// For forwarded requests (raft only), skip verification as it is
// assumed that the verification was done by the forwarder node.
if (
(!ctx->is_create_request &&
(!(consensus != nullptr &&
consensus->type() == ConsensusType::RAFT) ||
!ctx->session->original_caller.has_value())) &&
!verify_client_signature(
ctx->session->caller_cert, caller_id, signed_request.value()))
{
set_response_unauthorized(ctx);
return ctx->serialise_response();
}

bool is_primary = (consensus == nullptr) || consensus->is_primary() ||
ctx->is_create_request;
if (is_primary)
{
record_client_signature(tx, caller_id, signed_request.value());
}
}

update_history();

if (!is_primary && consensus->type() == ConsensusType::RAFT)
{
switch (handler->read_write)
{
case HandlerRegistry::Read:
{
if (ctx->session->is_forwarded)
if (ctx->session->is_forwarding)
{
return forward_or_redirect_json(ctx);
}
Expand All @@ -488,7 +474,7 @@ namespace ccf

case HandlerRegistry::Write:
{
ctx->session->is_forwarded = true;
ctx->session->is_forwarding = true;
return forward_or_redirect_json(ctx);
}

Expand All @@ -498,10 +484,10 @@ namespace ccf
ctx->get_request_header(http::headers::CCF_READ_ONLY);
if (!read_only_it.has_value() || (read_only_it.value() != "true"))
{
ctx->session->is_forwarded = true;
ctx->session->is_forwarding = true;
return forward_or_redirect_json(ctx);
}
else if (ctx->session->is_forwarded)
else if (ctx->session->is_forwarding)
{
return forward_or_redirect_json(ctx);
}
Expand Down
Loading

0 comments on commit 99f7521

Please sign in to comment.