Using JSch to SFTP when one must also switch user - java

I am using JSch in a Java client to connect to a remote server and get some files using SFTP. The following code has been working fine for me: -
JSch ssh = new JSch();
JSch.setConfig(FileTransferConstants.STRICT_HOST_KEY_CHECKING, FileTransferConstants.NO);
Session session = ssh.getSession(userName, host, port);
session.setPassword(password);
session.connect();
Channel channel = session.openChannel(FileTransferConstants.SFTP);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
sftp.cd(remoteDirectoryPath);
sftp.lcd(localDirectoryPath);
sftp.get(remoteDirectoryPath + remoteFileName, remoteFileName);
The problem is that there has now been a change of site policy. I am no longer permitted to log on directly as this user (userName above). I must first log on as my personal user and then su into the user that has access to the files I want to SFTP.
I don't think there is anyway I can refactor the above code to achieve this and so I have started looking at using a shell or exec channel instead. I have had little success so far and cannot seem to find any examples on the web, so I would be very grateful for any advice or pointers in the right direction. Many thanks.

I do not think you can do this directly with JSch. But with some modification of its code, it's probably doable.
Note that my answer assumes that the server is *nix-based (what is backed by your reference to su) and uses OpenSSH SFTP server.
You have to open SSH "exec" channel, to execute something like:
sudo /bin/sftp-server
But on top of that channel, you need to build the ChannelSftp instance, not ChannelExec.
So you will need to implement Session.openChannel-like method, that will open exec channel, but create ChannelSftp for it.
For some background, see how it's possible to do sudo with WinSCP SFTP client.
Note that while the FAQ claims, that you won't be able to use password for the sudo, that's true for WinSCP. But as you have a full control of the session with JSch, you may be able to feed the password to sudo.
For that you might override the ChannelSftp.start() to write the password to the channel input, before starting the actual SFTP session.
You still need the requiretty option be off, as the SFTP cannot work with TTY.
For general considerations when automating operations using a different/root account, see:
Allowing automatic command execution as root on Linux using SSH

Related

Getting "com.jcraft.jsch.JSchException: Auth fail" – but "ssh" can login using public key authentication

I am trying to connect a remote server using JSch but I am getting "Auth fail" exception. Below is my code:
String user = "user.name";
String host = "hostip";
try
{
JSch jsch = new JSch();
Session session = jsch.getSession(user, host);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
session.connect();
int assinged_port=session.setPortForwardingL(lport, rhost, rport);
System.out.println("localhost:"+assinged_port+" -> "+rhost+":"+rport);
}
catch(Exception e){System.err.print(e);}
However when I try to ssh from iTerm using the command ssh user.name#hostip I can successfully access the remote server using public key authentication.
Your OpenSSH ssh command automatically uses the private key you have configured for OpenSSH in your .ssh folder.
JSch won't automatically use OpenSSH keys. You have to explicitly tell it what key to use.
See Can we use JSch for SSH key-based communication?
Also note that JSch does not support all key formats that OpenSSH do.
See "Invalid privatekey" when using JSch
Obligatory warning: Do not use StrictHostKeyChecking=no to blindly accept all host keys. That is a security flaw. You lose a protection against MITM attacks. For the correct (and secure) approach, see: How to resolve Java UnknownHostKey, while using JSch SFTP library?
Others might be getting the same error for very different reasons. For example when connecting to a modern server that is JSch is no longer compatible with:
Public key authentication fails with JSch but work with OpenSSH with the same key
As #martin prikryl says:
JSch won't automatically use OpenSSH keys.
File file = new File(SystemUtils.getUserHome() + "/.ssh/id_rsa");
String knownHosts = SystemUtils.getUserHome() + "/.ssh/known_hosts";
jsch.setKnownHosts(knownHosts);
jsch.addIdentity(file.getPath());
SystemUtils belongs to Apache Commons Lang3
or you can use:
new File(System.getProperty("user.home"))

How to copy file from one Channel to another using Jsch? [duplicate]

I need to program a file transfer using JSch library. I have a simple directory with two folders -
In the SFTP_1 folder, I have a bitmap image. And the SFTP_2 folder is just an empty folder. My goal is to transfer the image using SFTP from SFTP_1 to SFTP_2 .
Here is my code thus far :
FileTransfer fileTransfer = new FileTransfer();
JSch jsch = new JSch();
String host = "127.0.0.1";
int port = 22;
String user = "user";
Session session = jsch.getSession(user, host, port);
session = jsch.getSession("username", "127.0.0.1", 22);
session.connect();
ChannelSftp sftp = null;
sftp = (ChannelSftp)session.openChannel("sftp") ; //channel;
sftp.rename(
"C:\\Users\\ADMIN\\Desktop\\Work\\ConnectOne_Bancorp\\Java_Work\\SFTP_1\\house.bmp",
"C:\\Users\\ADMIN\\Desktop\\Work\\ConnectOne_Bancorp\\Java_Work\\SFTP_2\\house.bmp");
session.disconnect();
What I would like to do is to simply transfer a file from one directory in my machine, to another directory. any tips appreciated, thanks !
Note that to copy between two folders, one doesn't need to use SFTP. One can copy from one folder to another without involving the SFTP protocol which is primarly used to copy files remotely, either from the local machine to a remote machine, or from a remote machine to (the same or a different) remote machine, or from the remote machine to the local machine.
That's because the FTP is a network based protocol. So using it (or any of it's related protocols) is going to use the network (or a simulated network).
The security that JSch provides is security designed to protect from certain kinds of attacks that occur on networks. It will not provide any extra security within the machine.
To copy files between folders on a single machine, the simplest way to do so is not to use JSch, like so
private static void copyFileUsingJava7Files(File source, File dest)
throws IOException {
Files.copy(source.toPath(), dest.toPath());
}
There are other techniques, and if you really want to use JSch, you need to realize that JSch must be provided a lot of "extra" information to connect to the machine you are on, because it will try to connect to this machine as if it were connecting from across the network
Session sessionRead = jsch.getSession("username", "127.0.0.1", 22);
sessionRead.connect();
Session sessionWrite = jsch.getSession("username", "127.0.0.1", 22);
sessionWrite.connect();
ChannelSftp channelRead = (ChannelSftp)sessionRead.openChannel("sftp");
channelRead.connect();
ChannelSftp channelWrite = (ChannelSftp)sessionWrite.openChannel("sftp");
channelWrite.connect();
PipedInputStream pin = new PipedInputStream(2048);
PipedOutputStream pout = new PipedOutputStream(pin);
channelRead.get("/path/to/your/file/including/filename.txt", pout);
channelWrite.put(pin, "/path/to/your/file/destination/including/filename.txt");
channelRead.disconnect();
channelWrite.disconnect();
sessionRead.disconnect();
sessionWrite.disconnect();
The above code lacks error checking, exception handling, and fall back routines for if files are missing, networks are not up, etc. But you should get the main idea.
It should also be obvious that using a network protocol where no network protocol needs to exist opens the door to a lot more failure scenarios. Only use the SFTP method if your program is soon meant to copy files that are not both located on your machine.
Actually JSch is designed for remote work, and file system modification is one of the type such work. #Edwin Buck answer uses network for coping between local folders on remote host. There is better approach:
session.connect();
ChannelExec exec = (ChannelExec) session.openChannel("exec");
exec.setCommand("cp a.out b.out");
exec.connect();
I have no windows on the hand, as result my sample is for unix. But the idea is simple: execute copy command on the remote host.
If the original poster is actually looking for a working example of JSch in action between two distinct FTP sites, here goes:
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
...
JSch jsch = new JSch();
JSch session = null;
try {
session = jsch.getSession(userid, sourceservername, sourceserverport);
session.setPassword(sourceserverpassword);
Properties props = new Properties();
props.put("StrictHostKeyChecking", "no");
session.setConfig(props);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChanelSftp channelsftp = (ChannelSftp) channel;
channelsftp.cd(sourcefilepath);
channelsftp.lcd(localfilepath);
FileOutputStream fos = new FileOutputStream(new File(localfilepath + "/" + localfilename));
channelsftp.get(sourcefilename, fos);
fos.flush();
fos.close();
channelsftp.disconnect()
session.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
In practice you might break up these actions into separate try{}catch(){} blocked statements so as to introduce more granular error reporting, as well as add any informational output lines to inform the user of status, etc. But this'll get you there. Admittedly while the JSch examples are better than most such examples from freeware libraries, even good ones like this one, there can be some omissions among them around details that can make or break your attempt to get the things to work. Hope this helps if not the original poster, then someone else looking for a working JSch example. Once you have it working, it does go like a charm, so it's worth the trouble.
A core SFTP protocol does not support duplicating remote files.
There's a draft of copy-file extension to the protocol, but that's supported by only few SFTP servers (ProFTPD mod_sftp and Bitvise SFTP server for example).
In the most widespread OpenSSH SFTP server it is supported only by very recent version 9.0.
And it's also not supported by the JSch library.
See also my answer to How can I copy/duplicate a file to another directory using SFTP?
So actually using the cp shell command over an "exec" channel (ChannelExec) is unfortunately the best available approach (assuming you connect to a *nix server and you have a shell access).
If you do not have a shell access, then your only option is indeed to download the file to a local temporary folder and upload it back to the new location (or use streams, to avoid a temporary file). This is what the accepted answer by #Edwin Buck shows.

SFTP Connection error

I am using jsch-0.1.51.jar for sftp connection in my program and it was working fine for last 1 year but suddenly program started throwing error :
Algorithm negotiation fail. Below code:
=========================================
jsch.addIdentity(sftpIdentityFilePath);
logger.info("*****************Getting SFTP Connection******************");
session = jsch.getSession(sftpUser, sftpHost, 2222);
System.out.println("crossed seesion initialize");
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("crossed seesion config");
session.connect();
System.out.println("crossed seesion connect");
channel= session.openChannel("sftp");
System.out.println("sftp server connected");
logger.info("SFTP server connected");
channel.connect();
logger.info("*****************SFTP Connected******************");
==============================================================
After finding the issue I have used a updated jar to jsch-0.1.54.jar. But it's throwing a different exception
2018-04-28 18:17:51 ERROR FileCopyMain:978 -
Session.connect: java.io.IOException: End of IO Stream Read
Also in both this cases when I am trying to run this program from Eclipse IDE then it's working fine. But when I am creating the jar file of this Java code then I am getting these issue.
Context of this SFTP connection code: I am connecting a server using private key to download files to my local
Can some body please help me out with this?
Algorithm negotiation fail.
This means that the client and server side could not agree on the encryption algorithm to be used to keep the SSH connection secure. When that happens, the server side will close the connection, leading to the IOException that you see.
The most likely explanation is that either the client side SSH implementation is out of date, or the server-side SSH implementation is out of date. There should be some clues in the jcsh "DEBUG" logging; see JSch logger - where can I configure the level. If that fails, look at the logs on the server side.
The solution will depend on what you find.

can I re-add remote host to known_host using JSCH?

I want to be able to remove the remote server key from known_hosts and add it again. The remote server gets updated often so I want to automatically remove the remote host key and add its new key to known_hosts. I can remove the key from known_hosts though it is clunky and uses a Process instead of going through JSCH. This works but I encounter this message whenever I try to access the server:
The authenticity of host '192.168.1.1 (192.168.1.1)' can't be established.
RSA key fingerprint is 10:10:30:00:e7:0c:d3:18:cf:ac:42:e2:f3:51:25:bg.
Are you sure you want to continue connecting (yes/no)?
I know it is possible to get around this message using a UserInfo but I use other ways of connecting to the remote server, such as a Process and the message would appear when I run those commands.
Is it possible to use JSCH to remove and add a host id from known_hosts?
Possibly related though it does not use jsch:
How can I write a program (script) to remove obsolete host keys from ~/.ssh/known_hosts?
Yes you can add a remote host entry into your known_hosts file using JSch. As Jim Garrison answered, there are other ways around the issue, but here is how to do it in your code:
First, understand that as a default, if you do not specify a known_hosts file, JSch can still work, it will just create a run-time known_host file in memory, and add entrys automatically so long as Strict Host Key Checking is set to 'no'.
If you DO specify Known Hosts file, then JSch will add new entries to that file when Strict Host Key Check is set to 'no'
JSch jsch = new JSch();
jsch.setKnownHosts(knownHostsFile);
logger.info("known hosts file set: " + knownHostsFile);
jsch.addIdentity(privateKey);
logger.info("rsa private key loaded: " + privateKey);
Session session = jsch.getSession(user, host, port);
java.util.Properties config = new java.util.Properties();
// this setting will cause JSCH to automatically add all target servers' entry to the known_hosts file
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
This is not very secure behavior, but is a handy way to get JSCh to setup a new server entry for you. After your known hosts file is setup, I recommend turning your StrictHostKeyChecking back to yes.
There is no need to programmatically manipulate known_hosts
ssh -oStrictHostKeyChecking=no remote-host
Or add
StrictHostKeyChecking no
to ~/.ssh/config. Read the ssh_config(5) man page for details.

How to perform scp between 2 remote unix boxes using Jsch?

I am writing a Java code using Jsch in which I need to perform scp between 2 remote UNIX boxes.
I tried to execute the scp command in the same way as to execute a normal command from my java code like this :
JSch jsch=new JSch();
Session session=jsch.getSession("user", "host", 22);
UserInfo ui=new MyUserInfo();
session.setUserInfo(ui);
session.setPassword("pwd");
session.connect();
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand("scp user#mademo.org:/home/user/demo.csv /home/user/demo.csv");
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream in=channel.getInputStream();
channel.connect();
//Printing operations
channel.disconnect();
session.disconnect();
Instead of prompting password for user#mademo.org as I expected. I get only errors :
Permission denied, please try again.
Permission denied, please try again.
Permission denied (publickey,gssapi-with-mic,password).
exit-status: 1
From executing the command on the UNIX box directly, I have found this :
scp asks for password to connect to the remote machine.. however using this code, it is not prompting for any password. So it is trying to perform scp without the password and trying thrice(NumberoOFAttempts flag) and gives the error messages...
I want to know how to make scp atleast prompt to enter something instead of just not taking password ... I have used the default implementation of MyUserInfo class given in jsch examples for my code...
Or
Is there any way to provide the password along with the scp command ?? I am trying to avoid using private/public key combinations as much as possible..
EDIT :
From Paulo's post.. Now I have added channel.setPty(true) to get a pseudo terminal for the command and it is asking for me to enter Password than failing automatically..
How should I specify the password now... I tried typing it in the console but it is not accepting and keeps on waiting for user input.... I tried setting it using channel.setInputStream(pwdbytes); but it keeps waiting at the command and not taking the input ..
Thanks for any help regarding this...
The scp command on your first Unix box tries to read the password from the console/terminal. But this will only work when there is a terminal associated with the process - and it is not, by default.
The easiest way to solve this would be to use channel.setPty(true) before connecting the channel, and then provide the password via the input/output streams.

Categories