21123cb5a9a1542b7ff01542f29078adc04764a0 max Sun Sep 6 07:31:34 2026 -0700 lib: check every component in isSafeRelativePath The loop that walks the '/'-separated components stepped to the terminator that replaced the slash instead of past it, so it ended after the first component and the rest of the path went unchecked. Callers are gfServer's dynamic mode, which takes genome and genomeDataDir from the client. diff --git src/lib/filePath.c src/lib/filePath.c index 111874ed9ea..094257139c3 100644 --- src/lib/filePath.c +++ src/lib/filePath.c @@ -1,262 +1,262 @@ /* filePath - stuff to handle file name parsing. */ /* Copyright (C) 2011 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "filePath.h" #include "portable.h" #include "net.h" void undosPath(char *path) /* Convert '\' to '/' in path. */ { subChar(path, '\\', '/'); } void splitPath(char *path, char dir[PATH_LEN], char name[FILENAME_LEN], char extension[FILEEXT_LEN]) /* Split a full path into components. The dir component will include the * trailing / if any. The extension component will include the starting * . if any. Pass in NULL for dir, name, or extension if you don't care about * that part. */ { char *dirStart, *nameStart, *extStart, *extEnd; int dirSize, nameSize, extSize; undosPath(path); dirStart = path; nameStart = strrchr(path,'/'); if (nameStart == NULL) nameStart = path; else nameStart += 1; extStart = strrchr(nameStart, '.'); if (extStart == NULL) extStart = nameStart + strlen(nameStart); extEnd = extStart + strlen(extStart); if ((dirSize = (nameStart - dirStart)) >= PATH_LEN) errAbort("Directory too long in %s", path); if ((nameSize = (extStart - nameStart)) >= FILENAME_LEN) errAbort("Name too long in %s", path); if ((extSize = (extEnd - extStart)) >= FILEEXT_LEN) errAbort("Extension too long in %s", path); if (dir != NULL) { memcpy(dir, dirStart, dirSize); dir[dirSize] = 0; } if (name != NULL) { memcpy(name, nameStart, nameSize); name[nameSize] = 0; } if (extension != NULL) { memcpy(extension, extStart, extSize); extension[extSize] = 0; } } static char *findSlashBefore(char *start, char *e) /* Return first slash before s (but not before start) */ { while (--e >= start) { if (*e == '/') return e; } return start; } char *expandRelativePath(char *baseDir, char *relPath) /* Expand relative path to more absolute one. */ { if (relPath[0] == '/') // hey, it's absolute actually... return cloneString(relPath); char *e = baseDir + strlen(baseDir); int slashCount; char *rel = relPath; char *result; int size, baseSize; undosPath(baseDir); undosPath(relPath); slashCount = countChars(baseDir, '/'); if (baseDir[0] == 0) slashCount = -1; while (startsWith("../", rel)) { if (slashCount < 0) { warn("More ..'s in \"%s\" than directories in \"%s\"", relPath, baseDir); return NULL; } else if (slashCount == 0) e = baseDir; else e = findSlashBefore(baseDir, e); slashCount -= 1; rel += 3; } baseSize = e - baseDir; size = strlen(rel) + 1; if (baseSize > 0) size += baseSize + 1; if (baseSize > 0) { result = needMem(size); memcpy(result, baseDir, baseSize); result[baseSize] = '/'; strcpy(result + baseSize + 1, rel); } else result = cloneString(rel); return result; } char *mustExpandRelativePath(char *dir, char* relPath) /* Given a dir and relative path, expand path. * Handy for processing symlinks. errAbort if expand fails. * Result should be freeMem'd.*/ { char *path = expandRelativePath(dir, relPath); if (!path) errAbort("Too many .. in path %s to make relative to submitDir %s\n", relPath, dir); return path; } char *pathRelativeToFile(char *baseFile, char *relPath) /* Given a base file name and a path relative to that, return * relative path interpreted as if it were seen from the * same directory holding the baseFile. * An example of using this would be in processing include * files. In this case the baseFile would be the current * source file, and the relPath would be from the include * statement. The returned result could then be used to * open the include file. */ { char dir[PATH_LEN]; splitPath(baseFile, dir, NULL, NULL); int dirLen = strlen(dir); if (dirLen > 0 && dir[dirLen-1] == '/') dir[dirLen-1] = 0; return expandRelativePath(dir, relPath); } char *mustPathRelativeToFile(char *baseFile, char *relPath) /* Make Path Relative To File or Abort. */ { char *path = pathRelativeToFile(baseFile, relPath); if (!path) errAbort("Too many .. in symlink path %s to make relative to %s\n", relPath, baseFile); return path; } char *makeRelativePath(char *from, char *to) /* Calculate a relative path from one absolute directory/file to another. * Assumptions: both from and to are canonicalized absolute paths beginning * at "/" or "//". Filenames are okay, but all directory names must end with * a "/" to distinguish them from files. * e.g., /test/dir/ is a directory, but /test/dir is a file. */ { int i, j, toCount, fromCount; char fromDir[PATH_LEN]; char toDir[PATH_LEN], toFile[FILENAME_LEN], toExt[FILEEXT_LEN]; char relPath [PATH_LEN] = ""; char *fromDirList[PATH_LEN], *toDirList[PATH_LEN]; boolean fromStartsDoubleSlash = FALSE, toStartsDoubleSlash = FALSE; if (startsWith("//", from) && from[2] != '/') { fromStartsDoubleSlash = TRUE; from[1] = '_'; // prevent initial // from being misinterpreted } if (startsWith("//", to) && to[2] != '/') { toStartsDoubleSlash = TRUE; to[1] = '_'; // prevent initial // from being misinterpreted } splitPath(from, fromDir, NULL, NULL); splitPath(to, toDir, toFile, toExt); fromCount = chopByChar(fromDir, '/', fromDirList, ArraySize(fromDirList)); toCount = chopByChar(toDir, '/', toDirList, ArraySize(toDirList)); if (fromStartsDoubleSlash == TRUE) fromDirList[1][0] = '/'; if (toStartsDoubleSlash == TRUE) toDirList[1][0] = '/'; for (i=1; i < min(fromCount-1, toCount-1); i++) { if (!sameString(fromDirList[i], toDirList[i])) break; } for (j=i; j < fromCount-1; j++) { safecat(relPath, sizeof relPath, "../"); } for (j=i; j < toCount-1; j++) { safecat(relPath, sizeof relPath, toDirList[j]); safecat(relPath, sizeof relPath, "/"); } safecat(relPath, sizeof relPath, toFile); safecat(relPath, sizeof relPath, toExt); return cloneString(relPath); } boolean isSafeRelativePath(char *path) /* check that path is relative and contains no ".." elements */ { if (startsWith("/", path)) return FALSE; char tmpPath[PATH_LEN]; safecpy(tmpPath, sizeof(tmpPath), path); char *p = tmpPath; while (TRUE) { char *end = strchr(p, '/'); if (end != NULL) *end = '\0'; if (sameString(p, "..")) return FALSE; if (end == NULL) break; else - p = end++; + p = end + 1; } return TRUE; } char *resolveDotDots(char *pathOrUrl) /* Given a file path or URL, return a version with ".." components resolved. * For URLs, only the path portion is simplified (scheme :// is preserved). * Double slashes are collapsed for file paths but not for URLs. * Result should be freeMem'd. */ { if (hasProtocol(pathOrUrl)) { struct netParsedUrl npu; netParseUrl(pathOrUrl, &npu); eatExcessDotsInPath(npu.file); return urlFromNetParsedUrl(&npu); } else { char result[PATH_LEN]; safecpy(result, sizeof(result), pathOrUrl); eatSlashSlashInPath(result); eatExcessDotsInPath(result); return cloneString(result); } }