IOS/ES: Implement ES_GetTicketFromView ioctlvs

This implements ioctlvs 0x40, 0x43, 0x44.
This commit is contained in:
Léo Lam 2017-05-20 14:12:38 +02:00
parent 7af05fd9e6
commit e92308fe7e
5 changed files with 134 additions and 6 deletions

View File

@ -504,17 +504,21 @@ 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);

View File

@ -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
{
@ -182,11 +186,11 @@ private:
IOCTL_ES_UNKNOWN_3D = 0x3D,
IOCTL_ES_UNKNOWN_3E = 0x3E,
IOCTL_ES_UNKNOWN_3F = 0x3F,
IOCTL_ES_UNKNOWN_40 = 0x40,
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);

View File

@ -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.

View File

@ -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.

View File

@ -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))