简体   繁体   中英

In a Java thread running as root, how can we apply Unix rights specific to a logged-in user?

We have a Java program run as root on Unix, that therefore can read for example the content of the folders /home/user1 and /home/user2 . However, if the Unix user "user1" is logged in in our application, he should not be able to access "/home/user2" data. We would like to use directly the Unix rights and not recreate all the permissions in our application ! So, could we...

  1. try to change the UID of our program depending on the user logged in ? Sounds difficult, and each file access is in different threads so the UID would be different on each thread of our program...
  2. use JNI to read permissions of " /home/user2 "...And then determine if user1 has sufficient permissions on " /home/user2 " ? (how ?).

The simplest and most portable way would be to spawn a child process, have it exec a wrapper written in C which changes the UID, drops all the privileges (be careful, writting a wrapper to do that is tricky - it is as hard as writing a setuid wrapper), and execs another java instance to which you talk via RMI. That java instance would do all the filesystem manipulation on behalf of the user.

For single-threaded Linux programs, you could instead use setfsuid() / setfsgid() , but that is not an option for portable or multithreaded programs.

Use SecurityManager!

  1. Put current unix user id into ThreadLocal
  2. Create your own SecurityManager that checks unix user permissions on checkRead() and checkWrite()
  3. System.setSecurityManager(new MySecurityManager())
  4. Enjoy

Update

There is no, of course, standard library to read unix file permissions. It's not WORA.

But I have tried briefly to find a ready to use library, and found this one: http://jan.newmarch.name/java/posix/ It uses JNI, but you don't need to write your own JNI code, which is a big relief. :) I'm sure there must also be others.

Class Stat from there gives you all required access information: http://jan.newmarch.name/java/posix/posix.Stat.html

Update 2

As folks mentioned, this approach fails to check for "non-standard" unix security features, such as ACL or Posix Capabilities (may be; not sure if they apply to files). But if the goal of being totally in sync with host OS security is set, then we even more need to use SecurityManager, because it's a JVM-wide protection mechanism! Yes, we can start a child SUID-process to verify the permissions (and keep it running, talking to it via pipe running while the user is logged in), but we need to do so from SecurityManager !

如果您只希望允许应用程序通过user1读取文件,我强烈建议应用程序以user1运行。

If everything else fails, you can run a shellscript from java and parse the result.

Described for example here

For those who were wondering, it's apparently not possible to do this by calling setuid with JNI for each independent thread. setuid affects the whole process, not just the thread that invoked it.

Should you want to call setuid within a single-threaded Java program there's a good example at http://www2.sys-con.com/itsg/virtualcd/Java/archives/0510/Silverman/index.html .

Another option would be to invert the approach: instead of the code running as root most of the time and either changing the user ID or somehow checking the permissions whenever it has to use some restricted resource, run as the user most of the time and talk to a smaller daemon running as root when it needs to do something only root can do. This also has the added benefit of reducing the attack surface.

Of course, you then have to authenticate the connection from the process running as the user to the process running as root.

I am also having the exact problem as Mikael, and got to this page looking for answers.

None of the answers are 100% satisfactionary for me. So I am thinking of 4 alternatives:

  1. Use a Linux group that has access to all the users. Run a single Java app under that group. This Java app can communicate to the 'root' app using whatever means.

    Potentially, it can be "hotel"-ed. eg 1 "hotel" (app with group permissions) per 100 users (or as appropriate). So if you have 10,000 users you need 100 hotels, which is quite manageable.

  2. Spawn a JVM for each child app under its own user ID. This is like calling a script, but rather than using stdin/stdio/stderr, use any communication protocol. In my case, I'm using XMPP and IO Data (which, since it's already in use by other components, it doesn't matter "where" aka which JVM it runs).

  3. Create a Super-Server 'root' app. This can be part of the original 'root' app or a separate service dedicated to service management.

    The Super-Server is responsible for handling incoming requests (ie it practically becomes a reverse proxy ) for the user-specific sub-apps, and launching the real child apps (if they're not running already), and passing messages back and forth between the client and the child app(s).

    Additionally, the child apps can be pooled (or even "passivated", if there's such thing), much in the way Java EE EJB Container does it. So even if there are 10,000 users and (potentially) 10,000 child apps servicing, the maximum number of child apps running are capped. Idle apps are shut down to make room for others.

  4. Same as #3 but rather than creating a proprietary service management mechanism, integrate with Upstart (or the service management framework in the underlying OS). ie there is a 'root' service that can control Upstart. Upstart can start, stop, restart, can query the status of the child services, just like it can control mysqld, Apache, etc.

For me, now, the quickest and simplest to implement would be #1. However, my ideal solution would be #4, but it will take time and testing whether it works well. (the concept itself borrows from inetd/xinetd and EJB, so I think it's pretty sound fundamentally)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM