/* * Convert a "text" filename argument to C string, and check it's allowable. * * Filename may be absolute or relative to the DataDir, but we only allow * absolute paths that match DataDir or Log_directory. */ static char * convert_and_check_filename(text *arg, bool logAllowed) { char *filename = text_to_cstring(arg); canonicalize_path(filename); /* filename can change length here */ if (is_absolute_path(filename)) { /* Disallow '/a/b/data/..' */ if (path_contains_parent_reference(filename)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("reference to parent directory (\"..\") not allowed")))); /* * Allow absolute paths if within DataDir or Log_directory, even * though Log_directory might be outside DataDir. */ if (!path_is_prefix_of_path(DataDir, filename) && (!logAllowed || !is_absolute_path(Log_directory) || !path_is_prefix_of_path(Log_directory, filename))) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed")))); } else if (!path_is_relative_and_below_cwd(filename)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("path must be in or below the current directory")))); return filename; }
/* * Detect whether a path is only in or below the current working directory. * An absolute path that matches the current working directory should * return false (we only want relative to the cwd). We don't allow * "/../" even if that would keep us under the cwd (it is too hard to * track that). */ bool path_is_relative_and_below_cwd(const char *path) { if (is_absolute_path(path)) return false; /* don't allow anything above the cwd */ else if (path_contains_parent_reference(path)) return false; #ifdef WIN32 /* * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is * relative to the cwd on that drive, or the drive's root directory if * that drive has no cwd. Because the path itself cannot tell us which is * the case, we have to assume the worst, i.e. that it is not below the * cwd. We could use GetFullPathName() to find the full path but that * could change if the current directory for the drive changes underneath * us, so we just disallow it. */ else if (isalpha((unsigned char) path[0]) && path[1] == ':' && !IS_DIR_SEP(path[2])) return false; #endif else return true; }
/* * Convert a "text" filename argument to C string, and check it's allowable. * * Filename may be absolute or relative to the DataDir, but we only allow * absolute paths that match DataDir or Log_directory. */ static char * convert_and_check_filename(text *arg) { char *filename; filename = text_to_cstring(arg); canonicalize_path(filename); /* filename can change length here */ /* Disallow ".." in the path */ if (path_contains_parent_reference(filename)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("reference to parent directory (\"..\") not allowed")))); if (is_absolute_path(filename)) { /* Allow absolute references within DataDir */ if (path_is_prefix_of_path(DataDir, filename)) return filename; /* The log directory might be outside our datadir, but allow it */ if (is_absolute_path(Log_directory) && path_is_prefix_of_path(Log_directory, filename)) return filename; ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed")))); return NULL; /* keep compiler quiet */ } else { return filename; } }
/* * Convert a "text" filename argument to C string, and check it's allowable. * * Filename may be absolute or relative to the DataDir, but we only allow * absolute paths that match DataDir or Log_directory. * * This does a privilege check against the 'pg_read_server_files' role, so * this function is really only appropriate for callers who are only checking * 'read' access. Do not use this function if you are looking for a check * for 'write' or 'program' access without updating it to access the type * of check as an argument and checking the appropriate role membership. */ static char * convert_and_check_filename(text *arg) { char *filename; filename = text_to_cstring(arg); canonicalize_path(filename); /* filename can change length here */ /* * Members of the 'pg_read_server_files' role are allowed to access any * files on the server as the PG user, so no need to do any further checks * here. */ if (is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_SERVER_FILES)) return filename; /* User isn't a member of the default role, so check if it's allowable */ if (is_absolute_path(filename)) { /* Disallow '/a/b/data/..' */ if (path_contains_parent_reference(filename)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("reference to parent directory (\"..\") not allowed")))); /* * Allow absolute paths if within DataDir or Log_directory, even * though Log_directory might be outside DataDir. */ if (!path_is_prefix_of_path(DataDir, filename) && (!is_absolute_path(Log_directory) || !path_is_prefix_of_path(Log_directory, filename))) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed")))); } else if (!path_is_relative_and_below_cwd(filename)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("path must be in or below the current directory")))); return filename; }