18

When running Files.walk(Paths.get("/var/")).count() as an unprivileged user, the execution might throw an exception as there are folders inside /var/ that need root permission to be traversed.

I am not looking for a way to execute a bash command as root (e.g. sudo find /var), using Process, etc.

I just want to make sure Files.walk(Paths.get("/var/")).count() does not throw an AccessDeniedException:

Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0
    at sun.reflect.NativeMethodAccessorImpl.invoke
    at sun.reflect.DelegatingMethodAccessorImpl.invoke
    at java.lang.reflect.Method.invoke
    at org.springframework.boot.devtools.restart.RestartLauncher.run
Caused by: java.io.UncheckedIOException: java.nio.file.AccessDeniedException: /var/cache/httpd
    at java.nio.file.FileTreeIterator.fetchNextIfNeeded
    at java.nio.file.FileTreeIterator.hasNext
    at java.util.Iterator.forEachRemaining
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining
    at java.util.stream.AbstractPipeline.copyInto
    at java.util.stream.AbstractPipeline.wrapAndCopyInto
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential
    at java.util.stream.AbstractPipeline.evaluate
    at java.util.stream.LongPipeline.reduce
    at java.util.stream.LongPipeline.sum
    at java.util.stream.ReferencePipeline.count
    at com.example.DemoApplication.main
    ... 5 more
Caused by: java.nio.file.AccessDeniedException: /var/cache/httpd
    at sun.nio.fs.UnixException.translateToIOException
    at sun.nio.fs.UnixException.rethrowAsIOException
    at sun.nio.fs.UnixException.rethrowAsIOException
    at sun.nio.fs.UnixFileSystemProvider.newDirectoryStream
    at java.nio.file.Files.newDirectoryStream
    at java.nio.file.FileTreeWalker.visit
    at java.nio.file.FileTreeWalker.next
    at java.nio.file.FileTreeIterator.fetchNextIfNeeded

This is just an example. Using filter(...) it is possible to work around the exception. But this example can be expanded to other use cases too.

So in short Is this possible at all, for CLI, JavaFX, etc. apps to gain root permission after they have been executed from command line via a method such as java -jar app.jar?

Behrang
  • 46,888
  • 25
  • 118
  • 160
  • 4
    If it were possible then it would be a major security issue... Imagine the app gains root access and does `rm -rf /`. OOOPS. – Tunaki Apr 24 '16 at 19:43
  • 7
    The app can pop up a dialog box and ask the user to enter the sudo/root password. Many native apps have this capability already. E.g. Gnome Software, Disk Utility, etc. OOOPS. ;-) – Behrang Apr 24 '16 at 19:45
  • I _think_ the general approach is to get the admin/root login info, and then spawn off a separate process as that user and have that separate process do the work. You'd probably have to do this differently on each platform. I don't know if there's a cross-platform framework that handles that for you (and as you probably know, asking for one would be OT on SO). For instance, see [here](http://stackoverflow.com/q/4662574/1076640) for the Windows answer. – yshavit Apr 24 '16 at 19:47
  • I see. I just noticed that Gnome's (Fedora's) Disk Usage Analyzer does not traverse these directories if it is not executed using `sudo`. It can ask for the user to enter the password before scanning a folder but that feature doesn't exist yet. Anyway, thanks for the explanation. [This](http://askubuntu.com/q/164819/128383) (gksu, etc.) might help as well. – Behrang Apr 24 '16 at 19:59
  • 5
    The right question to ask is "how do I ignore the locations where I have no access". –  May 05 '16 at 20:54
  • 2
    Be honest and asks for a sudo execution of your app. All linux users will prefer this approach – Joao Polo May 05 '16 at 21:00
  • @Arkadiy no, I started bounty exactly to shed light on idea of gaining permissions at runtime (is it possible or not) – AdamSkywalker May 05 '16 at 21:02
  • 1
    Related: http://stackoverflow.com/questions/2483755/how-to-programmatically-gain-root-privileges – Kedar Mhaswade May 05 '16 at 22:24
  • @Arkadly I don't want to gain access to those folders _dishonestly_ :)) I want to gain access to those folders in a platform independent way. And no, I need to have access to those locations not ignore them because I want to create the tree structure, size of all files, etc. (like Disk Usage Analyzer) and show it to the user. Having said that DUA is low on features and sophistication so unlike various other apps, it doesn't allow you to gain root access at runtime. – Behrang May 08 '16 at 06:37
  • What comes to my mind is Android phone (Linux kernel + Java) rooted (which is simply having su/sudo available in your sbin directory) and an app which requires root access. I am not an Android dev but from what I know it executes privileged commands by executing `su` from within java code using `Process` so it looks like that's the only way but you mentioned that's not what you are looking for. – MichaelDD May 11 '16 at 10:05

3 Answers3

9

If what you want is actually skipping the paths where you have no access, you have two approaches:

Streams

In the answer to this question it is explained how to obtain the stream of all files of a subtree you can access.

But this example can be expanded to other use cases too.

FileVisitor

Using a FileVisitor adds a lot of code, but grants you much more flexibility when walking directory trees. To solve the same problem you can replace Files.walk() with:

Files.walkFileTree(Path start, FileVisitor<? super Path> visitor);

extending SimpleFileVisitor (to count the files) and overriding some methods.

You can:

  1. Override the visitFileFailed method, to handle the case you cannot access a file for some reasons; (Lukasz_Plawny's advice)
  2. (optional) Override the preVisitDirectory method, checking for permissions before accessing the directory: if you can't access it, you can simply skip its subtree (keep in mind that you may be able to access a directory, but not all its files);

e.g. 1

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
    // you can log the exception 'exc'
    return FileVisitResult.SKIP_SUBTREE;
}

e.g. 2

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {

    if(!Files.isReadable(dir))
        return FileVisitResult.SKIP_SUBTREE;

    return FileVisitResult.CONTINUE;

}

FileVisitor docs

FileVisitor tutorial

Hope it helps.

Community
  • 1
  • 1
Linuslabo
  • 1,568
  • 2
  • 24
  • 34
  • 2
    My suggestion is to implement `visitFileFailed` method from a `FileVisitor`, which is invoked when a file couldn't be visited. When you add `return FileVisitResult.SKIP_SUBTREE;` to this method, then AccessDeniedException won't be thrown. – Lukasz_Plawny May 06 '16 at 21:51
3

Just a few completely untested ideas:

1) Run your app with root priviledges to begin with:

sudo java -jar myapp.jar

2) Let your app start a launcher-class that requests root permissions and then continues running the rest of your app:

java -jar myapp.jar

This in turn does execute a shell command, but only an xterm that prompts for root password, and then continues to run a java program with root permissions:

xterm -e "sudo sh -c 'java -jar /tmp/myrootapp.jar'"

or perhaps use something nicer-looking using gksudo. Mind the ' and ".

Maybe the myapp.jar extracts itself into a temporary directory. myapp.jar contains myrootapp.jar and thus it can launch it as described above. /tmp should of course be retrieved from within java, and preferably be a directory with a random name that only the user running myapp.jar has access to in order to prevent myrootapp.jar injection.

Cross-platform

You mentioned /var/ yourself, so I assumed you were on some sort of Linux. If this is supposed to work cross-platform, e.g. on Macintosh or Microsoft Windows too, you need to do some sort of system identification first. Then you can apply StrategyPattern in code to handle the various ways of letting myrootapp.jar obtain root or administrator permissions.

Community
  • 1
  • 1
Stephan Henningsen
  • 3,665
  • 1
  • 22
  • 29
  • 2
    However depending on your application, keep in mind that running your app with root privileges can introduce more harmful vulnerabilities. For example when you're using the Rhino engine, there exist injection-vulnerabilities, which allows an attacker to inject arbitrary Java-Code via the engine. In this case of course this injected code is run with root privileges. – Max May 11 '16 at 08:04
2

There is no easy way to change permissions. Java is not good at these tasks. There are only some tricks like check permissions on start and try to change permissions via su/sudo then restart application or using Java-gnome. Please read a bit more here: Java: Ask root privileges on Ubuntu

Community
  • 1
  • 1
Vladislav Kysliy
  • 3,488
  • 3
  • 32
  • 44