HTTP_C: Implement SetClientCertContext, GetSSLError, BeginRequest, BeginRequestAsync (#4753)

* HTTP_C: Implement SetClientCertContext and GetSSLError; Stubbed BeginRequest and BeginRequestAsync

* HTTP_C: Move logs to beginning of function calls
This commit is contained in:
Ben 2019-04-23 18:35:28 +02:00 committed by GitHub
parent 2706a4d658
commit 624271696e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 253 additions and 31 deletions

View file

@ -43,6 +43,10 @@ const ResultCode ERROR_TOO_MANY_CLIENT_CERTS = // 0xD8A0A0CB
ErrorLevel::Permanent); ErrorLevel::Permanent);
const ResultCode ERROR_WRONG_CERT_ID = // 0xD8E0B839 const ResultCode ERROR_WRONG_CERT_ID = // 0xD8E0B839
ResultCode(57, ErrorModule::SSL, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); ResultCode(57, ErrorModule::SSL, ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
const ResultCode ERROR_WRONG_CERT_HANDLE = // 0xD8A0A0C9
ResultCode(201, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent);
const ResultCode ERROR_CERT_ALREADY_SET = // 0xD8A0A03D
ResultCode(61, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent);
void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) { void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1, 1, 4); IPC::RequestParser rp(ctx, 0x1, 1, 4);
@ -79,6 +83,8 @@ void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) {
const Context::Handle context_handle = rp.Pop<u32>(); const Context::Handle context_handle = rp.Pop<u32>();
u32 pid = rp.PopPID(); u32 pid = rp.PopPID();
LOG_DEBUG(Service_HTTP, "called, context_id={} pid={}", context_handle, pid);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -105,7 +111,96 @@ void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HTTP, "called, context_id={} pid={}", context_handle, pid); }
void HTTP_C::BeginRequest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x9, 1, 0);
const Context::Handle context_handle = rp.Pop<u32>();
LOG_WARNING(Service_HTTP, "(STUBBED) called, context_id={}", context_handle);
auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data);
if (!session_data->initialized) {
LOG_ERROR(Service_HTTP, "Tried to make a request on an uninitialized session");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
// This command can only be called with a bound context
if (!session_data->current_http_context) {
LOG_ERROR(Service_HTTP, "Tried to make a request without a bound context");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP,
ErrorSummary::Internal, ErrorLevel::Permanent));
return;
}
if (session_data->current_http_context != context_handle) {
LOG_ERROR(
Service_HTTP,
"Tried to make a request on a mismatched session input context={} session context={}",
context_handle, *session_data->current_http_context);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
auto itr = contexts.find(context_handle);
ASSERT(itr != contexts.end());
// TODO(B3N30): Make the request
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void HTTP_C::BeginRequestAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0xA, 1, 0);
const Context::Handle context_handle = rp.Pop<u32>();
LOG_WARNING(Service_HTTP, "(STUBBED) called, context_id={}", context_handle);
auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data);
if (!session_data->initialized) {
LOG_ERROR(Service_HTTP, "Tried to make a request on an uninitialized session");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
// This command can only be called with a bound context
if (!session_data->current_http_context) {
LOG_ERROR(Service_HTTP, "Tried to make a request without a bound context");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP,
ErrorSummary::Internal, ErrorLevel::Permanent));
return;
}
if (session_data->current_http_context != context_handle) {
LOG_ERROR(
Service_HTTP,
"Tried to make a request on a mismatched session input context={} session context={}",
context_handle, *session_data->current_http_context);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
auto itr = contexts.find(context_handle);
ASSERT(itr != contexts.end());
// TODO(B3N30): Make the request
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
} }
void HTTP_C::CreateContext(Kernel::HLERequestContext& ctx) { void HTTP_C::CreateContext(Kernel::HLERequestContext& ctx) {
@ -238,6 +333,9 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) {
std::string value(value_size - 1, '\0'); std::string value(value_size - 1, '\0');
value_buffer.Read(&value[0], 0, value_size - 1); value_buffer.Read(&value[0], 0, value_size - 1);
LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value,
context_handle);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -294,9 +392,6 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(value_buffer); rb.PushMappedBuffer(value_buffer);
LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value,
context_handle);
} }
void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) {
@ -314,6 +409,9 @@ void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) {
std::string value(value_size - 1, '\0'); std::string value(value_size - 1, '\0');
value_buffer.Read(&value[0], 0, value_size - 1); value_buffer.Read(&value[0], 0, value_size - 1);
LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value,
context_handle);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -369,9 +467,93 @@ void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(value_buffer); rb.PushMappedBuffer(value_buffer);
}
LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, void HTTP_C::SetClientCertContext(Kernel::HLERequestContext& ctx) {
context_handle); IPC::RequestParser rp(ctx, 0x29, 2, 0);
const u32 context_handle = rp.Pop<u32>();
const u32 client_cert_handle = rp.Pop<u32>();
LOG_DEBUG(Service_HTTP, "called with context_handle={} client_cert_handle={}", context_handle,
client_cert_handle);
auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data);
if (!session_data->initialized) {
LOG_ERROR(Service_HTTP, "Tried to set client cert on an uninitialized session");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
// This command can only be called with a bound context
if (!session_data->current_http_context) {
LOG_ERROR(Service_HTTP, "Tried to set client cert without a bound context");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP,
ErrorSummary::Internal, ErrorLevel::Permanent));
return;
}
if (session_data->current_http_context != context_handle) {
LOG_ERROR(Service_HTTP,
"Tried to add set client cert on a mismatched session input context={} session "
"context={}",
context_handle, *session_data->current_http_context);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_STATE_ERROR);
return;
}
auto http_context_itr = contexts.find(context_handle);
ASSERT(http_context_itr != contexts.end());
auto cert_context_itr = client_certs.find(client_cert_handle);
if (cert_context_itr == client_certs.end()) {
LOG_ERROR(Service_HTTP, "called with wrong client_cert_handle {}", client_cert_handle);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_WRONG_CERT_HANDLE);
return;
}
if (http_context_itr->second.ssl_config.client_cert_ctx.lock()) {
LOG_ERROR(Service_HTTP,
"Tried to set a client cert to a context that already has a client cert");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERROR_CERT_ALREADY_SET);
return;
}
if (http_context_itr->second.state != RequestState::NotStarted) {
LOG_ERROR(Service_HTTP,
"Tried to set a client cert on a context that has already been started.");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidRequestState, ErrorModule::HTTP,
ErrorSummary::InvalidState, ErrorLevel::Permanent));
return;
}
http_context_itr->second.ssl_config.client_cert_ctx = cert_context_itr->second;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
}
void HTTP_C::GetSSLError(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2a, 2, 0);
const u32 context_handle = rp.Pop<u32>();
const u32 unk = rp.Pop<u32>();
LOG_WARNING(Service_HTTP, "(STUBBED) called, context_handle={}, unk={}", context_handle, unk);
auto http_context_itr = contexts.find(context_handle);
ASSERT(http_context_itr != contexts.end());
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
// Since we create the actual http/ssl context only when the request is submitted we can't check
// for SSL Errors here. Just submit no error.
rb.Push<u32>(0);
} }
void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) { void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) {
@ -381,6 +563,8 @@ void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) {
Kernel::MappedBuffer& cert_buffer = rp.PopMappedBuffer(); Kernel::MappedBuffer& cert_buffer = rp.PopMappedBuffer();
Kernel::MappedBuffer& key_buffer = rp.PopMappedBuffer(); Kernel::MappedBuffer& key_buffer = rp.PopMappedBuffer();
LOG_DEBUG(Service_HTTP, "called, cert_size {}, key_size {}", cert_size, key_size);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -396,18 +580,17 @@ void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs"); LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs");
result = ERROR_TOO_MANY_CLIENT_CERTS; result = ERROR_TOO_MANY_CLIENT_CERTS;
} else { } else {
++client_certs_counter; client_certs[++client_certs_counter] = std::make_shared<ClientCertContext>();
client_certs[client_certs_counter].handle = client_certs_counter; client_certs[client_certs_counter]->handle = client_certs_counter;
client_certs[client_certs_counter].certificate.resize(cert_size); client_certs[client_certs_counter]->certificate.resize(cert_size);
cert_buffer.Read(&client_certs[client_certs_counter].certificate[0], 0, cert_size); cert_buffer.Read(&client_certs[client_certs_counter]->certificate[0], 0, cert_size);
client_certs[client_certs_counter].private_key.resize(key_size); client_certs[client_certs_counter]->private_key.resize(key_size);
cert_buffer.Read(&client_certs[client_certs_counter].private_key[0], 0, key_size); cert_buffer.Read(&client_certs[client_certs_counter]->private_key[0], 0, key_size);
client_certs[client_certs_counter].session_id = session_data->session_id; client_certs[client_certs_counter]->session_id = session_data->session_id;
++session_data->num_client_certs; ++session_data->num_client_certs;
} }
LOG_DEBUG(Service_HTTP, "called, cert_size {}, key_size {}", cert_size, key_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
rb.Push(result); rb.Push(result);
rb.PushMappedBuffer(cert_buffer); rb.PushMappedBuffer(cert_buffer);
@ -418,6 +601,8 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x33, 1, 0); IPC::RequestParser rp(ctx, 0x33, 1, 0);
u8 cert_id = rp.Pop<u8>(); u8 cert_id = rp.Pop<u8>();
LOG_DEBUG(Service_HTTP, "called, cert_id={} cert_handle={}", cert_id, client_certs_counter);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -459,8 +644,8 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) {
const auto& it = std::find_if(client_certs.begin(), client_certs.end(), const auto& it = std::find_if(client_certs.begin(), client_certs.end(),
[default_cert_id, &session_data](const auto& i) { [default_cert_id, &session_data](const auto& i) {
return default_cert_id == i.second.cert_id && return default_cert_id == i.second->cert_id &&
session_data->session_id == i.second.session_id; session_data->session_id == i.second->session_id;
}); });
if (it != client_certs.end()) { if (it != client_certs.end()) {
@ -472,24 +657,24 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) {
return; return;
} }
++client_certs_counter; client_certs[++client_certs_counter] = std::make_shared<ClientCertContext>();
client_certs[client_certs_counter].handle = client_certs_counter; client_certs[client_certs_counter]->handle = client_certs_counter;
client_certs[client_certs_counter].certificate = ClCertA.certificate; client_certs[client_certs_counter]->certificate = ClCertA.certificate;
client_certs[client_certs_counter].private_key = ClCertA.private_key; client_certs[client_certs_counter]->private_key = ClCertA.private_key;
client_certs[client_certs_counter].session_id = session_data->session_id; client_certs[client_certs_counter]->session_id = session_data->session_id;
++session_data->num_client_certs; ++session_data->num_client_certs;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push<u32>(client_certs_counter); rb.Push<u32>(client_certs_counter);
LOG_DEBUG(Service_HTTP, "called, cert_id={}", cert_id);
} }
void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) { void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x34, 1, 0); IPC::RequestParser rp(ctx, 0x34, 1, 0);
ClientCertContext::Handle cert_handle = rp.Pop<u32>(); ClientCertContext::Handle cert_handle = rp.Pop<u32>();
LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle);
auto* session_data = GetSessionData(ctx.Session()); auto* session_data = GetSessionData(ctx.Session());
ASSERT(session_data); ASSERT(session_data);
@ -501,7 +686,7 @@ void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) {
return; return;
} }
if (client_certs[cert_handle].session_id != session_data->session_id) { if (client_certs[cert_handle]->session_id != session_data->session_id) {
LOG_ERROR(Service_HTTP, "called from another main session"); LOG_ERROR(Service_HTTP, "called from another main session");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
// This just return success without doing anything // This just return success without doing anything
@ -514,8 +699,6 @@ void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle);
} }
void HTTP_C::Finalize(Kernel::HLERequestContext& ctx) { void HTTP_C::Finalize(Kernel::HLERequestContext& ctx) {
@ -614,8 +797,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
{0x00060040, nullptr, "GetDownloadSizeState"}, {0x00060040, nullptr, "GetDownloadSizeState"},
{0x00070040, nullptr, "GetRequestError"}, {0x00070040, nullptr, "GetRequestError"},
{0x00080042, &HTTP_C::InitializeConnectionSession, "InitializeConnectionSession"}, {0x00080042, &HTTP_C::InitializeConnectionSession, "InitializeConnectionSession"},
{0x00090040, nullptr, "BeginRequest"}, {0x00090040, &HTTP_C::BeginRequest, "BeginRequest"},
{0x000A0040, nullptr, "BeginRequestAsync"}, {0x000A0040, &HTTP_C::BeginRequestAsync, "BeginRequestAsync"},
{0x000B0082, nullptr, "ReceiveData"}, {0x000B0082, nullptr, "ReceiveData"},
{0x000C0102, nullptr, "ReceiveDataTimeout"}, {0x000C0102, nullptr, "ReceiveDataTimeout"},
{0x000D0146, nullptr, "SetProxy"}, {0x000D0146, nullptr, "SetProxy"},
@ -645,8 +828,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
{0x00250080, nullptr, "AddDefaultCert"}, {0x00250080, nullptr, "AddDefaultCert"},
{0x00260080, nullptr, "SelectRootCertChain"}, {0x00260080, nullptr, "SelectRootCertChain"},
{0x002700C4, nullptr, "SetClientCert"}, {0x002700C4, nullptr, "SetClientCert"},
{0x00290080, nullptr, "SetClientCertContext"}, {0x00290080, &HTTP_C::SetClientCertContext, "SetClientCertContext"},
{0x002A0040, nullptr, "GetSSLError"}, {0x002A0040, &HTTP_C::GetSSLError, "GetSSLError"},
{0x002B0080, nullptr, "SetSSLOpt"}, {0x002B0080, nullptr, "SetSSLOpt"},
{0x002C0080, nullptr, "SetSSLClearOpt"}, {0x002C0080, nullptr, "SetSSLClearOpt"},
{0x002D0000, nullptr, "CreateRootCertChain"}, {0x002D0000, nullptr, "CreateRootCertChain"},

View file

@ -193,6 +193,24 @@ private:
*/ */
void InitializeConnectionSession(Kernel::HLERequestContext& ctx); void InitializeConnectionSession(Kernel::HLERequestContext& ctx);
/**
* HTTP_C::BeginRequest service function
* Inputs:
* 1 : Context handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void BeginRequest(Kernel::HLERequestContext& ctx);
/**
* HTTP_C::BeginRequestAsync service function
* Inputs:
* 1 : Context handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void BeginRequestAsync(Kernel::HLERequestContext& ctx);
/** /**
* HTTP_C::AddRequestHeader service function * HTTP_C::AddRequestHeader service function
* Inputs: * Inputs:
@ -223,6 +241,27 @@ private:
*/ */
void AddPostDataAscii(Kernel::HLERequestContext& ctx); void AddPostDataAscii(Kernel::HLERequestContext& ctx);
/**
* HTTP_C::SetClientCertContext service function
* Inputs:
* 1 : Context handle
* 2 : Cert context handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetClientCertContext(Kernel::HLERequestContext& ctx);
/**
* HTTP_C::GetSSLError service function
* Inputs:
* 1 : Context handle
* 2 : Unknown
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : SSL Error code
*/
void GetSSLError(Kernel::HLERequestContext& ctx);
/** /**
* HTTP_C::OpenClientCertContext service function * HTTP_C::OpenClientCertContext service function
* Inputs: * Inputs:
@ -280,7 +319,7 @@ private:
std::unordered_map<Context::Handle, Context> contexts; std::unordered_map<Context::Handle, Context> contexts;
/// Global list of ClientCert contexts currently opened. /// Global list of ClientCert contexts currently opened.
std::unordered_map<ClientCertContext::Handle, ClientCertContext> client_certs; std::unordered_map<ClientCertContext::Handle, std::shared_ptr<ClientCertContext>> client_certs;
struct { struct {
std::vector<u8> certificate; std::vector<u8> certificate;