Skip to content

Commit

Permalink
Merge pull request #5455 from leoetlino/es-get-ticket-from-view
Browse files Browse the repository at this point in the history
IOS/ES: Implement ES_GetTicketFromView ioctlvs
  • Loading branch information
leoetlino committed May 22, 2017
2 parents bd178ea + 48d02f9 commit f994538
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 8 deletions.
12 changes: 8 additions & 4 deletions Source/Core/Core/IOS/ES/ES.cpp
Expand Up @@ -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);
}
Expand Down
15 changes: 11 additions & 4 deletions Source/Core/Core/IOS/ES/ES.h
Expand Up @@ -116,6 +116,10 @@ class ES final : public Device
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
{
Expand Down Expand Up @@ -181,12 +185,12 @@ class ES final : public Device
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,
};

Expand Down Expand Up @@ -259,6 +263,9 @@ class ES final : public Device
// 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);
Expand Down
12 changes: 12 additions & 0 deletions Source/Core/Core/IOS/ES/Formats.cpp
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/IOS/ES/Formats.h
Expand Up @@ -181,6 +181,7 @@ class TicketReader final
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.
Expand Down
104 changes: 104 additions & 0 deletions Source/Core/Core/IOS/ES/Views.cpp
Expand Up @@ -6,6 +6,7 @@

#include <algorithm>
#include <cinttypes>
#include <cstddef>
#include <cstdio>
#include <vector>

Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit f994538

Please sign in to comment.