|
|
|
|
@@ -1,10 +1,9 @@
|
|
|
|
|
#include "dll/auth.h"
|
|
|
|
|
|
|
|
|
|
#define STEAM_ID_OFFSET_TICKET (4 + 8)
|
|
|
|
|
#define STEAM_TICKET_PROCESS_TIME 0.03
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline int generate_random_int() {
|
|
|
|
|
static inline int generate_random_int()
|
|
|
|
|
{
|
|
|
|
|
int a;
|
|
|
|
|
randombytes((char *)&a, sizeof(a));
|
|
|
|
|
return a;
|
|
|
|
|
@@ -107,7 +106,7 @@ static std::vector<uint8_t> sign_auth_data(const std::string &private_key_conten
|
|
|
|
|
mbedtls_entropy_free(&entropy_ctx);
|
|
|
|
|
|
|
|
|
|
#ifndef EMU_RELEASE_BUILD
|
|
|
|
|
// we nedd a live object until the printf does its job, hence this special handling
|
|
|
|
|
// we need a live object until the printf does its job, hence this special handling
|
|
|
|
|
std::string err_msg(256, 0);
|
|
|
|
|
mbedtls_strerror(result, &err_msg[0], err_msg.size());
|
|
|
|
|
PRINT_DEBUG("failed to parse private key: %s", err_msg.c_str());
|
|
|
|
|
@@ -161,7 +160,7 @@ static std::vector<uint8_t> sign_auth_data(const std::string &private_key_conten
|
|
|
|
|
signature.clear();
|
|
|
|
|
|
|
|
|
|
#ifndef EMU_RELEASE_BUILD
|
|
|
|
|
// we nedd a live object until the printf does its job, hence this special handling
|
|
|
|
|
// we need a live object until the printf does its job, hence this special handling
|
|
|
|
|
std::string err_msg(256, 0);
|
|
|
|
|
mbedtls_strerror(result, &err_msg[0], err_msg.size());
|
|
|
|
|
PRINT_DEBUG("RSA signing failed: %s", err_msg.c_str());
|
|
|
|
|
@@ -219,25 +218,18 @@ std::vector<uint8_t> AppTicketGC::Serialize() const
|
|
|
|
|
{
|
|
|
|
|
const uint64_t steam_id = id.ConvertToUint64();
|
|
|
|
|
|
|
|
|
|
// must be 52
|
|
|
|
|
// must be 24
|
|
|
|
|
constexpr size_t total_size =
|
|
|
|
|
sizeof(STEAM_APPTICKET_GCLen) +
|
|
|
|
|
sizeof(GCToken) +
|
|
|
|
|
sizeof(steam_id) +
|
|
|
|
|
sizeof(ticketGenDate) +
|
|
|
|
|
sizeof(STEAM_APPTICKET_SESSIONLEN) +
|
|
|
|
|
sizeof(one) +
|
|
|
|
|
sizeof(two) +
|
|
|
|
|
sizeof(ExternalIP) +
|
|
|
|
|
sizeof(InternalIP) +
|
|
|
|
|
sizeof(TimeSinceStartup) +
|
|
|
|
|
sizeof(TicketGeneratedCount);
|
|
|
|
|
sizeof(ticketGenDate);
|
|
|
|
|
|
|
|
|
|
// check the size at compile time, we must ensure the correct size
|
|
|
|
|
#ifndef EMU_RELEASE_BUILD
|
|
|
|
|
static_assert(
|
|
|
|
|
total_size == 52,
|
|
|
|
|
"AUTH::AppTicketGC::SER calculated size of serialized data != 52 bytes, your compiler has some incorrect sizes"
|
|
|
|
|
total_size == 24,
|
|
|
|
|
"AUTH::AppTicketGC::SER calculated size of serialized data != 24 bytes, your compiler has some incorrect sizes"
|
|
|
|
|
);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
@@ -246,15 +238,11 @@ std::vector<uint8_t> AppTicketGC::Serialize() const
|
|
|
|
|
" GCToken: " "%" PRIu64 "\n"
|
|
|
|
|
" user steam_id: " "%" PRIu64 "\n"
|
|
|
|
|
" ticketGenDate: %u\n"
|
|
|
|
|
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
|
|
|
|
|
" TimeSinceStartup: %u, TicketGeneratedCount: %u\n"
|
|
|
|
|
" SER size = %zu",
|
|
|
|
|
|
|
|
|
|
GCToken,
|
|
|
|
|
steam_id,
|
|
|
|
|
ticketGenDate,
|
|
|
|
|
ExternalIP, InternalIP,
|
|
|
|
|
TimeSinceStartup, TicketGeneratedCount,
|
|
|
|
|
total_size
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
@@ -271,6 +259,58 @@ pBuffer += sizeof(v)
|
|
|
|
|
SER_VAR(GCToken);
|
|
|
|
|
SER_VAR(steam_id);
|
|
|
|
|
SER_VAR(ticketGenDate);
|
|
|
|
|
|
|
|
|
|
#undef SER_VAR
|
|
|
|
|
|
|
|
|
|
#ifndef EMU_RELEASE_BUILD
|
|
|
|
|
// we nedd a live object until the printf does its job, hence this special handling
|
|
|
|
|
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
|
|
|
|
|
PRINT_DEBUG("final data [%zu bytes]:\n %s", buffer.size(), str.c_str());
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> AppTicketSession::Serialize() const
|
|
|
|
|
{
|
|
|
|
|
// must be 28
|
|
|
|
|
constexpr size_t total_size =
|
|
|
|
|
sizeof(STEAM_APPTICKET_SESSIONLEN) +
|
|
|
|
|
sizeof(one) +
|
|
|
|
|
sizeof(two) +
|
|
|
|
|
sizeof(ExternalIP) +
|
|
|
|
|
sizeof(InternalIP) +
|
|
|
|
|
sizeof(TimeSinceStartup) +
|
|
|
|
|
sizeof(TicketGeneratedCount);
|
|
|
|
|
|
|
|
|
|
// check the size at compile time, we must ensure the correct size
|
|
|
|
|
#ifndef EMU_RELEASE_BUILD
|
|
|
|
|
static_assert(
|
|
|
|
|
total_size == 28,
|
|
|
|
|
"AUTH::AppTicketSession::SER calculated size of serialized data != 28 bytes, your compiler has some incorrect sizes"
|
|
|
|
|
);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
PRINT_DEBUG(
|
|
|
|
|
"\n"
|
|
|
|
|
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
|
|
|
|
|
" TimeSinceStartup: %u, TicketGeneratedCount: %u\n"
|
|
|
|
|
" SER size = %zu",
|
|
|
|
|
|
|
|
|
|
ExternalIP, InternalIP,
|
|
|
|
|
TimeSinceStartup, TicketGeneratedCount,
|
|
|
|
|
total_size
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> buffer{};
|
|
|
|
|
buffer.resize(total_size);
|
|
|
|
|
|
|
|
|
|
uint8_t *pBuffer = &buffer[0];
|
|
|
|
|
|
|
|
|
|
#define SER_VAR(v) \
|
|
|
|
|
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
|
|
|
|
|
pBuffer += sizeof(v)
|
|
|
|
|
|
|
|
|
|
SER_VAR(STEAM_APPTICKET_SESSIONLEN);
|
|
|
|
|
SER_VAR(one);
|
|
|
|
|
SER_VAR(two);
|
|
|
|
|
@@ -444,7 +484,9 @@ std::vector<uint8_t> Auth_Data::Serialize() const
|
|
|
|
|
/*
|
|
|
|
|
* layout of Auth_Data with GC:
|
|
|
|
|
* ------------------------
|
|
|
|
|
* X bytes: GC data blob (currently 52 bytes)
|
|
|
|
|
* X bytes: GC data blob (currently 24 bytes)
|
|
|
|
|
* ------------------------
|
|
|
|
|
* X bytes: session data blob (currently 28 bytes)
|
|
|
|
|
* ------------------------
|
|
|
|
|
* 4 bytes: remaining Auth_Data blob size (4 + Y + Z)
|
|
|
|
|
* ------------------------
|
|
|
|
|
@@ -502,12 +544,15 @@ std::vector<uint8_t> Auth_Data::Serialize() const
|
|
|
|
|
size_t total_size_without_siglen = ticket_data_layout_length;
|
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> GCData{};
|
|
|
|
|
std::vector<uint8_t> SessionData{};
|
|
|
|
|
size_t gc_data_layout_length = 0;
|
|
|
|
|
if (HasGC) {
|
|
|
|
|
/*
|
|
|
|
|
* layout of GC data:
|
|
|
|
|
* ------------------------
|
|
|
|
|
* X bytes: GC data blob (currently 52 bytes)
|
|
|
|
|
* X bytes: GC data blob (currently 24 bytes)
|
|
|
|
|
* ------------------------
|
|
|
|
|
* X bytes: session data blob (currently 28 bytes)
|
|
|
|
|
* ------------------------
|
|
|
|
|
* 4 bytes: remaining Auth_Data blob size
|
|
|
|
|
* ------------------------
|
|
|
|
|
@@ -515,9 +560,12 @@ std::vector<uint8_t> Auth_Data::Serialize() const
|
|
|
|
|
* total layout length = X + 4
|
|
|
|
|
*/
|
|
|
|
|
GCData = GC.Serialize();
|
|
|
|
|
gc_data_layout_length +=
|
|
|
|
|
GCData.size() +
|
|
|
|
|
sizeof(uint32_t);
|
|
|
|
|
gc_data_layout_length += GCData.size();
|
|
|
|
|
if (HasSession) {
|
|
|
|
|
SessionData = Session.Serialize();
|
|
|
|
|
gc_data_layout_length += SessionData.size();
|
|
|
|
|
}
|
|
|
|
|
gc_data_layout_length += sizeof(uint32_t);
|
|
|
|
|
|
|
|
|
|
total_size_without_siglen += gc_data_layout_length;
|
|
|
|
|
}
|
|
|
|
|
@@ -542,6 +590,12 @@ pBuffer += sizeof(v)
|
|
|
|
|
memcpy(pBuffer, GCData.data(), GCData.size());
|
|
|
|
|
pBuffer += GCData.size();
|
|
|
|
|
|
|
|
|
|
// Handle session data if applicable
|
|
|
|
|
if (HasSession) {
|
|
|
|
|
memcpy(pBuffer, SessionData.data(), SessionData.size());
|
|
|
|
|
pBuffer += SessionData.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// when GC data is written (HasGC),
|
|
|
|
|
// the next 4 bytes after the GCData will be the length of the remaining data in the final buffer
|
|
|
|
|
// i.e. final buffer size - length of GCData layout
|
|
|
|
|
@@ -630,25 +684,25 @@ void Auth_Manager::launch_callback_gs_steam2(CSteamID steam_id, uint32_t user_id
|
|
|
|
|
GSClientSteam2Accept_t data2{};
|
|
|
|
|
data2.m_UserID = user_id;
|
|
|
|
|
data2.m_SteamID = steam_id.ConvertToUint64();
|
|
|
|
|
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2));
|
|
|
|
|
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
|
|
|
|
|
// Fire Steam3 callback.
|
|
|
|
|
GSClientApprove_t data3{};
|
|
|
|
|
data3.m_SteamID = data3.m_OwnerSteamID = steam_id;
|
|
|
|
|
callbacks->addCBResult(data3.k_iCallback, &data3, sizeof(data3));
|
|
|
|
|
callbacks->addCBResult(data3.k_iCallback, &data3, sizeof(data3), STEAM_TICKET_PROCESS_TIME * 2.0);
|
|
|
|
|
} else {
|
|
|
|
|
// Fire Steam2 callback. This will kick the client so no need to send Steam3 callback.
|
|
|
|
|
/*
|
|
|
|
|
GSClientSteam2Deny_t data2{};
|
|
|
|
|
data2.m_UserID = user_id;
|
|
|
|
|
data2.m_eSteamError = k_EDenyNotLoggedOn;
|
|
|
|
|
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2));
|
|
|
|
|
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
// Fire Steam3 callback. Only one of these is actually needed to kick the client.
|
|
|
|
|
GSClientDeny_t data3{};
|
|
|
|
|
data3.m_SteamID = steam_id;
|
|
|
|
|
data3.m_eDenyReason = k_EDenyNotLoggedOn; //TODO: other reasons?
|
|
|
|
|
callbacks->addCBResult(data3.k_iCallback, &data3, sizeof(data3));
|
|
|
|
|
*/
|
|
|
|
|
callbacks->addCBResult(data3.k_iCallback, &data3, sizeof(data3), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -657,16 +711,16 @@ void Auth_Manager::launch_callback_gs(CSteamID id, bool approved)
|
|
|
|
|
if (approved) {
|
|
|
|
|
GSClientApprove_t data{};
|
|
|
|
|
data.m_SteamID = data.m_OwnerSteamID = id;
|
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
} else {
|
|
|
|
|
GSClientDeny_t data{};
|
|
|
|
|
data.m_SteamID = id;
|
|
|
|
|
data.m_eDenyReason = k_EDenyNotLoggedOn; //TODO: other reasons?
|
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
|
|
|
|
Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, bool add_session_header )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
#define IP4_AS_DWORD_LITTLE_ENDIAN(a,b,c,d) (((uint32_t)d)<<24 | ((uint32_t)c)<<16 | ((uint32_t)b)<<8 | (uint32_t)a)
|
|
|
|
|
@@ -707,36 +761,50 @@ Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *p
|
|
|
|
|
if (settings->use_gc_token)
|
|
|
|
|
{
|
|
|
|
|
ticket_data.HasGC = true;
|
|
|
|
|
ticket_data.GC.GCToken = generate_random_int();
|
|
|
|
|
ticket_data.HasSession = add_session_header;
|
|
|
|
|
ticket_data.GC.GCToken = ((uint64_t)ticket_data.number << 32) | (uint64_t)generate_random_int();
|
|
|
|
|
ticket_data.GC.id = steam_id;
|
|
|
|
|
ticket_data.GC.ticketGenDate = (uint32_t)GenDate.count();
|
|
|
|
|
ticket_data.GC.ExternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1);
|
|
|
|
|
ticket_data.GC.InternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1);
|
|
|
|
|
ticket_data.GC.TimeSinceStartup = (uint32_t)std::chrono::duration_cast<std::chrono::seconds>(curTime - startup_time).count();
|
|
|
|
|
ticket_data.GC.TicketGeneratedCount = get_ticket_count();
|
|
|
|
|
ticket_data.Session.ExternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1);
|
|
|
|
|
ticket_data.Session.InternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1);
|
|
|
|
|
ticket_data.Session.TimeSinceStartup = (uint32_t)std::chrono::duration_cast<std::chrono::seconds>(curTime - startup_time).count();
|
|
|
|
|
ticket_data.Session.TicketGeneratedCount = get_ticket_count();
|
|
|
|
|
}
|
|
|
|
|
std::vector<uint8_t> ser = ticket_data.Serialize();
|
|
|
|
|
uint32_t ser_size = static_cast<uint32_t>(ser.size());
|
|
|
|
|
*pcbTicket = ser_size;
|
|
|
|
|
if (cbMaxTicket > 0 && static_cast<uint32_t>(cbMaxTicket) >= ser_size) {
|
|
|
|
|
if (static_cast<uint32_t>(cbMaxTicket) >= ser_size) {
|
|
|
|
|
memcpy(pTicket, ser.data(), ser_size);
|
|
|
|
|
*pcbTicket = ser_size;
|
|
|
|
|
} else {
|
|
|
|
|
*pcbTicket = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memset(pTicket, 123, cbMaxTicket);
|
|
|
|
|
((char *)pTicket)[0] = 0x14;
|
|
|
|
|
((char *)pTicket)[1] = 0;
|
|
|
|
|
((char *)pTicket)[2] = 0;
|
|
|
|
|
((char *)pTicket)[3] = 0;
|
|
|
|
|
uint64 steam_id_buff = steam_id.ConvertToUint64();
|
|
|
|
|
memcpy((char *)pTicket + STEAM_ID_OFFSET_TICKET, &steam_id_buff, sizeof(steam_id_buff));
|
|
|
|
|
*pcbTicket = STEAM_TICKET_MIN_SIZE;
|
|
|
|
|
uint32 ttt = generate_steam_ticket_id();
|
|
|
|
|
ticket_data.id = steam_id;
|
|
|
|
|
ticket_data.number = ttt;
|
|
|
|
|
ticket_data.number = generate_steam_ticket_id();
|
|
|
|
|
|
|
|
|
|
memcpy(((char *)pTicket) + sizeof(uint64), &ttt, sizeof(ttt));
|
|
|
|
|
memset(pTicket, 0, cbMaxTicket);
|
|
|
|
|
|
|
|
|
|
uint32_t gc_len = STEAM_APPTICKET_GCLen;
|
|
|
|
|
uint64_t token = ((uint64_t)ticket_data.number << 32) | (uint64_t)generate_random_int();
|
|
|
|
|
uint64_t steam_id_buff = steam_id.ConvertToUint64();
|
|
|
|
|
uint32_t ticket_gen_date = (uint32_t)std::chrono::system_clock::now().time_since_epoch().count();
|
|
|
|
|
|
|
|
|
|
uint8_t *pBuffer = (uint8_t *)pTicket;
|
|
|
|
|
|
|
|
|
|
#define SER_VAR(v) \
|
|
|
|
|
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
|
|
|
|
|
pBuffer += sizeof(v)
|
|
|
|
|
|
|
|
|
|
SER_VAR(gc_len);
|
|
|
|
|
SER_VAR(token);
|
|
|
|
|
SER_VAR(steam_id_buff);
|
|
|
|
|
SER_VAR(ticket_gen_date);
|
|
|
|
|
|
|
|
|
|
#undef SER_VAR
|
|
|
|
|
|
|
|
|
|
*pcbTicket = STEAM_TICKET_MIN_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef IP4_AS_DWORD_LITTLE_ENDIAN
|
|
|
|
|
@@ -756,9 +824,9 @@ HAuthTicket Auth_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcb
|
|
|
|
|
if (cbMaxTicket > STEAM_AUTH_TICKET_SIZE) cbMaxTicket = STEAM_AUTH_TICKET_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Auth_Data ticket_data = getTicketData(pTicket, cbMaxTicket, pcbTicket );
|
|
|
|
|
Auth_Data ticket_data = getTicketData(pTicket, cbMaxTicket, pcbTicket, true);
|
|
|
|
|
|
|
|
|
|
if (*pcbTicket > static_cast<uint32>(cbMaxTicket)) {
|
|
|
|
|
if (*pcbTicket == 0) {
|
|
|
|
|
return k_HAuthTicketInvalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -823,68 +891,115 @@ bool Auth_Manager::SendSteam2UserConnect( uint32 unUserID, const void *pvRawKey,
|
|
|
|
|
// pvCookie is Steam3 auth ticket, it comes from us.
|
|
|
|
|
// Steam3 ticket is technically optional but it should always be there if the client is using Goldberg
|
|
|
|
|
// so there's no real need to check Steam2 ticket.
|
|
|
|
|
if (cubCookie < STEAM_TICKET_MIN_SIZE) return false;
|
|
|
|
|
|
|
|
|
|
Auth_Data data;
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pvCookie + STEAM_ID_OFFSET_TICKET, sizeof(id));
|
|
|
|
|
uint32 number;
|
|
|
|
|
memcpy(&number, ((char *)pvCookie) + sizeof(uint64), sizeof(number));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = number;
|
|
|
|
|
if (pSteamIDUser) *pSteamIDUser = data.id;
|
|
|
|
|
// HACK: Generate Steam ID from IP address for unknown clients.
|
|
|
|
|
CSteamID fallbackID(unIPPublic, k_EUniversePublic, k_EAccountTypeIndividual);
|
|
|
|
|
Auth_Data data = validateTicket(pvCookie, cubCookie, fallbackID, pSteamIDUser);
|
|
|
|
|
if (!data.id.IsValid())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (auto & t : inbound) {
|
|
|
|
|
if (t.id == data.id) {
|
|
|
|
|
//Should this return false?
|
|
|
|
|
launch_callback_gs_steam2(id, unUserID, true);
|
|
|
|
|
launch_callback_gs_steam2(data.id, unUserID, true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inbound.push_back(data);
|
|
|
|
|
launch_callback_gs_steam2(id, unUserID, true);
|
|
|
|
|
launch_callback_gs_steam2(data.id, unUserID, true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Auth_Manager::SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser )
|
|
|
|
|
{
|
|
|
|
|
if (cubAuthBlobSize < STEAM_TICKET_MIN_SIZE) return false;
|
|
|
|
|
|
|
|
|
|
Auth_Data data;
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pvAuthBlob + STEAM_ID_OFFSET_TICKET, sizeof(id));
|
|
|
|
|
uint32 number;
|
|
|
|
|
memcpy(&number, ((char *)pvAuthBlob) + sizeof(uint64), sizeof(number));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = number;
|
|
|
|
|
if (pSteamIDUser) *pSteamIDUser = data.id;
|
|
|
|
|
// HACK: Generate Steam ID from IP address for unknown clients.
|
|
|
|
|
CSteamID fallbackID(unIPClient, k_EUniversePublic, k_EAccountTypeIndividual);
|
|
|
|
|
Auth_Data data = validateTicket(pvAuthBlob, cubAuthBlobSize, fallbackID, pSteamIDUser);
|
|
|
|
|
if (!data.id.IsValid())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (auto & t : inbound) {
|
|
|
|
|
if (t.id == data.id) {
|
|
|
|
|
//Should this return false?
|
|
|
|
|
launch_callback_gs(id, true);
|
|
|
|
|
launch_callback_gs(data.id, true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inbound.push_back(data);
|
|
|
|
|
launch_callback_gs(id, true);
|
|
|
|
|
launch_callback_gs(data.id, true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EBeginAuthSessionResult Auth_Manager::beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
|
|
|
|
|
Auth_Data Auth_Manager::validateTicket(const void *pAuthTicket, uint32 cbAuthTicket, CSteamID fallbackID, CSteamID *pSteamIDUser)
|
|
|
|
|
{
|
|
|
|
|
if (cbAuthTicket < STEAM_TICKET_MIN_SIZE) return k_EBeginAuthSessionResultInvalidTicket;
|
|
|
|
|
if (cbAuthTicket < STEAM_TICKET_MIN_SIZE)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
Auth_Data data;
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pAuthTicket + STEAM_ID_OFFSET_TICKET, sizeof(id));
|
|
|
|
|
uint32 number;
|
|
|
|
|
memcpy(&number, ((char *)pAuthTicket) + sizeof(uint64), sizeof(number));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = number;
|
|
|
|
|
if (*(uint32 *)pAuthTicket == 0x554d4548) { // "HEMU"
|
|
|
|
|
// This is a SmartSteamEmu ticket.
|
|
|
|
|
// 0x00: header magic
|
|
|
|
|
// 0x04: ticket version
|
|
|
|
|
// 0x08: external IP
|
|
|
|
|
// 0x0c: zero
|
|
|
|
|
// 0x10: Steam ID
|
|
|
|
|
// rest is unknown
|
|
|
|
|
PRINT_DEBUG("SmartSteamEmu ticket detected");
|
|
|
|
|
uint32 version;
|
|
|
|
|
memcpy(&version, (char *)pAuthTicket + 0x04, sizeof(version));
|
|
|
|
|
if (version != 315)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pAuthTicket + 0x10, sizeof(id));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = 0;
|
|
|
|
|
} else if (*(uint32 *)((char *)pAuthTicket + 0x08) == 0x726576) {
|
|
|
|
|
// This is a RevEmu ticket.
|
|
|
|
|
PRINT_DEBUG("RevEmu ticket detected");
|
|
|
|
|
uint32 version;
|
|
|
|
|
memcpy(&version, (char *)pAuthTicket + 0x00, sizeof(version));
|
|
|
|
|
if (version < 83 || cbAuthTicket < 0xa8)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pAuthTicket + 0x10, sizeof(id));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = 0;
|
|
|
|
|
} else if (*(uint32 *)pAuthTicket == STEAM_APPTICKET_GCLen) {
|
|
|
|
|
// Assume this is a standard Steam ticket. Used by us and real Steam.
|
|
|
|
|
// TODO: More robust checks.
|
|
|
|
|
PRINT_DEBUG("standard ticket detected");
|
|
|
|
|
uint32 number;
|
|
|
|
|
memcpy(&number, ((char *)pAuthTicket) + 0x08, sizeof(number));
|
|
|
|
|
uint64 id;
|
|
|
|
|
memcpy(&id, (char *)pAuthTicket + 0x0c, sizeof(id));
|
|
|
|
|
data.id = CSteamID(id);
|
|
|
|
|
data.number = number;
|
|
|
|
|
} else if (settings->block_unknown_clients) {
|
|
|
|
|
PRINT_DEBUG("unrecognized ticket format, rejecting");
|
|
|
|
|
return {};
|
|
|
|
|
} else if (fallbackID.IsValid()) {
|
|
|
|
|
PRINT_DEBUG("unrecognized ticket format");
|
|
|
|
|
data.id = fallbackID;
|
|
|
|
|
data.number = 0;
|
|
|
|
|
} else {
|
|
|
|
|
PRINT_DEBUG("unrecognized ticket format and no fallback steam id is provided, rejecting");
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data.created = std::chrono::high_resolution_clock::now();
|
|
|
|
|
if (pSteamIDUser) *pSteamIDUser = data.id;
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EBeginAuthSessionResult Auth_Manager::beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID)
|
|
|
|
|
{
|
|
|
|
|
Auth_Data data = validateTicket(pAuthTicket, cbAuthTicket, steamID, nullptr);
|
|
|
|
|
if (!data.id.IsValid() || data.id != steamID)
|
|
|
|
|
return k_EBeginAuthSessionResultInvalidTicket;
|
|
|
|
|
|
|
|
|
|
for (auto & t : inbound) {
|
|
|
|
|
if (t.id == data.id && !check_timedout(t.created, STEAM_TICKET_PROCESS_TIME)) {
|
|
|
|
|
|