summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorLéo Lam <leo@innovatetechnologi.es>2017-05-05 00:12:37 +0200
committerLéo Lam <leo@innovatetechnologi.es>2017-05-05 00:32:58 +0200
commit5fb2ad2b3a93ad095693e4694caa301bd2f243d0 (patch)
tree7c8091792a3005c292dbc92cdea94ba691a276ae /Source/Core
parenta7680a3d1a773883c2fe3689e6dff79176692389 (diff)
IOS/ES: Fix the implementation of ES_DeleteTicket
* It should take a ticket view, not a title ID. * It's missing a lot of checks. * It's not deleting tickets properly. * It's not deleting only the ticket it needs to delete. * It should not return -1017 when the ticket doesn't exist. * It's not returning the proper error code when a read/write fails. * It's not cleaning up the ticket directory if there is nothing left. This commit fixes its implementation.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/IOS/ES/Formats.cpp15
-rw-r--r--Source/Core/Core/IOS/ES/Formats.h3
-rw-r--r--Source/Core/Core/IOS/ES/TitleManagement.cpp41
3 files changed, 54 insertions, 5 deletions
diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp
index 17184bb942..831488d2df 100644
--- a/Source/Core/Core/IOS/ES/Formats.cpp
+++ b/Source/Core/Core/IOS/ES/Formats.cpp
@@ -300,6 +300,21 @@ std::vector<u8> TicketReader::GetTitleKey() const
return key;
}
+void TicketReader::DeleteTicket(u64 ticket_id_to_delete)
+{
+ std::vector<u8> new_ticket;
+ const size_t num_tickets = GetNumberOfTickets();
+ for (size_t i = 0; i < num_tickets; ++i)
+ {
+ const auto ticket_start = m_bytes.cbegin() + sizeof(Ticket) * i;
+ const u64 ticket_id = Common::swap64(&*ticket_start + offsetof(Ticket, ticket_id));
+ if (ticket_id != ticket_id_to_delete)
+ new_ticket.insert(new_ticket.end(), ticket_start, ticket_start + sizeof(Ticket));
+ }
+
+ m_bytes = std::move(new_ticket);
+}
+
s32 TicketReader::Unpersonalise()
{
const auto ticket_begin = m_bytes.begin();
diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h
index 5380afd953..6337aeaf32 100644
--- a/Source/Core/Core/IOS/ES/Formats.h
+++ b/Source/Core/Core/IOS/ES/Formats.h
@@ -193,6 +193,9 @@ public:
u64 GetTitleId() const;
std::vector<u8> GetTitleKey() const;
+ // Deletes a ticket with the given ticket ID from the internal buffer.
+ void DeleteTicket(u64 ticket_id);
+
// Decrypts the title key field for a "personalised" ticket -- one that is device-specific
// and has a title key that must be decrypted first.
s32 Unpersonalise();
diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp
index 93dfbee95c..cb0b6420f1 100644
--- a/Source/Core/Core/IOS/ES/TitleManagement.cpp
+++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp
@@ -6,6 +6,7 @@
#include <algorithm>
#include <cinttypes>
+#include <cstddef>
#include <utility>
#include <vector>
@@ -335,16 +336,46 @@ IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
IPCCommandResult ES::DeleteTicket(const IOCtlVRequest& request)
{
- if (!request.HasNumberOfValidVectors(1, 0))
+ if (!request.HasNumberOfValidVectors(1, 0) ||
+ request.in_vectors[0].size != sizeof(IOS::ES::TicketView))
+ {
return GetDefaultReply(ES_EINVAL);
+ }
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_DELETETICKET: title: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID);
+ const u64 title_id =
+ Memory::Read_U64(request.in_vectors[0].address + offsetof(IOS::ES::TicketView, title_id));
- // Presumably return -1017 when delete fails
- if (!File::Delete(Common::GetTicketFileName(TitleID, Common::FROM_SESSION_ROOT)))
+ if (!CanDeleteTitle(title_id))
return GetDefaultReply(ES_EINVAL);
+ auto ticket = DiscIO::FindSignedTicket(title_id);
+ if (!ticket.IsValid())
+ return GetDefaultReply(FS_ENOENT);
+
+ const u64 ticket_id =
+ Memory::Read_U64(request.in_vectors[0].address + offsetof(IOS::ES::TicketView, ticket_id));
+ ticket.DeleteTicket(ticket_id);
+
+ const std::vector<u8>& new_ticket = ticket.GetRawTicket();
+ const std::string ticket_path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT);
+ {
+ File::IOFile ticket_file(ticket_path, "wb");
+ if (!ticket_file || !ticket_file.WriteBytes(new_ticket.data(), new_ticket.size()))
+ return GetDefaultReply(ES_EIO);
+ }
+
+ // Delete the ticket file if it is now empty.
+ if (new_ticket.empty())
+ File::Delete(ticket_path);
+
+ // Delete the ticket directory if it is now empty.
+ const std::string ticket_parent_dir =
+ Common::RootUserPath(Common::FROM_CONFIGURED_ROOT) +
+ StringFromFormat("/ticket/%08x", static_cast<u32>(title_id >> 32));
+ const auto ticket_parent_dir_entries = File::ScanDirectoryTree(ticket_parent_dir, false);
+ if (ticket_parent_dir_entries.children.empty())
+ File::DeleteDir(ticket_parent_dir);
+
return GetDefaultReply(IPC_SUCCESS);
}