33

I have a simple code, like:

sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, path);
unlink(path);

int fd = socket(AF_UNIX, SOCK_STREAM, 0);
bind(fd, (sockaddr*)(&address), sizeof(address));
listen(fd, 100);

I want to atomically create the Unix Domain Socket file with a specific permissions, say: 0777. The manual doesn't say anything about socket file permissions with regard to umask or whatever. Even, if the umask does affect the socket file, then it's not an atomic way - in multi-threaded program.

I hope, there is a way to achieve my goal without using synchronization of umask() calls.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
abyss.7
  • 13,882
  • 11
  • 56
  • 100
  • Why care? Setting permissions after the fact has never caused an issue for me. It's not like there are processes sitting there waiting to pounce illegitimately on your socket -- and if there are, you have far bigger problems than socket permissions. – cHao Nov 24 '13 at 06:34
  • You do not need to worry so much about atomically setting permissions. If there's a rogue process trying that hard to access the socket, that they could get to it between the microsecond you create it and the microsecond you set permissions on it, then your machine is compromised, and you should be fixing that rather than trying to work around it. – cHao Nov 24 '13 at 06:43
  • 5
    @cHao: There may be the processes that are listening `inotify` and may try to connect to the socket before my process has set right permissions - it's a "good" process, but it will fail. My question is more the theoretical question, that an attemp to find workaround for some specific situation. – abyss.7 Nov 24 '13 at 06:44
  • Well, the `umask` `bind` method, if it worked, could be made thread-safe, you would only need to `fork` and then `umask` `bind` and then pass the FD for the opened socket back to the parent. – Dan D. Nov 24 '13 at 07:16
  • @DanD.: .. or protect access to the `umask();bind();umask()` triple using a mutex. – alk Nov 24 '13 at 15:56
  • @alk That wouldn't work as that mutex would have to block all the other threads and there is no way to do that without having that mutex be used at every system call that either effects or is effected by the umask. – Dan D. Nov 25 '13 at 01:22
  • 9
    @cHao: Ever heard of multiuser systems? Like unix? Like in one of the keywords of this question? If an application *doesn't* handle a process waiting to pounce illegitimately on it's socket the application is **severely** broken. – Marcus Apr 07 '15 at 22:24
  • @Marcus: If you have such a process, your **machine** is severely broken. You can be as paranoid as you want, but at some point you have to trust the machine you're running on, and the people who run it. It's actually not a huge step from "what if there's a rogue process?" to "what if the kernel is compromised?". That way lies madness and/or solipsism. – cHao Apr 08 '15 at 00:25
  • 6
    @cHao: If no malicious user has gained root access **and** the kernel does not contain any privilege escalation security holes you can be sure the kernel is not compromised, which is exactly my point. By *not* trusting processes running as another user on the same machine you prevent malicious processes from getting root access. – Marcus Apr 08 '15 at 18:28
  • @Marcus: No, in fact, you don't do a whole lot. There are like a half dozen other security sins you have to commit before a socket's permissions are even remotely an issue. If you're that paranoid, for example, then you're already not running as a privileged user anyway. But in any case, sockets can't be connected to before they're `listen`ing, so permissions aren't critical til then. – cHao Apr 09 '15 at 01:45
  • 2
    @cHao: The last part is a good point - setting permissions between bind() and listen() should be sufficient. – Marcus Apr 09 '15 at 10:05
  • I would like to use this comment chain as a teachable moment for any SO contributors that might benefit from it: do not waste four comments convincing many security-conscious readers that you're willfully wrongly dismissive of how a loss of any bit of security matters (because defense-in-depth is weakened by any and all situations where people go "well, we don't add thickness here, the other layers got it covered") just go directly to dropping the crucial wisdom of how the maximum security can be trivially achieved anyway. – mtraceur Oct 24 '21 at 06:13

3 Answers3

25

Another solution is to create a directory with the desired permissions, and then create the socket inside it (example code without any regard for error checking and buffer overflows):

// Create a directory with the proper permissions
mkdir(path, 0700);
// Append the name of the socket
strcat(path, "/socket_name");

// Create the socket normally
sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, path);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
bind(fd, (sockaddr*)(&address), sizeof(address));
listen(fd, 100);
Elias Mårtenson
  • 3,820
  • 23
  • 32
17

I had the best luck using chmod() (NOT fchmod()) using the file name for the unix domain socket after calling socket(), bind(), but, before calling listen().

  int return_value;
  const char *sock_path;
  struct sockaddr_un local;

  sock_path = "/tmp/mysocket";

  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (sockfd == -1)
  {
    perror("socket");
    exit(-1);
  }

  local.sun_family = AF_UNIX; 
  strcpy(local.sun_path, sock_path);
  unlink(local.sun_path);
  len = strlen(local.sun_path) + sizeof(local.sun_family);
  bind(sockfd, (struct sockaddr *)&local, len);

  chmod(sock_path, 0777);

  retval = listen(sockfd, BACKLOG);
  if (retval == -1)
  {
    perror("listen");
    exit(-1);
  }

. . . . .

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Don Carr
  • 321
  • 2
  • 2
  • 1
    It doesn't work, if some process monitors the socket via `inotify` - as I mentioned in a comment to my question. – abyss.7 Jul 03 '14 at 06:42
  • 8
    @abyss.7 Actually I just tested (Linux 3.8.0) and it does work. connect() will fail on a socket if listen() has not been called yet. So, it would seem you can safely chmod a socket before calling listen() without worry that an un-authorized user will connect. However, as Elias mentioned, it is best to create a directory with the desired permissions and the socket within the directory. – dataless Nov 17 '14 at 06:48
  • Another method: create the file with another filename, chmod() and then rename() to the final file name. I usually create the file with -TMP appended, then rename after chmod() and listen() – user2679859 Jun 14 '21 at 22:04
8

Forking, using umask and passing back the fd is the only portable way I can think of. Having a directory for the socket is better in any case, for example nobody can delete the socket if the directory doesn't have the proper permissions, and creating directories can be done atomically.

The bigger problem is that relying on permissions is not portable - many BSD-derived socket stacks simply ignore the permissions of enclosing directories and/or the socket itself.

On GNU/Linux systems, you can do it by calling fchmod() on the socket fd after socket() and before bind().

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Anonymous
  • 116
  • 1
  • 2
  • 4
    Exactly, fchmod() before bind() works without races in a multi-threaded environments, but is not portable to, let's say, some *BSD variants. Even POSIX says that fchmod() on socket has unspecified behaviour. You definitely *should* care about the permissions being set atomically. – nert Jul 22 '14 at 09:30
  • What does "forking and passing the fd back" mean? – Guido Nov 11 '14 at 00:00
  • 1
    @Guido You can pass file descriptors through a socket. See http://www.normalesup.org/~george/comp/libancillary/ – dataless Nov 17 '14 at 06:30
  • @Anonymous Can't you just create the socket, fork, umask, bind, and then return 0 to tell the parent the socket is ready? Easier than ancillary data... And do you know for sure which BSDs ignore permissions? Best info I can find is that 4.2 ignored socket perms, but checked dir perms. – dataless Nov 17 '14 at 06:32
  • I'm also curious about BSD ignoring permissions. Nothing i've read so far supports what you say about them being ignored on the parent directory, and it works correctly on OSX 10.10.1 at least. – Arran Cudbard-Bell Jan 22 '15 at 06:13
  • Note that `fchmod()` works great if applying less permissions. If you want more, you may still be hit by the `umask(0022)` (or whatever other value you might have). In that case, you need both `fchmod()` before the `bind()` and then `chmod()` after to add permissions that `umask()` may have removed. [See here for details](https://stackoverflow.com/questions/11781134/change-linux-socket-file-permissions/74329441#74329441). – Alexis Wilke Nov 05 '22 at 16:56