2

I have a very basic Java program that is using Jsch library to automate execution of some shell scripts located in my Unix box.

In order to automate execution of these shell scripts following steps need to be followed:

  • Login into Unix box with user john
  • Switch to another user simba
  • Provide credentials of this new user using OutputStream bypassing the input prompt at cli and flush it using os.flush()
  • Check that the user switch happened using whoami command

I wrote following program to achieve this functionality but as you can see in the console output the user switch did NOT happen and the second whoami command gives me the same username (ie: john)

Another question that comes to my mind is what should I do for commands that do not have any output from cli such as su -, logout, bash etc. It doesn't make sense to wait for the output in that infinite while(true) loop when nothing is going to be returned.

Please guide.

Current Console Output

Connecting SSH to my-unix-box.net - Please wait for few seconds... 
Connected!

Executing command: whoami
john
Executing command: su - simba
Setting suPasswd now....
Executing command: whoami
john  //WRONG: SHOULD BE "simba" 

Disconnected channel and session

Process finished with exit code 0

SSHConn.java

public class SSHConn {

    static Session session;
    static String[] commands = {"whoami", "su - simba", "whoami"};

    public static void main(String[] args) throws Exception {
        open();
        runCmd(commands);
        close();
    }

    public static void runCmd(String[] commands) throws JSchException, IOException {
        for (String cmd : commands) {
            System.out.println("Executing command: " + cmd);
            Channel channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(cmd);
            InputStream in = channel.getInputStream();
            OutputStream out = channel.getOutputStream();
            channel.connect();
            //passing creds only when you switch user
            if (cmd.startsWith("su -")) {
                System.out.println("Setting suPasswd now....");
                out.write((Constants.suPasswd + "\n").getBytes());
                out.flush();
            }
            System.out.println("Flushed suPasswd to cli...");
            //capture output that we receive from cli (note: some commands such as "su -" does not return anything)
            if (!cmd.startsWith("su -")) {
                captureCmdOutput(in, channel);
            }
            channel.setInputStream(null);
            channel.disconnect();
        }
    }

    public static void captureCmdOutput(InputStream in, Channel channel) throws IOException {
        System.out.println("Capturing cmdOutput now...");
        byte[] tmp = new byte[1024];
        while (true) {
            System.out.println("in the while loop...");
            while (in.available() > 0) {
                System.out.println("into the available loop...");
                int i = in.read(tmp, 0, 1024);
                if (i < 0) {
                    break;
                }
                System.out.print(new String(tmp, 0, i));
            }
            if (channel.isClosed()) {
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (Exception ee) {
                System.out.println(ee.getMessage());
            }
        }
        System.out.println("Command output captured...");
    }

    public static void open() throws JSchException {
        JSch jSch = new JSch();
        session = jSch.getSession(Constants.userId, Constants.host, 22);
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.setPassword(Constants.userPasswd);
        System.out.println("Connecting SSH to " + Constants.host + " - Please wait for few seconds... ");
        session.connect();
        System.out.println("Connected!\n");
    }

    public static void close() {
        session.disconnect();
        System.out.println("\nDisconnected channel and session");
    }

}

pom.xml

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.51</version>
</dependency>

su Command Usage:

 su [options] [-] [USER [arg]...]

Change the effective user id and group id to that of USER.
A mere - implies -l.   If USER not given, assume root.

Options:
 -m, -p, --preserve-environment  do not reset environment variables
 -g, --group <group>             specify the primary group
 -G, --supp-group <group>        specify a supplemental group

 -, -l, --login                  make the shell a login shell
 -c, --command <command>         pass a single command to the shell with -c
 --session-command <command>     pass a single command to the shell with -c
                                 and do not create a new session
 -f, --fast                      pass -f to the shell (for csh or tcsh)
 -s, --shell <shell>             run shell if /etc/shells allows it

 -h, --help     display this help and exit
 -V, --version  output version information and exit

For more details see su(1).
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Nital
  • 5,784
  • 26
  • 103
  • 195
  • @Martin - Tried the examples suggested in the duplicate posts link as well but no luck. I do not get any error but looks like the user is not getting switched. I do not get any error so it's difficult for me to figure out what's going on behind the scenes. Moreover the `sudo` command does not work in my case. May be it is not configured on my unix box. – Nital Jun 05 '19 at 20:06
  • So use `su`, if that works. But the answer is still the same. – Martin Prikryl Jun 05 '19 at 20:08
  • See also [JSch - How to issue commands as the user I have switched to](https://stackoverflow.com/q/41097999/850848) – Martin Prikryl Jun 05 '19 at 20:21
  • I looked into the examples and modified my source code but still no luck. I am wondering if is it my program or is it the corporate unix box that is blocking me from creating a programmatic connection. – Nital Jun 05 '19 at 20:44
  • If you want our help, you will have to describe the behavior you are getting way better than by *"no luck"*. – Martin Prikryl Jun 06 '19 at 04:33
  • Understood and completely agree with you @Martin. Without proper description of the problem it's very difficult to fix it. I will collect and recompile all the messages/errors and update this post soon. Appreciate your feedback and fast responses so far. – Nital Jun 06 '19 at 13:28
  • Updated my original question with an improved program and question. Hope this explains my problem in a better way. – Nital Jun 06 '19 at 16:13
  • Your problem is identical to the linked items. You're getting an `exec` channel for every command you run, so the `whoami` runs in one exec, which exits. The `su - simba` runs in another exec, and the second `whoami` runs in a third exec. each `exec` is a creation of a new channel, which will be owned by the logged in user. Each command is independent of the others, no state is kept behind (such as the username change). If you want to do something in the context of another user you can use `su - simba -c whoami` for example. An alternative is to use a single `shell` channel. – Anya Shenanigans Jun 06 '19 at 16:47
  • @Petesh - Thanks for the pointer. I am searching for any examples on how to use "shell" and found this link https://stackoverflow.com/questions/41097999/jsch-how-to-issue-commands-as-the-user-i-have-switched-to but this does not show how to execute commands. Any pointers on that ? – Nital Jun 06 '19 at 17:45
  • I finally got it working. Will post my answer soon so that everybody can see the solution. – Nital Jun 06 '19 at 17:58
  • 1
    I was missing two things: 1) Wasn't sure how to feed prompts whenever the terminal asks for it. Got that working with `out.write((Constants.suPasswd + "\n").getBytes());` and 2) Wasn't sure how to pass multiple shell commands using -c option. Got that working using this change `su - simba -c \"whoami ; pwd ;\"`. Make sure that you need to put your commands in double quotes, which I was not doing. Got clues from this SO link (https://stackoverflow.com/questions/41097999/jsch-how-to-issue-commands-as-the-user-i-have-switched-to) where @Martin pointed out in one of the comments. – Nital Jun 06 '19 at 18:05
  • Posted my complete working program in the duplicate link mentioned above for everybody's reference. – Nital Jun 06 '19 at 18:15
  • Yes, I already did that in the other SO page where you answered in one of the comments on how to pass multiple commands. – Nital Jun 07 '19 at 13:29

0 Answers0