JNI + setuid 问题

Posted

技术标签:

【中文标题】JNI + setuid 问题【英文标题】:JNI + setuid Question 【发布时间】:2011-05-12 23:15:55 【问题描述】:

我有一个以 www 用户身份运行的 Web 应用程序。然而,在某一时刻,它需要代表用户 Alice 和 Bob 从 Linux 文件系统读取文件。

执行此操作的一种方法是启动 shell (Runtime.exec()) 并调用 C setuid 可执行文件来更改用户 ID 并读取文件。

有没有办法通过 JNI 实现这一点(网络应用程序需要以 www 而不是 root 身份运行)?我尝试编写一个在 Linux 上调用本机方法的 Java JNI 程序(我将这些本机方法设为 root 拥有并设置了 setuid 位)。但除非我以 root 身份运行 Java 程序,否则它不会让我切换用户 ID。有什么我想念的吗?有没有办法做到这一点?

谢谢!

【问题讨论】:

【参考方案1】:

没有。那没有。 setuidsetgid只能在两种情况下使用(普通Linux上):

    进程是root。 进程是从其模式中具有setuidsetgid 位的文件启动的。

在前一种情况下,进程可以随意调用这些函数来采用任何身份。在后一种情况下,进程只能在父进程的 uid/gid 和启动它的文件的 uid/gid 之间来回切换。

即如果是setuid,则可以采用文件的uid,如果是setgid,则采用gid。

由于您在 java 中,执行程序是 java 本身,并且您不希望 that 是 setuid 或 setgid,除非您想造成巨大的安全风险。

您可以编写一个通过 JNI 调用接口启动 JVM 的 C 或 C++ 程序,并将该程序设置为 setuid 或 setgid,然后在其中调用的 JNI 代码可以进行适当的切换调用。

【讨论】:

【参考方案2】:

是的,这是可能的。您可以在此处查看示例:

https://github.com/kebernet/pretty/blob/master/src/main/java/com/reachcall/util/SetUID.java

下面是代码示例:

CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);

   public static int setuid(int uid) 
        if (Platform.isWindows()) 
            return OK;
        

        return CLibrary.INSTANCE.setuid(uid);
    

bmargulies 的回答不正确(或不完全正确)。 UNIX 中的非 root 进程可以设置 uid 或 gid,如果它具有相应的功能:

http://man7.org/linux/man-pages/man7/capabilities.7.html

CAP_SETUID

对进程 UID 进行任意操作 (setuid(2), setreuid(2), setresuid(2), setfsuid(2));

由于从操作系统的角度来看,您的程序作为“java”进程运行,因此必须为 java 可执行文件本身启用此 setguid 功能:

$ sudo setcap cap_setuid,cap_setgid+ep /usr/java/latest/bin/java

还要注意,java 可执行文件所在的文件系统没有使用“nosuid”选项挂载。

回到你原来的问题

网络应用需要以 www 而不是 root 身份运行

通常,让 Web 服务具有 setuid 功能并不是一个好主意。 最好将 uid 放在启动 Web 应用程序的 shell 包装脚本中。

【讨论】:

以上是关于JNI + setuid 问题的主要内容,如果未能解决你的问题,请参考以下文章

Linux 文件特殊权限 - SetUID

为啥 setuid 不作为所有者运行?

linux的setuid权限解释

Linux 权限 - setuid、setgid、sticky bit - 用一个命令删除所有

权限管理:文件特殊权限—SetUID

权限管理:文件特殊权限—SetUID