无法通过调用JNI在Java中设置seteuid

时间:2018-08-09 01:16:06

标签: java java-native-interface setuid

我的应用程序需要以非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. root chmod 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");
    

但是如果以非根用户身份运行,它将无法正常工作

/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

但是如果runner是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部分修改为可执行文件

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

1 个答案:

答案 0 :(得分:0)

seteuid对JNI不起作用的原因是,JVM进程的有效用户标识不是0(root)。

您显然正在尝试通过将本机库中的“ setuid”位设置为有效的用户ID。那行不通。同样,将JAR文件(或类文件)设置为setuid也不起作用。 setuid位仅在可执行文件上有意义。即 OS本身知道如何执行的文件。

那么我们如何为Java程序实现“ setuid根行为”?

  • 从理论上讲,您可以将/usr/bin/java标记为root的setuid。 请勿这样做!。如果这样做,您运行的每个Java程序都将以root用户身份运行。那就不好了。

  • 从理论上讲,您可以编写一个Shell脚本来启动您的应用程序。例如

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

    并将脚本标记为root的setuid。 请勿这样做! Setuid shell脚本存在安全风险。 (在某些版本的Linux / UNIX上,shell脚本始终不尊重setuid位。)

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

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

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

  • 应注意,用户不能通过干扰JAR文件的路径来诱使它运行错误的Java代码。

  • 还有其他我没想到的事情!

实际上,以特权用户身份运行Java应用程序可能更安全……完全不依赖“ setuid root”。