MinecraftConsoles/Minecraft.Client/PS3/XML/xmlFilesCallback.h
MrTheShy 4f2352361a Add Mojang/Ely.by/offline authentication system
The old Windows64 port had no real player identity — it used hardcoded
fake XUIDs, so anyone could impersonate anyone. This replaces that with
proper auth supporting Mojang, Ely.by, and offline accounts.

MCAuth library (new, MCAuth/):
  Mojang auth via MSA device code flow (XBL, SISU, MC services),
  Ely.by via Yggdrasil with 2FA, offline UUID generation matching
  Java Edition (MD5 v3 from "OfflinePlayer:<name>"). Multi-account
  manager with background token refresh, per-slot sessions, and
  on-disk token persistence. Server-side session verification via
  Mojang/Ely.by hasJoined API. Skin fetching and PNG validation
  from texture servers.

Network protocol (version bumped to 80):
  Three new packets (AuthScheme, AuthResponse, AuthResult) implement
  a server-driven auth handshake before login completes. Player
  identity migrated from 64-bit XUID to 128-bit GameUUID backed by
  two uint64 fields (hi/lo). readPlayerUID/writePlayerUID now
  serialize 16 bytes on the wire. Old and new clients cannot connect
  to each other — version mismatch is rejected at PreLogin.

Save migration:
  Map data mappings auto-migrate from old format: the old 64-bit
  XUID is placed in hi, lo is set to 0 as a sentinel. On first
  access by the real player, the sentinel entry is upgraded in-place
  to the full 128-bit UUID. Format detection is by file size (2080,
  2112, or 4160 bytes). Player .dat filenames inside saveData.ms
  change from decimal XUID to dashed UUID — old saves need manual
  entry renaming in the archive.

UI:
  NativeUIRenderer: immediate-mode drawing system (quads, text,
  9-slice panels, scrollbars, focus lists) for rendering auth
  screens without Flash/Scaleform. UIScene_MSAuth handles device
  code display, Ely.by credential input with 2FA, per-account
  skin head preview, and multi-account add/remove/switch.

Server:
  online-mode and auth-provider (mojang/elyby) in server.properties.
  Whitelist and ban checks validate against the server-verified UUID.
  Incompatible auth scheme logs which provider the server expects
  vs what the client is using.

Also fixes a pre-existing exploit where any client could send a
DebugOptionsPacket to grant themselves CraftAnything and other debug
privileges on any server — now requires OP status server-side.
2026-03-23 01:14:23 +01:00

236 lines
6.1 KiB
C++

#pragma once
#ifndef XMLMOJANGCALLBACK_H
#define XMLMOJANGCALLBACK_H
// xml reading
using namespace ATG;
class xmlMojangCallback : public ATG::ISAXCallback
{
public:
virtual HRESULT StartDocument() { return S_OK; };
virtual HRESULT EndDocument() { return S_OK; };
virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, CONST XMLAttribute *pAttributes, UINT NumAttributes )
{
WCHAR wTemp[35] = L"";
WCHAR wAttName[32] = L"";
WCHAR wNameXUID[32] = L"";
WCHAR wNameSkin[32] = L"";
WCHAR wNameCloak[32] = L"";
PlayerUID xuid = INVALID_XUID;
if (NameLen >31)
return S_FALSE;
else
wcsncpy( wAttName, strName, NameLen);
if ( _wcsicmp(wAttName,L"root") == 0)
{
return S_OK;
}
else if ( _wcsicmp(wAttName,L"data") == 0)
{
for(UINT i = 0; i < NumAttributes; i++)
{
wcsncpy_s( wAttName, pAttributes[i].strName, pAttributes[i].NameLen);
if (_wcsicmp(wAttName,L"name")==0)
{
if (pAttributes[i].ValueLen <= 32)
wcsncpy_s( wNameXUID, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
else if (_wcsicmp(wAttName,L"xuid")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
ZeroMemory(wTemp,sizeof(WCHAR)*35);
wcsncpy_s( wTemp, pAttributes[i].strValue, pAttributes[i].ValueLen);
{
char narrow[64] = {};
wcstombs_s(nullptr, narrow, wTemp, _TRUNCATE);
xuid = PlayerUID::fromDashed(std::string(narrow));
}
}
}
else if (_wcsicmp(wAttName,L"cape")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wNameCloak, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
else if (_wcsicmp(wAttName,L"skin")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wNameSkin, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
}
// if the xuid hasn't been defined, then we can't use the data
if(xuid.isValid())
{
return CConsoleMinecraftApp::RegisterMojangData(wNameXUID , xuid, wNameSkin, wNameCloak );
}
else return S_FALSE;
}
else
{
return S_FALSE;
}
};
virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) { return S_OK; };
virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ){ return S_OK; };
virtual HRESULT CDATABegin( ) { return S_OK; };
virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ){ return S_OK; };
virtual HRESULT CDATAEnd( ){ return S_OK; };
virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) { app.DebugPrintf("Error when Parsing xuids.XML\n"); };
};
class xmlDLCInfoCallback : public ATG::ISAXCallback
{
public:
virtual HRESULT StartDocument() { return S_OK; };
virtual HRESULT EndDocument() { return S_OK; };
virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, CONST XMLAttribute *pAttributes, UINT NumAttributes )
{
WCHAR wTemp[35] = L"";
WCHAR wAttName[32] = L"";
WCHAR wNameBanner[32] = L"";
WCHAR wType[32] = L"";
WCHAR wFirstSkin[32] = L"";
WCHAR wDataFile[32] = L"";
ULONGLONG ullFull=0ll;
ULONGLONG ullTrial=0ll;
int iGender=0;
int iConfig=0;
if (NameLen >31)
return S_FALSE;
else
wcsncpy_s( wAttName, strName, NameLen);
if ( _wcsicmp(wAttName,L"root") == 0)
{
return S_OK;
}
else if ( _wcsicmp(wAttName,L"data") == 0)
{
for(UINT i = 0; i < NumAttributes; i++)
{
wcsncpy_s( wAttName, pAttributes[i].strName, pAttributes[i].NameLen);
if (_wcsicmp(wAttName,L"Banner")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wNameBanner, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
else if (_wcsicmp(wAttName,L"Full")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
ZeroMemory(wTemp,sizeof(WCHAR)*35);
wcsncpy_s( wTemp, pAttributes[i].strValue, pAttributes[i].ValueLen);
ullFull=_wcstoui64(wTemp,NULL,16);
}
}
else if (_wcsicmp(wAttName,L"Trial")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
ZeroMemory(wTemp,sizeof(WCHAR)*35);
wcsncpy_s( wTemp, pAttributes[i].strValue, pAttributes[i].ValueLen);
ullTrial=_wcstoui64(wTemp,NULL,16);
}
}
else if (_wcsicmp(wAttName,L"FirstSkin")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wFirstSkin, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
else if (_wcsicmp(wAttName,L"DataFile")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wDataFile, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
else if (_wcsicmp(wAttName,L"Type")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
wcsncpy_s( wType, pAttributes[i].strValue, pAttributes[i].ValueLen);
}
}
else if (_wcsicmp(wAttName,L"Gender")==0)
{
if (_wcsicmp(wAttName,L"Male")==0)
{
iGender=1;
}
else if (_wcsicmp(wAttName,L"Female")==0)
{
iGender=2;
}
else
{
iGender=0;
}
}
else if(_wcsicmp(wAttName,L"Config")==0)
{
if (pAttributes[i].ValueLen <= 32)
{
iConfig=_wtoi(wTemp);
}
}
}
// if the xuid hasn't been defined, then we can't use the data
if(ullFull!=0LL)
{
#ifdef _DEBUG
wprintf(L"Type - %ls, Name - %ls, ",wType, wNameBanner);
#endif
app.DebugPrintf("Full = %lld, Trial %lld\n",ullFull,ullTrial);
return CConsoleMinecraftApp::RegisterDLCData(wType, wNameBanner , iGender, ullFull, ullTrial, wFirstSkin, iConfig, wDataFile );
}
else return S_FALSE;
}
else
{
return S_FALSE;
}
};
virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) { return S_OK; };
virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ){ return S_OK; };
virtual HRESULT CDATABegin( ) { return S_OK; };
virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ){ return S_OK; };
virtual HRESULT CDATAEnd( ){ return S_OK; };
virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) { app.DebugPrintf("Error when Parsing DLC.XML\n"); };
};
#endif