mirror of
https://github.com/PabloMK7/citra.git
synced 2024-11-25 00:44:05 +00:00
Merge pull request #4072 from B3n30/httpc3
Service::HTTP_C: Implement Client Cert opening and closing
This commit is contained in:
commit
f5a3ec18c2
2 changed files with 203 additions and 3 deletions
|
@ -26,12 +26,22 @@ enum {
|
||||||
/// already-initialized session, or when using the wrong context handle in a context-bound
|
/// already-initialized session, or when using the wrong context handle in a context-bound
|
||||||
/// session
|
/// session
|
||||||
SessionStateError = 102,
|
SessionStateError = 102,
|
||||||
|
TooManyClientCerts = 203,
|
||||||
|
NotImplemented = 1012,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResultCode ERROR_STATE_ERROR = // 0xD8A0A066
|
const ResultCode ERROR_STATE_ERROR = // 0xD8A0A066
|
||||||
ResultCode(ErrCodes::SessionStateError, ErrorModule::HTTP, ErrorSummary::InvalidState,
|
ResultCode(ErrCodes::SessionStateError, ErrorModule::HTTP, ErrorSummary::InvalidState,
|
||||||
ErrorLevel::Permanent);
|
ErrorLevel::Permanent);
|
||||||
|
const ResultCode ERROR_NOT_IMPLEMENTED = // 0xD960A3F4
|
||||||
|
ResultCode(ErrCodes::NotImplemented, ErrorModule::HTTP, ErrorSummary::Internal,
|
||||||
|
ErrorLevel::Permanent);
|
||||||
|
const ResultCode ERROR_TOO_MANY_CLIENT_CERTS = // 0xD8A0A0CB
|
||||||
|
ResultCode(ErrCodes::TooManyClientCerts, ErrorModule::HTTP, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Permanent);
|
||||||
|
const ResultCode ERROR_WRONG_CERT_ID = // 0xD8E0B839
|
||||||
|
ResultCode(57, ErrorModule::SSL, ErrorSummary::InvalidArgument, 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);
|
||||||
|
@ -55,6 +65,7 @@ void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
session_data->initialized = true;
|
session_data->initialized = true;
|
||||||
|
session_data->session_id = ++session_counter;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
// This returns 0xd8a0a046 if no network connection is available.
|
// This returns 0xd8a0a046 if no network connection is available.
|
||||||
|
@ -87,6 +98,7 @@ void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
session_data->initialized = true;
|
session_data->initialized = true;
|
||||||
|
session_data->session_id = ++session_counter;
|
||||||
// Bind the context to the current session.
|
// Bind the context to the current session.
|
||||||
session_data->current_http_context = context_handle;
|
session_data->current_http_context = context_handle;
|
||||||
|
|
||||||
|
@ -159,6 +171,7 @@ void HTTP_C::CreateContext(Kernel::HLERequestContext& ctx) {
|
||||||
// TODO(Subv): Find a correct default value for this field.
|
// TODO(Subv): Find a correct default value for this field.
|
||||||
contexts[context_counter].socket_buffer_size = 0;
|
contexts[context_counter].socket_buffer_size = 0;
|
||||||
contexts[context_counter].handle = context_counter;
|
contexts[context_counter].handle = context_counter;
|
||||||
|
contexts[context_counter].session_id = session_data->session_id;
|
||||||
|
|
||||||
session_data->num_http_contexts++;
|
session_data->num_http_contexts++;
|
||||||
|
|
||||||
|
@ -285,6 +298,150 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) {
|
||||||
context_handle);
|
context_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x32, 2, 4);
|
||||||
|
u32 cert_size = rp.Pop<u32>();
|
||||||
|
u32 key_size = rp.Pop<u32>();
|
||||||
|
Kernel::MappedBuffer& cert_buffer = rp.PopMappedBuffer();
|
||||||
|
Kernel::MappedBuffer& key_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
auto* session_data = GetSessionData(ctx.Session());
|
||||||
|
ASSERT(session_data);
|
||||||
|
|
||||||
|
ResultCode result(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
if (!session_data->initialized) {
|
||||||
|
LOG_ERROR(Service_HTTP, "Command called without Initialize");
|
||||||
|
result = ERROR_STATE_ERROR;
|
||||||
|
} else if (session_data->current_http_context != boost::none) {
|
||||||
|
LOG_ERROR(Service_HTTP, "Command called with a bound context");
|
||||||
|
result = ERROR_NOT_IMPLEMENTED;
|
||||||
|
} else if (session_data->num_client_certs >= 2) {
|
||||||
|
LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs");
|
||||||
|
result = ERROR_TOO_MANY_CLIENT_CERTS;
|
||||||
|
} else {
|
||||||
|
++client_certs_counter;
|
||||||
|
client_certs[client_certs_counter].handle = client_certs_counter;
|
||||||
|
client_certs[client_certs_counter].certificate.resize(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);
|
||||||
|
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;
|
||||||
|
|
||||||
|
++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);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(cert_buffer);
|
||||||
|
rb.PushMappedBuffer(key_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x33, 1, 0);
|
||||||
|
u8 cert_id = rp.Pop<u8>();
|
||||||
|
|
||||||
|
auto* session_data = GetSessionData(ctx.Session());
|
||||||
|
ASSERT(session_data);
|
||||||
|
|
||||||
|
if (!session_data->initialized) {
|
||||||
|
LOG_ERROR(Service_HTTP, "Command called without Initialize");
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ERROR_STATE_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_data->current_http_context != boost::none) {
|
||||||
|
LOG_ERROR(Service_HTTP, "Command called with a bound context");
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ERROR_NOT_IMPLEMENTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_data->num_client_certs >= 2) {
|
||||||
|
LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs");
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ERROR_TOO_MANY_CLIENT_CERTS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u8 default_cert_id = 0x40;
|
||||||
|
if (cert_id != default_cert_id) {
|
||||||
|
LOG_ERROR(Service_HTTP, "called with invalid cert_id {}", cert_id);
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ERROR_WRONG_CERT_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ClCertA.init) {
|
||||||
|
LOG_ERROR(Service_HTTP, "called but ClCertA is missing");
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(static_cast<ResultCode>(-1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& it = std::find_if(client_certs.begin(), client_certs.end(),
|
||||||
|
[default_cert_id, &session_data](const auto& i) {
|
||||||
|
return default_cert_id == i.second.cert_id &&
|
||||||
|
session_data->session_id == i.second.session_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != client_certs.end()) {
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push<u32>(it->first);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HTTP, "called, with an already loaded cert_id={}", cert_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
++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].private_key = ClCertA.private_key;
|
||||||
|
client_certs[client_certs_counter].session_id = session_data->session_id;
|
||||||
|
++session_data->num_client_certs;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push<u32>(client_certs_counter);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HTTP, "called, cert_id={}", cert_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x34, 1, 0);
|
||||||
|
ClientCertContext::Handle cert_handle = rp.Pop<u32>();
|
||||||
|
|
||||||
|
auto* session_data = GetSessionData(ctx.Session());
|
||||||
|
ASSERT(session_data);
|
||||||
|
|
||||||
|
if (client_certs.find(cert_handle) == client_certs.end()) {
|
||||||
|
LOG_ERROR(Service_HTTP, "Command called with a unkown client cert handle {}", cert_handle);
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
// This just return success without doing anything
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_certs[cert_handle].session_id != session_data->session_id) {
|
||||||
|
LOG_ERROR(Service_HTTP, "called from another main session");
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
// This just return success without doing anything
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_certs.erase(cert_handle);
|
||||||
|
session_data->num_client_certs--;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle);
|
||||||
|
}
|
||||||
|
|
||||||
void HTTP_C::DecryptClCertA() {
|
void HTTP_C::DecryptClCertA() {
|
||||||
static constexpr u32 iv_length = 16;
|
static constexpr u32 iv_length = 16;
|
||||||
|
|
||||||
|
@ -416,9 +573,9 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
|
||||||
{0x002F0082, nullptr, "RootCertChainAddCert"},
|
{0x002F0082, nullptr, "RootCertChainAddCert"},
|
||||||
{0x00300080, nullptr, "RootCertChainAddDefaultCert"},
|
{0x00300080, nullptr, "RootCertChainAddDefaultCert"},
|
||||||
{0x00310080, nullptr, "RootCertChainRemoveCert"},
|
{0x00310080, nullptr, "RootCertChainRemoveCert"},
|
||||||
{0x00320084, nullptr, "OpenClientCertContext"},
|
{0x00320084, &HTTP_C::OpenClientCertContext, "OpenClientCertContext"},
|
||||||
{0x00330040, nullptr, "OpenDefaultClientCertContext"},
|
{0x00330040, &HTTP_C::OpenDefaultClientCertContext, "OpenDefaultClientCertContext"},
|
||||||
{0x00340040, nullptr, "CloseClientCertContext"},
|
{0x00340040, &HTTP_C::CloseClientCertContext, "CloseClientCertContext"},
|
||||||
{0x00350186, nullptr, "SetDefaultProxy"},
|
{0x00350186, nullptr, "SetDefaultProxy"},
|
||||||
{0x00360000, nullptr, "ClearDNSCache"},
|
{0x00360000, nullptr, "ClearDNSCache"},
|
||||||
{0x00370080, nullptr, "SetKeepAlive"},
|
{0x00370080, nullptr, "SetKeepAlive"},
|
||||||
|
|
|
@ -42,6 +42,8 @@ enum class RequestState : u8 {
|
||||||
struct ClientCertContext {
|
struct ClientCertContext {
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
u32 session_id;
|
||||||
|
u8 cert_id;
|
||||||
std::vector<u8> certificate;
|
std::vector<u8> certificate;
|
||||||
std::vector<u8> private_key;
|
std::vector<u8> private_key;
|
||||||
};
|
};
|
||||||
|
@ -53,11 +55,13 @@ struct RootCertChain {
|
||||||
struct RootCACert {
|
struct RootCACert {
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
u32 session_id;
|
||||||
std::vector<u8> certificate;
|
std::vector<u8> certificate;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
u32 session_id;
|
||||||
std::vector<RootCACert> certificates;
|
std::vector<RootCACert> certificates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,6 +108,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
u32 session_id;
|
||||||
std::string url;
|
std::string url;
|
||||||
RequestMethod method;
|
RequestMethod method;
|
||||||
RequestState state = RequestState::NotStarted;
|
RequestState state = RequestState::NotStarted;
|
||||||
|
@ -120,6 +125,8 @@ struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
||||||
/// has been bound. Certain commands can only be called on a session with a bound context.
|
/// has been bound. Certain commands can only be called on a session with a bound context.
|
||||||
boost::optional<Context::Handle> current_http_context;
|
boost::optional<Context::Handle> current_http_context;
|
||||||
|
|
||||||
|
u32 session_id;
|
||||||
|
|
||||||
/// Number of HTTP contexts that are currently opened in this session.
|
/// Number of HTTP contexts that are currently opened in this session.
|
||||||
u32 num_http_contexts = 0;
|
u32 num_http_contexts = 0;
|
||||||
/// Number of ClientCert contexts that are currently opened in this session.
|
/// Number of ClientCert contexts that are currently opened in this session.
|
||||||
|
@ -196,10 +203,46 @@ private:
|
||||||
*/
|
*/
|
||||||
void AddRequestHeader(Kernel::HLERequestContext& ctx);
|
void AddRequestHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP_C::OpenClientCertContext service function
|
||||||
|
* Inputs:
|
||||||
|
* 1 : Cert size
|
||||||
|
* 2 : Key size
|
||||||
|
* 3 : (CertSize<<4) | 10
|
||||||
|
* 4 : Pointer to input cert
|
||||||
|
* 5 : (KeySize<<4) | 10
|
||||||
|
* 6 : Pointer to input key
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void OpenClientCertContext(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP_C::OpenDefaultClientCertContext service function
|
||||||
|
* Inputs:
|
||||||
|
* 1 : CertID
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Client Cert context handle
|
||||||
|
*/
|
||||||
|
void OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP_C::CloseClientCertContext service function
|
||||||
|
* Inputs:
|
||||||
|
* 1 : ClientCert Handle
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void CloseClientCertContext(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
void DecryptClCertA();
|
void DecryptClCertA();
|
||||||
|
|
||||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
|
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
|
||||||
|
|
||||||
|
/// The next number to use when a new HTTP session is initalized.
|
||||||
|
u32 session_counter = 0;
|
||||||
|
|
||||||
/// The next handle number to use when a new HTTP context is created.
|
/// The next handle number to use when a new HTTP context is created.
|
||||||
Context::Handle context_counter = 0;
|
Context::Handle context_counter = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue