diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 1d81da1..cf5c6cd 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 1d81da1230728993922126f756f6499b24be3eda +Subproject commit cf5c6cdad9ec4066d763c3317e98f7027d3172a6 diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp index e5a7ee3..c95fba6 100644 --- a/stratosphere/loader/source/ldr_nso.cpp +++ b/stratosphere/loader/source/ldr_nso.cpp @@ -224,7 +224,7 @@ u64 aslr_slide = 0; if (addspace_type & 0x20) { - aslr_slide = StratosphereRandomUtils::GetRandomU64((addspace_size - extents->total_size) >> 21) << 21; + aslr_slide = sts::rnd::GenerateRandomU64((addspace_size - extents->total_size) >> 21) << 21; } extents->base_address = addspace_start + aslr_slide; diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index e51c7bd..abca134 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "pm_boot2.hpp" #include "pm_registration.hpp" #include "pm_boot_mode.hpp" @@ -181,18 +183,15 @@ } static void WaitForMitm(const char *service) { - bool mitm_installed = false; + const auto name = sts::sm::ServiceName::Encode(service); - DoWithSmSession([&]() { - R_ASSERT(smManagerAmsInitialize()); - }); - ON_SCOPE_EXIT { smManagerAmsExit(); }; - - while (!mitm_installed) { - R_ASSERT(smManagerAmsHasMitm(&mitm_installed, service)); - if (!mitm_installed) { - svcSleepThread(1000000ull); + while (true) { + bool mitm_installed = false; + R_ASSERT(sts::sm::manager::HasMitm(&mitm_installed, name)); + if (mitm_installed) { + break; } + svcSleepThread(1000000ull); } } diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index d67811c..df6507b 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "pm_boot_mode.hpp" #include "pm_info.hpp" @@ -99,18 +100,14 @@ DoWithSmSession([&]() { R_ASSERT(fsprInitialize()); + R_ASSERT(smManagerInitialize()); /* This works around a bug with process permissions on < 4.0.0. */ RegisterPrivilegedProcessesWithFs(); /* Use AMS manager extension to tell SM that FS has been worked around. */ - { - R_ASSERT(smManagerAmsInitialize()); - smManagerAmsEndInitialDefers(); - smManagerAmsExit(); - } + R_ASSERT(sts::sm::manager::EndInitialDefers()); - R_ASSERT(smManagerInitialize()); R_ASSERT(lrInitialize()); R_ASSERT(ldrPmInitialize()); R_ASSERT(splInitialize()); diff --git a/stratosphere/sm/Makefile b/stratosphere/sm/Makefile index 2de980b..0017230 100644 --- a/stratosphere/sm/Makefile +++ b/stratosphere/sm/Makefile @@ -26,7 +26,7 @@ #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source +SOURCES := source source/impl DATA := data INCLUDES := include ../../common/include EXEFS_SRC := exefs_src diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp new file mode 100644 index 0000000..a09b097 --- /dev/null +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "sm_service_manager.hpp" + +namespace sts::sm::impl { + + /* Anonymous namespace for implementation details. */ + namespace { + /* Constexpr definitions. */ + static constexpr size_t ProcessCountMax = 0x40; + static constexpr size_t ServiceCountMax = 0x100; + static constexpr size_t AccessControlSizeMax = 0x200; + + /* Types. */ + struct ProcessInfo { + u64 pid; + size_t access_control_size; + u8 access_control[AccessControlSizeMax]; + + ProcessInfo() { + this->Free(); + } + + void Free() { + this->pid = InvalidProcessId; + this->access_control_size = 0; + std::memset(this->access_control, 0, sizeof(this->access_control)); + } + }; + + struct ServiceInfo { + ServiceName name; + u64 owner_pid; + AutoHandle port_h; + + /* Debug. */ + u64 max_sessions; + bool is_light; + + /* Mitm Extension. */ + u64 mitm_pid; + AutoHandle mitm_port_h; + AutoHandle mitm_query_h; + + /* Acknowledgement members. */ + bool mitm_waiting_ack; + u64 mitm_waiting_ack_pid; + AutoHandle mitm_fwd_sess_h; + + ServiceInfo() { + this->Free(); + } + + void Free() { + /* Close any open handles. */ + this->port_h.Clear(); + this->mitm_port_h.Clear(); + this->mitm_query_h.Clear(); + this->mitm_fwd_sess_h.Clear(); + + /* Reset all other members. */ + this->name = InvalidServiceName; + this->owner_pid = InvalidProcessId; + this->max_sessions = 0; + this->is_light = false; + this->mitm_pid = InvalidProcessId; + this->mitm_waiting_ack = false; + this->mitm_waiting_ack_pid = InvalidProcessId; + } + + void FreeMitm() { + /* Close mitm handles. */ + this->mitm_port_h.Clear(); + this->mitm_query_h.Clear(); + + /* Reset mitm members. */ + this->mitm_pid = InvalidProcessId; + } + + void AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd) { + /* Copy to output. */ + *out_pid = this->mitm_waiting_ack_pid; + *out_hnd = this->mitm_fwd_sess_h.Move(); + this->mitm_waiting_ack = false; + this->mitm_waiting_ack_pid = InvalidProcessId; + } + }; + + class AccessControlEntry { + private: + const u8 *entry; + size_t capacity; + public: + AccessControlEntry(const void *e, size_t c) : entry(reinterpret_cast(e)), capacity(c) { + /* ... */ + } + + AccessControlEntry GetNextEntry() const { + return AccessControlEntry(this->entry + this->GetSize(), this->capacity - this->GetSize()); + } + + size_t GetSize() const { + return this->GetServiceNameSize() + 1; + } + + size_t GetServiceNameSize() const { + return (this->entry[0] & 7) + 1; + } + + ServiceName GetServiceName() const { + return ServiceName::Encode(reinterpret_cast(this->entry + 1), this->GetServiceNameSize()); + } + + bool IsHost() const { + return (this->entry[0] & 0x80) != 0; + } + + bool IsWildcard() const { + return this->entry[this->GetServiceNameSize()] == '*'; + } + + bool IsValid() const { + /* Validate that we can access data. */ + if (this->entry == nullptr || this->capacity == 0) { + return false; + } + + /* Validate that the size is correct. */ + return this->GetSize() <= this->capacity; + } + }; + + class InitialProcessIdLimits { + public: + static constexpr u64 InitialProcessIdMin = 0x00; + static constexpr u64 InitialProcessIdMax = 0x50; + private: + u64 min; + u64 max; + public: + InitialProcessIdLimits() { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + /* On 5.0.0+, we can get precise limits from svcGetSystemInfo. */ + R_ASSERT(svcGetSystemInfo(&this->min, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum)); + R_ASSERT(svcGetSystemInfo(&this->max, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum)); + } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { + /* On 4.0.0-4.1.0, we can get the precise limits from normal svcGetInfo. */ + R_ASSERT(svcGetInfo(&this->min, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum)); + R_ASSERT(svcGetInfo(&this->max, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum)); + } else { + /* On < 4.0.0, we just use hardcoded extents. */ + this->min = InitialProcessIdMin; + this->max = InitialProcessIdMax; + } + + /* Ensure range is sane. */ + if (this->min > this->max) { + std::abort(); + } + } + + bool IsInitialProcess(u64 pid) const { + if (pid == InvalidProcessId) { + std::abort(); + } + return this->min <= pid && pid <= this->max; + } + }; + + /* Static members. */ + ProcessInfo g_process_list[ProcessCountMax]; + ServiceInfo g_service_list[ServiceCountMax]; + InitialProcessIdLimits g_initial_process_id_limits; + bool g_ended_initial_defers; + + /* Helper functions for interacting with processes/services. */ + ProcessInfo *GetProcessInfo(u64 pid) { + for (size_t i = 0; i < ProcessCountMax; i++) { + if (g_process_list[i].pid == pid) { + return &g_process_list[i]; + } + } + return nullptr; + } + + ProcessInfo *GetFreeProcessInfo() { + return GetProcessInfo(InvalidProcessId); + } + + bool HasProcessInfo(u64 pid) { + return GetProcessInfo(pid) != nullptr; + } + + ServiceInfo *GetServiceInfo(ServiceName service_name) { + for (size_t i = 0; i < ServiceCountMax; i++) { + if (g_service_list[i].name == service_name) { + return &g_service_list[i]; + } + } + return nullptr; + } + + ServiceInfo *GetFreeServiceInfo() { + return GetServiceInfo(InvalidServiceName); + } + + bool HasServiceInfo(ServiceName service) { + return GetServiceInfo(service) != nullptr; + } + + void GetServiceInfoRecord(ServiceRecord *out_record, const ServiceInfo *service_info) { + out_record->service = service_info->name; + out_record->owner_pid = service_info->owner_pid; + out_record->max_sessions = service_info->max_sessions; + out_record->mitm_pid = service_info->mitm_pid; + out_record->mitm_waiting_ack_pid = service_info->mitm_waiting_ack_pid; + out_record->is_light = service_info->is_light; + out_record->mitm_waiting_ack = service_info->mitm_waiting_ack; + } + + Result ValidateAccessControl(AccessControlEntry access_control, ServiceName service, bool is_host, bool is_wildcard) { + /* Iterate over all entries in the access control, checking to see if we have a match. */ + while (access_control.IsValid()) { + if (access_control.IsHost() == is_host) { + if (access_control.IsWildcard() == is_wildcard) { + /* Check for exact match. */ + if (access_control.GetServiceName() == service) { + return ResultSuccess; + } + } else if (access_control.IsWildcard()) { + /* Also allow fuzzy match for wildcard. */ + ServiceName ac_service = access_control.GetServiceName(); + if (std::memcmp(&ac_service, &service, access_control.GetServiceNameSize() - 1) == 0) { + return ResultSuccess; + } + } + } + access_control = access_control.GetNextEntry(); + } + + return ResultSmNotAllowed; + } + + Result ValidateAccessControl(AccessControlEntry restriction, AccessControlEntry access) { + /* Ensure that every entry in the access control is allowed by the restriction control. */ + while (access.IsValid()) { + R_TRY(ValidateAccessControl(restriction, access.GetServiceName(), access.IsHost(), access.IsWildcard())); + access = access.GetNextEntry(); + } + + return ResultSuccess; + } + + Result ValidateServiceName(ServiceName service) { + /* Service names must be non-empty. */ + if (service.name[0] == 0) { + return ResultSmInvalidServiceName; + } + + /* Get name length. */ + size_t name_len; + for (name_len = 1; name_len < sizeof(service); name_len++) { + if (service.name[name_len] == 0) { + break; + } + } + + /* Names must be all-zero after they end. */ + while (name_len < sizeof(service)) { + if (service.name[name_len++] != 0) { + return ResultSmInvalidServiceName; + } + } + + return ResultSuccess; + } + + bool IsInitialProcess(u64 pid) { + return g_initial_process_id_limits.IsInitialProcess(pid); + } + + bool IsValidProcessId(u64 pid) { + return pid != InvalidProcessId; + } + + bool ShouldDeferForInit(ServiceName service) { + /* Once end has been called, we're done. */ + if (g_ended_initial_defers) { + return false; + } + + /* This is a mechanism by which certain services will always be deferred until sm:m receives a special command. */ + /* This can be extended with more services as needed at a later date. */ + return service == ServiceName::Encode("fsp-srv"); + } + + Result GetMitmServiceHandleImpl(Handle *out, ServiceInfo *service_info, u64 pid) { + /* Send command to query if we should mitm. */ + { + IpcCommand c; + ipcInitialize(&c); + struct { + u64 magic; + u64 cmd_id; + u64 pid; + } *info = ((decltype(info))ipcPrepareHeader(&c, sizeof(*info))); + info->magic = SFCI_MAGIC; + info->cmd_id = 65000; + info->pid = pid; + R_TRY(ipcDispatch(service_info->mitm_query_h.Get())); + } + + /* Parse response to see if we should mitm. */ + bool should_mitm; + { + IpcParsedCommand r; + ipcParse(&r); + struct { + u64 magic; + u64 result; + bool should_mitm; + } *resp = ((decltype(resp))r.Raw); + + R_TRY(resp->result); + should_mitm = resp->should_mitm; + } + + /* If we shouldn't mitm, give normal session. */ + if (!should_mitm) { + return svcConnectToPort(out, service_info->port_h.Get()); + } + + /* Create both handles. */ + { + AutoHandle fwd_hnd, hnd; + R_TRY(svcConnectToPort(fwd_hnd.GetPointer(), service_info->port_h.Get())); + R_TRY(svcConnectToPort(hnd.GetPointer(), service_info->mitm_port_h.Get())); + service_info->mitm_fwd_sess_h = std::move(fwd_hnd); + *out = hnd.Move(); + } + + service_info->mitm_waiting_ack_pid = pid; + service_info->mitm_waiting_ack = true; + + return ResultSuccess; + } + + Result GetServiceHandleImpl(Handle *out, ServiceInfo *service_info, u64 pid) { + /* Clear handle output. */ + *out = INVALID_HANDLE; + + /* If not mitm'd or mitm service is requesting, get normal session. */ + if (!IsValidProcessId(service_info->mitm_pid) || service_info->mitm_pid == pid) { + return svcConnectToPort(out, service_info->port_h.Get()); + } + + /* We're mitm'd. Assert, because mitm service host dead is an error state. */ + R_ASSERT(GetMitmServiceHandleImpl(out, service_info, pid)); + return ResultSuccess; + } + + Result RegisterServiceImpl(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Don't try to register something already registered. */ + if (HasServiceInfo(service)) { + return ResultSmAlreadyRegistered; + } + + /* Adjust session limit, if compile flags tell us to. */ +#ifdef SM_MINIMUM_SESSION_LIMIT + if (max_sessions < SM_MINIMUM_SESSION_LIMIT) { + max_sessions = SM_MINIMUM_SESSION_LIMIT; + } +#endif + + /* Get free service. */ + ServiceInfo *free_service = GetFreeServiceInfo(); + if (free_service == nullptr) { + return ResultSmInsufficientServices; + } + + /* Create the new service. */ + *out = INVALID_HANDLE; + R_TRY(svcCreatePort(out, free_service->port_h.GetPointerAndClear(), max_sessions, is_light, free_service->name.name)); + + /* Save info. */ + free_service->name = service; + free_service->owner_pid = pid; + free_service->max_sessions = max_sessions; + free_service->is_light = is_light; + + return ResultSuccess; + } + } + + /* Process management. */ + Result RegisterProcess(u64 pid, const void *acid_sac, size_t acid_sac_size, const void *aci0_sac, size_t aci0_sac_size) { + /* Check that access control will fit in the ServiceInfo. */ + if (aci0_sac_size > AccessControlSizeMax) { + return ResultSmTooLargeAccessControl; + } + + /* Get free process. */ + ProcessInfo *proc = GetFreeProcessInfo(); + if (proc == nullptr) { + return ResultSmInsufficientProcesses; + } + + /* Validate restrictions. */ + if (!aci0_sac_size) { + return ResultSmNotAllowed; + } + R_TRY(ValidateAccessControl(AccessControlEntry(acid_sac, acid_sac_size), AccessControlEntry(aci0_sac, aci0_sac_size))); + + /* Save info. */ + proc->pid = pid; + proc->access_control_size = aci0_sac_size; + std::memcpy(proc->access_control, aci0_sac, proc->access_control_size); + return ResultSuccess; + } + + Result UnregisterProcess(u64 pid) { + /* Find the process. */ + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + + proc->Free(); + return ResultSuccess; + } + + /* Service management. */ + Result HasService(bool *out, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + *out = HasServiceInfo(service); + return ResultSuccess; + } + + Result GetServiceHandle(Handle *out, u64 pid, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* In 8.0.0, Nintendo removed the service apm:p -- however, all homebrew attempts to get */ + /* a handle to this when calling appletInitialize(). Because hbl has access to all services, */ + /* This would return true, and homebrew would *wait forever* trying to get a handle to a service */ + /* that will never register. Thus, in the interest of not breaking every single piece of homebrew */ + /* we will provide a little first class help. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800 && service == ServiceName::Encode("apm:p")) { + return ResultSmNotAllowed; + } + + /* Check that the process is registered and allowed to get the service. */ + if (!IsInitialProcess(pid)) { + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + + R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, false, false)); + } + + /* Get service info. Check to see if we need to defer this until later. */ + ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr || ShouldDeferForInit(service) || service_info->mitm_waiting_ack) { + return ResultServiceFrameworkRequestDeferredByUser; + } + + /* Get a handle from the service info. */ + R_TRY_CATCH(GetServiceHandleImpl(out, service_info, pid)) { + /* Convert Kernel result to SM result. */ + R_CATCH(ResultKernelOutOfSessions) { + return ResultSmInsufficientSessions; + } + } R_END_TRY_CATCH; + + return ResultSuccess; + } + + Result RegisterService(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered and allowed to register the service. */ + if (!IsInitialProcess(pid)) { + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + + R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false)); + } + + if (HasServiceInfo(service)) { + return ResultSmAlreadyRegistered; + } + + return RegisterServiceImpl(out, pid, service, max_sessions, is_light); + } + + Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions) { + u64 self_pid; + R_TRY(svcGetProcessId(&self_pid, CUR_PROCESS_HANDLE)); + + return RegisterServiceImpl(out, self_pid, service, max_sessions, false); + } + + Result UnregisterService(u64 pid, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered. */ + if (!IsInitialProcess(pid)) { + if (!HasProcessInfo(pid)) { + return ResultSmInvalidClient; + } + } + + /* Ensure that the service is actually registered. */ + ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr) { + return ResultSmNotRegistered; + } + + /* Check if we have permission to do this. */ + if (service_info->owner_pid != pid) { + return ResultSmNotAllowed; + } + + /* Unregister the service. */ + service_info->Free(); + return ResultSuccess; + } + + /* Mitm extensions. */ + Result HasMitm(bool *out, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + const ServiceInfo *service_info = GetServiceInfo(service); + *out = service_info != nullptr && IsValidProcessId(service_info->mitm_pid); + return ResultSuccess; + } + + Result InstallMitm(Handle *out, Handle *out_query, u64 pid, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered and allowed to register the service. */ + if (!IsInitialProcess(pid)) { + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + + R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false)); + } + + /* Validate that the service exists. */ + ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr) { + /* If it doesn't exist, defer until it does. */ + return ResultServiceFrameworkRequestDeferredByUser; + } + + /* Validate that the service isn't already being mitm'd. */ + if (IsValidProcessId(service_info->mitm_pid)) { + return ResultSmAlreadyRegistered; + } + + /* Always clear output. */ + *out = INVALID_HANDLE; + *out_query = INVALID_HANDLE; + + /* Create mitm handles. */ + { + AutoHandle hnd, port_hnd, qry_hnd, mitm_qry_hnd; + u64 x = 0; + R_TRY(svcCreatePort(hnd.GetPointer(), port_hnd.GetPointer(), service_info->max_sessions, service_info->is_light, reinterpret_cast(&x))); + R_TRY(svcCreateSession(qry_hnd.GetPointer(), mitm_qry_hnd.GetPointer(), 0, 0)); + + /* Copy to output. */ + service_info->mitm_pid = pid; + service_info->mitm_port_h = std::move(port_hnd); + service_info->mitm_query_h = std::move(mitm_qry_hnd); + *out = hnd.Move(); + *out_query = qry_hnd.Move(); + } + + return ResultSuccess; + } + + Result UninstallMitm(u64 pid, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered. */ + if (!IsInitialProcess(pid)) { + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + } + + /* Validate that the service exists. */ + ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr) { + return ResultSmNotRegistered; + } + + /* Validate that the client pid is the mitm process. */ + if (service_info->mitm_pid != pid) { + return ResultSmNotAllowed; + } + + /* Free Mitm session info. */ + service_info->FreeMitm(); + return ResultSuccess; + } + + Result AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd, u64 pid, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered. */ + if (!IsInitialProcess(pid)) { + ProcessInfo *proc = GetProcessInfo(pid); + if (proc == nullptr) { + return ResultSmInvalidClient; + } + } + + /* Validate that the service exists. */ + ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr) { + return ResultSmNotRegistered; + } + + /* Validate that the client pid is the mitm process, and that an acknowledgement is waiting. */ + if (service_info->mitm_pid != pid || !service_info->mitm_waiting_ack) { + return ResultSmNotAllowed; + } + + /* Acknowledge. */ + service_info->AcknowledgeMitmSession(out_pid, out_hnd); + return ResultSuccess; + } + + Result AssociatePidTidForMitm(u64 pid, u64 tid) { + for (size_t i = 0; i < ServiceCountMax; i++) { + const ServiceInfo *service_info = &g_service_list[i]; + if (IsValidProcessId(service_info->mitm_pid)) { + /* Send association command to all mitm processes. */ + IpcCommand c; + ipcInitialize(&c); + struct { + u64 magic; + u64 cmd_id; + u64 pid; + u64 tid; + } *info = ((decltype(info))ipcPrepareHeader(&c, sizeof(*info))); + info->magic = SFCI_MAGIC; + info->cmd_id = 65001; + info->pid = pid; + info->tid = tid; + ipcDispatch(service_info->mitm_query_h.Get()); + } + } + return ResultSuccess; + } + + /* Dmnt record extensions. */ + Result GetServiceRecord(ServiceRecord *out, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Validate that the service exists. */ + const ServiceInfo *service_info = GetServiceInfo(service); + if (service_info == nullptr) { + return ResultSmNotRegistered; + } + + GetServiceInfoRecord(out, service_info); + return ResultSuccess; + } + + Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count) { + u64 count = 0; + + for (size_t i = 0; i < ServiceCountMax && count < max_count; i++) { + const ServiceInfo *service_info = &g_service_list[i]; + if (service_info->name != InvalidServiceName) { + if (offset == 0) { + GetServiceInfoRecord(out++, service_info); + count++; + } else { + offset--; + } + } + } + + *out_count = 0; + return ResultSuccess; + } + + /* Deferral extension (works around FS bug). */ + Result EndInitialDefers() { + g_ended_initial_defers = true; + return ResultSuccess; + } + +} diff --git a/stratosphere/sm/source/impl/sm_service_manager.hpp b/stratosphere/sm/source/impl/sm_service_manager.hpp new file mode 100644 index 0000000..f836e89 --- /dev/null +++ b/stratosphere/sm/source/impl/sm_service_manager.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace sts::sm::impl { + + /* Process management. */ + Result RegisterProcess(u64 pid, const void *acid_sac, size_t acid_sac_size, const void *aci0_sac, size_t aci0_sac_size); + Result UnregisterProcess(u64 pid); + + /* Service management. */ + Result HasService(bool *out, ServiceName service); + Result GetServiceHandle(Handle *out, u64 pid, ServiceName service); + Result RegisterService(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light); + Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions); + Result UnregisterService(u64 pid, ServiceName service); + + /* Mitm extensions. */ + Result HasMitm(bool *out, ServiceName service); + Result InstallMitm(Handle *out, Handle *out_query, u64 pid, ServiceName service); + Result UninstallMitm(u64 pid, ServiceName service); + Result AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd, u64 pid, ServiceName service); + Result AssociatePidTidForMitm(u64 pid, u64 tid); + + /* Dmnt record extensions. */ + Result GetServiceRecord(ServiceRecord *out, ServiceName service); + Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count); + + /* Deferral extension (works around FS bug). */ + Result EndInitialDefers(); + +} diff --git a/stratosphere/sm/source/sm_dmnt_service.cpp b/stratosphere/sm/source/sm_dmnt_service.cpp index 6245c4d..6c9acf5 100644 --- a/stratosphere/sm/source/sm_dmnt_service.cpp +++ b/stratosphere/sm/source/sm_dmnt_service.cpp @@ -18,16 +18,16 @@ #include #include "sm_dmnt_service.hpp" -#include "sm_service_manager.hpp" +#include "impl/sm_service_manager.hpp" namespace sts::sm { Result DmntService::AtmosphereGetRecord(Out record, ServiceName service) { - return sm::GetServiceRecord(record.GetPointer(), service); + return impl::GetServiceRecord(record.GetPointer(), service); } void DmntService::AtmosphereListRecords(OutBuffer records, Out out_count, u64 offset) { - R_ASSERT(sm::ListServiceRecords(records.buffer, out_count.GetPointer(), offset, records.num_elements)); + R_ASSERT(impl::ListServiceRecords(records.buffer, out_count.GetPointer(), offset, records.num_elements)); } void DmntService::AtmosphereGetRecordSize(Out record_size) { diff --git a/stratosphere/sm/source/sm_dmnt_service.hpp b/stratosphere/sm/source/sm_dmnt_service.hpp index 70a3585..25cce03 100644 --- a/stratosphere/sm/source/sm_dmnt_service.hpp +++ b/stratosphere/sm/source/sm_dmnt_service.hpp @@ -17,7 +17,7 @@ #pragma once #include #include -#include "sm_types.hpp" +#include namespace sts::sm { diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index dd8f3ae..763bfed 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -22,11 +22,12 @@ #include #include -#include "sm_service_manager.hpp" #include "sm_user_service.hpp" #include "sm_manager_service.hpp" #include "sm_dmnt_service.hpp" +#include "impl/sm_service_manager.hpp" + extern "C" { extern u32 __start__; @@ -75,24 +76,26 @@ /* Nothing to clean up, because we're sm. */ } +using namespace sts; + int main(int argc, char **argv) { /* Create service waitable manager. */ static auto s_server_manager = WaitableManager(1); /* Create sm:, (and thus allow things to register to it). */ - s_server_manager.AddWaitable(new ManagedPortServer("sm:", 0x40)); + s_server_manager.AddWaitable(new ManagedPortServer("sm:", 0x40)); /* Create sm:m manually. */ Handle smm_h; - R_ASSERT(sts::sm::RegisterServiceForSelf(&smm_h, sts::sm::ServiceName::Encode("sm:m"), 1)); - s_server_manager.AddWaitable(new ExistingPortServer(smm_h, 1)); + R_ASSERT(sm::impl::RegisterServiceForSelf(&smm_h, sm::ServiceName::Encode("sm:m"), 1)); + s_server_manager.AddWaitable(new ExistingPortServer(smm_h, 1)); /*===== ATMOSPHERE EXTENSION =====*/ /* Create sm:dmnt manually. */ Handle smdmnt_h; - R_ASSERT(sts::sm::RegisterServiceForSelf(&smdmnt_h, sts::sm::ServiceName::Encode("sm:dmnt"), 1)); - s_server_manager.AddWaitable(new ExistingPortServer(smm_h, 1));; + R_ASSERT(sm::impl::RegisterServiceForSelf(&smdmnt_h, sm::ServiceName::Encode("sm:dmnt"), 1)); + s_server_manager.AddWaitable(new ExistingPortServer(smm_h, 1));; /*================================*/ diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 55bcb2f..03e493b 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -18,24 +18,24 @@ #include #include "sm_manager_service.hpp" -#include "sm_service_manager.hpp" +#include "impl/sm_service_manager.hpp" namespace sts::sm { Result ManagerService::RegisterProcess(u64 pid, InBuffer acid_sac, InBuffer aci0_sac) { - return sm::RegisterProcess(pid, acid_sac.buffer, acid_sac.num_elements, aci0_sac.buffer, aci0_sac.num_elements); + return impl::RegisterProcess(pid, acid_sac.buffer, acid_sac.num_elements, aci0_sac.buffer, aci0_sac.num_elements); } Result ManagerService::UnregisterProcess(u64 pid) { - return sm::UnregisterProcess(pid); + return impl::UnregisterProcess(pid); } void ManagerService::AtmosphereEndInitDefers() { - R_ASSERT(sm::EndInitialDefers()); + R_ASSERT(impl::EndInitialDefers()); } void ManagerService::AtmosphereHasMitm(Out out, ServiceName service) { - R_ASSERT(sm::HasMitm(out.GetPointer(), service)); + R_ASSERT(impl::HasMitm(out.GetPointer(), service)); } } diff --git a/stratosphere/sm/source/sm_manager_service.hpp b/stratosphere/sm/source/sm_manager_service.hpp index c63af81..80e6650 100644 --- a/stratosphere/sm/source/sm_manager_service.hpp +++ b/stratosphere/sm/source/sm_manager_service.hpp @@ -17,7 +17,7 @@ #pragma once #include #include -#include "sm_types.hpp" +#include namespace sts::sm { diff --git a/stratosphere/sm/source/sm_service_manager.cpp b/stratosphere/sm/source/sm_service_manager.cpp deleted file mode 100644 index 7dee60a..0000000 --- a/stratosphere/sm/source/sm_service_manager.cpp +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "sm_service_manager.hpp" - -namespace sts::sm { - - /* Anonymous namespace for implementation details. */ - namespace { - /* Constexpr definitions. */ - static constexpr size_t ProcessCountMax = 0x40; - static constexpr size_t ServiceCountMax = 0x100; - static constexpr size_t AccessControlSizeMax = 0x200; - - /* Types. */ - struct ProcessInfo { - u64 pid; - size_t access_control_size; - u8 access_control[AccessControlSizeMax]; - - ProcessInfo() { - this->Free(); - } - - void Free() { - this->pid = InvalidProcessId; - this->access_control_size = 0; - std::memset(this->access_control, 0, sizeof(this->access_control)); - } - }; - - struct ServiceInfo { - ServiceName name; - u64 owner_pid; - AutoHandle port_h; - - /* Debug. */ - u64 max_sessions; - bool is_light; - - /* Mitm Extension. */ - u64 mitm_pid; - AutoHandle mitm_port_h; - AutoHandle mitm_query_h; - - /* Acknowledgement members. */ - bool mitm_waiting_ack; - u64 mitm_waiting_ack_pid; - AutoHandle mitm_fwd_sess_h; - - ServiceInfo() { - this->Free(); - } - - void Free() { - /* Close any open handles. */ - this->port_h.Clear(); - this->mitm_port_h.Clear(); - this->mitm_query_h.Clear(); - this->mitm_fwd_sess_h.Clear(); - - /* Reset all other members. */ - this->name = InvalidServiceName; - this->owner_pid = InvalidProcessId; - this->max_sessions = 0; - this->is_light = false; - this->mitm_pid = InvalidProcessId; - this->mitm_waiting_ack = false; - this->mitm_waiting_ack_pid = InvalidProcessId; - } - - void FreeMitm() { - /* Close mitm handles. */ - this->mitm_port_h.Clear(); - this->mitm_query_h.Clear(); - - /* Reset mitm members. */ - this->mitm_pid = InvalidProcessId; - } - - void AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd) { - /* Copy to output. */ - *out_pid = this->mitm_waiting_ack_pid; - *out_hnd = this->mitm_fwd_sess_h.Move(); - this->mitm_waiting_ack = false; - this->mitm_waiting_ack_pid = InvalidProcessId; - } - }; - - class AccessControlEntry { - private: - const u8 *entry; - size_t capacity; - public: - AccessControlEntry(const void *e, size_t c) : entry(reinterpret_cast(e)), capacity(c) { - /* ... */ - } - - AccessControlEntry GetNextEntry() const { - return AccessControlEntry(this->entry + this->GetSize(), this->capacity - this->GetSize()); - } - - size_t GetSize() const { - return this->GetServiceNameSize() + 1; - } - - size_t GetServiceNameSize() const { - return (this->entry[0] & 7) + 1; - } - - ServiceName GetServiceName() const { - return ServiceName::Encode(reinterpret_cast(this->entry + 1), this->GetServiceNameSize()); - } - - bool IsHost() const { - return (this->entry[0] & 0x80) != 0; - } - - bool IsWildcard() const { - return this->entry[this->GetServiceNameSize()] == '*'; - } - - bool IsValid() const { - /* Validate that we can access data. */ - if (this->entry == nullptr || this->capacity == 0) { - return false; - } - - /* Validate that the size is correct. */ - return this->GetSize() <= this->capacity; - } - }; - - class InitialProcessIdLimits { - public: - static constexpr u64 InitialProcessIdMin = 0x00; - static constexpr u64 InitialProcessIdMax = 0x50; - private: - u64 min; - u64 max; - public: - InitialProcessIdLimits() { - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { - /* On 5.0.0+, we can get precise limits from svcGetSystemInfo. */ - R_ASSERT(svcGetSystemInfo(&this->min, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum)); - R_ASSERT(svcGetSystemInfo(&this->max, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum)); - } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { - /* On 4.0.0-4.1.0, we can get the precise limits from normal svcGetInfo. */ - R_ASSERT(svcGetInfo(&this->min, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum)); - R_ASSERT(svcGetInfo(&this->max, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum)); - } else { - /* On < 4.0.0, we just use hardcoded extents. */ - this->min = InitialProcessIdMin; - this->max = InitialProcessIdMax; - } - - /* Ensure range is sane. */ - if (this->min > this->max) { - std::abort(); - } - } - - bool IsInitialProcess(u64 pid) const { - if (pid == InvalidProcessId) { - std::abort(); - } - return this->min <= pid && pid <= this->max; - } - }; - - /* Static members. */ - ProcessInfo g_process_list[ProcessCountMax]; - ServiceInfo g_service_list[ServiceCountMax]; - InitialProcessIdLimits g_initial_process_id_limits; - bool g_ended_initial_defers; - - /* Helper functions for interacting with processes/services. */ - ProcessInfo *GetProcessInfo(u64 pid) { - for (size_t i = 0; i < ProcessCountMax; i++) { - if (g_process_list[i].pid == pid) { - return &g_process_list[i]; - } - } - return nullptr; - } - - ProcessInfo *GetFreeProcessInfo() { - return GetProcessInfo(InvalidProcessId); - } - - bool HasProcessInfo(u64 pid) { - return GetProcessInfo(pid) != nullptr; - } - - ServiceInfo *GetServiceInfo(ServiceName service_name) { - for (size_t i = 0; i < ServiceCountMax; i++) { - if (g_service_list[i].name == service_name) { - return &g_service_list[i]; - } - } - return nullptr; - } - - ServiceInfo *GetFreeServiceInfo() { - return GetServiceInfo(InvalidServiceName); - } - - bool HasServiceInfo(ServiceName service) { - return GetServiceInfo(service) != nullptr; - } - - void GetServiceInfoRecord(ServiceRecord *out_record, const ServiceInfo *service_info) { - out_record->service = service_info->name; - out_record->owner_pid = service_info->owner_pid; - out_record->max_sessions = service_info->max_sessions; - out_record->mitm_pid = service_info->mitm_pid; - out_record->mitm_waiting_ack_pid = service_info->mitm_waiting_ack_pid; - out_record->is_light = service_info->is_light; - out_record->mitm_waiting_ack = service_info->mitm_waiting_ack; - } - - Result ValidateAccessControl(AccessControlEntry access_control, ServiceName service, bool is_host, bool is_wildcard) { - /* Iterate over all entries in the access control, checking to see if we have a match. */ - while (access_control.IsValid()) { - if (access_control.IsHost() == is_host) { - if (access_control.IsWildcard() == is_wildcard) { - /* Check for exact match. */ - if (access_control.GetServiceName() == service) { - return ResultSuccess; - } - } else if (access_control.IsWildcard()) { - /* Also allow fuzzy match for wildcard. */ - ServiceName ac_service = access_control.GetServiceName(); - if (std::memcmp(&ac_service, &service, access_control.GetServiceNameSize() - 1) == 0) { - return ResultSuccess; - } - } - } - access_control = access_control.GetNextEntry(); - } - - return ResultSmNotAllowed; - } - - Result ValidateAccessControl(AccessControlEntry restriction, AccessControlEntry access) { - /* Ensure that every entry in the access control is allowed by the restriction control. */ - while (access.IsValid()) { - R_TRY(ValidateAccessControl(restriction, access.GetServiceName(), access.IsHost(), access.IsWildcard())); - access = access.GetNextEntry(); - } - - return ResultSuccess; - } - - Result ValidateServiceName(ServiceName service) { - /* Service names must be non-empty. */ - if (service.name[0] == 0) { - return ResultSmInvalidServiceName; - } - - /* Get name length. */ - size_t name_len; - for (name_len = 1; name_len < sizeof(service); name_len++) { - if (service.name[name_len] == 0) { - break; - } - } - - /* Names must be all-zero after they end. */ - while (name_len < sizeof(service)) { - if (service.name[name_len++] != 0) { - return ResultSmInvalidServiceName; - } - } - - return ResultSuccess; - } - - bool IsInitialProcess(u64 pid) { - return g_initial_process_id_limits.IsInitialProcess(pid); - } - - bool IsValidProcessId(u64 pid) { - return pid != InvalidProcessId; - } - - bool ShouldDeferForInit(ServiceName service) { - /* Once end has been called, we're done. */ - if (g_ended_initial_defers) { - return false; - } - - /* This is a mechanism by which certain services will always be deferred until sm:m receives a special command. */ - /* This can be extended with more services as needed at a later date. */ - return service == ServiceName::Encode("fsp-srv"); - } - - Result GetMitmServiceHandleImpl(Handle *out, ServiceInfo *service_info, u64 pid) { - /* Send command to query if we should mitm. */ - { - IpcCommand c; - ipcInitialize(&c); - struct { - u64 magic; - u64 cmd_id; - u64 pid; - } *info = ((decltype(info))ipcPrepareHeader(&c, sizeof(*info))); - info->magic = SFCI_MAGIC; - info->cmd_id = 65000; - info->pid = pid; - R_TRY(ipcDispatch(service_info->mitm_query_h.Get())); - } - - /* Parse response to see if we should mitm. */ - bool should_mitm; - { - IpcParsedCommand r; - ipcParse(&r); - struct { - u64 magic; - u64 result; - bool should_mitm; - } *resp = ((decltype(resp))r.Raw); - - R_TRY(resp->result); - should_mitm = resp->should_mitm; - } - - /* If we shouldn't mitm, give normal session. */ - if (!should_mitm) { - return svcConnectToPort(out, service_info->port_h.Get()); - } - - /* Create both handles. */ - { - AutoHandle fwd_hnd, hnd; - R_TRY(svcConnectToPort(fwd_hnd.GetPointer(), service_info->port_h.Get())); - R_TRY(svcConnectToPort(hnd.GetPointer(), service_info->mitm_port_h.Get())); - service_info->mitm_fwd_sess_h = std::move(fwd_hnd); - *out = hnd.Move(); - } - - service_info->mitm_waiting_ack_pid = pid; - service_info->mitm_waiting_ack = true; - - return ResultSuccess; - } - - Result GetServiceHandleImpl(Handle *out, ServiceInfo *service_info, u64 pid) { - /* Clear handle output. */ - *out = INVALID_HANDLE; - - /* If not mitm'd or mitm service is requesting, get normal session. */ - if (!IsValidProcessId(service_info->mitm_pid) || service_info->mitm_pid == pid) { - return svcConnectToPort(out, service_info->port_h.Get()); - } - - /* We're mitm'd. Assert, because mitm service host dead is an error state. */ - R_ASSERT(GetMitmServiceHandleImpl(out, service_info, pid)); - return ResultSuccess; - } - - Result RegisterServiceImpl(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Don't try to register something already registered. */ - if (HasServiceInfo(service)) { - return ResultSmAlreadyRegistered; - } - - /* Adjust session limit, if compile flags tell us to. */ -#ifdef SM_MINIMUM_SESSION_LIMIT - if (max_sessions < SM_MINIMUM_SESSION_LIMIT) { - max_sessions = SM_MINIMUM_SESSION_LIMIT; - } -#endif - - /* Get free service. */ - ServiceInfo *free_service = GetFreeServiceInfo(); - if (free_service == nullptr) { - return ResultSmInsufficientServices; - } - - /* Create the new service. */ - *out = INVALID_HANDLE; - R_TRY(svcCreatePort(out, free_service->port_h.GetPointerAndClear(), max_sessions, is_light, free_service->name.name)); - - /* Save info. */ - free_service->name = service; - free_service->owner_pid = pid; - free_service->max_sessions = max_sessions; - free_service->is_light = is_light; - - return ResultSuccess; - } - } - - /* Process management. */ - Result RegisterProcess(u64 pid, const void *acid_sac, size_t acid_sac_size, const void *aci0_sac, size_t aci0_sac_size) { - /* Check that access control will fit in the ServiceInfo. */ - if (aci0_sac_size > AccessControlSizeMax) { - return ResultSmTooLargeAccessControl; - } - - /* Get free process. */ - ProcessInfo *proc = GetFreeProcessInfo(); - if (proc == nullptr) { - return ResultSmInsufficientProcesses; - } - - /* Validate restrictions. */ - if (!aci0_sac_size) { - return ResultSmNotAllowed; - } - R_TRY(ValidateAccessControl(AccessControlEntry(acid_sac, acid_sac_size), AccessControlEntry(aci0_sac, aci0_sac_size))); - - /* Save info. */ - proc->pid = pid; - proc->access_control_size = aci0_sac_size; - std::memcpy(proc->access_control, aci0_sac, proc->access_control_size); - return ResultSuccess; - } - - Result UnregisterProcess(u64 pid) { - /* Find the process. */ - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - - proc->Free(); - return ResultSuccess; - } - - /* Service management. */ - Result HasService(bool *out, ServiceName service) { - *out = HasServiceInfo(service); - return ResultSuccess; - } - - Result GetServiceHandle(Handle *out, u64 pid, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* In 8.0.0, Nintendo removed the service apm:p -- however, all homebrew attempts to get */ - /* a handle to this when calling appletInitialize(). Because hbl has access to all services, */ - /* This would return true, and homebrew would *wait forever* trying to get a handle to a service */ - /* that will never register. Thus, in the interest of not breaking every single piece of homebrew */ - /* we will provide a little first class help. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800 && service == ServiceName::Encode("apm:p")) { - return ResultSmNotAllowed; - } - - /* Check that the process is registered and allowed to get the service. */ - if (!IsInitialProcess(pid)) { - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - - R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, false, false)); - } - - /* Get service info. Check to see if we need to defer this until later. */ - ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr || ShouldDeferForInit(service) || service_info->mitm_waiting_ack) { - return ResultServiceFrameworkRequestDeferredByUser; - } - - /* Get a handle from the service info. */ - R_TRY_CATCH(GetServiceHandleImpl(out, service_info, pid)) { - /* Convert Kernel result to SM result. */ - R_CATCH(ResultKernelOutOfSessions) { - return ResultSmInsufficientSessions; - } - } R_END_TRY_CATCH; - - return ResultSuccess; - } - - Result RegisterService(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Check that the process is registered and allowed to register the service. */ - if (!IsInitialProcess(pid)) { - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - - R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false)); - } - - if (HasServiceInfo(service)) { - return ResultSmAlreadyRegistered; - } - - return RegisterServiceImpl(out, pid, service, max_sessions, is_light); - } - - Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions) { - u64 self_pid; - R_TRY(svcGetProcessId(&self_pid, CUR_PROCESS_HANDLE)); - - return RegisterServiceImpl(out, self_pid, service, max_sessions, false); - } - - Result UnregisterService(u64 pid, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Check that the process is registered. */ - if (!IsInitialProcess(pid)) { - if (!HasProcessInfo(pid)) { - return ResultSmInvalidClient; - } - } - - /* Ensure that the service is actually registered. */ - ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr) { - return ResultSmNotRegistered; - } - - /* Check if we have permission to do this. */ - if (service_info->owner_pid != pid) { - return ResultSmNotAllowed; - } - - /* Unregister the service. */ - service_info->Free(); - return ResultSuccess; - } - - /* Mitm extensions. */ - Result HasMitm(bool *out, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - const ServiceInfo *service_info = GetServiceInfo(service); - *out = service_info != nullptr && IsValidProcessId(service_info->mitm_pid); - return ResultSuccess; - } - - Result InstallMitm(Handle *out, Handle *out_query, u64 pid, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Check that the process is registered and allowed to register the service. */ - if (!IsInitialProcess(pid)) { - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - - R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false)); - } - - /* Validate that the service exists. */ - ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr) { - /* If it doesn't exist, defer until it does. */ - return ResultServiceFrameworkRequestDeferredByUser; - } - - /* Validate that the service isn't already being mitm'd. */ - if (IsValidProcessId(service_info->mitm_pid)) { - return ResultSmAlreadyRegistered; - } - - /* Always clear output. */ - *out = INVALID_HANDLE; - *out_query = INVALID_HANDLE; - - /* Create mitm handles. */ - { - AutoHandle hnd, port_hnd, qry_hnd, mitm_qry_hnd; - u64 x = 0; - R_TRY(svcCreatePort(hnd.GetPointer(), port_hnd.GetPointer(), service_info->max_sessions, service_info->is_light, reinterpret_cast(&x))); - R_TRY(svcCreateSession(qry_hnd.GetPointer(), mitm_qry_hnd.GetPointer(), 0, 0)); - - /* Copy to output. */ - service_info->mitm_pid = pid; - service_info->mitm_port_h = std::move(port_hnd); - service_info->mitm_query_h = std::move(mitm_qry_hnd); - *out = hnd.Move(); - *out_query = qry_hnd.Move(); - } - - return ResultSuccess; - } - - Result UninstallMitm(u64 pid, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Check that the process is registered. */ - if (!IsInitialProcess(pid)) { - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - } - - /* Validate that the service exists. */ - ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr) { - return ResultSmNotRegistered; - } - - /* Validate that the client pid is the mitm process. */ - if (service_info->mitm_pid != pid) { - return ResultSmNotAllowed; - } - - /* Free Mitm session info. */ - service_info->FreeMitm(); - return ResultSuccess; - } - - Result AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd, u64 pid, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Check that the process is registered. */ - if (!IsInitialProcess(pid)) { - ProcessInfo *proc = GetProcessInfo(pid); - if (proc == nullptr) { - return ResultSmInvalidClient; - } - } - - /* Validate that the service exists. */ - ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr) { - return ResultSmNotRegistered; - } - - /* Validate that the client pid is the mitm process, and that an acknowledgement is waiting. */ - if (service_info->mitm_pid != pid || !service_info->mitm_waiting_ack) { - return ResultSmNotAllowed; - } - - /* Acknowledge. */ - service_info->AcknowledgeMitmSession(out_pid, out_hnd); - return ResultSuccess; - } - - Result AssociatePidTidForMitm(u64 pid, u64 tid) { - for (size_t i = 0; i < ServiceCountMax; i++) { - const ServiceInfo *service_info = &g_service_list[i]; - if (IsValidProcessId(service_info->mitm_pid)) { - /* Send association command to all mitm processes. */ - IpcCommand c; - ipcInitialize(&c); - struct { - u64 magic; - u64 cmd_id; - u64 pid; - u64 tid; - } *info = ((decltype(info))ipcPrepareHeader(&c, sizeof(*info))); - info->magic = SFCI_MAGIC; - info->cmd_id = 65001; - info->pid = pid; - info->tid = tid; - ipcDispatch(service_info->mitm_query_h.Get()); - } - } - return ResultSuccess; - } - - /* Dmnt record extensions. */ - Result GetServiceRecord(ServiceRecord *out, ServiceName service) { - /* Validate service name. */ - R_TRY(ValidateServiceName(service)); - - /* Validate that the service exists. */ - const ServiceInfo *service_info = GetServiceInfo(service); - if (service_info == nullptr) { - return ResultSmNotRegistered; - } - - GetServiceInfoRecord(out, service_info); - return ResultSuccess; - } - - Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count) { - u64 count = 0; - - for (size_t i = 0; i < ServiceCountMax && count < max_count; i++) { - const ServiceInfo *service_info = &g_service_list[i]; - if (service_info->name != InvalidServiceName) { - if (offset == 0) { - GetServiceInfoRecord(out++, service_info); - count++; - } else { - offset--; - } - } - } - - *out_count = 0; - return ResultSuccess; - } - - /* Deferral extension (works around FS bug). */ - Result EndInitialDefers() { - g_ended_initial_defers = true; - return ResultSuccess; - } - -} diff --git a/stratosphere/sm/source/sm_service_manager.hpp b/stratosphere/sm/source/sm_service_manager.hpp deleted file mode 100644 index 9bac407..0000000 --- a/stratosphere/sm/source/sm_service_manager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include "sm_types.hpp" - -namespace sts::sm { - - /* Process management. */ - Result RegisterProcess(u64 pid, const void *acid_sac, size_t acid_sac_size, const void *aci0_sac, size_t aci0_sac_size); - Result UnregisterProcess(u64 pid); - - /* Service management. */ - Result HasService(bool *out, ServiceName service); - Result GetServiceHandle(Handle *out, u64 pid, ServiceName service); - Result RegisterService(Handle *out, u64 pid, ServiceName service, size_t max_sessions, bool is_light); - Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions); - Result UnregisterService(u64 pid, ServiceName service); - - /* Mitm extensions. */ - Result HasMitm(bool *out, ServiceName service); - Result InstallMitm(Handle *out, Handle *out_query, u64 pid, ServiceName service); - Result UninstallMitm(u64 pid, ServiceName service); - Result AcknowledgeMitmSession(u64 *out_pid, Handle *out_hnd, u64 pid, ServiceName service); - Result AssociatePidTidForMitm(u64 pid, u64 tid); - - /* Dmnt record extensions. */ - Result GetServiceRecord(ServiceRecord *out, ServiceName service); - Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count); - - /* Deferral extension (works around FS bug). */ - Result EndInitialDefers(); - -} diff --git a/stratosphere/sm/source/sm_types.hpp b/stratosphere/sm/source/sm_types.hpp deleted file mode 100644 index 57b792d..0000000 --- a/stratosphere/sm/source/sm_types.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include - -namespace sts::sm { - - struct ServiceName { - static constexpr size_t MaxLength = 8; - - char name[MaxLength]; - - static constexpr ServiceName Encode(const char *name, size_t name_size) { - ServiceName out{}; - - for (size_t i = 0; i < MaxLength; i++) { - if (i < name_size) { - out.name[i] = name[i]; - } else { - out.name[i] = 0; - } - } - - return out; - } - - static constexpr ServiceName Encode(const char *name) { - return Encode(name, std::strlen(name)); - } - }; - static constexpr ServiceName InvalidServiceName = ServiceName::Encode(""); - static_assert(alignof(ServiceName) == 1, "ServiceName definition!"); - - inline bool operator==(const ServiceName &lhs, const ServiceName &rhs) { - return std::memcmp(&lhs, &rhs, sizeof(ServiceName)) == 0; - } - - inline bool operator!=(const ServiceName &lhs, const ServiceName &rhs) { - return !(lhs == rhs); - } - - /* For Debug Monitor extensions. */ - struct ServiceRecord { - ServiceName service; - u64 owner_pid; - u64 max_sessions; - u64 mitm_pid; - u64 mitm_waiting_ack_pid; - bool is_light; - bool mitm_waiting_ack; - }; - static_assert(sizeof(ServiceRecord) == 0x30, "ServiceRecord definition!"); - - /* For process validation. */ - static constexpr u64 InvalidProcessId = static_cast(-1ull); - -} diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 49b4f4c..bb2fd7c 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -18,7 +18,7 @@ #include #include "sm_user_service.hpp" -#include "sm_service_manager.hpp" +#include "impl/sm_service_manager.hpp" namespace sts::sm { @@ -37,37 +37,47 @@ Result UserService::GetService(Out out_h, ServiceName service) { R_TRY(this->EnsureInitialized()); - return sm::GetServiceHandle(out_h.GetHandlePointer(), this->pid, service); + return impl::GetServiceHandle(out_h.GetHandlePointer(), this->pid, service); } Result UserService::RegisterService(Out out_h, ServiceName service, u32 max_sessions, bool is_light) { R_TRY(this->EnsureInitialized()); - return sm::RegisterService(out_h.GetHandlePointer(), this->pid, service, max_sessions, is_light); + return impl::RegisterService(out_h.GetHandlePointer(), this->pid, service, max_sessions, is_light); } Result UserService::UnregisterService(ServiceName service) { R_TRY(this->EnsureInitialized()); - return sm::UnregisterService(this->pid, service); + return impl::UnregisterService(this->pid, service); } Result UserService::AtmosphereInstallMitm(Out srv_h, Out qry_h, ServiceName service) { R_TRY(this->EnsureInitialized()); - return sm::InstallMitm(srv_h.GetHandlePointer(), qry_h.GetHandlePointer(), this->pid, service); + return impl::InstallMitm(srv_h.GetHandlePointer(), qry_h.GetHandlePointer(), this->pid, service); } Result UserService::AtmosphereUninstallMitm(ServiceName service) { R_TRY(this->EnsureInitialized()); - return sm::UninstallMitm(this->pid, service); + return impl::UninstallMitm(this->pid, service); } Result UserService::AtmosphereAcknowledgeMitmSession(Out client_pid, Out fwd_h, ServiceName service) { R_TRY(this->EnsureInitialized()); - return sm::AcknowledgeMitmSession(client_pid.GetPointer(), fwd_h.GetHandlePointer(), this->pid, service); + return impl::AcknowledgeMitmSession(client_pid.GetPointer(), fwd_h.GetHandlePointer(), this->pid, service); } Result UserService::AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid) { R_TRY(this->EnsureInitialized()); - return sm::AssociatePidTidForMitm(pid, tid); + return impl::AssociatePidTidForMitm(pid, tid); + } + + Result UserService::AtmosphereHasMitm(Out out, ServiceName service) { + R_TRY(this->EnsureInitialized()); + return impl::HasMitm(out.GetPointer(), service); + } + + Result UserService::AtmosphereHasService(Out out, ServiceName service) { + R_TRY(this->EnsureInitialized()); + return impl::HasService(out.GetPointer(), service); } } diff --git a/stratosphere/sm/source/sm_user_service.hpp b/stratosphere/sm/source/sm_user_service.hpp index 54c060c..f548f55 100644 --- a/stratosphere/sm/source/sm_user_service.hpp +++ b/stratosphere/sm/source/sm_user_service.hpp @@ -17,7 +17,7 @@ #pragma once #include #include -#include "sm_types.hpp" +#include namespace sts::sm { @@ -35,6 +35,9 @@ AtmosphereUninstallMitm = 65001, AtmosphereAssociatePidTidForMitm = 65002, AtmosphereAcknowledgeMitmSession = 65003, + AtmosphereHasMitm = 65004, + + AtmosphereHasService = 65100, }; private: u64 pid = InvalidProcessId; @@ -53,6 +56,9 @@ virtual Result AtmosphereUninstallMitm(ServiceName service); virtual Result AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid); virtual Result AtmosphereAcknowledgeMitmSession(Out client_pid, Out fwd_h, ServiceName service); + virtual Result AtmosphereHasMitm(Out out, ServiceName service); + + virtual Result AtmosphereHasService(Out out, ServiceName service); public: DEFINE_SERVICE_DISPATCH_TABLE { MakeServiceCommandMeta(), @@ -64,6 +70,9 @@ MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + MakeServiceCommandMeta(), }; };