【问题标题】:Protect /dev/shm file保护 /dev/shm 文件
【发布时间】:2022-10-13 12:03:12
【问题描述】:

我正在开发一个通过shm_open() 使用共享内存的应用程序。它从/dev/shm 中的文件执行mmap(),并基于生产者/消费者方法。

是否有任何机制可以保护我的共享内存并只能由该应用程序访问?我知道可以使用加密,但是 linux(或编程语言)是否提供任何服务,以便文件只能由我的应用程序访问?

【问题讨论】:

  • 你确定你需要共享记忆,而不是私人的地图?您的要求似乎相互矛盾。
  • 实际上,内存必须在两个进程(我的应用程序的两个实例)之间共享。
  • 您应该能够保护它,以便只有当前用户可以读取或写入内存。但是您可能无法阻止当前用户访问它的其他进程。

标签: c linux security ipc shared-memory


【解决方案1】:

如果使用fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0);,则共享内存对象不能被任何其他进程打开(无需先更改访问模式)。如果成功(fd != -1),并且您立即通过int rc = shm_unlink(name);成功取消链接对象(rc == 0),则只有可以访问当前进程本身的进程才能访问该对象。

当另一个具有足够权限的进程可能已更改模式并打开对象时,这两个操作之间有一个小的时间窗口。要检查,请使用fcntl(fd, F_SETLEASE, F_WRLCK) 获取对象的写租约。只有当这是唯一可以访问该对象的进程时,它才会成功。

让应用程序的第一个实例绑定到先前同意的 Unix 域流套接字,命名或抽象,并在其上侦听传入连接。 (出于安全原因,重要的是使用fcntl(sockfd, F_SETFD, FD_CLOEXEC) 以避免将套接字泄漏给子进程,以防它执行()新的二进制文件。)

如果socket已经被绑定,则绑定失败;所以连接到那个套接字。当第一个实例接受新连接,或者第二个实例连接到 i 时,两者都必须使用int rc = getsockopt(connfd, SOL_SOCKET, SO_PEERCRED, &creds, &credslen);struct ucred creds; socklen_t credslen = sizeof creds;,以获取对方的凭据。

然后您可以检查对方的uid 是否与getuid()geteuid() 匹配,并使用例如验证stat() 路径"/proc/PID/exe"(其中PID 是另一边的pid)指的是与"/proc/self/exe" 在同一文件系统上的同一inode。如果他们这样做,双方都在执行相同的二进制文件。 (请注意,您还可以使用 POSIX 实时信号,通过 sigqueue(),传递一个数据令牌(int、void 指针或 uintptr_t/intptr_t,在 Linux 上恰好匹配 unsigned long/long ) 在它们之间。) 这很有用,例如,如果一个人想通知另一个人他们即将退出,而另一个人应该绑定并监听 Unix 域流套接字上的传入连接。)

然后,初始进程可以将共享对象描述的副本(通过描述符fd)传递给第二个进程,使用SCM_RIGHTSancillary message,例如共享对象的实际大小作为数据(推荐size_t 为此)。如果要传递其他内容,请使用结构。

第二个进程接收到的第一个(通常但不一定是唯一的)消息将包含辅助数据,其中包含引用共享对象的新文件描述符。请注意,因为这是一个 Unix 域流套接字,所以不保留消息边界,如果没有完整的数据有效负载,则需要使用循环来读取其余数据。

然后双方都可以关闭 Unix 域套接字。然后第二方可以mmap() 共享对象。

如果只有这对共享数据的进程,那么双方都可以关闭描述符,使得除了超级用户或内核之外的任何人都无法访问共享描述符。只要映射存在,内核就会保留一个内部引用;它相当于进程的描述符仍然打开,只是进程本身不能再访问或共享描述符,只能访问或共享共享内存本身。

因为共享对象已经被取消链接,所以不需要清理。一旦具有打开描述符或现有 mmap 的最后一个进程将其关闭、取消映射或退出,共享对象就会消失。


Linux 实现的 Unix 安全模型在以相同 uid 运行的进程之间没有严格的界限。特别是,他们可以检查彼此的/proc/PID/ 伪目录,包括/proc/PID/fd/ 下列出的打开文件描述符。

因此,对安全敏感的应用程序通常以专用用户的身份运行。即使当第二方是作为人类用户运行的进程,而第一方作为专用应用程序 uid 时,上述方案也能很好地工作。如果你使用命名的Unix域流套接字,你需要确保它的访问模式是合适的(你可以使用chmod()chgrp()等绑定到套接字后,改变命名的Unix域流套接字访问模式)。摘要 Unix 域流套接字没有文件系统可见节点,任何进程都可以连接到这样一个绑定的套接字。

当应用程序(作为自己的专用 uid 运行)和代理(作为用户 uid 运行)之间涉及权限边界时,确保双方都是他们在整个交换中声称的身份是很重要的。凭证仅在该时间点有效,已知的攻击方法是让有效代理在连接到套接字后立即执行恶意二进制文件,以便对方仍然看到原始凭证,但接下来的通信是控制一个邪恶的过程。 为避免这种情况,请确保套接字描述符未在 exec 之间共享(使用 CLOEXEC 描述符标志),并可选择多次检查对等凭据,例如最初和最后。


为什么这“复杂”?因为必须加入适当的安全性,所以不能事后将其添加到顶部,或者在无形中为您处理:它必须是方法的一部分。方法的改变必须反映在安全实现中,否则你就没有安全性。

在现实生活中,在你实现这个之后(对于相同的可执行二进制文件,以及特权服务或应用程序和用户代理文件),你会发现它并不像听起来那么复杂:每个step 有其目的,如果方法发生变化,可以对其进行调整。特别是,它根本没有多少 C 代码。 如果一个人想要或需要“更简单的东西”,那么他只需要选择安全敏感代码以外的东西。

【讨论】:

    猜你喜欢
    • 2013-06-03
    • 1970-01-01
    • 2021-04-10
    • 2012-04-02
    • 2020-03-07
    • 1970-01-01
    • 2021-06-02
    • 1970-01-01
    • 2014-12-16
    相关资源
    最近更新 更多