10

I am working on app in javafx , I am trying open an application using a command in terminal, I am running the command using my java code my command have some variable it's have path of my installer file which will not always be same because file name can be different as the builds are updated.
here is a sample as how I am running the command it's not the exact command which I am running but the command format is same.

Process process = Runtime.getRuntime().exec("echo password | sudo -S open -a safari");
    String line;
    BufferedReader input = new BufferedReader(new InputStreamReader(pb.getInputStream()));
    while ((line = input.readLine()) != null) {
        System.out.println(line);
    }
    input.close();

The process is not giving any output it stops there and nothing happens. The same command I have tried from terminal and it works fine.

I have tried the things mention in this link

How to execute bash command with sudo privileges in Java?

but it also didn't worked.

I am also running command like "chmod +x" from my java code these commands runs fine. my original command looks like this:-

runCommand = "echo" + " " + password + "| sudo -S " + "\"" + a.getAbsolutePath() + "\"" + " --deploymentFile="
                            + "\"" + b.getAbsolutePath() + "\"";

where a.getAbsolutePath() is the path of the installer file and b.getAbsolutePath() is the path of the deployment file we used to install the application.

pb.getInputStream()

prints the command and when I copy and paste it is terminal it's runs fine.

pb.getErrorStream()

doesn't give anything.

I have tried running

String[] cmd = {"/bin/bash","-c","echo tester| sudo -S ","\"",a.getAbsolutePath(),"\"","\""," --deploymentFile=","\"",b.getAbsolutePath()};

and

String[] cmd = {"/bin/bash","-c","echo tester| sudo -S",a.getAbsolutePath(),"--deploymentFile=","\"",b.getAbsolutePath()};

also here I got following error

getErrorStreamusage: sudo -h | -K | -k | -L | -V
getErrorStreamusage: sudo -v [-AknS] [-g groupname|#gid] [-p prompt] [-u user name|#uid]
getErrorStreamusage: sudo -l[l] [-AknS] [-g groupname|#gid] [-p prompt] [-U user name] [-u
getErrorStream            user name|#uid] [-g groupname|#gid] [command]
getErrorStreamusage: sudo [-AbEHknPS] [-C fd] [-g groupname|#gid] [-p prompt] [-u user
getErrorStream            name|#uid] [-g groupname|#gid] [VAR=value] [-i|-s] [<command>]
getErrorStreamusage: sudo -e [-AknS] [-C fd] [-g groupname|#gid] [-p prompt] [-u user
getErrorStream            name|#uid] file ...
Community
  • 1
  • 1
user3649361
  • 944
  • 4
  • 20
  • 40
  • why do you run `echo tester`? Also, `-S` shouldn't be necessary: a simple `sudo open -a safari` should suffice. – fedorqui May 23 '16 at 10:14
  • actually tester is password for the system, I need to run a command with the password. Isn't password needed with sudo. – user3649361 May 23 '16 at 10:46
  • 1
    If `tester` is the password, `echo`ing it might not be the most secure of ideas... – Arc676 May 23 '16 at 10:50
  • I have tried sudo open -a safari but the same result, actually I am trying to install some thing using command line so will need the password. – user3649361 May 23 '16 at 11:07

5 Answers5

10

sudo

I'd strongly suggest to edit the sudoers file and allow the user running the application to use the specific commands via sudo without prompting for a password instead of doing an echo passwd | sudo ... construction.

That way you avoid storing passwords in the clear (or at best slightly obfuscated) in an application or configuration file, and you avoid the need to call a shell with a shell script that calls sudo, etc.

Sudoers can be edited via the command visudo. See here as an example how it is done on unbuntu, but it's the same on any unix. https://askubuntu.com/questions/159007/how-do-i-run-specific-sudo-commands-without-a-password

Additional ref: https://www.sudo.ws/man/1.8.16/sudoers.man.html

I think you're asking the wrong question though...

Authorization on a mac

On a mac applications that need to perform operations that require additional rights are not supposed to use sudo to start with.

An app is supposed to use the authorization services instead.

References:

Community
  • 1
  • 1
  • 1
    And nothing about java. – Andremoniy May 27 '16 at 20:11
  • can I run it through java and one more thing I need to run this application in different systems so will I be able to do that ? – user3649361 Jun 01 '16 at 14:18
  • I think you should look into using the authorization service on a mac instead of focussing on sudo for what you seem to be doing. (edit above to give you a few pointers to that) –  Jun 01 '16 at 15:04
3

I think you must use the full path to the application. This should work:

Runtime rt = Runtime.getRuntime();
rt.exec("echo password | sudo -S open -a /Applications/Safari.app");

Update:

Based on your comment you could try to split the process. The chances are good that open needs an interactive session.

Create a script (e.g. openSafari.sh) that will open Safari as user.

#!/etc/bash
echo $1 | sudo -S open -a /Applications/Safari.app &

Make it executable: chmod +x openSafari.sh, and call that script from java.

Runtime rt = Runtime.getRuntime();
rt.exec("/pathTo/openSafari.sh 'sudoPassword'");
cb0
  • 8,415
  • 9
  • 52
  • 80
  • This also doesn't seems to work, commands like "ditto -xkV " + filepathwos + " " + outputPath; are working fine but the commands which need to launch some app doesn't seems to work. – user3649361 May 23 '16 at 11:50
  • my command have some variable safari one is only a example it's have path of my installer file which will not always be same because file name can be different as the builds are updated so it this case can I use script ?? – user3649361 May 23 '16 at 12:27
3

I would NOT recommend doing this - do not shout at me if you break something :)

But what you are asking for can be achieve with:

String[] cmd = {"/bin/bash","-c","echo password | sudo -S open -a <path to app>"};
Process pb = Runtime.getRuntime().exec(cmd);
Michael-O
  • 18,123
  • 6
  • 55
  • 121
Maciej Cygan
  • 5,351
  • 5
  • 38
  • 72
3

The Runtime.exec() methods run the given command directly, not via a shell. The particular overload you are using tokenizes the command at whitespace, and interprets the resulting tokens as the name of the command and the arguments. The only command executed by your invocation ...

Runtime.getRuntime().exec("echo password | sudo -S open -a safari");

... is therefore echo; everything else is an argument. The result is the following output to the process's standard output:

password | sudo -S open -a safari


There are at least a couple of ways to accomplish what you appear to want. The simplest modification of your original code would probably be

Process process = Runtime.getRuntime().exec(
        new String[] { "bash", "-c", "echo password | sudo -S open -a safari" });

That achieves what you thought you were getting, by explicitly invoking a shell to run the command.

But that's a substantial security risk, because the password will appear in plain text in the process list. You can instead have your Java program feed in the password directly, and then also avoid getting bash involved:

Process process = Runtime.getRuntime().exec("/usr/bin/sudo -S open -a safari");
Writer toSudo = new OutputStreamWriter(process.getOutputStream());
String password = "password";

toSudo.write(password);
toSudo.write('\n');  // sudo's docs demand a newline after the password
toSudo.close();      // but closing the stream might be sufficient

Other considerations:

  • It is wise to give a full path to the sudo command, as demonstrated, to avoid running a different sudo that happens to be found first in the path. Since you're going to give it the password to a privileged account, it is important to minimize the possibility of running a rogue program.

  • It would also be wise to avoid storing passwords in the program or in a configuration file; thus, solutions that involve feeding a password to sudo should also involve inputting the password interactively from a user.

  • Security-conscious Java programs often prefer to keep passwords in char arrays instead of in Strings, so that they can be proactively wiped when no longer needed. Strings cannot be changed, and they may hang around inside the VM indefinitely (even more so than many other objects).

  • Generally speaking, you need to drain a Process's input and error streams, concurrently, whether you do anything with the contents or not. If you do not do so then the external process may block and / or may fail to exit. That's probably not an issue with safari in particular, however, because I don't think it ordinarily produces any output on those streams.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

maybe use this

How to setup a SUDO_ASKPASS environment variable?

You can set the SUDO_PROMPT enviroment variable within the process call

Combine that with the fix to how you're using process jon bollinger, maciej, etc have mentioned.

Then have that bring up a password prompt for your user, or access a pki with your credentials in it. (for the love of god at least aes encrypt it if you go for the pki)

as you've said this is meant to run on others machines, it'll basically be a shitty uac prompt and that doesn't sound like a very mac/linux solution. swa66 is the best way, but this'll do quick and dirty.

Community
  • 1
  • 1
squishy
  • 61
  • 5