4

I have a web application that runs as a user www. However at one point it needs to read a file from a Linux filesystem on behalf of users Alice and Bob.

One way of doing this would be to launch a shell (Runtime.exec()) and call a C setuid executable to change userid and read the file.

Is there a way to achieve this with JNI (the web app needs to run as www and not root)? I tried to write a Java JNI program that calls native methods on Linux (I made these native methods owned by root and have setuid bits set). But unless I run the Java program as root, it does not let me switch user ids. Is there something that I am missing? Is there a way to achieve this?

Thanks!

Paula
  • 41
  • 1
  • 2

2 Answers2

4

No. There is not. setuid and setgid can only be used in two cases (on ordinary Linux):

  1. The process is root.
  2. The process was launched from a file with the setuid or setgid bits in its modes.

In the former case, the process may call these functions willy-nilly to adopt any identity. In the later case, the process my only switch back and forth between the uid/gid of the parent and the uid/gid of the file it was launched from.

That is, if setuid, it may adopt the uid of the file, if setgid, the gid.

Since you are in java, the exec-ed program is java itself, and you don't want that to be setuid or setgid, not unless you want to create a colossal security exposure.

You could write a C or C++ program that started the JVM via the JNI invocation interface, and set that program to be setuid or setgid, and then JNI code invoked in there could make the appropriate calls to switch.

bmargulies
  • 97,814
  • 39
  • 186
  • 310
1

Yep, it's possible. You can see an example here:

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's answer is not correct (or not quite correct). A non-root process in UNIX can set uid or gid, if it has respective capabilities:

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

CAP_SETUID

  • Make arbitrary manipulations of process UIDs (setuid(2), setreuid(2), setresuid(2), setfsuid(2));

Since from OS prospective, your program is running as "java" process, this setguid capability must be enabled for the java executable itself:

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

Also notice that filesystem where java executable resides, isn't mounted with "nosuid" option.

Back to your original question

the web app needs to run as www and not root

Normally it's not a good idea to let web service to have a setuid capability. It's better to drop uid in a shell wrapper script that starts up your web application.

Tagar
  • 13,911
  • 6
  • 95
  • 110