Merge pull request #5455 from leoetlino/es-get-ticket-from-view
IOS/ES: Implement ES_GetTicketFromView ioctlvs
This commit is contained in:
commit
f994538875
|
@ -504,22 +504,26 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
|
|||
case IOCTL_ES_GETBOOT2VERSION:
|
||||
return GetBoot2Version(request);
|
||||
|
||||
case IOCTL_ES_GET_V0_TICKET_FROM_VIEW:
|
||||
return GetV0TicketFromView(request);
|
||||
case IOCTL_ES_GET_TICKET_SIZE_FROM_VIEW:
|
||||
return GetTicketSizeFromView(request);
|
||||
case IOCTL_ES_GET_TICKET_FROM_VIEW:
|
||||
return GetTicketFromView(request);
|
||||
|
||||
case IOCTL_ES_VERIFYSIGN:
|
||||
case IOCTL_ES_UNKNOWN_3B:
|
||||
case IOCTL_ES_UNKNOWN_3C:
|
||||
case IOCTL_ES_UNKNOWN_3D:
|
||||
case IOCTL_ES_UNKNOWN_3E:
|
||||
case IOCTL_ES_UNKNOWN_3F:
|
||||
case IOCTL_ES_UNKNOWN_40:
|
||||
case IOCTL_ES_UNKNOWN_41:
|
||||
case IOCTL_ES_UNKNOWN_42:
|
||||
case IOCTL_ES_UNKNOWN_43:
|
||||
case IOCTL_ES_UNKNOWN_44:
|
||||
PanicAlert("IOS-ES: Unimplemented ioctlv 0x%x (%zu in vectors, %zu io vectors)",
|
||||
request.request, request.in_vectors.size(), request.io_vectors.size());
|
||||
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_ES, LogTypes::LERROR);
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
case IOCTL_ES_INVALID_3F:
|
||||
default:
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
}
|
||||
|
|
|
@ -116,6 +116,10 @@ public:
|
|||
ReturnCode DeleteTicket(const u8* ticket_view);
|
||||
ReturnCode DeleteSharedContent(const std::array<u8, 20>& sha1) const;
|
||||
|
||||
// Views
|
||||
ReturnCode GetV0TicketFromView(const u8* ticket_view, u8* ticket) const;
|
||||
ReturnCode GetTicketFromView(const u8* ticket_view, u8* ticket, u32* ticket_size) const;
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
|
@ -181,12 +185,12 @@ private:
|
|||
IOCTL_ES_UNKNOWN_3C = 0x3C,
|
||||
IOCTL_ES_UNKNOWN_3D = 0x3D,
|
||||
IOCTL_ES_UNKNOWN_3E = 0x3E,
|
||||
IOCTL_ES_UNKNOWN_3F = 0x3F,
|
||||
IOCTL_ES_UNKNOWN_40 = 0x40,
|
||||
IOCTL_ES_INVALID_3F = 0x3F,
|
||||
IOCTL_ES_GET_V0_TICKET_FROM_VIEW = 0x40,
|
||||
IOCTL_ES_UNKNOWN_41 = 0x41,
|
||||
IOCTL_ES_UNKNOWN_42 = 0x42,
|
||||
IOCTL_ES_UNKNOWN_43 = 0x43,
|
||||
IOCTL_ES_UNKNOWN_44 = 0x44,
|
||||
IOCTL_ES_GET_TICKET_SIZE_FROM_VIEW = 0x43,
|
||||
IOCTL_ES_GET_TICKET_FROM_VIEW = 0x44,
|
||||
IOCTL_ES_CHECKKOREAREGION = 0x45,
|
||||
};
|
||||
|
||||
|
@ -259,6 +263,9 @@ private:
|
|||
// Views for tickets and TMDs
|
||||
IPCCommandResult GetTicketViewCount(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTicketViews(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetV0TicketFromView(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTicketSizeFromView(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTicketFromView(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTMDViewSize(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTMDViews(const IOCtlVRequest& request);
|
||||
IPCCommandResult DIGetTicketView(const IOCtlVRequest& request);
|
||||
|
|
|
@ -261,6 +261,18 @@ const std::vector<u8>& TicketReader::GetRawTicket() const
|
|||
return m_bytes;
|
||||
}
|
||||
|
||||
std::vector<u8> TicketReader::GetRawTicket(u64 ticket_id_to_find) const
|
||||
{
|
||||
for (size_t i = 0; i < GetNumberOfTickets(); ++i)
|
||||
{
|
||||
const auto ticket_begin = m_bytes.begin() + sizeof(IOS::ES::Ticket) * i;
|
||||
const u64 ticket_id = Common::swap64(&*ticket_begin + offsetof(IOS::ES::Ticket, ticket_id));
|
||||
if (ticket_id == ticket_id_to_find)
|
||||
return std::vector<u8>(ticket_begin, ticket_begin + sizeof(IOS::ES::Ticket));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
|
||||
{
|
||||
// A ticket view is composed of a version + part of a ticket starting from the ticket_id field.
|
||||
|
|
|
@ -181,6 +181,7 @@ public:
|
|||
void DoState(PointerWrap& p);
|
||||
|
||||
const std::vector<u8>& GetRawTicket() const;
|
||||
std::vector<u8> GetRawTicket(u64 ticket_id) const;
|
||||
size_t GetNumberOfTickets() const;
|
||||
|
||||
// Returns a "raw" ticket view, without byte swapping. Intended for use from ES.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
|
@ -95,6 +96,109 @@ IPCCommandResult ES::GetTicketViews(const IOCtlVRequest& request)
|
|||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
ReturnCode ES::GetV0TicketFromView(const u8* ticket_view, u8* ticket) const
|
||||
{
|
||||
const u64 title_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, title_id)]);
|
||||
const u64 ticket_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, ticket_id)]);
|
||||
|
||||
const auto installed_ticket = DiscIO::FindSignedTicket(title_id);
|
||||
// TODO: when we get std::optional, check for presence instead of validity.
|
||||
// This is close enough, though.
|
||||
if (!installed_ticket.IsValid())
|
||||
return ES_NO_TICKET;
|
||||
|
||||
const std::vector<u8> ticket_bytes = installed_ticket.GetRawTicket(ticket_id);
|
||||
if (ticket_bytes.empty())
|
||||
return ES_NO_TICKET;
|
||||
|
||||
if (!GetTitleContext().active)
|
||||
return ES_EINVAL;
|
||||
|
||||
// Check for permission to export the ticket.
|
||||
const u32 title_identifier = static_cast<u32>(GetTitleContext().tmd.GetTitleId());
|
||||
const u32 permitted_title_mask =
|
||||
Common::swap32(ticket_bytes.data() + offsetof(IOS::ES::Ticket, permitted_title_mask));
|
||||
const u32 permitted_title_id =
|
||||
Common::swap32(ticket_bytes.data() + offsetof(IOS::ES::Ticket, permitted_title_id));
|
||||
const u8 title_export_allowed = ticket_bytes[offsetof(IOS::ES::Ticket, title_export_allowed)];
|
||||
|
||||
// This is the check present in IOS. The 5 does not correspond to any known constant, sadly.
|
||||
if (!title_identifier || (title_identifier & ~permitted_title_mask) != permitted_title_id ||
|
||||
(title_export_allowed & 0xF) != 5)
|
||||
{
|
||||
return ES_EACCES;
|
||||
}
|
||||
|
||||
std::copy(ticket_bytes.begin(), ticket_bytes.end(), ticket);
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
ReturnCode ES::GetTicketFromView(const u8* ticket_view, u8* ticket, u32* ticket_size) const
|
||||
{
|
||||
const u8 version = ticket_view[offsetof(IOS::ES::TicketView, version)];
|
||||
if (version == 1)
|
||||
{
|
||||
// Currently, we have no support for v1 tickets at all (unlike IOS), so we fake it
|
||||
// and return that there is no ticket.
|
||||
// TODO: implement GetV1TicketFromView when we gain v1 ticket support.
|
||||
ERROR_LOG(IOS_ES, "GetV1TicketFromView: Unimplemented -- returning -1028");
|
||||
return ES_NO_TICKET;
|
||||
}
|
||||
if (ticket != nullptr)
|
||||
{
|
||||
if (*ticket_size >= sizeof(IOS::ES::Ticket))
|
||||
return GetV0TicketFromView(ticket_view, ticket);
|
||||
return ES_EINVAL;
|
||||
}
|
||||
*ticket_size = sizeof(IOS::ES::Ticket);
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetV0TicketFromView(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(1, 1) ||
|
||||
request.in_vectors[0].size != sizeof(IOS::ES::TicketView) ||
|
||||
request.io_vectors[0].size != sizeof(IOS::ES::Ticket))
|
||||
{
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
}
|
||||
return GetDefaultReply(GetV0TicketFromView(Memory::GetPointer(request.in_vectors[0].address),
|
||||
Memory::GetPointer(request.io_vectors[0].address)));
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetTicketSizeFromView(const IOCtlVRequest& request)
|
||||
{
|
||||
u32 ticket_size = 0;
|
||||
if (!request.HasNumberOfValidVectors(1, 1) ||
|
||||
request.in_vectors[0].size != sizeof(IOS::ES::TicketView) ||
|
||||
request.io_vectors[0].size != sizeof(ticket_size))
|
||||
{
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
}
|
||||
const ReturnCode ret =
|
||||
GetTicketFromView(Memory::GetPointer(request.in_vectors[0].address), nullptr, &ticket_size);
|
||||
Memory::Write_U32(ticket_size, request.io_vectors[0].address);
|
||||
return GetDefaultReply(ret);
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetTicketFromView(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(2, 1) ||
|
||||
request.in_vectors[0].size != sizeof(IOS::ES::TicketView) ||
|
||||
request.in_vectors[1].size != sizeof(u32))
|
||||
{
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
}
|
||||
|
||||
u32 ticket_size = Memory::Read_U32(request.in_vectors[1].address);
|
||||
if (ticket_size != request.io_vectors[0].size)
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
|
||||
return GetDefaultReply(GetTicketFromView(Memory::GetPointer(request.in_vectors[0].address),
|
||||
Memory::GetPointer(request.io_vectors[0].address),
|
||||
&ticket_size));
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetTMDViewSize(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(1, 1))
|
||||
|
|
Loading…
Reference in New Issue