JNI + setuid 问题

4
我有一个网络应用程序,以用户www身份运行。但是在某一点上,它需要代表用户Alice和Bob从Linux文件系统中读取文件。
一种方法是启动一个shell(Runtime.exec()),并调用一个C setuid可执行文件来更改用户ID并读取文件。
是否有一种使用JNI的方法(Web应用程序需要作为www而不是root运行)?我尝试编写一个Java JNI程序,在Linux上调用本地方法(我将这些本地方法归属于root并设置了setuid位)。但是,除非我以root身份运行Java程序,否则它不允许我切换用户ID。我错过了什么吗?是否有一种方法可以实现这个?
谢谢!
2个回答

4
不可以。在普通的Linux系统中,setuidsetgid只能用于两种情况:
1. 进程是root。 2. 进程从一个带有setuidsetgid位的文件启动。
在前一种情况下,进程可以随意调用这些函数来采用任何标识。在后一种情况下,进程只能在父进程的uid/gid和文件启动时的uid/gid之间切换。
也就是说,如果设置了setuid,它可以采用文件的uid;如果设置了setgid,则可以采用文件的gid。
由于你使用的是Java,被执行的程序是Java本身,你不想让它成为那样setuidsetgid>,除非你想创建一个巨大的安全漏洞。
你可以编写一个C或C++程序,通过JNI调用接口启动JVM,并将该程序设置为setuidsetgid>,然后在其中调用的JNI代码可以进行适当的调用以进行切换。

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

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

Here's example of the code:

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”选项挂载。
回到您最初的问题。
通常不建议让Web服务具有setuid功能。更好的做法是在启动Web应用程序的shell包装脚本中降低UID。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接