Wes Miller
Wes Miller

Reputation: 2241

change linux socket file permissions

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

Answers (2)

Alexis Wilke
Alexis Wilke

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

Cong Ma
Cong Ma

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.

  • 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.

Upvotes: 6

Related Questions