b7e0b5f7674dfa7600149aab93d79bed3c3b926a chmalee Mon Jan 13 17:40:22 2025 -0800 When prefixing a file specified by the hubspace ui with the userDataDir, run realpath(3) on the file name to prevent looking up system files, refs #35018 diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c index 5b2af68..5a16df8 100644 --- src/hg/lib/userdata.c +++ src/hg/lib/userdata.c @@ -7,30 +7,31 @@ #include "common.h" #include "hash.h" #include "portable.h" #include "trashDir.h" #include "md5.h" #include "hgConfig.h" #include "dystring.h" #include "cheapcgi.h" #include "customFactory.h" #include "wikiLink.h" #include "userdata.h" #include "jksql.h" #include "hdb.h" #include "hubSpace.h" #include "hubSpaceQuotas.h" +#include <limits.h> char *getUserName() /* Query the right system for the users name */ { return (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL; } char *emailForUserName(char *userName) /* Fetch the email for this user from gbMembers hgcentral table */ { struct sqlConnection *sc = hConnectCentral(); struct dyString *query = sqlDyStringCreate("select email from gbMembers where userName = '%s'", userName); char *email = sqlQuickString(sc, dyStringCannibalize(&query)); hDisconnectCentral(&sc); // this should be freeMem'd: @@ -87,41 +88,47 @@ char *encUserName = cgiEncode(userName); char *userPrefix = md5HexForString(encUserName); userPrefix[2] = '\0'; struct dyString *userDirDy = dyStringNew(0); dyStringPrintf(userDirDy, "%s/%s/%s/", HUB_SPACE_URL, userPrefix, encUserName); retUrl = dyStringCannibalize(&userDirDy); } return retUrl; } char *prefixUserFile(char *userName, char *fname, char *parentDir) /* Allocate a new string that contains the full per-user path to fname, NULL otherwise. * parentDir is optional and will go in between the per-user dir and the fname */ { char *pathPrefix = getDataDir(userName); +char *path = NULL; if (pathPrefix) { if (parentDir) { struct dyString *ret = dyStringCreate("%s%s%s%s", pathPrefix, parentDir, lastChar(parentDir) == '/' ? "" : "/", fname); - return dyStringCannibalize(&ret); + path = dyStringCannibalize(&ret); } else - return catTwoStrings(pathPrefix, fname); + path = catTwoStrings(pathPrefix, fname); + char canonicalPath[PATH_MAX]; + realpath(path, canonicalPath); + // after canonicalizing the path, make sure it starts with the userDataDir, to prevent + // deleting files like blah/../../../../systemFile.text + if (startsWith(pathPrefix, canonicalPath)) + return cloneString(canonicalPath); } -else return NULL; } static boolean checkHubSpaceRowExists(struct hubSpace *row) /* Return TRUE if row already exists */ { struct sqlConnection *conn = hConnectCentral(); struct dyString *queryCheck = sqlDyStringCreate("select count(*) from hubSpace where userName='%s' and fileName='%s' and parentDir='%s'", row->userName, row->fileName, row->parentDir); int ret = sqlQuickNum(conn, dyStringCannibalize(&queryCheck)); hDisconnectCentral(&conn); return ret > 0; } char *hubNameFromPath(char *path) /* Return the last directory component of path. Assume that a '.' char in the last component