diff --git a/config/scontext.cfg b/config/scontext.cfg index 83229ebb..59a6fb1a 100644 --- a/config/scontext.cfg +++ b/config/scontext.cfg @@ -1,17 +1,22 @@ -// define script contexts to allow certain commands -// in (untrusted) map configuration files +// define script security contexts to restrict commands to certain contexts // do NOT modify -scriptcontext 1 alias -scriptcontext 1 loadsky -scriptcontext 1 mapmodelreset -scriptcontext 1 mapmodel -scriptcontext 1 texturereset -scriptcontext 1 texture -scriptcontext 1 fog -scriptcontext 1 fogcolour -scriptcontext 1 mapsound -scriptcontext 1 watercolour +// supported contexts +alias context_core 0 // hardcoded scripts +alias context_cfg 1 // known scripts +alias context_prompt 2 // command propmpt +alias context_mapcfg 3 // map configs, we don't trust them + +// set allowed commands for the map config context +alias mapcfgidents [ alias loadsky mapmodelreset mapmodel texturereset texture fog fogcolour mapsound watercolour ] +loop i (listlen $mapcfgidents) [ + scriptcontext $context_mapcfg (at $mapcfgidents $i) +] + +// isolate the map config context +// this disables access from this context to identifiers located in other contexts +// also it removes all aliases created in this context once the running context changes +isolatecontext $context_mapcfg // secure this configuration for the rest of the game sealcontexts diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index f8b121d5..c5066d3e 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -215,7 +215,7 @@ void movelocalplayer() moveplayer(player1, 10, false); } } - else if(!intermission) + else if(player1->state!=CS_SPECTATE && !intermission) { moveplayer(player1, 20, true); checkitems(player1); diff --git a/source/src/command.cpp b/source/src/command.cpp index f18c36ba..77e9ff4e 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -4,9 +4,10 @@ #include "pch.h" #include "cube.h" +bool allowidentaccess(ident *id); char *exchangestr(char *o, char *n) { delete[] o; return newstring(n); } -int execcontext = IEXC_CORE; +int execcontext; hashtable *idents = NULL; // contains ALL vars/commands/aliases @@ -259,7 +260,7 @@ char *executeret(const char *p) // all evaluation hap } else { - if(execcontext > id->context) + if(!allowidentaccess(id)) { conoutf("not allowed in this execution context: %s", id->name); continue; @@ -577,26 +578,36 @@ void identnames(vector &names, bool builtinonly) enumerateht(*idents) if(!builtinonly || idents->enumc->data.type != ID_ALIAS) names.add(idents->enumc->key); } -void changescriptcontext(int newcontext) +vector contextstack; +bool contextsealed = false; +bool contextisolated[IEXC_NUM] = { false }; + +void pushscontext(int newcontext) { - if(newcontext < execcontext) // clean up aliases created in the old context + contextstack.add((execcontext = newcontext)); +} + +int popscontext() +{ + ASSERT(contextstack.length() > 0); + int old = contextstack.pop(); + execcontext = contextstack.last(); + + if(execcontext < old && old >= IEXC_MAPCFG) // clean up aliases created in the old (map cfg) context { enumerateht(*idents) { ident *id = &idents->enumc->data; - if(id->type == ID_ALIAS && id->context > newcontext) + if(id->type == ID_ALIAS && id->context > execcontext) { idents->remove(idents->enumc->key); i--; } } } - execcontext = newcontext; + return execcontext; } - -bool contextsealed = false; - void scriptcontext(char *context, char *idname) { if(contextsealed) return; @@ -606,8 +617,22 @@ void scriptcontext(char *context, char *idname) if(c >= 0 && c < IEXC_NUM) id->context = c; } +void isolatecontext(int context) +{ + if(context >= 0 && context < IEXC_NUM && !contextsealed) contextisolated[context] = true; +} + void sealcontexts() { contextsealed = true; } +bool allowidentaccess(ident *id) // check if ident is allowed in current context +{ + ASSERT(execcontext >= 0 && execcontext < IEXC_NUM); + if(!id) return false; + if(!contextisolated[execcontext]) return true; // only check if context is isolated + return execcontext <= id->context; +} + COMMAND(scriptcontext, ARG_2STR); +COMMAND(isolatecontext, ARG_1INT); COMMAND(sealcontexts, ARG_NONE); diff --git a/source/src/command.h b/source/src/command.h index 06925747..9814bdb1 100644 --- a/source/src/command.h +++ b/source/src/command.h @@ -26,8 +26,10 @@ struct ident int narg; // ID_VAR, ID_COMMAND char *action, *executing; // ID_ALIAS bool persist; + int context; + ident() {} // ID_VAR @@ -72,7 +74,7 @@ enum // function signatures for script functions, see command.cpp ARG_VARI, ARG_VARIW }; -enum { IEXC_CORE = 0, IEXC_FOREIGN, IEXC_NUM }; // script execution context +enum { IEXC_CORE = 0, IEXC_CFG, IEXC_PROMPT, IEXC_MAPCFG, IEXC_NUM }; // script execution context // nasty macros for registering script functions, abuses globals to avoid excessive infrastructure #define COMMANDN(name, fun, nargs) static bool __dummy_##fun = addcommand(#name, (void (*)())fun, nargs) diff --git a/source/src/main.cpp b/source/src/main.cpp index 3ded9f95..c491a681 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -34,6 +34,7 @@ void quit() // normal exit if(resetcfg) deletecfg(); else writecfg(); cleanup(NULL); + popscontext(); exit(EXIT_SUCCESS); } @@ -406,6 +407,8 @@ int main(int argc, char **argv) int uprate = 0, maxcl = DEFAULTCLIENTS, scthreshold = -5, port = 0; const char *sdesc = "", *ip = "", *master = NULL, *passwd = "", *maprot = NULL, *adminpwd = NULL, *srvmsg = NULL; + pushscontext(IEXC_CFG); + #define initlog(s) puts("init: " s) initing = true; @@ -622,6 +625,7 @@ int main(int argc, char **argv) } #endif } + quit(); return EXIT_SUCCESS; } diff --git a/source/src/protos.h b/source/src/protos.h index ba248b3d..c10036e5 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -45,6 +45,10 @@ extern void changescriptcontext(int newcontext); extern void explodelist(const char *s, vector &elems); extern char *indexlist(const char *s, int pos); extern char *parseword(const char *&p); +extern void pushscontext(int newcontext); +extern int popscontext(); +extern int curscontext(); +extern int execcontext; // console extern void keypress(int code, bool isdown, int cooked); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index a71e34ea..fdd46ddc 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -66,7 +66,10 @@ void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name) s_sprintf(mmi.name)("mapmodels/%s", name); } -void mapmodelreset() { mapmodels.setsize(0); } +void mapmodelreset() +{ + if(execcontext==IEXC_MAPCFG) mapmodels.setsize(0); +} mapmodelinfo &getmminfo(int i) { return mapmodels.inrange(i) ? mapmodels[i] : *(mapmodelinfo *)0; } diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 6193b6f1..f7f63a0a 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -376,11 +376,13 @@ bool load_world(char *mname) // still supports all map formats that have conoutf("read map %s (%d milliseconds)", cgzname, SDL_GetTicks()-lastmillis); conoutf("%s", hdr.maptitle); startmap(mname); + + pushscontext(IEXC_MAPCFG); // untrusted altogether execfile("config/default_map_settings.cfg"); - changescriptcontext(IEXC_FOREIGN); execfile(pcfname); execfile(mcfname); - changescriptcontext(IEXC_CORE); + popscontext(); + c2skeepalive(); loopi(256) if(texuse[i]) lookuptexture(i); c2skeepalive();