views:

1227

answers:

6

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 ?).
A: 

if you only want the app to be allowed to read files by user1 i strongly suggest the app runs as user1.

It is not possible, it is a multi threaded application in which thousands of users can be logged in simultaneously.
Mikael Mechoulam
A: 

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

Described for example here

TheMarko
+2  A: 

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.

CesarB
+1: The Unix setuid API call is the solution. The question is how to access the API call in a meaningful way. Spawn subprocess is about it.
S.Lott
I suggest you skip the RMI conversation with the sub-subprocess. Treat it as an atomic OS-like command -- give it everything it needs to do it's job quickly and cleanly.
S.Lott
It's not a simplest way, and not most portable. Simplest way is to use a custom SecurityManager. Most portable is to use a custom SecurityManager.
Vladimir Dyuzhev
@Vladimir Dyuzhev: a custom SecurityManager would either have to reimplement all the Unix permission checking, which is precisely what the original poster wants to avoid (and is tricky; what about ACLs, for instance?), or somehow change the current user id, which is per-process.
CesarB
correct, if exact obeying (non-standard) ACL/PCAPs required, then external process is the most portable way. but still is not simplest.
Vladimir Dyuzhev
A: 

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.

Alnitak
Even if the OS creates a real separate thread for each Java thread, setuid can still be process-global. AFAIK, that is the case on Linux.
CesarB
Yes, you're right, I've now tested it - setuid does appear to be process global on Linux. @Mikael - what O/S is your server running?
Alnitak
It should be compatible on all Unix platforms (solaris, aix, linux...).
Mikael Mechoulam
It always was process-wide. On any Unix.
Vladimir Dyuzhev
+1  A: 

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!

Vladimir Dyuzhev
Thanks, may I ask you to precise some points ?2) For my "own SecurityManager" : but to check unix user permissions for a given user, I still need JNI, right ?3) What do you mean exactly ?
Mikael Mechoulam
For the solution (without child SUID process) which use the library you mention, it does not answer if a given user can read a file, does it ? (--> we don't know the groups it belongs to).
Mikael Mechoulam
It does, IMHO. Read /etc/group file on startup, you'll know the groups. You'll have to read /etc/passwd to know unix ids anyway.
Vladimir Dyuzhev
@Vladimir Dyuzhev: No, wrong. There is no guarantee the groups list is on /etc/group, and the users lists is on /etc/passwd; the sysadmin might be using /etc/nsswitch.conf (so the users list can be on LDAP, for instance). You MUST use getpwent()/getgrent() to get that information.
CesarB
OK, so getpwent() be it. :) The point is still valid: to ensure a consistent security policy for the application as a whole, you have to use the SecurityManager.
Vladimir Dyuzhev
A: 

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.

CesarB
...Not possible, since there is not <one> user but thousands !
Mikael Mechoulam