I have a public folder pub with subfolders and files in it. A user gives me now a relative filepath, I perform some mappings, and I read the file with fstream and return it to the user.
The problem is now if the user gives me a path like e.g. ../fileXY.txt or some other fancy stuff considering path traversal or other types of filepath injection. fstream is just gonna accept it and read potential files outside of my public pub folder or even worse give them a list of all files on my system etc... .
Before reinventing the wheel, I searched in the filesystem library
and I have seen there is this std::filesystem::canonical function and there is quite a talk about the normal form. I have a general question here, can this function and the variant std::filesystem::weakly_canonical be used to prevent this types of vulnerabilities? So basically is it enough?
Further, my system's filesystem library is still in experimental mode and the std::filesystem::weakly_canonical is missing. But I cannot use the canonical because the files must exist in canonical. In my case I have certain mappings and the files dont exist in that sense. So I would need to mimic the weakly_canonical function, but how?
I have seen a related stackoverflow question on realpath for nonexisting paths and he was suggested to repeat the canonical as long as the path exist and then to add the nonexisting part to it, but that is again vulnerable to these type of injections. So do I have to roll my own weakly_canonical or can I somehow mimic it by combining some std::experimental::filesystem functions?
Short answer no.
Long answer this is modeled after posix realpath
I understand the source of confusion. From realpath
The realpath() function shall derive, from the pathname pointed to by file_name, an absolute pathname that resolves to the same directory entry, whose resolution does not involve '.', '..
From cppref path you can also see that the double dot is removed. However the path still points to the same file. It's just that redundant elements are removed.
If you are processing values from a db/webapp/whatever where your program has different privileges than the user who supplied the path, you need to sanitize the filename first by escaping double dots. Dots are fine.
Perhaps you can use a regex to escape double dots with a backslash thus rendering them ineffective.
#include <iostream>
#include <filesystem>
#include <string>
#include <regex>
int main()
{
std::string bad = "../bad/../other";
std::filesystem::path p(bad);
std::cout << std::filesystem::weakly_canonical(p) << std::endl;
std::regex r(R"(\.\.)");
p = std::regex_replace(bad, r, "\\.\\.");
std::cout << std::filesystem::weakly_canonical(p) << std::endl;
}
Output
"/tmp/other"
"/tmp/1554895428.8689194/\.\./bad/\.\./other"
Run sample
I can see how you could employ weakly_canonical() to prevent path traversal - similar to what is described here - by checking that the result is prefixed with your base path. E.g.
#include <iostream>
#include <filesystem>
#include <optional>
// Returns the canonical form of basepath/relpath if the canonical form
// is under basepath, otherwise returns std::nullopt.
// Note that one would probably require that basepath is sanitized,
// safe for use in this context and absolute.
// Thanks to https://portswigger.net/web-security/file-path-traversal
// for the basic idea.
std::optional<std::filesystem::path> abspath_no_traversal(
const std::filesystem::path & basepath,
const std::filesystem::path & relpath) {
const auto abspath = std::filesystem::weakly_canonical(basepath / relpath);
// thanks to https://stackoverflow.com/questions/1878001/how-do-i-check-if-a-c-stdstring-starts-with-a-certain-string-and-convert-a
const auto index = abspath.string().rfind(basepath.string(), 0);
if (index != 0) {
return std::nullopt;
}
return abspath;
}
Since I am no security expert, I welcome any corrections.
For example I have the string "root/data/home/file1.txt" I would like to get "root/data/home" Is there a convenient function in C++ that allows me to do this or should I code it myself?
You can do basic string manipulation, i.e.
std::string path = "root/data/home/file1.txt";
// no error checking here
std::string prefix = path.substr(0, path.find_last_of('/'));
or take a third option like Boost.Filesystem:
namespace fs = boost::filesystem;
fs::path path = "root/data/home/file1.txt";
fs::path prefix = path.parent_path();
If you're on a POSIX system, try dirname(3).
There's certainly no convenient function in the language itself. The string library provides find_last_of, which should do well.
This is rather platform-dependent. For example, Windows uses '\' for a path separator (mostly), Unix uses '/', and MacOS (prior to OSX) uses ':'.
The Windows-specific API is PathRemoveFileSpec.
How would I compare 2 strings to determine if they refer to the same path in Win32 using C/C++?
While this will handle a lot of cases it misses some things:
_tcsicmp(szPath1, szPath2) == 0
For example:
forward slashes / backslashes
relative / absolute paths.
[Edit] Title changed to match an existing C# question.
Open both files with CreateFile, call GetFileInformationByHandle for both, and compare dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. If all three are equal they both point to the same file:
GetFileInformationByHandle function
BY_HANDLE_FILE_INFORMATION Structure
Filesystem library
Since C++17 you can use the standard filesystem library. Include it using #include <filesystem>. You can access it even in older versions of C++, see footnote.
The function you are looking for is equivalent, under namespace std::filesystem:
bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );
To summarize from the documentation: this function takes two paths as parameters and returns true if they reference the same file or directory, false otherwise. There is also a noexcept overload that takes a third parameter: an std::error_code in which to save any possible error.
Example
#include <filesystem>
#include <iostream>
//...
int main() {
std::filesystem::path p1 = ".";
std::filesystem::path p2 = fs::current_path();
std::cout << std::filesystem::equivalent(p1, p2);
//...
}
Output:
1
Using filesystem before C++17
To use this library in versions prior to C++17 you have to enable experimental language features in your compiler and include the library in this way: #include <experimental/filesystem>. You can then use its functions under the namespace std::experimental::filesystem. Please note that the experimental filesystem library may differ from the C++17 one. See the documentation here.
For example:
#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);
See this question: Best way to determine if two path reference to same file in C#
The question is about C#, but the answer is just the Win32 API call GetFileInformationByHandle.
use the GetFullPathName from kernel32.dll, this will give you the absolute path of the file. Then compare it against the other path that you have using a simple string compare
edit: code
TCHAR buffer1[1000];
TCHAR buffer2[1000];
TCHAR buffer3[1000];
TCHAR buffer4[1000];
GetFullPathName(TEXT("C:\\Temp\\..\\autoexec.bat"),1000,buffer1,NULL);
GetFullPathName(TEXT("C:\\autoexec.bat"),1000,buffer2,NULL);
GetFullPathName(TEXT("\\autoexec.bat"),1000,buffer3,NULL);
GetFullPathName(TEXT("C:/autoexec.bat"),1000,buffer4,NULL);
_tprintf(TEXT("Path1: %s\n"), buffer1);
_tprintf(TEXT("Path2: %s\n"), buffer2);
_tprintf(TEXT("Path3: %s\n"), buffer3);
_tprintf(TEXT("Path4: %s\n"), buffer4);
the code above will print the same path for all three path representations.. you might want to do a case insensitive search after that
A simple string comparison is not sufficient for comparing paths for equality. In windows it's quite possible for c:\foo\bar.txt and c:\temp\bar.txt to point to exactly the same file via symbolic and hard links in the file system.
Comparing paths properly essentially forces you to open both files and compare low level handle information. Any other method is going to have flaky results.
Check out this excellent post Lucian made on the subject. The code is in VB but it's pretty translatable to C/C++ as he PInvoke'd most of the methods.
http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx
Based on answers about GetFileInformationByHandle(), here is the code.
Note: This will only work if the file already exists...
//Determine if 2 paths point ot the same file...
//Note: This only works if the file exists
static bool IsSameFile(LPCWSTR szPath1, LPCWSTR szPath2)
{
//Validate the input
_ASSERT(szPath1 != NULL);
_ASSERT(szPath2 != NULL);
//Get file handles
HANDLE handle1 = ::CreateFileW(szPath1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE handle2 = ::CreateFileW(szPath2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
bool bResult = false;
//if we could open both paths...
if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION fileInfo1;
BY_HANDLE_FILE_INFORMATION fileInfo2;
if (::GetFileInformationByHandle(handle1, &fileInfo1) && ::GetFileInformationByHandle(handle2, &fileInfo2))
{
//the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number)
bResult = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow;
}
}
//free the handles
if (handle1 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle1);
}
if (handle2 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle2);
}
//return the result
return bResult;
}
If you have access to the Boost libraries, try
bool boost::filesystem::path::equivalent( const path& p1, const path& p2 )
http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html#equivalent
To summarize from the docs: Returns true if the given path objects resolve to the same file system entity, else false.
What you need to do is get the canonical path.
For each path you have ask the file system to convert to a canonical path or give you an identifier the uniquely identifies the file (such as the iNode).
Then compare the canonical path or the unique identifier.
Note:
Do not try and figure out the conical path yourself the File System can do things with symbolic links etc that that are not easily tractable unless you are very familiar with the filesystem.
Comparing the actual path strings will not produce accurate results if you refer to UNC or Canonical paths (i.e. anything other than a local path).
shlwapi.h has some Path Functions that may be of use to you in determing if your paths are the same.
It contains functions like PathIsRoot that could be used in a function of greater scope.
If the files exist and you can deal with the potential race condition and performance hit from opening the files, an imperfect solution that should work on any platform is to open one file for writing by itself, close it, and then open it for writing again after opening the other file for writing. Since write access should only be allowed to be exclusive, if you were able to open the first file for writing the first time but not the second time then chances are you blocked your own request when you tried to open both files.
(chances, of course, are also that some other part of the system has one of your files open)
Open both files and use GetFinalPathNameByHandle() against the HANDLEs. Then compare the paths.
Is there a platform-agnostic and filesystem-agnostic method to obtain the full path of the directory from where a program is running using C/C++? Not to be confused with the current working directory. (Please don't suggest libraries unless they're standard ones like clib or STL.)
(If there's no platform/filesystem-agnostic method, suggestions that work in Windows and Linux for specific filesystems are welcome too.)
Here's code to get the full path to the executing app:
Variable declarations:
char pBuf[256];
size_t len = sizeof(pBuf);
Windows:
int bytes = GetModuleFileName(NULL, pBuf, len);
return bytes ? bytes : -1;
Linux:
int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if(bytes >= 0)
pBuf[bytes] = '\0';
return bytes;
If you fetch the current directory when your program first starts, then you effectively have the directory your program was started from. Store the value in a variable and refer to it later in your program. This is distinct from the directory that holds the current executable program file. It isn't necessarily the same directory; if someone runs the program from a command prompt, then the program is being run from the command prompt's current working directory even though the program file lives elsewhere.
getcwd is a POSIX function and supported out of the box by all POSIX compliant platforms. You would not have to do anything special (apart from incliding the right headers unistd.h on Unix and direct.h on windows).
Since you are creating a C program it will link with the default c run time library which is linked to by ALL processes in the system (specially crafted exceptions avoided) and it will include this function by default. The CRT is never considered an external library because that provides the basic standard compliant interface to the OS.
On windows getcwd function has been deprecated in favour of _getcwd. I think you could use it in this fashion.
#include <stdio.h> /* defines FILENAME_MAX */
#ifdef WINDOWS
#include <direct.h>
#define GetCurrentDir _getcwd
#else
#include <unistd.h>
#define GetCurrentDir getcwd
#endif
char cCurrentPath[FILENAME_MAX];
if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
{
return errno;
}
cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */
printf ("The current working directory is %s", cCurrentPath);
This is from the cplusplus forum
On windows:
#include <string>
#include <windows.h>
std::string getexepath()
{
char result[ MAX_PATH ];
return std::string( result, GetModuleFileName( NULL, result, MAX_PATH ) );
}
On Linux:
#include <string>
#include <limits.h>
#include <unistd.h>
std::string getexepath()
{
char result[ PATH_MAX ];
ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
return std::string( result, (count > 0) ? count : 0 );
}
On HP-UX:
#include <string>
#include <limits.h>
#define _PSTAT64
#include <sys/pstat.h>
#include <sys/types.h>
#include <unistd.h>
std::string getexepath()
{
char result[ PATH_MAX ];
struct pst_status ps;
if (pstat_getproc( &ps, sizeof( ps ), 0, getpid() ) < 0)
return std::string();
if (pstat_getpathname( result, PATH_MAX, &ps.pst_fid_text ) < 0)
return std::string();
return std::string( result );
}
If you want a standard way without libraries: No. The whole concept of a directory is not included in the standard.
If you agree that some (portable) dependency on a near-standard lib is okay: Use Boost's filesystem library and ask for the initial_path().
IMHO that's as close as you can get, with good karma (Boost is a well-established high quality set of libraries)
I know it is very late at the day to throw an answer at this one but I found that none of the answers were as useful to me as my own solution. A very simple way to get the path from your CWD to your bin folder is like this:
int main(int argc, char* argv[])
{
std::string argv_str(argv[0]);
std::string base = argv_str.substr(0, argv_str.find_last_of("/"));
}
You can now just use this as a base for your relative path. So for example I have this directory structure:
main
----> test
----> src
----> bin
and I want to compile my source code to bin and write a log to test I can just add this line to my code.
std::string pathToWrite = base + "/../test/test.log";
I have tried this approach on Linux using full path, alias etc. and it works just fine.
NOTE:
If you are on windows you should use a '\' as the file separator not '/'. You will have to escape this too for example:
std::string base = argv[0].substr(0, argv[0].find_last_of("\\"));
I think this should work but haven't tested, so comment would be appreciated if it works or a fix if not.
Filesystem TS is now a standard ( and supported by gcc 5.3+ and clang 3.9+ ), so you can use current_path() function from it:
std::string path = std::experimental::filesystem::current_path();
In gcc (5.3+) to include Filesystem you need to use:
#include <experimental/filesystem>
and link your code with -lstdc++fs flag.
If you want to use Filesystem with Microsoft Visual Studio, then read this.
No, there's no standard way. I believe that the C/C++ standards don't even consider the existence of directories (or other file system organizations).
On Windows the GetModuleFileName() will return the full path to the executable file of the current process when the hModule parameter is set to NULL. I can't help with Linux.
Also you should clarify whether you want the current directory or the directory that the program image/executable resides. As it stands your question is a little ambiguous on this point.
On Windows the simplest way is to use the _get_pgmptr function in stdlib.h to get a pointer to a string which represents the absolute path to the executable, including the executables name.
char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
Maybe concatenate the current working directory with argv[0]? I'm not sure if that would work in Windows but it works in linux.
For example:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv) {
char the_path[256];
getcwd(the_path, 255);
strcat(the_path, "/");
strcat(the_path, argv[0]);
printf("%s\n", the_path);
return 0;
}
When run, it outputs:
jeremy#jeremy-desktop:~/Desktop$ ./test
/home/jeremy/Desktop/./test
For Win32 GetCurrentDirectory should do the trick.
You can not use argv[0] for that purpose, usually it does contain full path to the executable, but not nessesarily - process could be created with arbitrary value in the field.
Also mind you, the current directory and the directory with the executable are two different things, so getcwd() won't help you either.
On Windows use GetModuleFileName(), on Linux read /dev/proc/procID/.. files.
Just my two cents, but doesn't the following code portably work in C++17?
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main(int argc, char* argv[])
{
std::cout << "Path is " << fs::path(argv[0]).parent_path() << '\n';
}
Seems to work for me on Linux at least.
Based on the previous idea, I now have:
std::filesystem::path prepend_exe_path(const std::string& filename, const std::string& exe_path = "");
With implementation:
fs::path prepend_exe_path(const std::string& filename, const std::string& exe_path)
{
static auto exe_parent_path = fs::path(exe_path).parent_path();
return exe_parent_path / filename;
}
And initialization trick in main():
(void) prepend_exe_path("", argv[0]);
Thanks #Sam Redway for the argv[0] idea. And of course, I understand that C++17 was not around for many years when the OP asked the question.
Just to belatedly pile on here,...
there is no standard solution, because the languages are agnostic of underlying file systems, so as others have said, the concept of a directory based file system is outside the scope of the c / c++ languages.
on top of that, you want not the current working directory, but the directory the program is running in, which must take into account how the program got to where it is - ie was it spawned as a new process via a fork, etc. To get the directory a program is running in, as the solutions have demonstrated, requires that you get that information from the process control structures of the operating system in question, which is the only authority on this question. Thus, by definition, its an OS specific solution.
#include <windows.h>
using namespace std;
// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
const unsigned long maxDir = 260;
char currentDir[maxDir];
GetCurrentDirectory(maxDir, currentDir);
return string(currentDir);
}
For Windows system at console you can use system(dir) command. And console gives you information about directory and etc. Read about the dir command at cmd. But for Unix-like systems, I don't know... If this command is run, read bash command. ls does not display directory...
Example:
int main()
{
system("dir");
system("pause"); //this wait for Enter-key-press;
return 0;
}
Works with starting from C++11, using experimental filesystem, and C++14-C++17 as well using official filesystem.
application.h:
#pragma once
//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>
namespace std {
namespace filesystem = experimental::filesystem;
}
#endif
std::filesystem::path getexepath();
application.cpp:
#include "application.h"
#ifdef _WIN32
#include <windows.h> //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h> //readlink
#endif
std::filesystem::path getexepath()
{
#ifdef _WIN32
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(NULL, path, MAX_PATH);
return path;
#else
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
#endif
}
For relative paths, here's what I did. I am aware of the age of this question, I simply want to contribute a simpler answer that works in the majority of cases:
Say you have a path like this:
"path/to/file/folder"
For some reason, Linux-built executables made in eclipse work fine with this. However, windows gets very confused if given a path like this to work with!
As stated above there are several ways to get the current path to the executable, but the easiest way I find works a charm in the majority of cases is appending this to the FRONT of your path:
"./path/to/file/folder"
Just adding "./" should get you sorted! :) Then you can start loading from whatever directory you wish, so long as it is with the executable itself.
EDIT: This won't work if you try to launch the executable from code::blocks if that's the development environment being used, as for some reason, code::blocks doesn't load stuff right... :D
EDIT2: Some new things I have found is that if you specify a static path like this one in your code (Assuming Example.data is something you need to load):
"resources/Example.data"
If you then launch your app from the actual directory (or in Windows, you make a shortcut, and set the working dir to your app dir) then it will work like that.
Keep this in mind when debugging issues related to missing resource/file paths. (Especially in IDEs that set the wrong working dir when launching a build exe from the IDE)
A library solution (although I know this was not asked for).
If you happen to use Qt:
QCoreApplication::applicationDirPath()
Path to the current .exe
#include <Windows.h>
std::wstring getexepathW()
{
wchar_t result[MAX_PATH];
return std::wstring(result, GetModuleFileNameW(NULL, result, MAX_PATH));
}
std::wcout << getexepathW() << std::endl;
// -------- OR --------
std::string getexepathA()
{
char result[MAX_PATH];
return std::string(result, GetModuleFileNameA(NULL, result, MAX_PATH));
}
std::cout << getexepathA() << std::endl;
This question was asked 15 years ago, so the existing answers are now incorrect. If you're using C++17 or greater, the solution is very straightforward today:
#include <filesystem>
std::cout << std::filesystem::current_path();
See cppreference.com for more information.
On POSIX platforms, you can use getcwd().
On Windows, you may use _getcwd(), as use of getcwd() has been deprecated.
For standard libraries, if Boost were standard enough for you, I would have suggested Boost::filesystem, but they seem to have removed path normalization from the proposal. You may have to wait until TR2 becomes readily available for a fully standard solution.
Boost Filesystem's initial_path() behaves like POSIX's getcwd(), and neither does what you want by itself, but appending argv[0] to either of them should do it.
You may note that the result is not always pretty--you may get things like /foo/bar/../../baz/a.out or /foo/bar//baz/a.out, but I believe that it always results in a valid path which names the executable (note that consecutive slashes in a path are collapsed to one).
I previously wrote a solution using envp (the third argument to main() which worked on Linux but didn't seem workable on Windows, so I'm essentially recommending the same solution as someone else did previously, but with the additional explanation of why it is actually correct even if the results are not pretty.
As Minok mentioned, there is no such functionality specified ini C standard or C++ standard. This is considered to be purely OS-specific feature and it is specified in POSIX standard, for example.
Thorsten79 has given good suggestion, it is Boost.Filesystem library. However, it may be inconvenient in case you don't want to have any link-time dependencies in binary form for your program.
A good alternative I would recommend is collection of 100% headers-only STLSoft C++ Libraries Matthew Wilson (author of must-read books about C++). There is portable facade PlatformSTL gives access to system-specific API: WinSTL for Windows and UnixSTL on Unix, so it is portable solution. All the system-specific elements are specified with use of traits and policies, so it is extensible framework. There is filesystem library provided, of course.
The linux bash command
which progname will report a path to program.
Even if one could issue the which command from within your program and direct the output to a tmp file and the program
subsequently reads that tmp file, it will not tell you if that program is the one executing. It only tells you where a program having that name is located.
What is required is to obtain your process id number, and to parse out the path to the name
In my program I want to know if the program was
executed from the user's bin directory or from another in the path
or from /usr/bin. /usr/bin would contain the supported version.
My feeling is that in Linux there is the one solution that is portable.
Use realpath() in stdlib.h like this:
char *working_dir_path = realpath(".", NULL);
The following worked well for me on macOS 10.15.7
brew install boost
main.cpp
#include <iostream>
#include <boost/filesystem.hpp>
int main(int argc, char* argv[]){
boost::filesystem::path p{argv[0]};
p = absolute(p).parent_path();
std::cout << p << std::endl;
return 0;
}
Compiling
g++ -Wall -std=c++11 -l boost_filesystem main.cpp