无法通过调用 JNI 在 java 中设置 euid

Fail to seteuid in java by calling JNI

我的应用程序需要 运行 作为 none-root 用户,但需要切换到其他用户才能执行一些命令。

我试过:

  1. 写一个JNI,

    JNIEXPORT void JNICALL Java_SetUIDJNI_setuid(JNIEnv *env, jobject thisObj,jstring uname) {
        const char *name = jstringTostring(env,uname);
        int pid;
        struct passwd *p;
        show_ids();
        if ((p = getpwnam(name)) == NULL) {
            perror(name);
            exit(EXIT_FAILURE);
        }
        pid = (int) p->pw_uid;
        printf("pid=%d\n",pid);
        if (seteuid (pid) < 0) {
            perror ("setuid");
            exit (EXIT_FAILURE);
        }
        show_ids();
    }
    
  2. 将其构建为 rootchmod u+s

    -rwsr-xr-x 1 root root  76155 Aug  7 16:56 libsetuid.so*
    
  3. 在java

    中调用
    api = new SetUIDJNI();  // invoke the native method
    api.setuid("slurm");
    

但是如果运行它作为none-root,它不起作用

/opt/jdk1.8/jre/bin/java -Djava.library.path=../jni HelloJNI
 The real user ID is: 1000
 The effective user ID is :1000   <= which expected is 0
 setuid: Operation not permitted

但是如果 运行ner 是 root

The real user ID is: 0
The effective user ID is :0
pid=1002The real user ID is: 0
The effective user ID is :1002

这里有什么问题吗?

更新

修改JNI部分为可执行文件c

void show_ids (void)
 {
    printf ("The real user ID is: %d\n", getuid());
    printf ("The effective user ID is :%d\n", geteuid());
  }

 int main(void)
 {

    show_ids();
    if (seteuid (1002) < 0) {
      perror ("setuid");
      exit (EXIT_FAILURE);
    }
    show_ids();
    return (0);
  }

将其构建为 root 和 运行 chmod u+s

-rwsr-xr-x  1 root root 8814 Aug  9 11:44 a.out*

运行 它作为普通用户并且工作

./a.out
The real user ID is: 1000
The effective user ID is :0
The real user ID is: 1000
The effective user ID is :1002

seteuid 无法从 JNI 为您工作的原因是 JVM 进程的有效用户 ID 不是 0(root)。

您显然试图通过在您的本机库上设置 "setuid" 位来创建有效的用户 ID。那行不通的。同样,将 JAR 文件(或 class 文件)设置为 setuid 将不起作用。 setuid 位仅对可执行文件有意义;即 OS 本身 知道如何执行的文件。

那么我们如何为 Java 程序实现 "setuid root behavior"?

  • 理论上,您可以将 /usr/bin/java 标记为 root 的 setuid。 不要那样做! 如果你那样做,你 运行 的每个 Java 程序都将 运行 为 root。那可就糟了。

  • 理论上,您可以编写 shell 脚本来启动您的应用程序;例如

       #!/bin/sh
       java some.pkg.Main "$@"
    

    并将脚本标记为 root 的 setuid。 不要那样做! Setuid shell 脚本存在安全风险。 (在某些版本的 Linux / UNIX 中,setuid 位无论如何都不被 shell 脚本使用。)

解决方案是用本机代码编写自定义 JVM 启动器,编译并 link 它,并使启动器可执行 setuid。请注意,必须 非常仔细地 编写启动程序,以防止有人破坏它。例如:

  • 它应该忽略 CLASSPATH 环境变量,并且不应允许以任何其他方式提供 class路径。

  • 不应将用户提供的JAR文件作为参数。

  • 应该注意用户不能通过干扰 JAR 文件的路径来欺骗它进入 运行 错误的 Java 代码。

  • 还有其他的……我没想到的!

事实上,运行 Java 应用程序作为特权用户可能更安全......并且根本不依赖 "setuid root"。