void Webadmin_ConsoleCommand(xml_t* xmlobj, const char* command, int uid) { char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; char buffer[960]; char cmd[48]; int power, powercmd, oldpower, oldinvokeruid, oldinvokerclnum, i; if((power = Auth_GetClPowerByUID(uid)) < 100) { i = 0; /* Get the current user's power 1st */ while ( command[i] != ' ' && command[i] != '\0' && command[i] != '\n' && i < 32 ){ i++; } if(i > 29 || i < 3) return; Q_strncpyz(cmd,command,i+1); //Prevent buffer overflow as well as prevent the execution of priveleged commands by using seperator characters Q_strncpyz(buffer, command, sizeof(buffer)); Q_strchrrepl(buffer,';','\0'); Q_strchrrepl(buffer,'\n','\0'); Q_strchrrepl(buffer,'\r','\0'); // start redirecting all print outputs to the packet powercmd = Cmd_GetPower(cmd); if(powercmd > power) { XA(" Insufficient permissions! "); return; } xmlobjFlush = xmlobj; oldpower = Cmd_GetInvokerPower(); oldinvokeruid = Cmd_GetInvokerUID(); oldinvokerclnum = Cmd_GetInvokerClnum(); Cmd_SetCurrentInvokerInfo(uid, power, -1); Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, Webadmin_FlushRedirect); Cmd_ExecuteSingleCommand(0,0, buffer); Cmd_SetCurrentInvokerInfo(oldinvokeruid, oldpower, oldinvokerclnum); }else{ xmlobjFlush = xmlobj; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, Webadmin_FlushRedirect); Cmd_ExecuteSingleCommand(0,0, command); #ifdef PUNKBUSTER if(!Q_stricmpn(command, "pb_sv_", 6)) PbServerForceProcess(); #endif } Com_EndRedirect(); xmlobjFlush = NULL; }
/* * @brief A client issued an rcon command. Shift down the remaining args and * redirect all output to the invoking client. */ static void Svc_RemoteCommand(void) { const _Bool auth = Sv_RconAuthenticate(); const char *addr = Net_NetaddrToString(&net_from); // first print to the server console if (auth) Com_Print("Rcon from %s:\n%s\n", addr, net_message.data + 4); else Com_Print("Bad rcon from %s:\n%s\n", addr, net_message.data + 4); // then redirect the remaining output back to the client Com_BeginRedirect(RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, Sv_FlushRedirect); if (auth) { char remaining[MAX_STRING_CHARS]; int32_t i; remaining[0] = 0; for (i = 2; i < Cmd_Argc(); i++) { strcat(remaining, Cmd_Argv(i)); strcat(remaining, " "); } Cmd_ExecuteString(remaining); } else { Com_Print("Bad rcon_password\n"); } Com_EndRedirect(); }
/* =============== SVC_RemoteCommand A client issued an rcon command. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand(void) { int i; char remaining[1024]; i = Rcon_Validate(); if (i == 0) Com_Printf("Bad rcon from %s:\n%s\n", NET_AdrToString(net_from), net_message.data + 4); else Com_Printf("Rcon from %s:\n%s\n", NET_AdrToString(net_from), net_message.data + 4); Com_BeginRedirect(RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if (!Rcon_Validate()) { Com_Printf("Bad rcon_password.\n"); } else { remaining[0] = 0; for (i = 2; i < Cmd_Argc(); i++) { strcat(remaining, Cmd_Argv(i)); strcat(remaining, " "); } Cmd_ExecuteString(remaining); } Com_EndRedirect(); }
void GScr_CbufAddTextEx(){ char string[1024]; char outputbuf[1024]; if(Scr_GetNumParam() != 1){ Scr_Error("Usage: execex(<string>)\n"); } Com_sprintf(string, sizeof(string), "%s\n",Scr_GetString(0)); cmd_exec_redirect_buf[0] = '\0'; if(!Q_stricmpn(string, "map", 3) || !Q_stricmpn(string, "fast_restart", 12)) { Cbuf_AddText( string ); }else{ Com_BeginRedirect(outputbuf, sizeof(outputbuf), GScr_CbufExecRedirect); Cmd_ExecuteSingleCommand(0,0, string); Com_EndRedirect(); cmd_exec_redirect_buf[sizeof(cmd_exec_redirect_buf) -1] = '\0'; } Scr_AddString( cmd_exec_redirect_buf ); }
/** * @brief A client issued an rcon command. Shift down the remaining args. Redirect all printfs */ static void SVC_RemoteCommand (struct net_stream *stream) { char buf[256]; const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false); bool valid = Rcon_Validate(Cmd_Argv(1)); if (!valid) Com_Printf("Bad rcon from %s:\n%s\n", peername, Cmd_Argv(1)); else Com_Printf("Rcon from %s:\n%s\n", peername, Cmd_Argv(1)); Com_BeginRedirect(stream, sv_outputbuf, SV_OUTPUTBUF_LENGTH); if (!valid) /* inform the client */ Com_Printf("Bad rcon_password.\n"); else { char remaining[1024] = ""; int i; /* execute the rcon commands */ for (i = 2; i < Cmd_Argc(); i++) { Q_strcat(remaining, Cmd_Argv(i), sizeof(remaining)); Q_strcat(remaining, " ", sizeof(remaining)); } /* execute the string */ Cmd_ExecuteString(remaining); } Com_EndRedirect(); }
/* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { qboolean valid; unsigned int time; char remaining[1024]; // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc. // (OOB messages are the bottleneck here) #define SV_OUTPUTBUF_LENGTH (1024 - 16) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; char *cmd_aux; // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 time = Com_Milliseconds(); if ( (unsigned)( time - lasttime ) < 500u ) { return; } lasttime = time; if ( !strlen( sv_rconPassword->string ) || strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { valid = qfalse; Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } else { valid = qtrue; Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf ("No rconpassword set on the server.\n"); } else if ( !valid ) { Com_Printf ("Bad rconpassword.\n"); } else { remaining[0] = 0; // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 // get the command directly, "rcon <pass> <command>" to avoid quoting issues // extract the command by walking // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing cmd_aux = Cmd_Cmd(); cmd_aux+=4; while(cmd_aux[0]==' ') cmd_aux++; while(cmd_aux[0] && cmd_aux[0]!=' ') // password cmd_aux++; while(cmd_aux[0]==' ') cmd_aux++; Q_strcat( remaining, sizeof(remaining), cmd_aux); Cmd_ExecuteString (remaining); } Com_EndRedirect (); }
void Webadmin_ConsoleCommand(xml_t* xmlobj, const char* command, uint64_t steamid) { char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; char buffer[960]; char cmd[48]; int power, i, powercmd; power = Cmd_GetInvokerPower(); if(power < 100) { i = 0; /* Get the current user's power 1st */ while ( command[i] != ' ' && command[i] != '\0' && command[i] != '\n' && i < 32 ){ i++; } if(i > 29 || i < 3) return; Q_strncpyz(cmd,command,i+1); //Prevent buffer overflow as well as prevent the execution of priveleged commands by using seperator characters Q_strncpyz(buffer, command, sizeof(buffer)); Q_strchrrepl(buffer,';','\0'); Q_strchrrepl(buffer,'\n','\0'); Q_strchrrepl(buffer,'\r','\0'); // start redirecting all print outputs to the packet powercmd = Cmd_GetPower(cmd); if(powercmd > power) { XA(" Insufficient permissions! "); return; } xmlobjFlush = xmlobj; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, Webadmin_FlushRedirect); Cmd_ExecuteSingleCommand(0,0, buffer); }else{ xmlobjFlush = xmlobj; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, Webadmin_FlushRedirect); Cmd_ExecuteSingleCommand(0,0, command); } Com_EndRedirect(); xmlobjFlush = NULL; }
/* * ================ * SVC_Status * * Responds with all the info that qplug or qspy can see * ================ */ void SVC_Status(void) { Netchan_OutOfBandPrint(NS_SERVER, net_from, "print\n%s", SV_StatusString()); #if 0 Com_BeginRedirect(RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); Com_Printf(SV_StatusString()); Com_EndRedirect(); #endif }
void Cmd_Status_f(gentity_t* player) { redirectAddress = svs_clients[GENTITY_TO_CLIENTNUM(player)].adr; Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); Cmd_ExecuteSingleCommand(0, 0, "status\n"); Com_EndRedirect(); }
static void SV_StartRedirect_f( void ) { #define SV_OUTPUTBUF_LENGTH (1024 - 16) int clientNum; static char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; clientNum = atoi( Cmd_Argv(1) ); if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) return; redirect_client = svs.clients + clientNum; Com_EndRedirect( ); Com_BeginRedirect( sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_ClientRedirect ); }
void SV_PrintQuery(client_t* cl, char* string, void (formatPrint)(char* str)){ char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; if(cl){ svse.redirectClient = cl; Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_ReliableSendRedirect); formatPrint(string); Com_EndRedirect(); }else{ formatPrint(string); } }
/** * @brief A client issued an rcon command. Shift down the remaining args. Redirect all printfs */ static void SVC_RemoteCommand (struct net_stream* stream) { char buf[64]; const char* peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false); /* Prevent using rcon as an amplifier and make dictionary attacks impractical */ if (SVC_RateLimitAddress(*stream)) { Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit from %s exceeded, dropping request\n", peername); return; } const bool valid = Rcon_Validate(Cmd_Argv(1)); if (!valid) { static leakyBucket_t bucket; /* Make DoS via rcon impractical */ if (SVC_RateLimit(&bucket, 10, 1000)) { Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit exceeded, dropping request\n"); return; } Com_Printf("Bad rcon from %s with password: '******'\n", peername, Cmd_Argv(1)); } else { Com_Printf("Rcon from %s\n", peername); } static char sv_outputbuf[1024]; Com_BeginRedirect(stream, sv_outputbuf, sizeof(sv_outputbuf)); if (!valid) { /* inform the client */ Com_Printf(BAD_RCON_PASSWORD); } else { char remaining[1024] = ""; int i; /* execute the rcon commands */ for (i = 2; i < Cmd_Argc(); i++) { Q_strcat(remaining, sizeof(remaining), "%s ", Cmd_Argv(i)); } /* execute the string */ Cmd_ExecuteString("%s", remaining); } Com_EndRedirect(); }
/* =============== SVC_RconRecoveryRemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RconRecoveryRemoteCommand( netadr_t from, msg_t *msg ) { qboolean valid; unsigned int time; // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc. // (OOB messages are the bottleneck here) #define SV_OUTPUTBUF_LENGTH (1024 - 16) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 time = Com_Milliseconds(); if ( !strlen( sv_rconRecoveryPassword->string ) || strcmp (Cmd_Argv(1), sv_rconRecoveryPassword->string) ) { // MaJ - If the rconpassword is bad and one just happned recently, don't spam the log file, just die. if ( (unsigned)( time - lasttime ) < 600u ) return; valid = qfalse; Com_Printf ("Bad rcon recovery from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } else { // MaJ - If the rconpassword is good, allow it much sooner than a bad one. if ( (unsigned)( time - lasttime ) < 180u ) return; valid = qtrue; Com_Printf ("Rcon recovery from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } lasttime = time; // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf ("No rcon recovery password set on the server.\n"); } else if ( !valid ) { Com_Printf ("Bad rcon recovery password.\n"); } else { Com_Printf ("rconPassword %s\n" , sv_rconPassword->string ); } Com_EndRedirect (); }
/* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { qboolean valid; unsigned int i, time; char remaining[1024]; #define SV_OUTPUTBUF_LENGTH (MAX_MSGLEN - 16) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; time = Com_Milliseconds(); if (time<(lasttime+500)) { return; } lasttime = time; if ( !strlen( sv_rconPassword->string ) || strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { valid = qfalse; Com_DPrintf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } else { valid = qtrue; Com_DPrintf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf ("No rconpassword set.\n"); } else if ( !valid ) { Com_Printf ("Bad rconpassword.\n"); } else { remaining[0] = 0; for (i=2 ; i<Cmd_Argc() ; i++) { strcat (remaining, Cmd_Argv(i) ); strcat (remaining, " "); } Cmd_ExecuteString (remaining); } Com_EndRedirect (); }
/* * SVC_RemoteCommand * * A client issued an rcon command. * Shift down the remaining args * Redirect all printfs */ static void SVC_RemoteCommand( const socket_t *socket, const netadr_t *address ) { int i; char remaining[1024]; flush_params_t extra; i = Rcon_Validate(); if( i == 0 ) Com_Printf( "Bad rcon from %s:\n%s\n", NET_AddressToString( address ), Cmd_Args() ); else Com_Printf( "Rcon from %s:\n%s\n", NET_AddressToString( address ), Cmd_Args() ); extra.socket = socket; extra.address = address; Com_BeginRedirect( RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect, ( const void * )&extra ); if( sv_showRcon->integer ) Com_Printf( "Rcon Packet %s\n", NET_AddressToString( address ) ); if( !Rcon_Validate() ) { Com_Printf( "Bad rcon_password.\n" ); } else { remaining[0] = 0; for( i = 2; i < Cmd_Argc(); i++ ) { Q_strncatz( remaining, "\"", sizeof( remaining ) ); Q_strncatz( remaining, Cmd_Argv( i ), sizeof( remaining ) ); Q_strncatz( remaining, "\" ", sizeof( remaining ) ); } Cmd_ExecuteString( remaining ); } Com_EndRedirect(); }
/* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { qboolean valid; unsigned int time; char remaining[1024]; // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc. // (OOB messages are the bottleneck here) #define SV_OUTPUTBUF_LENGTH (1024 - 16) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; char *cmd_aux; fileHandle_t rconLog = 0; // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 time = Com_Milliseconds(); if ( (unsigned)( time - lasttime ) < 500u ) { return; } lasttime = time; if(strlen(sv_rconLog->string)) { rconLog = FS_FOpenFileAppend(sv_rconLog->string); if (!rconLog) { Com_Printf("Warning: Unable to open sv_rconLog: \"%s\"", sv_rconLog->string); Cvar_Set ("sv_rconLog", ""); } } const char *message = ""; if ( !strlen( sv_rconPassword->string ) || strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { valid = qfalse; message = va("Bad rcon from %s: %s\n", NET_AdrToString (from), Cmd_ArgsFrom(2)); } else { valid = qtrue; message = va("Rcon from %s: %s\n", NET_AdrToString (from), Cmd_ArgsFrom(2)); } Com_Printf (message); if (rconLog) { qtime_t qt; Com_RealTime(&qt); char *timestamp = va( "%02i/%02i/%02i %02i:%02i:%02i ", qt.tm_mday, qt.tm_mon, qt.tm_year-100, qt.tm_hour, qt.tm_min, qt.tm_sec ); FS_Write(timestamp, strlen(timestamp), rconLog); FS_Write(message, strlen(message), rconLog); FS_FCloseFile(rconLog); } // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf ("No rconpassword set on the server.\n"); } else if ( !valid ) { Com_Printf ("Bad rconpassword.\n"); } else { remaining[0] = 0; // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 // get the command directly, "rcon <pass> <command>" to avoid quoting issues // extract the command by walking // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing cmd_aux = Cmd_Cmd(); cmd_aux+=4; while(cmd_aux[0]==' ') cmd_aux++; while(cmd_aux[0] && cmd_aux[0]!=' ') // password cmd_aux++; while(cmd_aux[0]==' ') cmd_aux++; Q_strcat( remaining, sizeof(remaining), cmd_aux); Cmd_ExecuteString (remaining); } Com_EndRedirect (); }
/* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { #if defined RTCW_SP qboolean valid; int i; char remaining[1024]; #define SV_OUTPUTBUF_LENGTH ( MAX_MSGLEN - 16 ) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; if ( !strlen( sv_rconPassword->string ) || strcmp( Cmd_Argv( 1 ), sv_rconPassword->string ) ) { valid = qfalse; Com_DPrintf( "Bad rcon from %s:\n%s\n", NET_AdrToString( from ), Cmd_Argv( 2 ) ); } else { valid = qtrue; Com_DPrintf( "Rcon from %s:\n%s\n", NET_AdrToString( from ), Cmd_Argv( 2 ) ); } // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect( sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect ); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf( "No rconpassword set.\n" ); } else if ( !valid ) { Com_Printf( "Bad rconpassword.\n" ); } else { remaining[0] = 0; for ( i = 2 ; i < Cmd_Argc() ; i++ ) { strcat( remaining, Cmd_Argv( i ) ); strcat( remaining, " " ); } Cmd_ExecuteString( remaining ); } Com_EndRedirect(); #else qboolean valid; unsigned int time; char remaining[1024]; // show_bug.cgi?id=376 // if we send an OOB print message this size, 1.31 clients die in a Com_Printf buffer overflow // the buffer overflow will be fixed in > 1.31 clients // but we want a server side fix // we must NEVER send an OOB message that will be > 1.31 MAXPRINTMSG (4096) #define SV_OUTPUTBUF_LENGTH ( 256 - 16 ) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; char *cmd_aux; // TTimo - show_bug.cgi?id=534 time = Com_Milliseconds(); if ( time < ( lasttime + 500 ) ) { return; } lasttime = time; if ( !strlen( sv_rconPassword->string ) || strcmp( Cmd_Argv( 1 ), sv_rconPassword->string ) ) { valid = qfalse; Com_Printf( "Bad rcon from %s:\n%s\n", NET_AdrToString( from ), Cmd_Argv( 2 ) ); } else { valid = qtrue; Com_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( from ), Cmd_Argv( 2 ) ); } // start redirecting all print outputs to the packet svs.redirectAddress = from; // FIXME TTimo our rcon redirection could be improved // big rcon commands such as status lead to sending // out of band packets on every single call to Com_Printf // which leads to client overflows // see show_bug.cgi?id=51 // (also a Q3 issue) Com_BeginRedirect( sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect ); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf( "No rconpassword set on the server.\n" ); } else if ( !valid ) { Com_Printf( "Bad rconpassword.\n" ); } else { remaining[0] = 0; // ATVI Wolfenstein Misc #284 // get the command directly, "rcon <pass> <command>" to avoid quoting issues // extract the command by walking // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing cmd_aux = Cmd_Cmd(); cmd_aux += 4; while ( cmd_aux[0] == ' ' ) cmd_aux++; while ( cmd_aux[0] && cmd_aux[0] != ' ' ) // password cmd_aux++; while ( cmd_aux[0] == ' ' ) cmd_aux++; Q_strcat( remaining, sizeof( remaining ), cmd_aux ); Cmd_ExecuteString( remaining ); } Com_EndRedirect(); #endif // RTCW_XX }
void SVC_RemoteCommand(netadr_t from, void* msg) { bool valid; unsigned int time; char remaining[1024] = {0}; size_t current = 0; static unsigned int lasttime = 0; remaining[0] = '\0'; time = Com_Milliseconds(); if (time < (lasttime + 100)) { return; } lasttime = time; if (!sv_rconPassword) { return; } if (!strlen(sv_rconPassword->current.string) || strcmp(Cmd_Argv(1), sv_rconPassword->current.string)) { valid = false; Com_Printf(1, "Bad rcon from %s:\n%s\n", NET_AdrToString(from), Cmd_Argv(2)); } else { valid = true; Com_Printf(1, "Rcon from %s:\n%s\n", NET_AdrToString(from), Cmd_Argv(2)); } // start redirecting all print outputs to the packet redirectAddress = from; Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if (!valid) { if (!strlen(sv_rconPassword->current.string)) { Com_Printf(0, "The server must set 'rcon_password' for clients to use 'rcon'.\n"); } else { Com_Printf(0, "Invalid password.\n"); } } else { remaining[0] = 0; if (Cmd_Argc() > 2) { for (int i = 2; i < Cmd_Argc(); i++) { current = Com_AddToString(Cmd_Argv(i), remaining, current, sizeof(remaining), true); current = Com_AddToString(" ", remaining, current, sizeof(remaining), false); } } else { memset(remaining, 0, sizeof(remaining)); strncpy(remaining, Cmd_Argv(2), sizeof(remaining) - 1); } Cmd_ExecuteSingleCommand(0, 0, remaining); } Com_EndRedirect(); if (strlen(remaining) > 0) { Com_Printf(0, "handled rcon: %s\n", remaining); } }
qboolean SV_ExecuteRemoteCmd(int clientnum, const char *msg){ char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; char cmd[30]; char buffer[256]; char *printPtr; int i = 0; int j = 0; int powercmd; int power; client_t *cl; if(!cmdSystemInitialized){ SV_SendServerCommand(redirectClient, "e \"Error: Remote control system is not initialized\n\""); Com_Printf("Error: Remote control system is not initialized\n"); return qfalse; } if(clientnum < 0 || clientnum > 63) return qfalse; cl = &svs.clients[clientnum]; redirectClient = cl; while ( msg[i] != ' ' && msg[i] != '\0' && msg[i] != '\n' && i < 32 ){ i++; } if(i > 29 || i < 3) return qfalse; Q_strncpyz(cmd,msg,i+1); //Prevent buffer overflow as well as prevent the execution of priveleged commands by using seperator characters Q_strncpyz(buffer,msg,256); Q_strchrrepl(buffer,';','\0'); Q_strchrrepl(buffer,'\n','\0'); Q_strchrrepl(buffer,'\r','\0'); // start redirecting all print outputs to the packet power = SV_RemoteCmdGetClPower(cl); powercmd = Cmd_GetPower(cmd); if(!Q_stricmpn(cmd,"auth",4)){ printPtr = cmd; }else{ printPtr = buffer; } if(powercmd == -1){ SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\n^3Command execution failed - Invalid command invoked - Type ^2$cmdlist ^3to get a list of all available commands\"", printPtr); return qfalse; } if(powercmd > power){ SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\n^3Command execution failed - Insufficient power to execute this command.\n^3You need at least ^6%i ^3powerpoints to invoke this command.\n^3Type ^2$cmdlist ^3to get a list of all available commands\"", printPtr, powercmd); return qtrue; } Com_Printf( "Command execution: %s Invoked by: %s InvokerUID: %i Power: %i\n", printPtr, cl->name, cl->uid, power); Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_ReliableSendRedirect); i = cmdInvoker.currentCmdPower; cmdInvoker.currentCmdPower = power; cmdInvoker.authserver = qfalse; j = cmdInvoker.currentCmdInvoker; cmdInvoker.currentCmdInvoker = cl->uid; cmdInvoker.clientnum = clientnum; Cmd_ExecuteSingleCommand( 0, 0, buffer ); #ifdef PUNKBUSTER if(!Q_stricmpn(buffer, "pb_sv_", 6)) PbServerForceProcess(); #endif SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\"", buffer); cmdInvoker.currentCmdPower = i; cmdInvoker.currentCmdInvoker = j; cmdInvoker.clientnum = -1; Com_EndRedirect(); return qtrue; }
qboolean SV_ExecuteRemoteCmd(int clientnum, const char *msg){ char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; char cmd[30]; char buffer[256]; char *printPtr; int i = 0; int j = 0; int powercmd; int power; client_t *cl; qboolean critcmd; if(clientnum < 0 || clientnum > 63) return qfalse; cl = &svs.clients[clientnum]; redirectClient = cl; while ( msg[i] != ' ' && msg[i] != '\0' && msg[i] != '\n' && i < 32 ){ i++; } if(i > 29 || i < 3) return qfalse; Q_strncpyz(cmd,msg,i+1); if(!Q_stricmpn(cmd, "auth", 4)){ if(!Q_stricmp(cmd, "authChangePassword")) { Q_strncpyz(cmd, "changePassword", sizeof(cmd)); } else if(!Q_stricmp(cmd, "authSetAdmin")) { Q_strncpyz(cmd, "AdminAddAdminWithPassword", sizeof(cmd)); } else if(!Q_stricmp(cmd, "authUnsetAdmin")) { Q_strncpyz(cmd, "AdminRemoveAdmin", sizeof(cmd)); } else if(!Q_stricmp(cmd, "authListAdmins")) { Q_strncpyz(cmd, "adminListAdmins", sizeof(cmd)); } }else if(!Q_stricmp(cmd, "cmdpowerlist")){ Q_strncpyz(cmd, "AdminListCommands", sizeof(cmd)); }else if(!Q_stricmp(cmd, "setCmdMinPower")){ Q_strncpyz(cmd, "AdminChangeCommandPower", sizeof(cmd)); } //Prevent buffer overflow as well as prevent the execution of priveleged commands by using seperator characters Q_strncpyz(buffer,msg,256); Q_strchrrepl(buffer,';','\0'); Q_strchrrepl(buffer,'\n','\0'); Q_strchrrepl(buffer,'\r','\0'); // start redirecting all print outputs to the packet power = Auth_GetClPower(cl); powercmd = Cmd_GetPower(cmd); if(strstr(cmd, "password")) { printPtr = "hiddencmd"; critcmd = qtrue; }else{ printPtr = buffer; critcmd = qfalse; } if(powercmd == -1){ SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\n^3Command execution failed - Invalid command invoked - Type ^2$cmdlist ^3to get a list of all available commands\"", printPtr); return qfalse; } if(powercmd > power){ SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\n^3Command execution failed - Insufficient power to execute this command.\n^3You need at least ^6%i ^3powerpoints to invoke this command.\n^3Type ^2$cmdlist ^3to get a list of all available commands\"", printPtr, powercmd); return qtrue; } Com_Printf( "Command execution: %s Invoked by: %s InvokerUID: %i Power: %i\n", printPtr, cl->name, cl->uid, power); Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_ReliableSendRedirect); i = Cmd_GetInvokerUID(); j = Cmd_GetInvokerPower(); Cmd_SetCurrentInvokerInfo(cl->uid, power, clientnum); Cmd_ExecuteSingleCommand( 0, 0, buffer ); #ifdef PUNKBUSTER if(!Q_stricmpn(buffer, "pb_sv_", 6)) PbServerForceProcess(); #endif if(!critcmd) { SV_SendServerCommand(redirectClient, "e \"^5Command^2: %s\"", buffer); } Cmd_SetCurrentInvokerInfo(i, j, -1); Com_EndRedirect(); return qtrue; }
qboolean HL2Rcon_SourceRconEvent(netadr_t *from, msg_t *msg, int connectionId){ // int packetlen; int packettype; int type; int8_t team; int8_t clientnum; int32_t *updatelen; char* command; char* chatline; char sv_outputbuf[HL2RCON_SOURCEOUTPUTBUF_LENGTH]; msg_t msg2; byte data[20000]; char stringbuf[8 * MAX_STRING_CHARS]; MSG_BeginReading(msg); while(msg->readcount < msg->cursize) { //packetlen = MSG_ReadLong(msg); if(connectionId < 0 || connectionId >= MAX_RCONUSERS) return qtrue; rconUser_t* user; user = &sourceRcon.activeRconUsers[connectionId]; user->lastpacketid = MSG_ReadLong(msg); packettype = MSG_ReadLong(msg); switch(packettype) { case SERVERDATA_GETSTATUS: //status request //Pop the end of body byte MSG_ReadByte(msg); MSG_Init(&msg2, data, sizeof(data)); MSG_WriteLong(&msg2, 0); //writing 0 for now MSG_WriteLong(&msg2, user->lastpacketid); // ID MSG_WriteLong(&msg2, SERVERDATA_STATUSRESPONSE); // Type: status response SV_WriteRconStatus(&msg2); MSG_WriteByte(&msg2, 0); //Adjust the length updatelen = (int32_t*)msg2.data; *updatelen = msg2.cursize - 4; NET_SendData(from->sock, &msg2); break; case SERVERDATA_EXECCOMMAND: command = MSG_ReadString(msg, stringbuf, sizeof(stringbuf)); //Pop the end of body byte MSG_ReadByte(msg); Com_Printf("Rcon from: %s command: %s\n", NET_AdrToString(from), command); sourceRcon.redirectUser = connectionId+1; HL2Rcon_ExecuteConsoleCommand(command, user->uid); sourceRcon.redirectUser = 0; break; case SERVERDATA_TURNONSTREAM: type = MSG_ReadByte(msg); //Pop the end of body byte MSG_ReadByte(msg); sourceRcon.redirectUser = connectionId+1; Com_BeginRedirect (sv_outputbuf, sizeof(sv_outputbuf), HL2Rcon_SourceRconFlushRedirect); HL2Rcon_SourceRconStreaming_enable( type, user->uid ); Com_EndRedirect (); sourceRcon.redirectUser = 0; break; case SERVERDATA_SAY: clientnum = MSG_ReadByte(msg); // -1 if Team or for all is used team = MSG_ReadByte(msg); // teamnumber or -1 if it is for all team or clientnum is set chatline = MSG_ReadString(msg, stringbuf, sizeof(stringbuf)); //Pop the end of body byte MSG_ReadByte(msg); sourceRcon.redirectUser = connectionId+1; HL2Rcon_SayToPlayers(clientnum, team, chatline); sourceRcon.redirectUser = 0; break; default: //Not a source rcon packet Com_Printf("Not a valid source rcon packet from: %s received. Type: %d - Closing connection\n", NET_AdrToString(from), packettype); return qtrue; } } return qfalse; }
/* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand(netadr_t from, msg_t * msg) { bool valid; unsigned int time; char remaining[1024]; // show_bug.cgi?id=376 // if we send an OOB print message this size, 1.31 clients die in a Com_Printf buffer overflow // the buffer overflow will be fixed in > 1.31 clients // but we want a server side fix // we must NEVER send an OOB message that will be > 1.31 MAXPRINTMSG (4096) #define SV_OUTPUTBUF_LENGTH ( 256 - 16 ) char sv_outputbuf[SV_OUTPUTBUF_LENGTH], *cmd_aux; static unsigned int lasttime = 0; // TTimo - show_bug.cgi?id=534 time = Com_Milliseconds(); // Do we have a whitelist for rcon? if(sv_WhiteListRcon->string && *sv_WhiteListRcon->string) { // Prevent use of rcon from addresses that have not been whitelisted if(!SV_IsRconWhitelisted(&from)) { Com_Printf( "SVC_RemoteCommand: attempt from %s who is not whitelisted\n", NET_AdrToString( from ) ); NET_OutOfBandPrint(NS_SERVER, from, "print\nClient not found whitelist data.\n"); SV_DropClientsByAddress(&from, "Client tried to access to RCON password."); return; } } if ( !strlen( sv_rconPassword->string ) || strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { // MaJ - If the rconpassword is bad and one just happned recently, don't spam the log file, just die. if ( (unsigned)( time - lasttime ) < 500u ) { return; } valid = false; Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } else { // MaJ - If the rconpassword is good, allow it much sooner than a bad one. if ( (unsigned)( time - lasttime ) < 200u ) { return; } valid = true; Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } lasttime = time; // start redirecting all print outputs to the packet svs.redirectAddress = from; // FIXME TTimo our rcon redirection could be improved // big rcon commands such as status lead to sending // out of band packets on every single call to Com_Printf // which leads to client overflows // see show_bug.cgi?id=51 // (also a Q3 issue) Com_BeginRedirect(sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if(!strlen(sv_rconPassword->string)) { Com_Printf("No rconpassword set on the server.\n"); } else if(!valid) { Com_Printf("Bad rconpassword.\n"); } else { remaining[0] = 0; // ATVI Wolfenstein Misc #284 // get the command directly, "rcon <pass> <command>" to avoid quoting issues // extract the command by walking // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing cmd_aux = Cmd_Cmd(); cmd_aux += 4; while(cmd_aux[0] == ' ') { cmd_aux++; } while(cmd_aux[0] && cmd_aux[0] != ' ') { // password cmd_aux++; } while(cmd_aux[0] == ' ') { cmd_aux++; } Q_strcat(remaining, sizeof(remaining), cmd_aux); Cmd_ExecuteString(remaining); } Com_EndRedirect(); }