【问题标题】:How to create Unix Domain Socket with a specific permissions in C?如何在 C 中创建具有特定权限的 Unix 域套接字?
【发布时间】:2013-12-08 22:05:18
【问题描述】:

我有一个简单的代码,比如:

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);

我想原子地创建具有特定权限的 Unix 域套接字文件,例如:0777。该手册没有说明关于umask 或其他任何内容的套接字文件权限。即使umask 确实影响了套接字文件,那么它也不是一种原子方式——在多线程程序中。

我希望,有一种方法可以在不使用 umask() 调用同步的情况下实现我的目标。

【问题讨论】:

  • 为什么要关心?事后设置权限从未对我造成问题。这并不是说有进程坐在那里等待非法突袭你的套接字——如果有的话,你的问题比套接字权限要大得多。
  • @cHao:可能有进程正在侦听inotify,并且可能会在我的进程设置正确权限之前尝试连接到套接字——这是一个“好”的进程,但它会失败。我的问题更多是理论上的问题,即尝试为某些特定情况找到解决方法。
  • @cHao:听说过多用户系统吗?像unix?就像这个问题的关键词之一?如果应用处理等待非法突袭其套接字的进程,则应用严重损坏。
  • @cHao:如果没有恶意用户获得 root 访问权限并且内核不包含任何权限提升安全漏洞,您可以确定内核没有受到危害,这正是我的观点。通过信任在同一台机器上以其他用户身份运行的进程,您可以防止恶意进程获得 root 访问权限。
  • @cHao:最后一部分很好——在bind()和listen()之间设置权限就足够了。

标签: c sockets unix unix-socket umask


【解决方案1】:

另一种解决方案是创建一个具有所需权限的目录,然后在其中创建套接字(示例代码不考虑错误检查和缓冲区溢出):

// 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);

【讨论】:

  • 这是最好的解决方案,因为 unix-socket 的父目录的所有权会影响其连接能力。
  • 对我不起作用。父目录是777,socket还是755,所以只能由所有者写。
  • 这可能是因为模式中有错字。我已经用正确的答案编辑了回复。
【解决方案2】:

分叉,使用 umask 并传回 fd 是我能想到的唯一可移植方式。在任何情况下都有一个套接字目录会更好,例如,如果目录没有适当的权限,则没有人可以删除套接字,并且可以原子地创建目录。

更大的问题是依赖权限是不可移植的——许多 BSD 派生的套接字堆栈只是忽略了封闭目录和/或套接字本身的权限。

在 GNU/Linux 系统上,您可以通过在 socket() 之后和 bind() 之前在套接字 fd 上调用 fchmod 来实现

【讨论】:

  • 确切地说,在 bind() 之前的 fchmod() 在多线程环境中没有竞争,但不能移植到某些 BSD 变体。甚至 POSIX 也说套接字上的 fchmod() 具有未指定的行为。您绝对应该*应该关心以原子方式设置的权限。
  • “fork and pass the fd back”是什么意思?
  • @Guido 您可以通过套接字传递文件描述符。见normalesup.org/~george/comp/libancillary
  • @Anonymous 不能直接创建socket,fork,umask,bind,然后返回0告诉父socketsocket准备好了吗?比辅助数据更容易...而且您确定哪些 BSD 忽略了权限?我能找到的最佳信息是 4.2 忽略了套接字权限,但检查了目录权限。
  • @dataless,太棒了。谢谢!
【解决方案3】:

在调用 socket()、bind() 之后,但在调用 listen() 之前,我使用 chmod()(不是 fchmod)使用 unix 域套接字的文件名,运气最好。

  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);
  }

。 . . . .

【讨论】:

  • 如果某个进程通过inotify 监视套接字,它就不起作用了——正如我在对我的问题的评论中提到的那样。
  • @abyss.7 实际上我刚刚测试过(Linux 3.8.0)并且它确实有效。如果尚未调用 listen(),则 connect() 将在套接字上失败。因此,您似乎可以在调用 listen() 之前安全地对套接字进行 chmod,而不必担心未经授权的用户会连接。但是,正如 Elias 所提到的,最好创建一个具有所需权限的目录和目录中的套接字。
  • 另一种方法:使用另一个文件名 chmod() 创建文件,然后 rename() 为最终文件名。我通常创建附加了 -TMP 的文件,然后在 chmod() 和 listen() 之后重命名
猜你喜欢
  • 2014-02-16
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 2016-10-25
  • 1970-01-01
相关资源
最近更新 更多