Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Address flaw in CGI mapfile loading that makes it possible to bypass …
…security controls (#6313) (#6314)

* Create coverity-scan.yml

* Update coverity-scan.yml

* Avoid resource leak... (CID 1503409)

* Revert "Avoid resource leak... (CID 1503409)"

This reverts commit 7d261af.

* Updated...

* Limit action to MapServer/MapServer repo, run every Sunday (for now).

* Always force map parameter values through validation checks. Add validation checks on environment variable names.

* msIsValidRegex(): fix memleak

Co-authored-by: Even Rouault <even.rouault@spatialys.com>
  • Loading branch information
sdlime and rouault committed Apr 30, 2021
1 parent 3c3c9b3 commit 82a3eb5
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
30 changes: 30 additions & 0 deletions mapfile.c
Expand Up @@ -104,6 +104,16 @@ int msValidateParameter(char *value, char *pattern1, char *pattern2, char *patte
return(MS_FAILURE);
}

int msIsValidRegex(const char* e) {
ms_regex_t re;
if(ms_regcomp(&re, e, MS_REG_EXTENDED|MS_REG_NOSUB) != 0) {
msSetError(MS_REGEXERR, "Failed to compile expression (%s).", "msEvalRegex()", e);
return(MS_FALSE);
}
ms_regfree(&re);
return MS_TRUE;
}

int msEvalRegex(const char *e, const char *s)
{
ms_regex_t re;
Expand All @@ -124,6 +134,26 @@ int msEvalRegex(const char *e, const char *s)
return(MS_TRUE);
}

int msCaseEvalRegex(const char *e, const char *s)
{
ms_regex_t re;

if(!e || !s) return(MS_FALSE);

if(ms_regcomp(&re, e, MS_REG_EXTENDED|MS_REG_ICASE|MS_REG_NOSUB) != 0) {
msSetError(MS_REGEXERR, "Failed to compile expression (%s).", "msEvalRegex()", e);
return(MS_FALSE);
}

if(ms_regexec(&re, s, 0, NULL, 0) != 0) { /* no match */
ms_regfree(&re);
return(MS_FALSE);
}
ms_regfree(&re);

return(MS_TRUE);
}

#ifdef USE_MSFREE
void msFree(void *p)
{
Expand Down
3 changes: 2 additions & 1 deletion mapserv.c
Expand Up @@ -160,7 +160,8 @@ int main(int argc, char *argv[])

/* push high-value ENV vars into the CPL global config - primarily for IIS/FastCGI */
const char* const apszEnvVars[] = {
"CURL_CA_BUNDLE", "MS_MAPFILE", "MS_MAP_NO_PATH", "MS_MAP_PATTERN",
"CURL_CA_BUNDLE", "MS_MAPFILE", "MS_MAP_NO_PATH", "MS_MAP_PATTERN", "MS_MAP_ENV_PATTERN",
"MS_MAP_BAD_PATTERN", "MS_MAP_ENV_BAD_PATTERN",
NULL /* guard */ };
for( int i = 0; apszEnvVars[i] != NULL; ++i ) {
const char* value = getenv(apszEnvVars[i]);
Expand Down
2 changes: 2 additions & 0 deletions mapserver.h
Expand Up @@ -2122,7 +2122,9 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char
MS_DLL_EXPORT char *msWriteReferenceMapToString(referenceMapObj *ref);
MS_DLL_EXPORT char *msWriteLegendToString(legendObj *legend);
MS_DLL_EXPORT char *msWriteClusterToString(clusterObj *cluster);
MS_DLL_EXPORT int msIsValidRegex(const char* e);
MS_DLL_EXPORT int msEvalRegex(const char *e, const char *s);
MS_DLL_EXPORT int msCaseEvalRegex(const char *e, const char *s);
#ifdef USE_MSFREE
MS_DLL_EXPORT void msFree(void *p);
#else
Expand Down
56 changes: 41 additions & 15 deletions mapservutil.c
Expand Up @@ -201,41 +201,67 @@ mapObj *msCGILoadMap(mapservObj *mapserv)
int i, j;
mapObj *map = NULL;

const char *ms_map_bad_pattern_default = "[/\\]{2}|[/\\]?\\.+[/\\]|,";
const char *ms_map_env_bad_pattern_default = "^(AUTH_.*|CERT_.*|CONTENT_(LENGTH|TYPE)|DOCUMENT_(ROOT|URI)|GATEWAY_INTERFACE|HTTP.*|QUERY_STRING|PATH_(INFO|TRANSLATED)|REMOTE_.*|REQUEST_(METHOD|URI)|SCRIPT_(FILENAME|NAME)|SERVER_.*)";

int ms_mapfile_tainted = MS_TRUE;
const char *ms_mapfile = CPLGetConfigOption("MS_MAPFILE", NULL);

const char *ms_map_no_path = CPLGetConfigOption("MS_MAP_NO_PATH", NULL);
const char *ms_map_pattern = CPLGetConfigOption("MS_MAP_PATTERN", NULL);
const char *ms_map_env_pattern = CPLGetConfigOption("MS_MAP_ENV_PATTERN", NULL);

const char *ms_map_bad_pattern = CPLGetConfigOption("MS_MAP_BAD_PATTERN", NULL);
if(ms_map_bad_pattern == NULL) ms_map_bad_pattern = ms_map_bad_pattern_default;

const char *ms_map_env_bad_pattern = CPLGetConfigOption("MS_MAP_ENV_BAD_PATTERN", NULL);
if(ms_map_env_bad_pattern == NULL) ms_map_env_bad_pattern = ms_map_env_bad_pattern_default;

for(i=0; i<mapserv->request->NumParams; i++) /* find the mapfile parameter first */
if(strcasecmp(mapserv->request->ParamNames[i], "map") == 0) break;

if(i == mapserv->request->NumParams) {
if(ms_mapfile != NULL) {
map = msLoadMap(ms_mapfile,NULL);
} else {
if(ms_mapfile == NULL) {
msSetError(MS_WEBERR, "CGI variable \"map\" is not set.", "msCGILoadMap()"); /* no default, outta here */
return NULL;
}
ms_mapfile_tainted = MS_FALSE;
} else {
if(getenv(mapserv->request->ParamValues[i])) /* an environment variable references the actual file to use */
map = msLoadMap(getenv(mapserv->request->ParamValues[i]), NULL);
else {
/* by here we know the request isn't for something in an environment variable */
if(ms_map_no_path != NULL) {
msSetError(MS_WEBERR, "Mapfile not found in environment variables and this server is not configured for full paths.", "msCGILoadMap()");
if(getenv(mapserv->request->ParamValues[i])) { /* an environment variable references the actual file to use */
/* validate env variable name */
if(msIsValidRegex(ms_map_env_bad_pattern) == MS_FALSE || msCaseEvalRegex(ms_map_env_bad_pattern, mapserv->request->ParamValues[i]) == MS_TRUE) {
msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()");
return NULL;
}

if(ms_map_pattern != NULL && msEvalRegex(ms_map_pattern, mapserv->request->ParamValues[i]) != MS_TRUE) {
msSetError(MS_WEBERR, "Parameter 'map' value fails to validate.", "msCGILoadMap()");
if(ms_map_env_pattern != NULL && msEvalRegex(ms_map_env_pattern, mapserv->request->ParamValues[i]) != MS_TRUE) {
msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()");
return NULL;
}
ms_mapfile = getenv(mapserv->request->ParamValues[i]);
} else {
/* by now we know the request isn't for something in an environment variable */
if(ms_map_no_path != NULL) {
msSetError(MS_WEBERR, "CGI variable \"map\" not found in environment and this server is not configured for full paths.", "msCGILoadMap()");
return NULL;
}
ms_mapfile = mapserv->request->ParamValues[i];
}
}

/* ok to try to load now */
map = msLoadMap(mapserv->request->ParamValues[i], NULL);
/* validate ms_mapfile if tainted */
if(ms_mapfile_tainted == MS_TRUE) {
if(msIsValidRegex(ms_map_bad_pattern) == MS_FALSE || msEvalRegex(ms_map_bad_pattern, ms_mapfile) == MS_TRUE) {
msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()");
return NULL;
}
if(ms_map_pattern != NULL && msEvalRegex(ms_map_pattern, ms_mapfile) != MS_TRUE) {
msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()");
return NULL;
}
}


/* ok to try to load now */
map = msLoadMap(ms_mapfile, NULL);
if(!map) return NULL;

if(!msLookupHashTable(&(map->web.validation), "immutable")) {
Expand Down

0 comments on commit 82a3eb5

Please sign in to comment.