Merge pull request #6896 from leoetlino/title-import
ES: Fix content check in ImportTitleDone
This commit is contained in:
commit
fbf79f837f
|
@ -931,7 +931,10 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
|
||||||
Common::ScopeGuard ca_guard{[&] { iosc.DeleteObject(handle, PID_ES); }};
|
Common::ScopeGuard ca_guard{[&] { iosc.DeleteObject(handle, PID_ES); }};
|
||||||
ret = iosc.ImportCertificate(ca_cert.GetBytes().data(), IOSC::HANDLE_ROOT_KEY, handle, PID_ES);
|
ret = iosc.ImportCertificate(ca_cert.GetBytes().data(), IOSC::HANDLE_ROOT_KEY, handle, PID_ES);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(ca) failed with error %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
IOSC::Handle issuer_handle;
|
IOSC::Handle issuer_handle;
|
||||||
const IOSC::ObjectSubType subtype =
|
const IOSC::ObjectSubType subtype =
|
||||||
|
@ -942,7 +945,10 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
|
||||||
Common::ScopeGuard issuer_guard{[&] { iosc.DeleteObject(issuer_handle, PID_ES); }};
|
Common::ScopeGuard issuer_guard{[&] { iosc.DeleteObject(issuer_handle, PID_ES); }};
|
||||||
ret = iosc.ImportCertificate(issuer_cert.GetBytes().data(), handle, issuer_handle, PID_ES);
|
ret = iosc.ImportCertificate(issuer_cert.GetBytes().data(), handle, issuer_handle, PID_ES);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(issuer) failed with error %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the SHA1 of the signed blob.
|
// Calculate the SHA1 of the signed blob.
|
||||||
const size_t skip = type == VerifyContainerType::Device ? offsetof(SignatureECC, issuer) :
|
const size_t skip = type == VerifyContainerType::Device ? offsetof(SignatureECC, issuer) :
|
||||||
|
@ -955,7 +961,10 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
|
||||||
const std::vector<u8> signature = signed_blob.GetSignatureData();
|
const std::vector<u8> signature = signed_blob.GetSignatureData();
|
||||||
ret = iosc.VerifyPublicKeySign(sha1, issuer_handle, signature.data(), PID_ES);
|
ret = iosc.VerifyPublicKeySign(sha1, issuer_handle, signature.data(), PID_ES);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_VerifyPublicKeySign failed with error %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode == VerifyMode::UpdateCertStore)
|
if (mode == VerifyMode::UpdateCertStore)
|
||||||
{
|
{
|
||||||
|
@ -970,7 +979,10 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
|
||||||
|
|
||||||
// Import the signed blob to iosc_handle (if a handle was passed to us).
|
// Import the signed blob to iosc_handle (if a handle was passed to us).
|
||||||
if (ret == IPC_SUCCESS && iosc_handle)
|
if (ret == IPC_SUCCESS && iosc_handle)
|
||||||
|
{
|
||||||
ret = iosc.ImportCertificate(signed_blob.GetBytes().data(), issuer_handle, iosc_handle, PID_ES);
|
ret = iosc.ImportCertificate(signed_blob.GetBytes().data(), issuer_handle, iosc_handle, PID_ES);
|
||||||
|
ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(final) failed with error %d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,8 @@ static void ResetTitleImportContext(ES::Context* context, IOSC& iosc)
|
||||||
|
|
||||||
ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
|
ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
|
||||||
{
|
{
|
||||||
|
INFO_LOG(IOS_ES, "ImportTmd");
|
||||||
|
|
||||||
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
|
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
|
||||||
// to either /import or /title. So here we simply have to set the import TMD.
|
// to either /import or /title. So here we simply have to set the import TMD.
|
||||||
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
||||||
|
@ -155,16 +157,26 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
|
||||||
ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
|
ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
|
||||||
context.title_import_export.tmd, cert_store);
|
context.title_import_export.tmd, cert_store);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTmd: VerifyContainer failed with error %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (!InitImport(context.title_import_export.tmd))
|
if (!InitImport(context.title_import_export.tmd))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTmd: Failed to initialise title import");
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
ret =
|
ret =
|
||||||
InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle);
|
InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTmd: InitBackupKey failed with error %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG(IOS_ES, "ImportTmd: All checks passed, marking context as valid");
|
||||||
context.title_import_export.valid = true;
|
context.title_import_export.valid = true;
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -418,37 +430,54 @@ IPCCommandResult ES::ImportContentEnd(Context& context, const IOCtlVRequest& req
|
||||||
return GetDefaultReply(ImportContentEnd(context, content_fd));
|
return GetDefaultReply(ImportContentEnd(context, content_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HasAllRequiredContents(IOS::HLE::Kernel& ios, const IOS::ES::TMDReader& tmd)
|
||||||
|
{
|
||||||
|
const u64 title_id = tmd.GetTitleId();
|
||||||
|
const std::vector<IOS::ES::Content> contents = tmd.GetContents();
|
||||||
|
const IOS::ES::SharedContentMap shared_content_map{ios.GetFS()};
|
||||||
|
return std::all_of(contents.cbegin(), contents.cend(), [&](const IOS::ES::Content& content) {
|
||||||
|
if (content.IsOptional())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (content.IsShared())
|
||||||
|
return shared_content_map.GetFilenameFromSHA1(content.sha1).has_value();
|
||||||
|
|
||||||
|
// Note: the import hasn't been finalised yet, so the whole title directory
|
||||||
|
// is still in /import, not /title.
|
||||||
|
const std::string path =
|
||||||
|
Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content.id);
|
||||||
|
return ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ReturnCode ES::ImportTitleDone(Context& context)
|
ReturnCode ES::ImportTitleDone(Context& context)
|
||||||
{
|
{
|
||||||
if (!context.title_import_export.valid || context.title_import_export.content.valid)
|
if (!context.title_import_export.valid || context.title_import_export.content.valid)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTitleDone: No title import, or a content import is still in progress");
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure all listed, non-optional contents have been imported.
|
// For system titles, make sure all listed, non-optional contents have been imported.
|
||||||
const u64 title_id = context.title_import_export.tmd.GetTitleId();
|
const u64 title_id = context.title_import_export.tmd.GetTitleId();
|
||||||
const std::vector<IOS::ES::Content> contents = context.title_import_export.tmd.GetContents();
|
if (title_id - 0x100000001LL <= 0x100 &&
|
||||||
const IOS::ES::SharedContentMap shared_content_map{m_ios.GetFS()};
|
!HasAllRequiredContents(m_ios, context.title_import_export.tmd))
|
||||||
const bool has_all_required_contents =
|
{
|
||||||
std::all_of(contents.cbegin(), contents.cend(), [&](const IOS::ES::Content& content) {
|
ERROR_LOG(IOS_ES, "ImportTitleDone: Some required contents are missing");
|
||||||
if (content.IsOptional())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (content.IsShared())
|
|
||||||
return shared_content_map.GetFilenameFromSHA1(content.sha1).has_value();
|
|
||||||
|
|
||||||
// Note: the import hasn't been finalised yet, so the whole title directory
|
|
||||||
// is still in /import, not /title.
|
|
||||||
const std::string path = Common::GetImportTitlePath(title_id) +
|
|
||||||
StringFromFormat("/content/%08x.app", content.id);
|
|
||||||
return m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
|
|
||||||
});
|
|
||||||
if (!has_all_required_contents)
|
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!WriteImportTMD(context.title_import_export.tmd))
|
if (!WriteImportTMD(context.title_import_export.tmd))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTitleDone: Failed to write import TMD");
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (!FinishImport(context.title_import_export.tmd))
|
if (!FinishImport(context.title_import_export.tmd))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "ImportTitleDone: Failed to finalise title import");
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
|
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
|
||||||
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
||||||
|
|
Loading…
Reference in New Issue