dolphin icon indicating copy to clipboard operation
dolphin copied to clipboard

Add Approved Patch Allowlist for Achievements

Open LillyJadeKatrin opened this issue 1 year ago • 11 comments

Prototype of a system to whitelist known game patches that are allowed to be used while RetroAchievements Hardcore mode is active. ApprovedInis.txt contains known hashes for the ini files as they appear in the repo, and can be compared to the local versions of these files to ensure they have not been edited locally by the player. ApprovedInis.txt is hashed and verified similarly first, with its hash residing as a const string within AchievementManager.h, ensuring ApprovedInis and the hashes within cannot be modified without editing Dolphin's source code and recompiling completely.

LillyJadeKatrin avatar Jun 22 '24 03:06 LillyJadeKatrin

While the concept is reasonable, hashing the entire INI seems very restrictive.

If I'm reading the current version of the code correctly, if the user has edited the Sys or the User INI for any reason (e.g. to automatically load a specific controller profile upon loading the game, to apply custom graphics settings, to automatically enable/disable Dual Core or other settings), it will get blocked if Hardcore mode is enabled, even if such edits doesn't touch any settings restricted by it.

Ideally, only the [OnFrame] section (maybe [OnFrameEnabled] too) should be hashed/validated (perhaps parsing them and hashing the resulting object in memory, that way comments or other formatting differences can be ignored too), to still allow enforcing which patches can be loaded in Hardcore mode without completely blocking any other edit the user may have made to the INI.

Speaking of the ApprovedInis.txt, maybe I'm reading too much into it but having the only safety against tampering being a hash comparison with a known hash embedded in the source code doesn't seem exactly foolproof either. Yes, Dolphin would have to be recompiled in order to accept a tampered ApprovedInis.txt, but I feel like something external (read as "not as simple as just recompiling the code") should be enforcing the hash integrity too...

mbc07 avatar Jun 22 '24 06:06 mbc07

While I think it's fine to reject edited Sys INIs, as the user isn't supposed to edit those anyway, I do agree that hashing the patches rather than the whole INI would be cleaner.

To ensure nobody updates the INIs and forgets to update the hashes, how about adding a unit test that checks that all hashes match? I suppose this would require you to list file names in ApprovedInis.txt.

Speaking of the ApprovedInis.txt, maybe I'm reading too much into it but having the only safety against tampering being a hash comparison with a known hash embedded in the source code doesn't seem exactly foolproof either. Yes, Dolphin would have to be recompiled in order to accept a tampered ApprovedInis.txt, but I feel like something external (read as "not as simple as just recompiling the code") should be enforcing the hash integrity too...

If the user recompiles Dolphin, the security model is screwed anyway. You could easily do something like telling the server hardcode mode is enabled when it isn't.

JosJuice avatar Jun 22 '24 06:06 JosJuice

Good feedback, I'm glad this is a good first step. I'll look into how to apply it to the patches more directly. I got someone asking if there was a way to allow specific AR or Gecko codes so while that isn't critical at this time I'll try to consider it.

Speaking of the ApprovedInis.txt, maybe I'm reading too much into it but having the only safety against tampering being a hash comparison with a known hash embedded in the source code doesn't seem exactly foolproof either. Yes, Dolphin would have to be recompiled in order to accept a tampered ApprovedInis.txt, but I feel like something external (read as "not as simple as just recompiling the code") should be enforcing the hash integrity too...

My exact personal cutoff for security is "if someone can cause problems without editing source and recompiling, block it". Honestly RetroAchievements' server is only so secure and it wouldn't be that difficult for someone to [REDACTED] if they know how to do relatively simple coding concepts, even writing a program completely from scratch. So I figure this is a reasonable endpoint to catch everyone who can't code. At that point, it's probably just less effort to play the damn game and earn it the normal way.

LillyJadeKatrin avatar Jun 22 '24 12:06 LillyJadeKatrin

Undertested, but perhaps a fair bit closer to the goal.

LillyJadeKatrin avatar Jun 24 '24 05:06 LillyJadeKatrin

A more general question... is there any need for Gecko or AR codes in this system at all? I'm pretty sure all of our 'fix game' patches use our built-in patch format (can you find a counterexample?), and I'm looking at the code for AR codes here and I'm pretty sure the way you're handling these is very naive -- I haven't actually tested this, but I suspect I could enter a code while a game is running and it would not pass through the approval check, and for that matter it would probably end up deleting all my non-approved codes from my user ini if I modified the code list in hardcore mode. So to reduce complexity here, can we reduce this down to just Patches?

AdmiralCurtiss avatar Jun 27 '24 19:06 AdmiralCurtiss

Also I just realized that you're re-parsing and re-hashing the entire approved code list for every single code you check. Please just do that once on AchievementManager init.

AdmiralCurtiss avatar Jun 27 '24 19:06 AdmiralCurtiss

Also I just realized that you're re-parsing and re-hashing the entire approved code list for every single code you check. Please just do that once on AchievementManager init.

I suppose I could keep the list in memory, then - otherwise I'd have no real way to guarantee that the player didn't mess with it immediately before booting a game, which was my mindset for the current way.

LillyJadeKatrin avatar Jun 28 '24 01:06 LillyJadeKatrin

A more general question... is there any need for Gecko or AR codes in this system at all? I'm pretty sure all of our 'fix game' patches use our built-in patch format (can you find a counterexample?), and I'm looking at the code for AR codes here and I'm pretty sure the way you're handling these is very naive -- I haven't actually tested this, but I suspect I could enter a code while a game is running and it would not pass through the approval check, and for that matter it would probably end up deleting all my non-approved codes from my user ini if I modified the code list in hardcore mode. So to reduce complexity here, can we reduce this down to just Patches?

I can for the time being but I know there's definitely people who have been bugging me about their precious 60fps widescreen hacks and I was under the impression that that's where those hacks were applied.

LillyJadeKatrin avatar Jun 28 '24 01:06 LillyJadeKatrin

Sure, but is that essential to launching this feature? I mean we can do it, but I just hope you're aware that auditing three systems for any potential issues is a lot more work than just one.

AdmiralCurtiss avatar Jun 28 '24 14:06 AdmiralCurtiss

I suppose I could keep the list in memory, then

Yes, obviously...? What else would you even suggest?

AdmiralCurtiss avatar Jun 28 '24 14:06 AdmiralCurtiss

I suppose I could keep the list in memory, then

Yes, obviously...? What else would you even suggest?

Simply that this runs once upon startup of each game. I initially thought it would be preferable to allow that process to take a small amount of extra time for the benefit of not consuming that patch of unused memory when games need a lot of memory available. Apologies for being wrong.

LillyJadeKatrin avatar Jun 29 '24 23:06 LillyJadeKatrin

Oh, running once per game is not a bad idea, if you can find a good time to actually unload it. But I wouldn't worry too much about it, this will take a few kilobytes at most.

And sorry if that came across as mean, that wasn't my intention, I just genuinely couldn't think of any other way to interpret my request.

AdmiralCurtiss avatar Jun 30 '24 19:06 AdmiralCurtiss

This change has been absorbed into #12913

LillyJadeKatrin avatar Jul 07 '24 17:07 LillyJadeKatrin