change linux socket file permissions - c++

First, yes this is related to this stack overflow question, but I'm having a slightly different set of circumstances and my post there is not getting an answer.
So, on my Dell desktop workstation, Ubuntu 10.04 32 bit, I have developed a server program that is designed to offer a Unix-Domain socket to a PHP "program" run by Apache. (note: umask = 0022) I named the socket file /home/wmiller/ACT/web_socket_file. (ACT is a reference to the product name). /home/wmiller/ACT has permissions of 777. /home/wmiller/ACT/web_socket_file gets created with permissions of 777.
Now, I copy the program to my test platform, a Q7 format Intel processor board, which also has Ubuntu 10.04 32 bit and umask = 0022. Same directories, same 777 permission on the dir. However, now when i run the code /home/wmiller/ACT/web_socket_file comes up with 755 permissions and Apache/PHP can't open the Unix Domain socket because it gets r-x permissions instead of rw- or rwx. Apache is running in uid = www-data.
sockaddr_un webServAddr;
remove( g_webSocketFileName.c_str() ); // to erase any lingering file from last time
memset(&webServAddr, 0, sizeof(webServAddr));
webServAddr.sun_family = AF_UNIX;
snprintf( webServAddr.sun_path, UNIX_PATH_MAX, "%s", g_webSocketFileName.c_str() );
if (( g_webServerSock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 )
{
PLOG( ERROR ) << "Failed to acquire the web Server socket: "; // uses google glog tool
return -1;
}
So I tried both of these and neither worked.
chmod( g_webSocketFileName.c_str(), S_IRWXU | S_IRWXG | S_IRWXO );
and
char temp[100];
sprintf( temp , "chmod o+w %s\n", g_webSocketFileName.c_str() );
system( temp );
Tried permissions of 777 and o+w.
I even tried adding a
unlink( g_webSocketFileName.c_str() );
But no help there.
Anyone have suggestions on why ir works on one machine and not on another almost identical machine?
Would I be better off to put the socket file elsewhere? Is there a standard place-where-socket-files-go?

On Linux, you need to call fchmod() on the Unix domain socket file descriptor before bind(). In this way the bind() call will create the filesystem object with the specified permissions. Calling fchmod() on an already bound socket is not effective.
Using chmod() could lead to TOCTTOU race condition. If possible, use fchmod() instead.
This is a Linux-specific hack. On most BSD systems, fchmod() will fail on a socket fd and set EINVAL.
Edit. I found this system-dependent behavior difference by tinkering. Perhaps the best "source" for this should be the kernel source code itself.
On FreeBSD, it appears that fchmod() on a Unix domain socket is defined as a no-op that sets EINVAL (Ref1)
On Linux, it appears that a Unix domain socket fd is created just like an inode, along with file modes (but with S_IFSOCK bitwise-or'ed in). (Ref2) Linux's fchmod() implementation will then happily apply changes to such an object. When binding a Unix domain socket to an address, the file modes are used in creating the filesystem-object. (Ref3) According to man 2 stat, S_IFSOCK is present in POSIX.1-2001.
If I read the sources wrong, please feel free to correct me.

As Cong Ma said, under Linux you should look at using fchmod() before the bind(). However, the umask() is still going to be applied. So the correct sequence goes like this:
// create the socket
int s = socket();
// restrict permissions
#ifdef __linux__
fchmod(s, S_IRUSR | S_IWUSR);
#endif
// bind the socket now
bind(s, &u, sizeof(u));
// finally, fix the permissions to your liking
chmod(u.sun_path, 0666); // <- change 0666 to what your permissions
Important Note: the code here does not show the error handling which is required to make sure things work as expected. See complete example here.
What is the problem with fchmod()?
If you try to set the exact mode that you need in fchmod(), the file gets created by bind() and at that point the umask gets applied. That means with a umask such as 022, you still do not get the write permissions for the group and other users (i.e. you would get 0644 instead of 0666).
One way to use fchmod() and skip on the chmod() after the bind() is to change umask with:
umask(0);
bind(...);
However, if like many of us you are running in a multithreaded application, changing the umask is probably not an option. The solution above works without having to use umask(0) which long term is a better way of doing things.

Related

Protecting against Time-of-check to time-of-use?

I was reading: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
They showed this code to be buggy and I totally understand why it's so:
if (access("file", W_OK) != 0) {
exit(1);
}
// Attacker: symlink("/etc/passwd", "file");
fd = open("file", O_WRONLY);
// Actually writing over /etc/passwd
write(fd, buffer, sizeof(buffer));
But the real question is how to protect against this type of exploits?
You can use the O_NOFOLLOW flag. It will cause the open to fail if basename of the path is a symbolic link. That would solve the described attack.
To cover links along the directory path, you can check whether frealpath(fd, ...) matches what you would expect.
Another way to prevent a process from overwriting /etc/passwd is to run it as non-root so that it won't have permission. Or, you can use chroot - or more generally, a container - to prevent the host system's /etc/passwd being visible to the process.
More generally though, filesystem TOCTOU is unsolvable at the moment on Linux. You would need transaction support either on filesystem or system call level - which are lacking.
There is no failproof solution.
Be also aware of Rice's theorem. It might be relevant.
But you could adopt a system wide convention (and document it) that every program accessing a given file is using locking facilities like flock(2).

Close shared files programmatically

The company I'm working with has a program written in ye olde vb6, which is updated pretty frequently, and most clients run the executable from a mapped network drive. This actually has surprisingly few issues, the biggest of which is automatic updates. Currently the updater program (written in c++) renames the existing exe, then downloads and places the new version into the old version's place. This generally works fine, but in some environments it simply fails.
The solution is running this command from microsoft:
for /f "skip=4 tokens=1" %a in ('net files') do net files %a /close
This command closes all network files that are shared (well... most) and then the updater can replace the exe.
In C++ I can use the System(""); function to run that command, or I could redirect the output of net files, and iterate through the results looking for the particular file in question and run net file /close command to close them. But it would be much much nicer if there were winapi functions that have similar capabilities for better reliability and future safety.
Is there any way for me to programmatically find all network shared files and close relevant ones?
You can programmatically do what net file /close does. Just include lmshare.h and link to Netapi32.dll. You have two functions to use: NetFileEnum to enumerate all open network files (on a given computer) and NetFileClose to close them.
Quick (it assumes program is running on same server and there are not too many open connections, see last paragraph) and dirty (no error checking) example:
FILE_INFO_2* pFiles = NULL;
DWORD nRead = 0, nTotal = 0;
NetFileEnum(
NULL, // servername, NULL means localhost
"c:\\directory\\path", // basepath, directory where VB6 program is
NULL, // username, searches for all users
2, // level, we just need resource ID
(LPBYTE*)&pFiles, // bufptr, need to use a double pointer to get the buffer
MAX_PREFERRED_LENGTH, // prefmaxlen, collect as much as possible
&nRead, // entriesread, number of entries stored in pFiles
&nTotal, // totalentries, ignore this
NULL //resume_handle, ignore this
);
for (int i=0; i < nRead; ++i)
NetFileClose(NULL, pFiles[i].fi2_id);
NetApiBufferFree(pFiles);
Refer to MSDN for details about NetFileEnum and NetFileClose. Note that NetFileEnum may return ERROR_MORE_DATA if more data is available.

Calling GetFileAttributesW() removes a pipe

I create a pipe (//./pipe/mycoolpipe) with a very long timeout, from process A,
pipe = ::CreateNamedPipe(
name_.c_str(),
direction_,
PIPE_TYPE_BYTE | PIPE_WAIT,
1,
...,
...,
PIPE_TIMEOUT,
);
MS sysinternals pipelist.exe is enumerating my pipe:
pipelist.exe | grep mycoolpipe
//./pipe/mycoolpipe
then from process B I'd like to read the file attributes:
::GetFileAttributesW(p.c_str()) // p == //./pipe/mycoolpipe
and right after calling GetFileAttributesW, the pipe is gone - WHAT? WHY?
Note: debbuging proved executing GetFileAttributesW makes the pipe gone - pipelist.exe is not enumerating it any more right after calling GetFileAttributesW . Timeout is not involved.
GetFileAttributesW() has undefined behavior for non-filesystem objects, so just don't do it.
http://permalink.gmane.org/gmane.os.cygwin.patches/1973
https://cygwin.com/ml/cygwin-patches/2004-q2/msg00193.html
But, while //server/pipe/name may be a UNC path, it is not a path to a file.
And certain Win32 functions (including GetFileAttributes) do not work on
those paths. When I say "do not work", I mean the Win32 SDK actually says
not to call them on those paths, and when I do it on my XP Pro SP1 (with all
updates), odd behavior ensues. This is undefined behavior. Sometimes I can
see weird stuff at the filesystem level using SysInternals' FileMon.
GetFileAttributes behavior is not defined for pipe paths. So it cannot be called on those paths. I believe it returns -1 on my XP system - after it opens and closes the pipe, changing the semantics for the server! But there's no guarantee what it would return on other systems.
What we're up against here is a failing of the Win32 API:
1) GetFileAttributes cannot be called on a pipe path.
2) There is no function that can tell you that a path is a pipe path unless you actually open it and use GetFileType - which changes server semantics.
3) Therefore, you have to detect pipe paths and avoid calling GetFileAttributes.

open file folder in the directory

I want my C++ application to implement "open File Folder" functionality like in that firefox and download manager. This is the code that I've come up with.
int File::openTempFile(std::string temp_file_dir)
{
std::string file_path = temp_file_dir + "/" ;
file_path = file_path + this->additional_info ;
// if there is already an temporary file then delete it//
if( temporary_file != "" )
{
// :TODO: open temporary file stack //
// so when the application dinit we could remove those //
// remove(temporary_file.c_str() );
}
/* write temporary file */
FILE* fp = fopen (file_path.c_str(), "w");
if( fp== NULL)
return FALSE;
fwrite( m_data, 1, m_size, fp);
fclose(fp);
// now open it using natulus //
char * parmList[] = {strdup("nautilus"),strdup(file_path.c_str() )} ;
int pid;
if(( pid= fork() ) == -1)
perror("fork failed");
if( pid ==0 ){
int a = execvp("nautilus" , parmList);
printf("exevp failed to load the temporary file");
}
temporary_file = file_path ;
return TRUE;
}
But Instead of a one nautilus window , it open 3 windows.Any idea what is the reason for that ?And how could I get nautilus "open with" dialog just instead of showing it on the directory ?
When opening a folder, I use the xdg-open command which opens the specified folder with the preferred file manager for a given desktop environment.
On my system when I'm running KDE, it calls dolphin to display the folder, and xfce4, it invokes thunar, as these are the preferred file managers as per my settings. I often don't want nautilus to be used, as the interface differs from my preferred apps, doesn't look consistent with the other applications - in terms of widgets, style, etc. - and won't group the same in my taskbar.
It also uses separate preferences for my default open-with settings, and conflicts with my workflow.
Additionally, nautilus isn't necessarily guaranteed to be on my system.
For example, on some of my older systems, where I've emerged a custom gentoo system where I'm either constrained by RAM or HDD space, I've only emerged say twm and xfce4, so nautilus doesn't exist.
As far as the nautilus-specific behaviour is concerned, I have had similar problems with nautilus creates a desktop on initial invokation (as is the case when I am running twm).
Invoking nautilus --help shows the following options:
Application Options:
-c, --check Perform a quick set of self-check tests.
--version Show the version of the program.
-g, --geometry=GEOMETRY Create the initial window with the given geometry.
-w, --new-window Always open a new window for browsing specified URIs
-n, --no-default-window Only create windows for explicitly specified URIs.
--no-desktop Never manage the desktop (ignore the GSettings preference).
--force-desktop Always manage the desktop (ignore the GSettings preference).
-q, --quit Quit Nautilus.
-s, --select Select specified URI in parent folder.
--display=DISPLAY X display to use
Unfortunately, I can't help you specifically invoke the nautilus "open with..." dialog, although xdg-open will use the default application when a file is specified. Perhaps polling configurations within the mimeapps.list file (which can be in one of several cascading override locations: including, but not limited to, user-desktop, user, sysadmin-desktop, sysadmin, default-desktop, and default).

How to check if a file has been opened by another application in C++?

I know, that there's the is_open() function in C++, but I want one program to check if a file hasn't been opened by another application. Is there any way to do it using standard library?
EDIT - Clarified in the answers that this is for a Linux application.
Not only the standard library does not have this funcionality, it's not even possible in general. You could (on linux) check /proc/*/fd — but it is possible that your program does not have permission to do it on processes from other users (this is the default in Ubuntu, for instance).
No, the standard library has no such functionality.
If you control the other process (have source code), the best plan is to use advisory locks in both processes. This locking is defined in POSIX, and will be portable across operating systems.
In Linux, you can use the utility lsof to see what files are opened by other processes.
This is limited to what you have permissions for - you have to do the check as a privileged user, or you'll only get results for files opened by the same user as the one doing the check.
I only know of the command line utility, not of any system call you can use directly from C code.
In Linux, it's also possible to turn on mandatory locking for a given filesystem (mount -o mand), and set special flags on the file (chmod g-x,g+s). Then when your process attempts to acquire a write lock, it will fail if another process has the file open. This is hardly ever used, but if you completely control the system in question, it may be an option.
The following code may work.
int main(int argc, char ** argv)
{
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
if (fcntl(fd, F_SETLEASE, F_WRLCK) && EAGAIN == errno) {
puts("file has been opened");
}
else {
fcntl(fd, F_SETLEASE, F_UNLCK);
puts("file has not been opened");
}
close(fd);
return 0;
}
Perhaps you could just try and get a full write lock? It'll fail if anyone else has it open for reading or writing.
fopen("myfile.txt", "r+")
If it's not cross platform and is Win32, then you can request even more fine-grained set of locks.
See here
and look at dwShareMode, value of 0, as well as the other parameters.
Nope. Unless other application uses advisory locks.
See http://docs.sun.com/app/docs/doc/816-0213/6m6ne37v5?a=view
Non-natively, you could call out to Sysinternals' handle.exe as a last resort...
As #Neil Butterworth says, the standard library doesnt.
In unix you can use fcntl to use file locks.
You could write a wrapper for your open function, that checks for a lock (and locks if none exists) a file if its open by no one else. You shold write a wrapper for close as well, that releases that lock on file close.
Seeing you have tagged linux -> there's a command-line too and API that have been added to the Linux kernel and do just that: inotify.
Here's the man page.
In Windows this little and dirty trick will work (if the file exists and you have the right permissions)
if ( 0 != rename("c:/foo.txt", "c:/foo.txt") ) {
printf("already opened\n");
}
It's likely to work also in Linux.