mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 22:00:05 +00:00 
			
		
		
		
	service::HTTP_C: Implement OpenClientCertContext, OpenDefaultClientCertContext, CloseClientCertContext
This commit is contained in:
		
							parent
							
								
									5e658efdb8
								
							
						
					
					
						commit
						c6bdcf72a4
					
				
					 2 changed files with 216 additions and 3 deletions
				
			
		|  | @ -27,12 +27,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 = // 0xD8A0A0CB
 | ||||||
|  |     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); | ||||||
|  | @ -56,6 +66,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.
 | ||||||
|  | @ -88,6 +99,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; | ||||||
| 
 | 
 | ||||||
|  | @ -160,6 +172,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++; | ||||||
| 
 | 
 | ||||||
|  | @ -286,6 +299,163 @@ 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); | ||||||
|  | 
 | ||||||
|  |     if (!session_data->initialized) { | ||||||
|  |         LOG_ERROR(Service_HTTP, "Command called without Initialize"); | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); | ||||||
|  |         rb.Push(ERROR_STATE_ERROR); | ||||||
|  |         rb.PushMappedBuffer(cert_buffer); | ||||||
|  |         rb.PushMappedBuffer(key_buffer); | ||||||
|  |         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, 4); | ||||||
|  |         rb.Push(ERROR_NOT_IMPLEMENTED); | ||||||
|  |         rb.PushMappedBuffer(cert_buffer); | ||||||
|  |         rb.PushMappedBuffer(key_buffer); | ||||||
|  |         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, 4); | ||||||
|  |         rb.Push(ERROR_TOO_MANY_CLIENT_CERTS); | ||||||
|  |         rb.PushMappedBuffer(cert_buffer); | ||||||
|  |         rb.PushMappedBuffer(key_buffer); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ++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_SUCCESS); | ||||||
|  |     rb.PushMappedBuffer(cert_buffer); | ||||||
|  |     rb.PushMappedBuffer(key_buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) { | ||||||
|  |     constexpr u8 default_cert_id = 0x40; | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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); | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
| 
 | 
 | ||||||
|  | @ -417,9 +587,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"}, | ||||||
|  |  | ||||||
|  | @ -43,6 +43,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; | ||||||
| }; | }; | ||||||
|  | @ -54,11 +56,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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -105,6 +109,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; | ||||||
|  | @ -121,6 +126,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.
 | ||||||
|  | @ -197,10 +204,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…
	
	Add table
		Add a link
		
	
		Reference in a new issue