这是一个问题:程序需要在执行操作时记住特定用户的特权.例如,如果其权限为755,则joe不能修改/ home / larry.
目前我的策略是这样的
>获取文件的所有者/组
>将其与尝试执行操作的用户的用户ID /组ID进行比较
>如果匹配(或者如果不匹配),请使用文件中权限字段的相应部分来允许或拒绝操作
这是明智的吗?有更简单的方法吗?
起初,我在考虑在用户的帐户下运行应用程序的多个实例 – 但这不是一个选项,因为那时只有一个实例可以侦听给定的TCP端口.
Unix系统有两组独立的凭证:真实用户/组ID和有效用户/组ID.真实集合标识您实际是谁,有效集合定义您可以访问的内容.您可以根据需要更改有效的uid / gid,如果您是普通用户并且再次返回 – 因为您的真实用户/组ID在转换期间保持root.因此,在单个进程中执行此操作的另一种方法是使用seteuid / gid根据需要来回应用不同用户的权限.如果您的服务器守护程序以root身份运行或具有CAP_SETUID,则允许这样做.
但是,请注意,如果您能够随心所欲地切换有效的uid / gid并且您的应用程序被破坏,那么该subversion可能会将有效的uid / gid切换回0并且您可能会遇到严重的安全漏洞.这就是为什么尽快永久删除所有权限,包括您的真实用户uid / gid是谨慎的原因.
因此,以root身份运行单个侦听套接字是正常且更安全的,然后通过调用setuid来分叉并更改实际和有效用户ID.然后它无法改变回来.您的分叉进程将具有accept()ed的套接字,因为它是一个fork.每个进程只关闭它们不需要的文件描述符;套接字保持活动状态,因为它们在相反的进程中被文件描述符引用.
您也可以尝试通过自己单独检查权限来强制执行权限,但我希望很明显这可能容易出错,有很多边缘情况并且更容易出错(例如,它不适用于POSIX ACL除非你具体实施,否则).
所以,你有三个选择:
> fork和setgid()/ setuid()给你想要的用户.如果需要通信,请在fork之前使用pipe(2)或socketpair(2).
>不要根据需要使用fork和seteuid()/ setegid()(安全性较低:更有可能意外危及服务器).
>不要搞乱系统凭据;手动执行权限(安全性较低:更有可能导致授权错误).
如果你需要与守护进程通信,那么虽然在套接字或管道中执行它可能更难,但第一个选项确实是正确的安全方式.例如,见how ssh does privilege separation.您可能还会考虑是否可以更改您的体系结构,而不是任何通信,该进程可以只共享一些内存或磁盘空间.
您提到您考虑为每个用户运行单独的进程,但需要一个侦听TCP端口.你仍然可以这样做.只需让主守护进程侦听TCP端口并将请求分派给每个用户守护进程并根据需要进行通信(例如,通过Unix域套接字).这实际上与拥有一个分叉主守护进程几乎相同;我认为后者会变得更容易实现.
进一步阅读:凭证(7)联机帮助页.还要注意Linux有文件系统uid / gids;这与有效的uid / gids几乎相同,除了发送信号等其他内容.如果您的用户没有shell访问权限且无法运行任意代码,那么您无需担心差异.