Reputation: 2241
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?
Upvotes: 1
Views: 6519
Reputation: 20730
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.
Upvotes: 1
Reputation: 11302
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.
fchmod()
on a Unix domain socket is defined as a no-op that sets EINVAL
(Ref1)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.
Upvotes: 6