From 786a6cdce13bb6faaaacca6da4abe0f21fcd74cf Mon Sep 17 00:00:00 2001 From: ac-stef Date: Mon, 27 Apr 2015 15:25:20 +0200 Subject: [PATCH] filename filtering for media files No more spaces in filenames. The only allowed chars are a-z, A-Z, 0-9 and "-_.()". An exception are texture filenames, which may also have a prefix command like "" included. Also fixed some memory leaks in media download. --- source/src/audiomanager.cpp | 1 + source/src/client.cpp | 17 ++++++++++------- source/src/md3.h | 3 ++- source/src/protocol.cpp | 27 +++++++++++++++++++-------- source/src/protocol.h | 7 +++++-- source/src/rendermodel.cpp | 22 +++++++++++++--------- source/src/texture.cpp | 22 ++++++++++++++++++++-- source/src/zip.cpp | 1 + 8 files changed, 71 insertions(+), 29 deletions(-) diff --git a/source/src/audiomanager.cpp b/source/src/audiomanager.cpp index 6ff5a6ee..98a641c0 100644 --- a/source/src/audiomanager.cpp +++ b/source/src/audiomanager.cpp @@ -811,6 +811,7 @@ COMMANDF(registersound, "siii", (char *name, int *vol, int *loop, int *audiblera COMMANDF(mapsound, "si", (char *name, int *maxuses) { + filtertext(name, name, FTXT__MEDIAFILEPATH); audiomgr.addsound(name, 255, *maxuses, true, mapsounds, false, 0); intret(mapconfigdata.mapsoundlines.length()); copystring(mapconfigdata.mapsoundlines.add().name, name); diff --git a/source/src/client.cpp b/source/src/client.cpp index 708c2ed5..f7a790b8 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -1146,21 +1146,24 @@ int downloadpackages() bool requirepackage(int type, const char *path) { - if(!havecurl || canceldownloads || type < 0 || type >= PCK_NUM || pendingpackages.access(path)) return false; + pckserver *source = NULL; + loopv(pckservers) if(pckservers[i]->responsive) { source = pckservers[i]; break; } + if(!source) { conoutf(_("\f3no responsive source server found, can't download")); return false; } + + char *cleanpath = unixpath(newstring(path)); + filtertext(cleanpath, cleanpath, FTXT__MEDIAFILEPATH); + if(strcmp(path, cleanpath)) { conoutf("refuse to download \"%s\": illegal characters", path); delstring(cleanpath); return false; } + if(!havecurl || canceldownloads || type < 0 || type >= PCK_NUM || pendingpackages.access(cleanpath)) { delstring(cleanpath); return false; } package *pck = new package; - pck->name = unixpath(newstring(path)); + pck->name = cleanpath; pck->type = type; - loopv(pckservers) if(pckservers[i]->responsive) { pck->source = pckservers[i]; break; } - if(!pck->source) { conoutf(_("\f3no responsive source server found, can't download")); return false; } + pck->source = source; pck->pending = true; - pendingpackages.access(pck->name, pck); - return true; } - void writepcksourcecfg() { if(pckservers.length()) diff --git a/source/src/md3.h b/source/src/md3.h index 6d5545a9..1a729f6d 100644 --- a/source/src/md3.h +++ b/source/src/md3.h @@ -255,6 +255,7 @@ struct md3 : vertmodel void md3load(char *model) { if(!loadingmd3) { conoutf("not loading an md3"); return; }; + filtertext(model, model, FTXT__MEDIAFILEPATH); defformatstring(filename)("%s/%s", md3dir, model); md3::md3part &mdl = *new md3::md3part; loadingmd3->parts.add(&mdl); @@ -265,7 +266,7 @@ void md3load(char *model) void md3skin(char *objname, char *skin) { - if(!objname || !skin) return; + filtertext(skin, skin, FTXT__MEDIAFILEPATH); if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); return; }; md3::part &mdl = *loadingmd3->parts.last(); bool used = false; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index c7866169..4e1d026d 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -123,6 +123,8 @@ void getstring(char *text, ucharbuf &p, int len) // dst can be identical to src; dst needs to be of size "min(len, strlen(s)) + 1" // returns dst +//#define FILENAMESALLLOWERCASE + char *filtertext(char *dst, const char *src, int flags, int len) { char *res = dst; @@ -137,7 +139,8 @@ char *filtertext(char *dst, const char *src, int flags, int len) leet = (flags & FTXT_LEET) != 0, // translates leetspeak toupp = (flags & FTXT_TOUPPER) != 0, // translates to all-uppercase tolow = (flags & FTXT_TOLOWER) != 0, // translates to all-lowercase - filename = (flags & FTXT_FILENAME) != 0, // removes characters, that are not allowed in filenames on all supported systems - does not filter COM, PRN, etc. + filename = (flags & FTXT_FILENAME) != 0, // strict a-z, 0-9 and "-_.()" (also translates "[]" and "{}" to "()"), removes everything between '<' and '>' + allowslash = (flags & FTXT_ALLOWSLASH) != 0, // only in combination with FTXT_FILENAME mapname = (flags & FTXT_MAPNAME) != 0, // only allows lowercase chars, digits, '_', '-' and '.'; probably should be used in combination with TOLOWER cropwhite = (flags & FTXT_CROPWHITE) != 0, // removes leading and trailing whitespace pass = false; @@ -153,9 +156,13 @@ char *filtertext(char *dst, const char *src, int flags, int len) } #endif if(leet || mapname) nocolor = true; +#ifdef FILENAMESALLLOWERCASE + if(filename) tolow = true; +#endif bool trans = toupp || tolow || leet || filename || fillblanks; bool leadingwhite = cropwhite; char *lastwhite = NULL; + bool insidepointybrackets = false; for(int c = *src; c; c = *++src) { c &= 0x7F; // 7-bit ascii. not negotiable. @@ -171,19 +178,23 @@ char *filtertext(char *dst, const char *src, int flags, int len) } if(filename) { - const char *org = "*?![]{};:/\\", - *asc = "__o()()__oo", // 'o' -> ignore + if(c == '>') insidepointybrackets = false; + else if(c == '<') insidepointybrackets = true; + if(insidepointybrackets || c == '>') continue; // filter anything between '<' and '>' as this may contain commands for texture loading + const char *org = "[]{}\\", + *asc = "()()/", *a = strchr(org, c); - if(a) - { - c = asc[a - org]; - if(c == 'o') continue; - } + if(a) c = asc[a - org]; } if(tolow) c = tolower(c); else if(toupp) c = toupper(c); if(fillblanks && c == ' ') c = '_'; } + if(filename && !(islower(c) +#ifndef FILENAMESALLLOWERCASE + || isupper(c) +#endif + || isdigit(c) || strchr("._-()", c) || (allowslash && c == '/'))) continue; if(safecs && strchr("($)\"", c)) continue; if(c == '\t') { diff --git a/source/src/protocol.h b/source/src/protocol.h index 03128187..9b26c49d 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -150,8 +150,9 @@ enum #define FTXT_TOUPPER (1<<17) #define FTXT_TOLOWER (1<<18) #define FTXT_FILENAME (1<<19) -#define FTXT_MAPNAME (1<<20) -#define FTXT_CROPWHITE (1<<21) +#define FTXT_ALLOWSLASH (1<<20) +#define FTXT_MAPNAME (1<<21) +#define FTXT_CROPWHITE (1<<22) // character classes #define FTXT__SERVDESC (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_TABTOBLANK) #define FTXT__MAPMSG (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_TABTOBLANK) @@ -169,4 +170,6 @@ enum #define FTXT__KICKBANREASON (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_NOCOLOR | FTXT_TABTOBLANK) #define FTXT__FAVCATEGORY (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_NOCOLOR) #define FTXT__GLOBALNAME (FTXT_NOWHITE | FTXT_NOCOLOR | FTXT_FILLBLANKS) +#define FTXT__MEDIAFILENAME (FTXT_FILENAME) +#define FTXT__MEDIAFILEPATH (FTXT_FILENAME | FTXT_ALLOWSLASH) diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index 25e4f6f0..24d23b96 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -111,15 +111,19 @@ const char *mmshortname(const char *name) { return !strncmp(name, mmpath, strlen void mapmodel(int *rad, int *h, int *zoff, char *snap, char *name) { - intret(mapmodels.length()); - mapmodelinfo &mmi = mapmodels.add(); - mmi.rad = *rad; - mmi.h = *h; - mmi.zoff = *zoff; - mmi.m = NULL; - formatstring(mmi.name)("%s%s", mmpath, name); - mapmodelchanged = 1; - flagmapconfigchange(); + if(*snap && *name) // ignore "mapmodel" commands with insufficient parameters + { + intret(mapmodels.length()); + mapmodelinfo &mmi = mapmodels.add(); + mmi.rad = *rad; + mmi.h = *h; + mmi.zoff = *zoff; + mmi.m = NULL; + filtertext(name, name, FTXT__MEDIAFILEPATH); + formatstring(mmi.name)("%s%s", mmpath, name); + mapmodelchanged = 1; + flagmapconfigchange(); + } } COMMAND(mapmodel, "iiiss"); diff --git a/source/src/texture.cpp b/source/src/texture.cpp index f342b8c6..5c088c21 100644 --- a/source/src/texture.cpp +++ b/source/src/texture.cpp @@ -501,7 +501,23 @@ void texture(float *scale, char *name) { intret(slots.length()); Slot &s = slots.add(); - copystring(s.name, name); + filtertext(s.name, parentdir(name), FTXT__MEDIAFILEPATH); // filter parts separately, because filename may (legally) contain "" + if(*s.name) concatstring(s.name, "/"); + name = (char *)behindpath(name); + if(*name == '<') + { + char *endcmd = strchr(name, '>'); + if(endcmd) + { + *endcmd = '\0'; + concatstring(s.name, name); + concatstring(s.name, ">"); + name = endcmd + 1; + } + } + filtertext(name, name, FTXT__MEDIAFILENAME); + concatstring(s.name, name); + s.tex = NULL; s.loaded = false; s.orgscale = *scale; @@ -586,6 +602,7 @@ void loadskymap(bool reload) if(!reload) { int n = strlen(legacyprefix); + filtertext(loadsky, loadsky, FTXT__MEDIAFILEPATH); if(!strncmp(loadsky, legacyprefix, n)) { // remove the prefix char *t = loadsky; @@ -613,7 +630,8 @@ void loadnotexture(char *c) *mapconfigdata.notexturename = '\0'; if(c[0]) { - defformatstring(p)("packages/textures/%s", c); + filtertext(mapconfigdata.notexturename, c, FTXT__MEDIAFILEPATH); + defformatstring(p)("packages/textures/%s", mapconfigdata.notexturename); noworldtexture = textureload(p); if(noworldtexture==notexture) conoutf("could not load alternative texture '%s'.", p); } diff --git a/source/src/zip.cpp b/source/src/zip.cpp index 7ec3f3db..b4244951 100644 --- a/source/src/zip.cpp +++ b/source/src/zip.cpp @@ -211,6 +211,7 @@ bool extractzipfile(ziparchive *a, zipfile *f, const char *name); bool fitspackage(char *filename, int type) { + if(strchr(filename, ' ')) return false; // refuse all filenames with spaces char *extension = strrchr(filename, '.'); if(!extension) return false; ++extension;