In my application, I am trying to check if a path is inside of a specific directory. E.g., I want the files in the path /x/y/z not to be accessible by parts of my application. I cannot use traditional file permissions, as other parts of the application should be able to access these files.
Several Internet resources suggest the use of realpath to first canonicalize paths, i.e., resolving all symlinks and instances of .. (e.g. 1, 2).
However, it seems not to be possible to perform path resolution followed by an open without an race condition (TOCTOU).
char *resolved = realpath("/my/potentially/dangerous/path.txt", NULL);
// someone changes any part of the path to a symlink to something else <--- race condition
if (check_path(resolved)) {
// <--- race condition
int fd = open(resolved, O_RDONLY);
}
Am I overlooking something or does POSIX (and Linux) not provide any way to do something like this without a race condition?
What about 'openat2' (Linux only)?
And once you have a file descriptor, see man open
Description:
A file descriptor is a reference to an open file description; this
reference is unaffected if pathname is subsequently removed or
modified to refer to a different file.
Related
I'm working on below process control issue reported by fortify which is described in https://vulncat.fortify.com/en/detail?id=desc.dataflow.abap.process_control#C%2FC%2B%2B
The function load() in filename.c calls dlopen() on line 3. The call loads a library without specifying an absolute path.
It could result in the program using a malicious library supplied by an attacker.
I have below function which is getting invoked in different places of code.
void* load(char* name)
{
void* handle;
handle = dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
return(handle);
}
void somefunc()
{
void *login_module_handle = load("/home/myuser/load_this_shared_lib.so");
}
Here I'm already using absolute path, but don't understand why Fortify still reports the error.
The possible recommendation from Fortify is as shown below.
Whenever possible, libraries should be controlled by the application and executed using an absolute path.
In cases where the path is not known at compile time, such as for cross-platform applications, an absolute path should be constructed from known values during execution.
Any suggestion would be helpful.
The load function could in theory be called someplace else that doesn't pass a full path or, even worse, a variable populated by the user that isn't properly checked.
load might better be implemented as a macro in this case:
#define load(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
Then when the substitution happens, dlopen is actually given a string constant specifying an absolute path that Fortify should be able to see.
There are two related but separate problems here:
How to load dynamic libraries in a secure manner at run time
How to make Fortify see security is properly taken care of
The common approach that Fortify recommends, is to define a fixed path to a directory, in which plugins must be located. It is assumed that only those with sufficient privileges have access to that directory. The pattern is similar to
#define _GNU_SOURCE
#ifndef PLUGIN_PATH
#define PLUGIN_PATH "/usr/lib/thisapp/plugins"
#endif
#ifndef PLUGIN_SUFFIX
#define PLUGIN_SUFFIX ".so"
#endif
// Try to load dynamic library 'name' under PLUGIN_PATH.
// Returns NULL if failed, with errno set.
// If this returns NULL and errno==0, use dlerror() to
// get the error message.
void *load(const char *name)
{
// Empty or NULL name is not allowed, and it may not start with '.'.
if (!name || !*name || *name == '.') {
errno = EINVAL;
return NULL;
}
// Name may not contain '/'.
if (strchr(name, '/')) {
errno = EINVAL;
return NULL;
}
char *path = NULL;
int pathlen = asprintf(&path, "%s/%s%s", PLUGIN_PATH, name, PLUGIN_SUFFIX);
if (pathlen < 1 || !path) {
errno = ENOMEM;
return NULL;
}
void *handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
free(path);
errno = 0; /* If handle is NULL, look at dlerror() */
return handle;
}
In general, a fixed path named after the application in the /usr/lib tree, say /usr/lib/application/plugins/ can be considered secure, assuming it is installed so the directory and all its parent directories are owned by superuser and a privileged group, with no write access to the others (i.e., drwxrwxr-x or drwr-xr-x).
If the user can specify full path to the library, each directory along the path must be vetted, to verify if they may be modified by unprivileged users. If any are suspect, then the library is not secure.
The library file itself can be opened read-only, and a read lease (see fcntl()) placed on it, if the process has sufficient privileges (CAP_LEASE capability, or run as a privileged user). This will fail if any process has the file open for writing, meaning another process is able to modify it while we use it. If the lease is granted, then any other process trying to open the file for writing causes them to block, and this process be notified by a signal. This process can delay (but not fully block) the other process from opening the library, for up to lease-break-time, typically 45 seconds. This way, a secure process can detect if someone (even privileged) tries to modify the plugin while it is being used.
With a valid read lease, it is time to examine the file ownership and mode (via fstat() using the file descriptor used to open it read-only), to see if it is owned and only modifiable by privileged users. If it is modifiable by unprivileged users, it is not secure.
After all the above checks, given the file descriptor number FD, the path provided to dlopen() is /proc/self/fd/FD. This reuses the same file descriptor, ensuring we do not have a TOCTTOU race window (time of check to time of use).
Whether Fortify recognizes the above measures, or just complains whenever it sees dlopen() used at all, I have no idea: I do not use it myself.
I have a daemon process that spawns several threads one of which handles HTTP requests. The handler is intended to return a file located in
resources/html/index.html
I have the following code:
void * read_file_ex(char *file_name, int32_t *data_len) {
FILE *fp;
fp = fopen(file_name, "r");
... // more code to fetch file contents
fclose(fp);
}
void * read_file(char *file_name){
return read_file_ex(file_name, NULL);
}
And in the thread, I call:
read_file("resources/html/index.html");
The code crashes with a "Segmentation Fault" error when a request is made for that file.
When I use GDB to break at fopen, I notice that a NULL is returned and errno is set to 2 (File not found).
Also, when I change the code to use the absolute path of the file:
/usr/sbin/app/resources/html/index.html
then `fopen()' is able to find the index file and everything works fine.
Another thing to mention is that this error happens when run on Debian Linux but not on Ubuntu 12.04 which makes my question look even dumber.
I forgot to add that I am running the program from the same folder that contains the `resources' folder.
If the current directory of the process is not /usr/sbin/app (and it seems a bit unlikely that the current directory would be /usr/bin/app), then the relative pathname won't work. You should always check the return result from fopen() before attempting to use it. There are endless reasons why an open operation can fail even if you're in the correct directory, let alone when there's a chance that you aren't.
Note that if your process uses functions like daemon(), or is run via a daemonize program, the current directory can be changed to / even if you expected it to be somewhere else.
If you need to check the current directory of the process (a process has a single current directory common to all threads), you can use the getcwd() to get the current working directory.
If you need to change directory (again) after daemonizing your process, you can use chdir() to do so. There's also fchdir() which can be used to change back to a directory if you have an open file descriptor for the directory.
I have a situation where I need to get a file name so that I can call the readlink() function. All I have is an integer that was originally stored as a file descriptor via an open() command. Problem is, I don't have access to the function where the open() command executed (if I did, then I wouldn't be posting this). The return value from open() was stored in a struct that I do have access to.
char buf[PATH_MAX];
char tempFD[2]; //file descriptor number of the temporary file created
tempFD[0] = fi->fh + '0';
tempFD[1] = '\0';
char parentFD[2]; //file descriptor number of the original file
parentFD[0] = (fi->fh - 1) + '0';
parentFD[1] = '\0';
if (readlink(tempFD, buf, sizeof(buf)) < 0) {
log_msg("\treadlink() error\n");
perror("readlink() error");
} else
log_msg("readlink() returned '%s' for '%s'\n", buf, tempFD);
This is part of the FUSE file system. The struct is called fi, and the file descriptor is stored in fh, which is of type uint64_t. Because of the way this program executes, I know that the two linked files have file descriptor numbers that are always 1 apart. At least that's my working assumption, which I am trying to verify with this code.
This compiles, but when I run it, my log file shows a readlink error every time. My file descriptors have the correct integer values stored in them, but it's not working.
Does anyone know how I can get the file name from these integer values? Thanks!
If it's acceptable that your code becomes non portable and is tied to being run on a somewhat modern version of Linux, then you can use /proc/<pid>/fd/<fd>. However, I would recommend against adding '0' to the fd as a means to get the string representing the number, because it uses the assumption that fd < 10.
However it would be best if you were able to just pick up the filename instead of relying on /proc. At the very least, you can replace calls to the library's function with a wrapper function using a linker flag. Example of usage is gcc program.c -Wl,-wrap,theFunctionToBeOverriden -o program, all calls to the library function will be linked against __wrap_theFunctionToBeOverriden; the original function is accessible under the name __real_theFunctionToBeOverriden. See this answer https://stackoverflow.com/a/617606/111160 for details.
But, back to the answer not involving linkage rerouting: you can do it something like
char fd_path[100];
snprintf("/proc/%d/fd/%d", sizeof(fd_path), getpid(), fi->fh);
You should now use this /proc/... path (it is a softlink) rather than using the path it links to.
You can call readlink to find the actual path in the filesystem. However, doing so introduces a security vulnerability and I suggest against using the path readlink returns.
When the file the descriptor points at is deleted,unlinked, then you can still access it through the /proc/... path. However, when you readlink on it, you get the original pathname (appended with a ' (deleted)' text).
If your file was /tmp/a.txt and it gets deleted, readlink on the /proc/... path returns /tmp/a.txt (deleted). If this path exists, you will be able to access it!, while you wanted to access a different file (/tmp/a.txt). An attacker may be able to provide hostile contents in the /tmp/a.txt (deleted) file.
On the other hand, if you just access the file through the /proc/... path, you will access the correct (unlinked but still alive) file, even if the path claims to be a link to something else.
I see that standard C has no way of telling if a file is already opened in another process. So the answer should contain several examples for each platform. I need that check for Visual C++ / Windows though.
Windows: Try to open the file in exclusive mode. If it works, no one else has opened the file and will not be able to open the file
HANDLE fh;
fh = CreateFile(filename, GENERIC_READ, 0 /* no sharing! exclusive */, NULL, OPEN_EXISTING, 0, NULL);
if ((fh != NULL) && (fh != INVALID_HANDLE_VALUE))
{
// the only open file to filename should be fh.
// do something
CloseHandle(fh);
}
MS says: dwShareMode
The sharing mode of an object, which can be read, write, both, delete, all of these, or none (refer to the following table).
If this parameter is zero and CreateFile succeeds, the object cannot be shared and cannot be opened again until the handle is closed.
You cannot request a sharing mode that conflicts with the access mode that is specified in an open request that has an open handle, because that would result in the following sharing violation: ERROR_SHARING_VIOLATION.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx
extension:
how to delete a (not readonly) file filesystem which no one has open for read/write?
access right FILE_READ_ATTRIBUTES, not DELETE. DELETE could cause problems on smb share (to MS Windows Servers) - CreateFile will leave with a still open FileHandle /Device/Mup:xxx filename - why ever and whatever this Mup is. Will not happen with access right FILE_READ_ATTRIBUTES
use FILE_FLAG_OPEN_REPARSE_POINT to delete filename. Else you will delete the target of a symbolic link - which is usually not what you want
HANDLE fh;
fh = CreateFile(filename, FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE /* no RW sharing! */, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_DELETE_ON_CLOSE, NULL);
if ((fh != NULL) && (fh != INVALID_HANDLE_VALUE))
{
DeleteFile(filename); /* looks stupid?
* but FILE_FLAG_DELETE_ON_CLOSE will not work on some smb shares (e.g. samba)!
* FILE_SHARE_DELETE should allow this DeleteFile() and so the problem could be solved by additional DeleteFile()
*/
CloseHandle(fh); /* a file, which no one has currently opened for RW is delete NOW */
}
what to do with an open file? If the file is open and you are allowed to do an unlink, you will be left a file where subsequent opens will lead to ACCESS_DENIED.
If you have a temporary folder, then it could be a good idea to rename(filename, tempdir/filename.delete) and delete tempdir/filename.delete.
There's no way tell, unless the other process explicitly forbids access to the file. In MSVC, you'd do so with _fsopen(), specifying _SH_DENYRD for the shflag argument. The notion of being interested whether a file is opened that isn't otherwise locked is deeply flawed on a multitasking operating system. It might be opened a microsecond after you'd have found it wasn't. That's also the reason that Windows doesn't have a IsFileLocked() function.
If you need synchronized access to files, you'll need to add this with a named mutex, use CreateMutex().
Getting the open_files information is DIFFICULT, it's like pulling teeth, and if you don't have an immediate need for it you shouldn't be asking for "several examples for each platform" just for the hell of it. Just my opinion, of course.
Linux and many Unix systems have a system utility called lsof which finds open file handles and stuff. The way it does so is by accessing /dev/kmem, which is a pseudo-file containing a copy of "live" kernel memory, i.e. the working storage of the operating system kernel. There are tables of open files in there, naturally, and the memory structure is open-source and documented, so it's just a matter of a lot of busywork for lsof to go in there, find the information and format it for the user.
Documentation for the deep innards of Windows, on the other hand, is practically nonexistent, and I'm not aware that the data structures are somehow exposed to the outside. I'm no Windows expert, but unless the Windows API explicitly offers this kind of information it may simply not be available.
Whatever is available is probably being used by Mark Russinovich's SysInternals utilities; the first one that comes to mind is FileMon. Looking at those may give you some clues. Update: I've just been informed that SysInternals Handles.exe is even closer to what you want.
If you manage to figure that out, good; otherwise you may be interested in catching file open/close operations as they happen: The Windows API offers a generous handful of so-called Hooks: http://msdn.microsoft.com/en-us/library/ms997537.aspx. Hooks allow you to request notification when certain things happen in the system. I believe there's one that will tell you when a program –systemwide– opens a file. So you can make your own list of files opened for the duration you're listening to your hooks. I don't know for sure but I suspect this may be what FileMon does.
The Windows API, including the hook functions, can be accessed from C. Systemwide hooks will require you to create a DLL to be loaded alongside your program.
Hope these hints help you get started.
For Windows, this code works also:
boolean isClosed(File f) { return f.renameTo(f); }
An opened file can not be renamed, and a rename to same name does not cause another error. So if the rename succeeds, not having really done something, you know the file is not open.
Any such check would be inherently racy. Another process could always open the file between the point where you did the check and the point where you accessed the file.
The answers so far should tell you that finding out the information you've asked for is tricky, non-portable, and often inherently unreliable. So, from my perspective, the real answer is don't do that. Try to find a way to think about your real problem so that this question doesn't arise.
this can't be that hard guys.
do this:
try{
File fileout = new File(path + ".xls");
FileOutPutStream out = new FileOutPutStream(fileout);
}
catch(FileNotFoundException e1){
// if a MS Windows process is already using the file, this exception will be thrown
}
catch(Exception e){
}
You can use something like this. It is not a proper solution. But it works,
bool IsFileDownloadComplete(const std::wstring& dir, const std::wstring& fileName)
{
std::wstring originalFileName = dir + fileName;
std::wstring tempFileName = dir + L"temp";
while(true)
{
int ret = rename(convertWstringToString(originalFileName).c_str(), convertWstringToString(tempFileName).c_str());
if(ret == 0)
break;
Sleep(10);
}
/** File is not open. Rename to original. */
int ret = rename(convertWstringToString(tempFileName).c_str(), convertWstringToString(originalFileName).c_str());
if(ret != 0)
throw std::exception("File rename failed");
return true;
}
How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.
Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.
Note
I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!
So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.
Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.
Convert file-descriptor to native file handle with _get_osfhandle()
Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
Use CreateFile to open a new handle to that file
Create a file descriptor for that handle with _open_osfhandle()
Et voilà, we have a new file descriptor referring to the same file, but with its own permissions, position, etc.
Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.
Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:
#include <stdio.h>
int get_perms(int fd) {
int i;
FILE * base = __iob_func();
for (i=0; i<_IOB_ENTRIES; i++)
if (base[i]._file == fd)
return base[i]._flag; // we've found our file
return 0; // file wasn't found.
}
Since this involved some spelunking, I wrote a quick test to verify that it might actually work:
#ifdef TEST
#include <io.h>
void show_perms(int perms, char const *caption) {
printf("File opened for %s\n", caption);
printf("Read permission = %d\n", (perms & _IOREAD)!=0);
printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}
int main(int argc, char **argv) {
FILE *file1, *file2;
int perms1, perms2;
file1=fopen(argv[1], "w");
perms1 = get_perms(_fileno(file1));
fclose(file1);
file2=fopen(argv[1], "r");
perms2 = get_perms(_fileno(file2));
fclose(file2);
show_perms(perms1, "writing");
show_perms(perms2, "reading");
return 0;
}
#endif
And the results seem to indicate success:
File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0
You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.
In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).
Requires the redistributable Win32 FileID API Library
So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.
It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).
I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:
/proc/self/fd/[descriptor]
and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.
Maybe you can explain a little more and I can try to update to help.
Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.
This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.