fix ".." handling and add more strict path filtering

Enable path() to collapse multiple levels of "../" (sauer backport). Clean
up multiple consecutive path delimiters. Remove feature to concat several
paths with '&' delimiters (unused in AC).

Filter paths in findfile() to disallow access to files below the AC root
directory.

This commit fixes the problem which prevented downloaded map models from
using stock textures.
This commit is contained in:
ac-stef
2015-05-27 15:47:04 +02:00
parent a067ea2a0b
commit 2349c7e508
+34 -30
View File
@@ -33,43 +33,45 @@ char *makerelpath(const char *dir, const char *file, const char *prefix, const c
return tmp;
}
char *path(char *s)
{
for(char *curpart = s;;)
char *c = s;
// skip "<decal>"
if(c[0] == '<')
{
char *endpart = strchr(curpart, '&');
if(endpart) *endpart = '\0';
if(curpart[0]=='<')
{
char *file = strrchr(curpart, '>');
if(!file) return s;
curpart = file+1;
char *enddecal = strrchr(c, '>');
if(!enddecal) return s;
c = enddecal + 1;
}
// substitute with single, proper path delimiters
for(char *t = c; (t = strpbrk(t, "/\\")); )
{
*t++ = PATHDIV;
size_t d = strspn(t, "/\\");
if(d) memmove(t, t + d, strlen(t + d) + 1); // remove multiple path delimiters
}
// collapse ".."-parts
for(char *prevdir = NULL, *curdir = s;;)
{
prevdir = curdir[0] == PATHDIV ? curdir + 1 : curdir;
curdir = strchr(prevdir, PATHDIV);
if(!curdir) break;
if(prevdir + 1 == curdir && prevdir[0]=='.')
{ // simply remove "./"
memmove(prevdir, curdir + 1, strlen(curdir));
curdir = prevdir;
}
for(char *t = curpart; (t = strpbrk(t, "/\\")); *t++ = PATHDIV);
for(char *prevdir = NULL, *curdir = s;;)
{
prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir;
curdir = strchr(prevdir, PATHDIV);
if(!curdir) break;
if(prevdir+1==curdir && prevdir[0]=='.')
else if(curdir[1] == '.' && curdir[2] == '.' && curdir[3] == PATHDIV)
{ // collapse "/foo/../" to "/"
if(prevdir + 2 == curdir && prevdir[0] == '.' && prevdir[1] == '.') continue; // foo is also ".." -> skip
memmove(prevdir, curdir + 4, strlen(curdir + 3));
if(prevdir >= c + 2 && prevdir[-1] == PATHDIV)
{
memmove(prevdir, curdir+1, strlen(curdir+1)+1);
curdir = prevdir;
}
else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV)
{
if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue;
memmove(prevdir, curdir+4, strlen(curdir+4)+1);
curdir = prevdir;
prevdir -= 2;
while(prevdir > c && prevdir[-1] != PATHDIV) --prevdir;
}
curdir = prevdir;
}
if(endpart)
{
*endpart = '&';
curpart = endpart+1;
}
else break;
}
return s;
}
@@ -217,6 +219,8 @@ void addpackagedir(const char *dir)
const char *findfile(const char *filename, const char *mode)
{
while(filename[0] == PATHDIV) filename++; // skip leading pathdiv
while(!strncmp(".." PATHDIVS, filename, 3)) filename += 3; // skip leading "../" (don't allow access to files below "AC root dir")
static string s;
formatstring(s)("%s%s", homedir, filename); // homedir may be ""
if(homedir[0] && fileexists(s, mode)) return s;