/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * $Logfile: /DescentIII/Main/Dmfc/dmfcremote.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003/08/26 03:57:21 $ * $Author: kevinb $ * * Remote administration * * $Log: dmfcremote.cpp,v $ * Revision 1.1.1.1 2003/08/26 03:57:21 kevinb * initial 1.5 import * * * 6 7/14/99 11:48a Jeff * localized text for patch fixes * * 5 7/09/99 6:18p Jeff * added $remoteadminlogout and $wait commands * * 4 7/08/99 6:25p Jeff * remote admin in and working * * 3 7/08/99 2:38a Jeff * rough implementation of remote administration checked in. Still needs * some polishing, but should work basically. * * 2 7/07/99 7:07p Jeff * created dmfcremote.cpp * * $NoKeywords: $ */ #include "gamedll_header.h" #include "DMFC.h" #include "dmfcinternal.h" #include "encryption.h" #include #include extern char **DMFCStringTable; extern int DMFCStringTableSize; extern char *_DMFCErrorString; extern DMFCBase *basethis; char *DMFCGetString(int d); typedef struct { bool authorized; // whether the player is authorized ubyte curr_key[8]; // current encryption key } tRemotePlayerData; static tRemotePlayerData Authorized_players[MAX_PLAYER_RECORDS]; static char Remote_Admin_pass[64]; bool Use_remote_admin = false; static bool IAmAnAdmin = false; static void Remote_encrypt(ubyte key[8], int size_buffer, ubyte *buffer, int *new_size, ubyte **new_buffer); static void Remote_decrypt(ubyte key[8], int size_buffer, ubyte *buffer, int *new_size, ubyte **new_buffer); // Sends a string to the server (function encrypts it before sending) void Remote_SendStringToServer(char *string); // Recieves a string from the server (function decrypts it) void Remote_RecieveStringFromServer(ubyte *data, char *buffer, int size); // Sends a string to a client (function encrypts it before sending) void Remote_SendStringToClient(int client, char *string); // Recieves a string from a client (function decrypts it) int Remote_RecieveStringFromClient(ubyte *data, char *buffer, int size); // Handles remote admin packets from a client void Remote_HandleClientPacket(ubyte *data); // Handles remote admin packets from a client void Remote_HandleServerPacket(ubyte *data); // Sends a message from the server to a client void Remote_SendMessage(int client, char *message); // Receives a message from the server void Remote_GetMessage(ubyte *data); // Logs a player out from being an administrator void Remote_Logout(int precnum); // filters any remote commands not allowed to be executed remotely // -1 : not found // 0 : can't call // 1 : can call int FilterNonRemoteCommands(char *command); // Initializes the remote administration system void Remote_Initialize(void) { // initialize encryption keys srand((unsigned int)time(0)); int i, j; for (i = 0; i < MAX_PLAYER_RECORDS; i++) { Authorized_players[i].authorized = false; for (j = 0; j < 8; j++) { Authorized_players[i].curr_key[j] = rand() % 255; } } memset(Remote_Admin_pass, 0, 64); // setup packet recievers #ifdef WIN32 basethis->RegisterPacketReceiver(SPID_REMOTETOSERVER, Remote_HandleClientPacket); basethis->RegisterPacketReceiver(SPID_REMOTETOCLIENT, Remote_HandleServerPacket); basethis->RegisterPacketReceiver(SPID_SERVERREMOTEMSG, Remote_GetMessage); #else basethis->RegisterPacketReceiver(SPID_REMOTETOSERVER, &Remote_HandleClientPacket); basethis->RegisterPacketReceiver(SPID_REMOTETOCLIENT, &Remote_HandleServerPacket); basethis->RegisterPacketReceiver(SPID_SERVERREMOTEMSG, &Remote_GetMessage); #endif if (basethis->GetLocalRole() == LR_SERVER) { bool bTemp; int pass_len = 64; DatabaseRead1("DMFCRemoteAdminPass", Remote_Admin_pass, &pass_len); if (DatabaseRead3("DMFCRemoteAdmin", &bTemp)) { Remote_Enable(bTemp); } } } // Processes remote administration for the frame void Remote_ProcessFrame(void) { if (basethis->GetLocalRole() != LR_SERVER) return; // go through all the players, and update their state (whether // they are in the game or not) int p; for (p = 0; p < MAX_PLAYER_RECORDS; p++) { player_record *pr = PRec_GetPRecord(p); if (Authorized_players[p].authorized) { // this is an authorized player, update them // make sure they are in the game if (!pr || pr->state != STATE_INGAME) { // the player is no longer in the game mprintf((0, "REMOTE: Removing authorization for %s\n", (pr->callsign) ? (pr->callsign) : "")); Remote_Logout(p); // change the key for the slot int j; for (j = 0; j < 8; j++) { Authorized_players[p].curr_key[j] = rand() % 255; } } else { // the player is authorized and in the game } } } } // Gets the key for the given player record ubyte *Remote_GetKey(int prec) { ASSERT(prec >= 0 && prec < MAX_PLAYER_RECORDS); return Authorized_players[prec].curr_key; } // Enables/Disables remote administration void Remote_Enable(bool enable) { if (basethis->GetLocalRole() != LR_SERVER) return; Use_remote_admin = enable; if (*Remote_Admin_pass == '\0' && enable) { Use_remote_admin = false; } mprintf((0, "Remote Administration: %s\n", (Use_remote_admin) ? "On" : "Off")); if (!Use_remote_admin) { // log out all the logged in players int p; for (p = 0; p < MAX_PLAYER_RECORDS; p++) { if (Authorized_players[p].authorized) { // log the player out Remote_Logout(p); } } } DatabaseWrite2("DMFCRemoteAdmin", Use_remote_admin); } // sets the administration password for the server // pass in an unencrypted password void Remote_SetPassword(char *password) { if (basethis->GetLocalRole() != LR_SERVER) return; strncpy(Remote_Admin_pass, password, 63); Remote_Admin_pass[63] = '\0'; DatabaseWrite1("DMFCRemoteAdminPass", Remote_Admin_pass, strlen(Remote_Admin_pass)); } // Logs a player record in as an administrator bool Remote_Login(int precnum, char *pass) { if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS) return false; player_record *pr = PRec_GetPRecord(precnum); if (!pr || pr->state != STATE_INGAME) { return false; } if (Authorized_players[precnum].authorized) return true; if (strcmp(pass, Remote_Admin_pass) == 0) { Authorized_players[precnum].authorized = true; } return Authorized_players[precnum].authorized; } // Logs a player out from being an administrator void Remote_Logout(int precnum) { if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS) return; if (!Authorized_players[precnum].authorized) return; player_record *pr = PRec_GetPRecord(precnum); if (!pr) return; if (pr->state == STATE_INGAME) { Remote_SendStringToClient(pr->pnum, "LOGOUTSUCCESS"); Remote_SendMessage(pr->pnum, DTXT_REMOTELOGGEDOUT); } Authorized_players[precnum].authorized = false; DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_HUDMSGLOGGEDOUT, pr->callsign); } // Lists the players logged in void Remote_ListLogins(void) { int prec; player_record *pr; char buffer[512]; char temp[64]; int len, curlen; curlen = 0; *buffer = '\0'; for (prec = 0; prec < MAX_PLAYER_RECORDS; prec++) { if (Authorized_players[prec].authorized) { pr = PRec_GetPRecord(prec); if (pr && pr->state != STATE_EMPTY) { len = strlen(pr->callsign); if (curlen + len > 500) { // output the hudmessage DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), buffer); *buffer = '\0'; curlen = 0; } if (*buffer != '\0') { strncat(buffer, ":", sizeof(buffer) - strlen(buffer) - 1); } snprintf(temp, sizeof(temp), "%s[%d]", pr->callsign, prec); strncat(buffer, temp, sizeof(buffer) - strlen(buffer) - 1); curlen = strlen(buffer); } } } if (*buffer != '\0') { DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), buffer); } } extern player_record Player_records[MAX_PLAYER_RECORDS]; int translate_precptr_to_index(player_record *pr) { int index = (pr - Player_records); if (index < 0 || index >= MAX_PLAYER_RECORDS) index = -1; return index; } // Sets a clients key void Remote_SetMyKey(ubyte key[8]) { player_record *pr; pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum()); int prec = translate_precptr_to_index(pr); if (prec == -1) { mprintf((0, "INVALID PREC!\n")); Int3(); return; } memcpy(Authorized_players[prec].curr_key, key, 8); } // handles a remote command (client side) void Remote_ClientProcess(char *command) { if (basethis->GetLocalRole() == LR_SERVER) return; if (!IAmAnAdmin) { // only let "login" go through if (!strnicmp(command, "login", strlen("login"))) { mprintf((0, "Sending login attempt for remote administration\n")); } else { return; } } char *ptr; ptr = command; while (*ptr == ' ') ptr++; if (*ptr) { Remote_SendStringToServer(ptr); } } // handles a remote command (server side) void Remote_ServerProcess(int precnum, char *command) { if (basethis->GetLocalRole() != LR_SERVER) return; if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS) return; if (!Use_remote_admin) return; if (!Authorized_players[precnum].authorized) { // check to see if the player is trying to login int loginlen = strlen("login "); if (!strnicmp(command, "login ", loginlen)) { char *ptr = &command[loginlen - 1]; while (*ptr == ' ') ptr++; if (Remote_Login(precnum, ptr)) { // successful login player_record *pr = PRec_GetPRecord(precnum); DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_HUDMSGLOGGEDIN, pr->callsign); Remote_SendStringToClient(pr->pnum, "LOGINSUCCESS"); Remote_SendMessage(pr->pnum, DTXT_REMOTELOGGEDIN); } return; } } player_record *pr = PRec_GetPRecord(precnum); // check to see if they are logging out if (!stricmp(command, "logout")) { // log the player out Remote_Logout(precnum); return; } // process the command if (command[0] == '$') { switch (FilterNonRemoteCommands(command)) { case 1: { DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_REMOTEEXECUTE, pr->callsign, command); if (basethis->InputCommandHandle(command)) { // command handled Remote_SendMessage(pr->pnum, DTXT_REMOTESUCCESS); } else { // command not handled Remote_SendMessage(pr->pnum, DTXT_REMOTENOTSUCCESS); } } break; case 0: { // command not handled Remote_SendMessage(pr->pnum, DTXT_REMOTENOTALLOWED); } break; default: { // command not handled Remote_SendMessage(pr->pnum, DTXT_REMOTENOTFOUND); } break; } } else { // command not handled Remote_SendMessage(pr->pnum, DTXT_REMOTENOTFOUND); } } // filters any remote commands not allowed to be executed remotely int FilterNonRemoteCommands(char *command) { // cut off whats after the first word (the command) char cmd[64]; strncpy(cmd, command, 63); cmd[63] = '\0'; char *ptr = cmd; while (*ptr != ' ' && *ptr != '\0') ptr++; *ptr = '\0'; return basethis->CanInputCommandBeUsedRemotely((*cmd == '$') ? cmd + 1 : cmd); } // Sends a message from the server to a client void Remote_SendMessage(int client, char *message) { if (basethis->GetLocalRole() != LR_SERVER) return; int len = strlen(message); int maxlen = ((MAX_GAME_DATA_SIZE - 5) < 511) ? (MAX_GAME_DATA_SIZE - 5) : 511; if (len > maxlen) { message[maxlen] = '\0'; len = maxlen; } int count = 0; ubyte data[MAX_GAME_DATA_SIZE]; basethis->StartPacket(data, SPID_SERVERREMOTEMSG, &count); MultiAddString(message, data, &count); basethis->SendPacket(data, count, client); } // Receives a message from the server void Remote_GetMessage(ubyte *data) { int count = 0; char buffer[512]; MultiGetString(buffer, data, &count); DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), "==%s==", buffer); } // Sends a string to the server (function encrypts it before sending) void Remote_SendStringToServer(char *string) { if (basethis->GetLocalRole() == LR_SERVER) { Int3(); return; } int slen; int new_strlen; ubyte *packet_data; slen = strlen(string); player_record *pr; pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum()); int prec = translate_precptr_to_index(pr); if (prec == -1) { mprintf((0, "INVALID PREC!\n")); Int3(); return; } Remote_encrypt(Authorized_players[prec].curr_key, slen, (ubyte *)string, &new_strlen, &packet_data); if (new_strlen == 0) { mprintf((0, "COULDN'T ENCRYPT\n")); Int3(); return; } // now we can send off the packet int count = 0; ubyte data[MAX_GAME_DATA_SIZE]; basethis->StartPacket(data, SPID_REMOTETOSERVER, &count); MultiAddByte(prec, data, &count); MultiAddInt(new_strlen, data, &count); memcpy(&data[count], packet_data, new_strlen); count += new_strlen; basethis->SendPacket(data, count, SP_SERVER); free(packet_data); } // Handles remote admin packets from a client void Remote_HandleServerPacket(ubyte *data) { char buffer[512]; Remote_RecieveStringFromServer(data, buffer, 512); if (!strcmp(buffer, "LOGOUTSUCCESS")) { // we logged out IAmAnAdmin = false; } else if (!strcmp(buffer, "LOGINSUCCESS")) { // we logged in IAmAnAdmin = true; } } // Recieves a string from the server (function decrypts it) void Remote_RecieveStringFromServer(ubyte *data, char *buffer, int size) { int count = 0; int len = MultiGetInt(data, &count); player_record *pr; pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum()); int prec = translate_precptr_to_index(pr); if (prec == -1) { mprintf((0, "INVALID PREC!\n")); Int3(); return; } ASSERT(len > 0); if (len <= 0) return; ubyte *packet; packet = (ubyte *)malloc(len); memcpy(packet, &data[count], len); count += len; int new_size; ubyte *new_buffer; Remote_decrypt(Authorized_players[prec].curr_key, len, packet, &new_size, &new_buffer); if (new_buffer) { strncpy(buffer, (char *)new_buffer, size - 1); buffer[size - 1] = '\0'; free(new_buffer); } else { *buffer = '\0'; } free(packet); } // Sends a string to a client (function encrypts it before sending) void Remote_SendStringToClient(int client, char *string) { if (basethis->GetLocalRole() != LR_SERVER) { Int3(); return; } int slen; int new_strlen; ubyte *packet_data; slen = strlen(string); player_record *pr; pr = PRec_GetPRecordByPnum(client); int prec = translate_precptr_to_index(pr); if (prec == -1) { mprintf((0, "INVALID PREC!\n")); Int3(); return; } // check to make sure the player is authorized before sending if (!Authorized_players[prec].authorized) { Int3(); return; } Remote_encrypt(Authorized_players[prec].curr_key, slen, (ubyte *)string, &new_strlen, &packet_data); if (new_strlen == 0) { mprintf((0, "COULDN'T ENCRYPT\n")); Int3(); return; } // now we can send off the packet int count = 0; ubyte data[MAX_GAME_DATA_SIZE]; basethis->StartPacket(data, SPID_REMOTETOCLIENT, &count); MultiAddInt(new_strlen, data, &count); memcpy(&data[count], packet_data, new_strlen); count += new_strlen; basethis->SendPacket(data, count, client); free(packet_data); } // Handles remote admin packets from a client void Remote_HandleClientPacket(ubyte *data) { char buffer[512]; int prec = Remote_RecieveStringFromClient(data, buffer, 512); if (prec == -1) return; Remote_ServerProcess(prec, buffer); } // Recieves a string from a client (function decrypts it) int Remote_RecieveStringFromClient(ubyte *data, char *buffer, int size) { int count = 0; int prec = MultiGetByte(data, &count); int len = MultiGetInt(data, &count); ASSERT(len > 0); ASSERT(prec >= 0 && prec < MAX_PLAYER_RECORDS); if (prec < 0 || prec >= MAX_PLAYER_RECORDS) return -1; if (len <= 0) return -1; ubyte *packet; packet = (ubyte *)malloc(len); memcpy(packet, &data[count], len); count += len; int new_size; ubyte *new_buffer; Remote_decrypt(Authorized_players[prec].curr_key, len, packet, &new_size, &new_buffer); if (new_buffer) { size = (len < size) ? len + 1 : size; strncpy(buffer, (char *)new_buffer, size - 1); buffer[size - 1] = '\0'; free(new_buffer); } else { *buffer = '\0'; } free(packet); return prec; } void Remote_pad_buffer(int orig_size, const ubyte *orig_buffer, int *new_size, ubyte **new_buffer) { // bump buffer up to size of mod 8 *new_size = orig_size; if (orig_size < 8 || (orig_size % 8) != 0) { // we need to pad up int chunks = orig_size / 8; *new_size = (chunks + 1) * 8; } *new_buffer = (ubyte *)malloc(*new_size); if (!(*new_buffer)) { *new_size = 0; return; } memset(*new_buffer, 0, *new_size); memcpy(*new_buffer, orig_buffer, orig_size); } static void Remote_encrypt(ubyte key[8], int size_buffer, ubyte *buffer, int *new_size, ubyte **new_buffer) { *new_buffer = NULL; ubyte *buff; Remote_pad_buffer(size_buffer, buffer, new_size, &buff); if (*new_size == 0) return; IceKey ice(0); ice.set((ubyte *)key); int i, num_chunks = 0; int chunk_offset = 0; ubyte *cipher; cipher = (ubyte *)malloc(*new_size); if (!cipher) return; num_chunks = (*new_size) / 8; for (i = 0; i < num_chunks; i++) { ice.encrypt(&buff[chunk_offset], &cipher[chunk_offset]); chunk_offset += 8; } free(buff); *new_buffer = cipher; } static void Remote_decrypt(ubyte key[8], int size_buffer, ubyte *buffer, int *new_size, ubyte **new_buffer) { *new_buffer = NULL; ubyte *buff; Remote_pad_buffer(size_buffer, buffer, new_size, &buff); if (*new_size == 0) return; IceKey ice(0); ice.set((ubyte *)key); int i, num_chunks = 0; int chunk_offset = 0; ubyte *cipher; cipher = (ubyte *)malloc(*new_size); if (!cipher) return; num_chunks = (*new_size) / 8; for (i = 0; i < num_chunks; i++) { ice.decrypt(&buff[chunk_offset], &cipher[chunk_offset]); chunk_offset += 8; } free(buff); *new_buffer = cipher; }