2023-12-27 15:21:59 +08:00
|
|
|
#include "dll/auth.h"
|
2023-12-22 03:25:37 +08:00
|
|
|
|
2023-12-22 15:04:25 +08:00
|
|
|
static inline int generate_random_int() {
|
|
|
|
int a;
|
|
|
|
randombytes((char *)&a, sizeof(a));
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32 generate_steam_ticket_id() {
|
|
|
|
/* not random starts with 2? */
|
|
|
|
static uint32 a = 1;
|
|
|
|
++a;
|
|
|
|
// this must never return 0, it is reserved for "k_HAuthTicketInvalid" when the auth APIs fail
|
|
|
|
if (a == 0) ++a;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2023-12-24 07:27:05 +08:00
|
|
|
static uint32_t get_ticket_count() {
|
|
|
|
static uint32_t a = 0;
|
|
|
|
++a;
|
|
|
|
// this must never return 0, on overflow just go back to 1 again
|
|
|
|
if (a == 0) a = 1;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2023-12-22 15:04:25 +08:00
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
static void steam_auth_manager_ticket_callback(void *object, Common_Message *msg)
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("steam_auth_manager_ticket_callback\n");
|
|
|
|
|
|
|
|
Auth_Manager *auth_manager = (Auth_Manager *)object;
|
|
|
|
auth_manager->Callback(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
Auth_Manager::Auth_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks) {
|
|
|
|
this->network = network;
|
|
|
|
this->settings = settings;
|
|
|
|
this->callbacks = callbacks;
|
|
|
|
|
|
|
|
this->network->setCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this);
|
|
|
|
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this);
|
|
|
|
}
|
|
|
|
|
2024-03-23 23:01:27 +08:00
|
|
|
Auth_Manager::~Auth_Manager()
|
|
|
|
{
|
|
|
|
this->network->rmCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this);
|
|
|
|
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this);
|
|
|
|
}
|
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
#define STEAM_TICKET_PROCESS_TIME 0.03
|
|
|
|
|
|
|
|
void Auth_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp, double delay)
|
|
|
|
{
|
2024-03-23 23:01:27 +08:00
|
|
|
ValidateAuthTicketResponse_t data{};
|
2023-12-22 03:25:37 +08:00
|
|
|
data.m_SteamID = id;
|
|
|
|
data.m_eAuthSessionResponse = resp;
|
|
|
|
data.m_OwnerSteamID = id;
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), delay);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Auth_Manager::launch_callback_gs(CSteamID id, bool approved)
|
|
|
|
{
|
|
|
|
if (approved) {
|
2024-03-23 23:01:27 +08:00
|
|
|
GSClientApprove_t data{};
|
2023-12-22 03:25:37 +08:00
|
|
|
data.m_SteamID = data.m_OwnerSteamID = id;
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
} else {
|
2024-03-23 23:01:27 +08:00
|
|
|
GSClientDeny_t data{};
|
2023-12-22 03:25:37 +08:00
|
|
|
data.m_SteamID = id;
|
|
|
|
data.m_eDenyReason = k_EDenyNotLoggedOn; //TODO: other reasons?
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define STEAM_ID_OFFSET_TICKET (4 + 8)
|
|
|
|
#define STEAM_TICKET_MIN_SIZE (4 + 8 + 8)
|
|
|
|
#define STEAM_TICKET_MIN_SIZE_NEW 170
|
2023-12-22 15:08:29 +08:00
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
|
|
|
{
|
2023-12-22 15:08:29 +08:00
|
|
|
|
|
|
|
#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)
|
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
Auth_Data ticket_data;
|
2023-12-22 15:15:22 +08:00
|
|
|
CSteamID steam_id = settings->get_local_steam_id();
|
2023-12-22 03:25:37 +08:00
|
|
|
if (settings->enable_new_app_ticket)
|
|
|
|
{
|
2023-12-22 15:15:22 +08:00
|
|
|
ticket_data.id = steam_id;
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.number = generate_steam_ticket_id();
|
|
|
|
ticket_data.Ticket.Version = 4;
|
2023-12-22 15:15:22 +08:00
|
|
|
ticket_data.Ticket.id = steam_id;
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.Ticket.AppId = settings->get_local_game_id().AppID();
|
2023-12-22 15:08:29 +08:00
|
|
|
ticket_data.Ticket.ExternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1); //TODO
|
|
|
|
ticket_data.Ticket.InternalIP = IP4_AS_DWORD_LITTLE_ENDIAN(127, 0, 0, 1);
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.Ticket.AlwaysZero = 0;
|
|
|
|
const auto curTime = std::chrono::system_clock::now();
|
2023-12-22 15:12:52 +08:00
|
|
|
const auto GenDate = std::chrono::duration_cast<std::chrono::seconds>(curTime.time_since_epoch());
|
|
|
|
ticket_data.Ticket.TicketGeneratedDate = (uint32_t)GenDate.count();
|
|
|
|
uint32_t expTime = (uint32_t)(GenDate + std::chrono::hours(24)).count();
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.Ticket.TicketGeneratedExpireDate = expTime;
|
|
|
|
ticket_data.Ticket.Licenses.resize(0);
|
2023-12-22 15:15:46 +08:00
|
|
|
ticket_data.Ticket.Licenses.push_back(0); //TODO
|
2023-12-22 03:25:37 +08:00
|
|
|
unsigned int dlcCount = settings->DLCCount();
|
|
|
|
ticket_data.Ticket.DLCs.resize(0); //currently set to 0
|
2023-12-22 15:14:03 +08:00
|
|
|
for (int i = 0; i < dlcCount; ++i)
|
2023-12-22 03:25:37 +08:00
|
|
|
{
|
|
|
|
DLC dlc;
|
|
|
|
AppId_t appid;
|
|
|
|
bool available;
|
|
|
|
std::string name;
|
|
|
|
if (!settings->getDLC(i, appid, available, name)) break;
|
|
|
|
dlc.AppId = (uint32_t)appid;
|
2023-12-22 15:15:46 +08:00
|
|
|
dlc.Licenses.resize(0); //TODO
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.Ticket.DLCs.push_back(dlc);
|
|
|
|
}
|
|
|
|
ticket_data.HasGC = false;
|
|
|
|
if (settings->use_gc_token)
|
|
|
|
{
|
|
|
|
ticket_data.HasGC = true;
|
|
|
|
ticket_data.GC.GCToken = generate_random_int();
|
2023-12-22 15:15:22 +08:00
|
|
|
ticket_data.GC.id = steam_id;
|
2023-12-22 15:12:52 +08:00
|
|
|
ticket_data.GC.ticketGenDate = (uint32_t)GenDate.count();
|
2023-12-22 15:08:29 +08:00
|
|
|
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);
|
2023-12-22 15:12:52 +08:00
|
|
|
ticket_data.GC.TimeSinceStartup = (uint32_t)std::chrono::duration_cast<std::chrono::seconds>(curTime - startup_time).count();
|
2023-12-24 07:27:05 +08:00
|
|
|
ticket_data.GC.TicketGeneratedCount = get_ticket_count();
|
2023-12-22 03:25:37 +08:00
|
|
|
}
|
|
|
|
std::vector<uint8_t> ser = ticket_data.Serialize();
|
|
|
|
*pcbTicket = ser.size();
|
|
|
|
memcpy(pTicket, ser.data(), ser.size());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memset(pTicket, 123, cbMaxTicket);
|
|
|
|
((char *)pTicket)[0] = 0x14;
|
|
|
|
((char *)pTicket)[1] = 0;
|
|
|
|
((char *)pTicket)[2] = 0;
|
|
|
|
((char *)pTicket)[3] = 0;
|
2023-12-22 15:15:22 +08:00
|
|
|
uint64 steam_id_buff = steam_id.ConvertToUint64();
|
|
|
|
memcpy((char *)pTicket + STEAM_ID_OFFSET_TICKET, &steam_id_buff, sizeof(steam_id_buff));
|
2023-12-22 03:25:37 +08:00
|
|
|
*pcbTicket = cbMaxTicket;
|
|
|
|
|
2023-12-22 15:15:22 +08:00
|
|
|
ticket_data.id = steam_id;
|
2023-12-22 03:25:37 +08:00
|
|
|
ticket_data.number = generate_steam_ticket_id();
|
|
|
|
uint32 ttt = ticket_data.number;
|
|
|
|
|
|
|
|
memcpy(((char *)pTicket) + sizeof(uint64), &ttt, sizeof(ttt));
|
|
|
|
}
|
2023-12-22 15:08:29 +08:00
|
|
|
|
|
|
|
#undef IP4_AS_DWORD_LITTLE_ENDIAN
|
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
return ticket_data;
|
|
|
|
}
|
2023-12-22 15:08:29 +08:00
|
|
|
|
2023-12-22 03:25:37 +08:00
|
|
|
//Conan Exiles doesn't work with 512 or 128, 256 seems to be the good size
|
|
|
|
//Steam returns 234
|
|
|
|
#define STEAM_AUTH_TICKET_SIZE 256 //234
|
|
|
|
|
|
|
|
uint32 Auth_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
|
|
|
{
|
|
|
|
if (settings->enable_new_app_ticket)
|
|
|
|
{
|
|
|
|
if (cbMaxTicket < STEAM_TICKET_MIN_SIZE_NEW) return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (cbMaxTicket < STEAM_TICKET_MIN_SIZE) return 0;
|
|
|
|
if (cbMaxTicket > STEAM_AUTH_TICKET_SIZE) cbMaxTicket = STEAM_AUTH_TICKET_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Auth_Data ticket_data = getTicketData(pTicket, cbMaxTicket, pcbTicket );
|
|
|
|
uint32 ttt = ticket_data.number;
|
|
|
|
GetAuthSessionTicketResponse_t data;
|
|
|
|
data.m_hAuthTicket = ttt;
|
|
|
|
data.m_eResult = k_EResultOK;
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
|
|
|
outbound.push_back(ticket_data);
|
|
|
|
|
|
|
|
return ttt;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Auth_Manager::getWebApiTicket( const char* pchIdentity )
|
|
|
|
{
|
|
|
|
// https://docs.unity.com/ugs/en-us/manual/authentication/manual/platform-signin-steam
|
|
|
|
GetTicketForWebApiResponse_t data{};
|
|
|
|
uint32 cbTicket = 0;
|
|
|
|
Auth_Data ticket_data = getTicketData(data.m_rgubTicket, STEAM_AUTH_TICKET_SIZE, &cbTicket);
|
|
|
|
data.m_cubTicket = (int)cbTicket;
|
|
|
|
uint32 ttt = ticket_data.number;
|
|
|
|
data.m_hAuthTicket = ttt;
|
|
|
|
data.m_eResult = k_EResultOK;
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
|
|
|
|
|
|
|
|
outbound.push_back(ticket_data);
|
|
|
|
|
|
|
|
return ttt;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSteamID Auth_Manager::fakeUser()
|
|
|
|
{
|
|
|
|
Auth_Data data = {};
|
|
|
|
data.id = generate_steam_anon_user();
|
|
|
|
inbound.push_back(data);
|
|
|
|
return data.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Auth_Manager::cancelTicket(uint32 number)
|
|
|
|
{
|
|
|
|
auto ticket = std::find_if(outbound.begin(), outbound.end(), [&number](Auth_Data const& item) { return item.number == number; });
|
|
|
|
if (outbound.end() == ticket)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Auth_Ticket *auth_ticket = new Auth_Ticket();
|
|
|
|
auth_ticket->set_number(number);
|
|
|
|
auth_ticket->set_type(Auth_Ticket::CANCEL);
|
|
|
|
Common_Message msg;
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.set_allocated_auth_ticket(auth_ticket);
|
|
|
|
network->sendToAll(&msg, true);
|
|
|
|
|
|
|
|
outbound.erase(ticket);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
for (auto & t : inbound) {
|
|
|
|
if (t.id == data.id) {
|
|
|
|
//Should this return false?
|
|
|
|
launch_callback_gs(id, true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inbound.push_back(data);
|
|
|
|
launch_callback_gs(id, true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
EBeginAuthSessionResult Auth_Manager::beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
|
|
|
|
{
|
|
|
|
if (cbAuthTicket < STEAM_TICKET_MIN_SIZE) return k_EBeginAuthSessionResultInvalidTicket;
|
|
|
|
|
|
|
|
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;
|
|
|
|
data.created = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
for (auto & t : inbound) {
|
|
|
|
if (t.id == data.id && !check_timedout(t.created, STEAM_TICKET_PROCESS_TIME)) {
|
|
|
|
return k_EBeginAuthSessionResultDuplicateRequest;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inbound.push_back(data);
|
|
|
|
launch_callback(steamID, k_EAuthSessionResponseOK, STEAM_TICKET_PROCESS_TIME);
|
|
|
|
return k_EBeginAuthSessionResultOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Auth_Manager::countInboundAuth()
|
|
|
|
{
|
|
|
|
return inbound.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Auth_Manager::endAuth(CSteamID id)
|
|
|
|
{
|
|
|
|
bool erased = false;
|
|
|
|
auto t = std::begin(inbound);
|
|
|
|
while (t != std::end(inbound)) {
|
|
|
|
if (t->id == id) {
|
|
|
|
erased = true;
|
|
|
|
t = inbound.erase(t);
|
|
|
|
} else {
|
|
|
|
++t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return erased;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Auth_Manager::Callback(Common_Message *msg)
|
|
|
|
{
|
|
|
|
if (msg->has_low_level()) {
|
|
|
|
if (msg->low_level().type() == Low_Level::CONNECT) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
|
|
|
PRINT_DEBUG("TICKET DISCONNECT\n");
|
|
|
|
auto t = std::begin(inbound);
|
|
|
|
while (t != std::end(inbound)) {
|
|
|
|
if (t->id.ConvertToUint64() == msg->source_id()) {
|
|
|
|
launch_callback(t->id, k_EAuthSessionResponseUserNotConnectedToSteam);
|
|
|
|
t = inbound.erase(t);
|
|
|
|
} else {
|
|
|
|
++t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->has_auth_ticket()) {
|
|
|
|
if (msg->auth_ticket().type() == Auth_Ticket::CANCEL) {
|
2023-12-25 13:25:50 +08:00
|
|
|
PRINT_DEBUG("TICKET CANCEL " "%" PRIu64 "\n", msg->source_id());
|
2023-12-22 03:25:37 +08:00
|
|
|
uint32 number = msg->auth_ticket().number();
|
|
|
|
auto t = std::begin(inbound);
|
|
|
|
while (t != std::end(inbound)) {
|
|
|
|
if (t->id.ConvertToUint64() == msg->source_id() && t->number == number) {
|
|
|
|
PRINT_DEBUG("TICKET CANCELED\n");
|
|
|
|
launch_callback(t->id, k_EAuthSessionResponseAuthTicketCanceled);
|
|
|
|
t = inbound.erase(t);
|
|
|
|
} else {
|
|
|
|
++t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|