I'm using Ganymede ssh2 to connect to server from my Java app and do some work there.
It works perfectly but problems are ssh commands that request approval, eg.
a command
stop someService
returns
Are you sure (y/n)?
and after appropriate key stroke (y/n) it moves on.
Currently I'm using implementation given by ssh2 ganymed example, smth like this:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
public class ConnectUtil(String hostname, String username, String password, String command)
{
try
{
Connection conn = new Connection(hostname);
conn.connect();
boolean isAuthenticated = conn.authenticateWithPassword(username, password);
if (isAuthenticated == false)
throw new IOException("Authentication failed.");
Session sess = conn.openSession();
sess.execCommand(command);
InputStream stdout = new StreamGobbler(sess.getStdout());
BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
while (true)
{
String line = br.readLine();
if (line == null)
break;
System.out.println(line);
}
sess.close();
conn.close();
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
After calling this util class with above mentioned command('stop someService'), it gets stucked at
String line = br.readLine();
and everything breaks after server timeout.
Any ideas on how to solve this issue are more than welcome.
Thanks a lot,
Milos.
Assuming Session has a getStdin() method, you should write the response to the question to that.
Either that or find out if the command that's demanding input has a 'non-interactive' mode that won't prompt.
If you're going to use stdin instead of exec then you have to do session.startShell() first. Read the FAQs.
I use this code...
sess.executeCommand("ena"+'\n' +"pass"+'\n' +"exit"+'\n');
Related
I'm trying to make a java class in order to authenticate users against local SASL. My saslauthd configuration is like this:
$ cat /etc/sysconfig/saslauthd
# Directory in which to place saslauthd's listening socket, pid file, and so
# on. This directory must already exist.
SOCKETDIR=/run/saslauthd
# Mechanism to use when checking passwords. Run "saslauthd -v" to get a list
# of which mechanism your installation was compiled with the ablity to use.
MECH=pam
# Additional flags to pass to saslauthd on the command line. See saslauthd(8)
# for the list of accepted flags.
FLAGS="-t 1"
Basically it redirects an authentication against PAM. So, if I'm doing for example a test like this.
testsaslauthd -s login -u <user> -p <password>
0: OK "Success."
It is all working correctly.
I now want to manage this mechanism through Java so I compiled something like this:
import java.util.Arrays;
import java.util.List;
import java.io.*;
public class PamAuthenticator {
public static void main(String args[]) {
String s = null;
try {
Process p = Runtime.getRuntime().exec("testsaslauthd -s "+args[2]+" -u "+args[0]+" -p "+args[1]);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
System.exit(0);
}
catch (IOException e) {
System.out.println("Exception: ");
e.printStackTrace();
System.exit(-1);
}
}
}
This is correctly working:
$ java -cp .:* PamAuthenticator <user> <password> login
0: OK "Success."
My problem is that I don't want to execute the testsaslauthd command, since this is just a test command. Is there something better and smart I can do in order to try the authentication agains SASL with java?
You are on the right track, not to use the code above. Besides being a test solution it would introduce a serious security problem: command injection.
From Java 1.6 there is an interface called SaslClient. This does exactly what you need. An example on the JDK8 version of it:
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.util.HashMap;
public class Test {
public static void main(String[] args) throws SaslException {
String userName = "username";
String password = "password";
SaslClient saslClient = Sasl.createSaslClient(new String[]{"PLAIN"},
null, null, null, new HashMap<>(), callbacks -> {
for (final Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback.class.cast(callback).setName(userName);
continue;
}
if (callback instanceof PasswordCallback) {
PasswordCallback.class.cast(callback).setPassword(password.toCharArray());
continue;
}
throw new UnsupportedCallbackException(callback);
}
});
}
}
Of course you should alter the source of the username and password.
I am creating a Java HTTP server that checks to make sure a client is not banned before redirecting to the main server. I have already created everything for the server that is needed, I just don't know how to redirect to another port that is running the main server. Here is my code:
package netlyaccesscontrol;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class AllowedCheck {
public static void main(String[] args) {
String line = null;
try {
FileReader reader = new FileReader("Banned.txt");
BufferedReader buffer = new BufferedReader(reader);
ServerSocket s = new ServerSocket(80);
Socket c = s.accept();
String clientIP = c.getInetAddress().toString();
while ((line = buffer.readLine()) != null) {
if (clientIP == line) {
s.close();
} else {
// redirect to main server here
}
}
} catch (FileNotFoundException ex) {
System.out.println("The banned IP address file does not exist.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
The redirection that you are thinking of is something supported by HTTP and the browsers. There's a specific HTTP response code that tells the caller to redirect and a way to specify it.
Raw sockets are a low-level network protocol that is not going to support redirection as you expect. The most you might be able to do is have this program be a proxy and, upon success, push all incoming data/outgoing responses to/from the ultimate server. But what you have here is by no means going to cut it.
I'm trying to use the printWorkingDirectory() from Apache Commons FTP but it's only returning null. I can't navigate directories, list files, etc.
Log in pass all is success but how ever I try I can not change current directory.
I use this following code:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
public class FTPDownloadFileDemo {
public static void main(String[] args) {
String server = "FTP server Address";
int port = portNo;
String user = "User Name";
String pass = "Pasword";
FTPClient ftpClient = new FTPClient();
String dir = "stocks/";
try {
ftpClient.connect(server, port);
ftpClient.login(user, pass);
System.out.println( ftpClient.printWorkingDirectory());//Always null
//change current directory
ftpClient.changeWorkingDirectory(dir);
boolean success = ftpClient.changeWorkingDirectory(dir);
// showServerReply(ftpClient);
if (success)// never success
System.out.println("Successfully changed working directory.");
System.out.println(ftpClient.printWorkingDirectory());// Always null
} catch (IOException ex) {
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
This is rather old question that deserves an answer. This issue is likely a result of using FTPClient when secure connection is required. You may have to switch to FTPSClient if that is, indeed, the case. Further, output the response from the server with the following code snippet to troubleshoot the issue if secure client doesn't solve the it:
ftpClient.addProtocolCommandListener(
new PrintCommandListener(
new PrintWriter(new OutputStreamWriter(System.out, "UTF-8")), true));
Also, a server can reject your login attempt if your IP address is not white listed. So, being able to see the logs is imperative. The reason you see null when printing current working directory is because you are not logged in. Login method will not throw an exception but rather return a boolean value indicating if the operation succeeded. You are checking for success when changing a directory but not doing so when logging in.
boolean success = ftpClient.login(user, pass);
I faced the same, but I came across with a simple step.
Just added this.
boolean success = ftpClient.changeWorkingDirectory(dir);
ftpClient.printWorkingDirectory(); //add this line after changing the working directory
System.out.println(ftpClient.printWorkingDirectory()); //wont be getting null
Here I have the code and the console output
FTPClient.changeWorkingDirectory - Unknown parser type: "/Path" is current directory
I know I replied too soon ;-P, but I saw this post recently. Hope this helps to future searchers ;-)
I am following below steps for running commands on ssh server.
Connecting to ssh server
Using devpush command again logging to server (using expectit library for user input prompt).
Finally running remote commands using jsch library.
The issue I am facing is that, my code is going in infinite loop it is able to login to ssh server but not able to run the commands.
Can any one please help me on this as I am using both the libraries for first time.
package com.dmotorworks.cdk;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import net.sf.expectit.*;
import net.sf.expectit.matcher.Matchers;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class ShellClient {
public void loginToPabloServer() throws IOException, JSchException{
String hostname="pablo.dmotorworks.com";
String username="gaikwasu";
String password="Nexus#80900";
final String endLineStr=" # ";
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, 22);
session.setPassword(password);
jsch.setKnownHosts("C://Users//gaikwasu//.ssh//known_hosts");
session.connect();
System.out.println("Connected");
Channel channel=session.openChannel("shell");
channel.connect();
Expect expect=new ExpectBuilder()
.withOutput(channel.getOutputStream())
.withInputs(channel.getInputStream(), channel.getExtInputStream())
.withEchoOutput(System.out)
.withEchoInput(System.err)
.withExceptionOnFailure()
.build();
expect.expect(Matchers.contains("-bash-4.1$"));
expect.send("devpush\n");
expect.expect(Matchers.contains("[sudo] password for"));
expect.send(password+"\n");
DataInputStream dataIn = new DataInputStream(channel.getInputStream());
DataOutputStream dataOut = new DataOutputStream(channel.getOutputStream());
BufferedReader bufferReader= new BufferedReader(new InputStreamReader(dataIn));
dataOut.writeBytes("cd custom\n");
dataOut.writeBytes("ls -lrt\n");
dataOut.flush();
String line=bufferReader.readLine();
while(!line.endsWith(endLineStr)) {
System.out.println(line);
}
channel.disconnect();
session.disconnect();
expect.close();
}
public static void main(String[] args) {
ShellClient shellClient=new ShellClient();
try {
shellClient.loginToPabloServer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSchException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Most probably the cause of the infinitive loop is because the Expect instance reads the input data in a separate thread at the same time while you are trying to same data in the main thread.
Try to close the Expect instance before reading the data. It may help.
However, I strongly recommend to keep using the Expect instance after the sending password. ExpectIt can help with extracting the information from the remote command output. For example:
expect.sendLine(password);
expect.expect(Matchers.contains("-bash-4.1$"));
expect.sendLine("cd custom");
expect.expect(Matchers.contains("-bash-4.1$"));
expect.sendLine("ls -lrt");
// the lsResult variable should contain the result of the 'ls -l' command
String lsResult = expect.expect(Matchers.contains("-bash-4.1$")).getBefore();
System.out.println(lsResult);
Please take a look at this example.
I am using Jsch library to execute about 1000 different of shell scripts in few linux machines and update the status into a table.
I used jsch exec channel [ChannelExec], it was working only for the single script, if the shell script calls another script, ChannelExec was not giving correct result.
now I am using shell channnel of jsch. it is working well to get output from any kind of shell script.
the problem is, if I execute many shell scripts at once , I am getting all result in one bulk.
there is no way to get one Shell script executed and its result received.
if i want to get individual scripts execution result, I need to login to the machine for each script execution , this is taking very long time.
can somebody post a solution, suggestion on how to go about , login into machine once and execute multiple scripts and receive each script result individually.
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
public class JschShellExample {
public static void main(String[] args) {
try {
JSch jsch = new JSch();
Session session = jsch.getSession("user", "10.32.248.158", 22);
session.setPassword("password");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
config.put("PreferredAuthentications","publickey,keyboard-interactive,password");
session.setConfig(config);
session.connect(100);
Channel channel = session.openChannel("shell");
OutputStream inputstream_for_the_channel = channel.getOutputStream();
PrintStream commander = new PrintStream(inputstream_for_the_channel, true);
channel.setOutputStream(null);
channel.connect(100);
//shell script
commander.println("cd /user/home/work ; ./checkstaus.sh ; exit");
commander.flush();
System.out.println(channel.getExitStatus());
InputStream outputstream_from_the_channel = channel.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(outputstream_from_the_channel));
String line = null;
StringBuilder sb = new StringBuilder();
boolean isloginStringPassed = false ;
while ((line = br.readLine()) != null) {
sb.append(line.trim());
}
System.out.println("Result ="+sb.toString());
channel.disconnect();
session.disconnect();
System.out.println("completed .. ");
} catch (JSchException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Usually when you connect to a remote machine for the first time and after executing a command you'll get a shell command line prompt printed to the output stream. You can use it as a marker between output of different shell commands.
You may consider using an Expect-like third party library which simplify working with a remote services and capturing output. There a good set of options you can try:
Expect4J
ExpectJ
Expect-for-Java
However, when I was about to solve similar problem I found these libraries are rather old and hard to use in a commercial software. So I created my own and made it available for others. It it called ExpectIt. The advantages of my library it are stated on the project home page.
Here is an example of interacting with a public remote SSH service:
JSch jSch = new JSch();
Session session = jSch.getSession("new", "sdf.org");
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("shell");
Expect expect = new ExpectBuilder()
.withOutput(channel.getOutputStream())
.withInputs(channel.getInputStream(), channel.getExtInputStream())
.withEchoOutput(adapt(System.out))
// .withInputFilters(removeColors(), removeNonPrintable())
.withErrorOnTimeout(true)
.build();
// try-with-resources is omitted for simplicity
channel.connect();
expect.expect(contains("[RETURN]"));
expect.sendLine();
String ipAddress = expect.expect(regexp("Trying (.*)\\.\\.\\.")).group(1);
System.out.println("Captured IP: " + ipAddress);
expect.expect(contains("login:"));
expect.sendLine("new");
expect.expect(contains("(Y/N)"));
expect.send("N");
expect.expect(regexp(": $"));
// finally is omitted
channel.disconnect();
session.disconnect();
expect.close();
You can also take a look a at this example interacting with a Karaf shell which captures individual command output.