-- lua module to prevent ws overrun exploit -- version: 1 -- author: reyalp@gmail.com -- confugration: -- add wsfix.lua to lua_modules, or merge into your existing et_ClientCommand -- TY McSteve for reporting this to us. function et_ClientCommand(cno,cmd) if string.lower(cmd) == "ws" then local n = tonumber(et.trap_Argv(1)) if n and n < 0 or n > 21 then et.G_LogPrint(string.format("wsfix: client %d bad ws %d\n",cno,n)) return 1 end end return 0 end -- lua module to prevent various borkage by invalid userinfo -- version: 1 -- author: reyalp@gmail.com -- confugration: -- add userinfocheck.lua to lua_modules, or merge into your existing module --require("rllib/vstrict").init() --AutoDeclare() -- returns nil if ok, or reason function check_userinfo( cno ) local userinfo = et.trap_GetUserinfo(cno) -- printf("check_userinfo: [%s]\n",userinfo) -- the game never seems to make userinfos without a leading backslash, -- or with a trailing backslash, so reject those from the start if (string.sub(userinfo,1,1) ~= "\\" ) then return "missing leading slash" end -- shouldn't really be possible, since the engine stuffs ip\ip:port on the end if (string.sub(userinfo,-1,1) == "\\" ) then return "trailing slash" end -- now that we know it is properly formed, count the slashes local n = 0 for _ in string.gfind(userinfo,"\\") do n = n + 1 end if math.mod(n,2) == 1 then return "unbalanced" end local m local t = {} -- right number of slashes, check for dupe keys for m in string.gfind(userinfo,"\\([^\\]*)\\") do if string.len(m) == 0 then return "empty key" end m = string.lower(m) if t[m] then return "duplicate key" end t[m] = true end -- return nil end -- merged with fakeplimit below -- check in et_ClientConnect since kicking in -- the initial et_ClientUserinfoChanged is a bit screwy --[[ function et_ClientConnect( cno, firsttime, isbot ) -- printf("connect %d\n",cno) local reason = check_userinfo( cno ) if ( reason ) then et.G_LogPrint(string.format("userinfocheck connect: client %d bad userinfo %s\n",infocheck_client,reason)) return "bad userinfo" end end --]] -- 3.2.6 and earlier doesn't actually call et_ClientUserinfoChanged -- every time the userinfo changes, -- so we use et_RunFrame to check every so often -- comment this out or adjust to taste infocheck_lasttime=0 infocheck_client=0 -- check a client every 5 sec infocheck_freq=5000 function et_RunFrame( leveltime ) if ( infocheck_lasttime + infocheck_freq > leveltime ) then return end -- printf("infocheck %d %d\n", infocheck_client, leveltime) infocheck_lasttime = leveltime if ( et.gentity_get( infocheck_client, "inuse" ) ) then local reason = check_userinfo( infocheck_client ) if ( reason ) then et.G_LogPrint(string.format("userinfocheck frame: client %d bad userinfo %s\n",infocheck_client,reason)) et.trap_SetUserinfo( cno, "name\\badinfo" ) et.trap_DropClient( cno, "bad userinfo", 0 ) end end infocheck_client = infocheck_client + 1 if ( infocheck_client >= tonumber(et.trap_Cvar_Get("sv_maxclients")) ) then infocheck_client = 0 end end function et_ClientUserinfoChanged( cno ) -- printf("clientuserinfochanged %d\n",cno) local reason = check_userinfo( cno ) if ( reason ) then et.G_LogPrint(string.format("userinfocheck infochanged: client %d bad userinfo %s\n",cno,reason)) et.trap_SetUserinfo( cno, "name\\badinfo" ) et.trap_DropClient( cno, "bad userinfo", 0 ) end end --NoAutoDeclare() -- lua module to prevent guid borkage -- version: 1 -- author: reyalp@gmail.com -- confugration: -- add guidcheck.lua to lua_modules, or merge into your existing et_Print() callback. -- TY pants --require("rllib/vstrict").init() --AutoDeclare() -- default to kick with no temp ban for now DEF_GUIDCHECK_BANTIME = 0 function bad_guid(cno,reason) local bantime = tonumber(et.trap_Cvar_Get( "guidcheck_bantime" )) if not bantime or bantime < 0 then bantime = DEF_GUIDCHECK_BANTIME end et.G_LogPrint(string.format("guidcheck: client %d bad GUID %s\n",cno,reason)) -- we don't send them the reason. They can figure it out for themselves. et.trap_DropClient(cno,"You are banned from this server",bantime) end function check_guid_line(text) --find a GUID line local guid,netname local mstart,mend,cno = string.find(text,"^etpro IAC: (%d+) GUID %[") if not mstart then return end text=string.sub(text,mend+1) --GUID] [NETNAME]\n mstart,mend,guid = string.find(text,"^([^%]]*)%] %[") if not mstart then bad_guid(cno,"couldn't parse guid") return end --NETNAME]\n text=string.sub(text,mend+1) netname = et.gentity_get(cno,"pers.netname") mstart,mend = string.find(text,netname,1,true) if not mstart or mstart ~= 1 then bad_guid(cno,"couldn't parse name") return end text=string.sub(text,mend+1) if text ~= "]\n" then bad_guid(cno,"trailing garbage") return end -- printf("guidcheck: etpro GUID %d %s %s\n",cno,guid,netname) -- {N} is too complicated! mstart,mend = string.find(guid,"^%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x$") if not mstart then bad_guid(cno,"malformed") return end -- printf("guidcheck: OK\n") end function et_Print(text) check_guid_line(text) end --NoAutoDeclare() -- lua module to limit fakeplayers DOS -- http://aluigi.altervista.org/fakep.htm -- used if cvar is not set -- author: reyalp@gmail.com -- confugration: -- add fakeplimit.lua to lua_modules -- set ip_max_clients cvar as desired. If not set, defaults to the value below. --FAKEPLIMIT_VERSION = "1.0" DEF_IP_MAX_CLIENTS = 3 et.G_Printf = function(...) et.G_Print(string.format(unpack(arg))) end function IPForClient(clientNum) -- TODO listen servers may be 'localhost' local userinfo = et.trap_GetUserinfo( clientNum ) if userinfo == "" then return "" end local ip = et.Info_ValueForKey( userinfo, "ip" ) -- strip port ip = string.sub(ip,string.find(ip,"(%d+%.%d+%.%d+%.%d+)")) -- et.G_Printf("IPForClient(%d) = [%s]\n",clientNum,ip) return ip end function et_ClientConnect( clientNum, firstTime, isBot ) -- userinfocheck stuff. Do this before IP limit -- printf("connect %d\n",cno) local reason = check_userinfo( clientNum ) if ( reason ) then et.G_LogPrint(string.format("userinfocheck connect: client %d bad userinfo %s\n",infocheck_client,reason)) return "bad userinfo" end local ip = IPForClient( clientNum ) local count = 1 -- we count as the first one local max = tonumber(et.trap_Cvar_Get( "ip_max_clients" )) if not max or max <= 0 then max = DEF_IP_MAX_CLIENTS end -- et.G_Printf("firstTime %d\n",firstTime); -- it's probably safe to only do this on firsttime, but checking -- every time doesn't hurt much -- validate userinfo to filter out the people blindly using luigi's code local userinfo = et.trap_GetUserinfo( clientNum ) -- et.G_Printf("userinfo: [%s]\n",userinfo) if et.Info_ValueForKey( userinfo, "rate" ) == "" then et.G_Printf("fakeplimit.lua: invalid userinfo from %s\n",ip) return "invalid connection" end for i = 0, et.trap_Cvar_Get("sv_maxclients") - 1 do -- pers.connected is set correctly for fake players -- can't rely on userinfo being empty if i ~= clientNum and et.gentity_get(i,"pers.connected") > 0 and ip == IPForClient(i) then count = count + 1 if count > max then et.G_Printf("fakeplimit.lua: too many connections from %s\n",ip) -- TODO should we drop / ban all connections from this IP ? return string.format("only %d connections per IP are allowed on this server",max) end end end end