0

I have adjusted my /etc/pam.d/su file such that I can switch user from a particular user vs to root without password.

When I run su - -c "ls -l /tmp" using the terminal it produces the output and it does not require any password. But when I try to do it using java ProcessBuilder it doesn't work. Following is my code:

import java.io.File;
import java.util.concurrent.TimeUnit;

class a {
    public static void main(String args[]) {
        try {
            String cmd[] = {"su", "-", "-c", "\"ls -l /tmp\""};
            ProcessBuilder pb = new ProcessBuilder(cmd).redirectOutput(new File("a"));
            Process process   = pb.start();
            if(!process.waitFor(5, TimeUnit.SECONDS)){
                process.destroy();
                process.waitFor();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I have checked my output file a (as in above code), it is blank. But when String cmd[] = {"ls", "-l", "/tmp"}; my output file a is not blank, it produces proper output. I want to use su to run other programs but I am just test it using ls.

Is there a way to run the above java code properly?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
SamratV
  • 224
  • 2
  • 12
  • 1
    Take out the literal quotes. It should just be `"ls -l /tmp"`, not `"\"ls -l /tmp\""`. – Charles Duffy Apr 07 '19 at 15:08
  • To be clear, when you run `su -c "ls -l /tmp"`, the quotes used there in bash are *syntactic*, not literal -- they tell bash where (not) to split words into arguments, they aren't part of the command actually passed to `su`. Your quotes in the Java syntax specifying the start and end of each individual string serve the same purpose. – Charles Duffy Apr 07 '19 at 15:10
  • @CharlesDuffy I have tried that, it doesn't work. – SamratV Apr 07 '19 at 15:10
  • It certainly is closer to working than what you're doing now. – Charles Duffy Apr 07 '19 at 15:10
  • ...going beyond that is going to involve going into a bunch of details around how you're using `su`, and how you expect it to get a TTY handle to use to prompt for a password. (`sudo` can be configured easily enough to work in passwordless notty mode; for `su`, that probably involves getting into your system's PAM configuration). – Charles Duffy Apr 07 '19 at 15:14
  • Anyhow, getting into PAM configuration for su would make this an unreasonably broad question (and out-of-scope, as PAM config is system administration rather than software development), so I'm going to ignore that part and provide a simpler demonstration. You can confirm for yourself that `bash -c "ls -l /tmp"` works, right? But using ProcessBuilder with `{"bash", "-c", "\"ls -l /tmp\""}` doesn't, whereas `{"bash", "-c", "ls -l /tmp"}` does. – Charles Duffy Apr 07 '19 at 15:16
  • ...if you want to keep scope narrow, I'd suggest using `strace -f` or `sysdig` to trace the actual syscalls. If you can verify that `su` is correctly `exec`'d, then you know that it isn't a Java problem or a ProcessBuilder problem at all, and can focus in on where things *are* breaking (be that failed authentication or differing usage between interactive and noninteractive modes or whatever else). – Charles Duffy Apr 07 '19 at 15:20
  • @CharlesDuffy `{"bash", "-c", "ls -l /tmp"}` works for sure. I know that but I want use `su` because I want to run certain command with root privilege. – SamratV Apr 07 '19 at 15:21
  • 1
    Wrong tool for the job. Don't *escalate* privileges from inside your executable; instead, either start with high privileges and drop them when and where you don't need them, or provide hooks that the user can replace with an escalation mechanism of their choice. When you build a program that hardcodes use of `su` or `sudo` or whatever else, you're ensuring that it'll never work on a system that hasn't been configured to allow that particular mode of escalation. – Charles Duffy Apr 07 '19 at 15:23
  • ...which is to say, yes, you can make `su` work when called from Java *for your specific system*, but that's creating a bunch of fragility. Generally speaking, conscientious system administrators don't let daemons run `su`, so you'll be restraining where and how your software can be run. – Charles Duffy Apr 07 '19 at 15:26
  • ...if the commands you want to escalate are really narrow, another longstanding approach is to build defensively-written setuid wrappers and exec *those*. One of the useful things about that approach is that it makes it easy for anyone running a security audit to know where to focus, and your code doesn't need to be allowed to run `su` with any command it wants, but is only allowed to run the (defensively-written, single-purpose) shims. – Charles Duffy Apr 07 '19 at 15:26
  • @CharlesDuffy I want my java code to run some scripts as root. How do I achieve that? Can you please point to some resource? – SamratV Apr 07 '19 at 15:29
  • I just told you several different mechanisms to achieve it. See https://stackoverflow.com/questions/4212387/considerations-for-a-setuid-wrapper for an example question by someone using a setuid wrapper to run something as root from an unprivileged program in Python -- sure, it's not Java, but the real effort happens in C either way. – Charles Duffy Apr 07 '19 at 15:31
  • ...the "start privileged and drop" approach, by contrast, has you check that your program has root *when it's started* (and typically just exit with an error telling the user to restart it as root otherwise), fork a limited subprocess that only does critical/privileged things still with root, and then drop privileges back down to the unprivileged user account. That's a common pattern that has the advantage that a program that starts out with root automatically has permissions to drop privileges down to any other user, and all the magic happens at startup time so you can't get errors later. – Charles Duffy Apr 07 '19 at 15:36
  • @CharlesDuffy My program is basically a website written in java. It runs on tomcat server as unprivileged user. – SamratV Apr 07 '19 at 15:39
  • 1
    The setuid-wrapper approach is typically best for that scenario, with the wrapper carefully written and audited to prevent it from being subverted if invoked by an attacker. (Not the *only* available option -- you could also put your privileged operations behind a message bus, to toss out another example -- but typically-best). You certainly *don't* want to let your unprivileged account to run `su` -- it's not very unprivileged at all, in that case. – Charles Duffy Apr 07 '19 at 17:04
  • @CharlesDuffy Setuid-wrapper is absolute magic. Thanks a lot! – SamratV Apr 07 '19 at 17:06
  • 1
    Just be sure the wrapper you're using is written by someone security-conscious. Doing it right means having a separate wrapper per command you might want to run, with the wrapper locking down the specific command and its arguments (and environment-variable values), and guarding against injection attacks. – Charles Duffy Apr 07 '19 at 17:11

0 Answers0