I'm trying to write a Java code that can ssh to a unix server and reset a user's password. I use jsch for the connection and command execution. I tested on few servers (CentOS, Ubuntu, HP-UX) and it works fine.
When I tested on AIX, I got this problem where it prompt for user's New password. My program will just hang until a timeout occured. Then an error will comeout. I did google on how to prevent the user interaction here, but unfortunately, the AIX that I'm working on doesn't have chpasswd
. While googling more about the error code here, I found out about expect4j library that can be used for this case.
I tried following some example like here and applied into my code.
This is my code:
public void executeSetPassword(final String userName, final GuardedString password) {
JSch.setLogger(new JSCHLogger());
if ((userName != null) && (password != null)) {
JSch jsch = new JSch();
String host = configuration.getHost();
String remoteUser = configuration.getRemoteUser();
GuardedString passwd = configuration.getPassword();
final Session session;
try {
session = jsch.getSession(remoteUser, host, 22);
passwd.access(new Accessor(){
@Override
public void access(char[] clearChars) {
session.setPassword(new String(clearChars));
}});
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
logger.info("sessionStatus is " + session.isConnected());
final ChannelExec channel=(ChannelExec) session.openChannel("exec");
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
channel.setCommand("echo -e " + "\"" + new String(clearChars) + "\\n" + new String(clearChars) + "\"" + " | passwd " + userName + ";pwdadm -c " + userName);
}});
channel.setErrStream(System.err);
channel.setPty(true);
final Expect4j expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
channel.connect();
final StringBuilder buffer = new StringBuilder();
Closure closure = new Closure() {
public void run(ExpectState expectState) throws Exception {
buffer.append(expectState.getBuffer());//string buffer for appending output of executed command
}
};
expect.expect("New password");
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
try {
expect.send(new String(clearChars));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(channel.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
logger.info(line);
if( line.contains("New password:") == true) {
logger.info("set something here");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(channel.isClosed()) {
channel.disconnect();
session.disconnect();
expect.close();
logger.info("Exit status = " + channel.getExitStatus());
}
}
catch ( Exception e) {
throw new RuntimeException(e);
}
}
When I tried running it, the same thing happens. Does it mean that my expect is not working correctly?
This is the log:
INFO: Connecting to 192.168.1.39 port 22
INFO: Connection established
INFO: Remote version string: SSH-1.99-OpenSSH_3.8.1p1
INFO: Local version string: SSH-2.0-JSCH-0.1.53
INFO: CheckCiphers: aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256
INFO: aes256-ctr is not available.
INFO: aes192-ctr is not available.
INFO: aes256-cbc is not available.
INFO: aes192-cbc is not available.
INFO: CheckKexes: diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
INFO: CheckSignatures: ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
INFO: SSH_MSG_KEXINIT sent
INFO: SSH_MSG_KEXINIT received
INFO: kex: server: diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1
INFO: kex: server: ssh-rsa,ssh-dss
INFO: kex: server: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc,[email protected],aes128-ctr,aes192-ctr,aes256-ctr
INFO: kex: server: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc,[email protected],aes128-ctr,aes192-ctr,aes256-ctr
INFO: kex: server: hmac-md5,hmac-sha1,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96
INFO: kex: server: hmac-md5,hmac-sha1,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96
INFO: kex: server: none,zlib
INFO: kex: server: none,zlib
INFO: kex: server:
INFO: kex: server:
INFO: kex: client: diffie-hellman-group1-sha1
INFO: kex: client: ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc
INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc
INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
INFO: kex: client: none
INFO: kex: client: none
INFO: kex: client:
INFO: kex: client:
INFO: kex: server->client aes128-ctr hmac-md5 none
INFO: kex: client->server aes128-ctr hmac-md5 none
INFO: SSH_MSG_KEXDH_INIT sent
INFO: expecting SSH_MSG_KEXDH_REPLY
INFO: ssh_rsa_verify: signature true
WARN: Permanently added '192.168.1.39' (RSA) to the list of known hosts.
INFO: SSH_MSG_NEWKEYS sent
INFO: SSH_MSG_NEWKEYS received
INFO: SSH_MSG_SERVICE_REQUEST sent
INFO: SSH_MSG_SERVICE_ACCEPT received
INFO: Authentications that can continue: publickey,keyboard-interactive,password
INFO: Next authentication method: publickey
INFO: Authentications that can continue: keyboard-interactive,password
INFO: Next authentication method: keyboard-interactive
INFO: Authentications that can continue: password
INFO: Next authentication method: password
INFO: Authentication succeeded (password).
Thread Id: 1 Time: 2015-06-18 13:44:07.318 Class: com.mastersam.connectors.unix.UnixConnector Method: executeSetPassword(UnixConnector.java:421) Level: INFO Message: 3004-709 Error changing password for "fikrie".
I also tried changing ssh channel instead of exec. This is the code after changing it:
final ChannelShell channel=(ChannelShell) session.openChannel("shell");
channel.setPty(true);
final Expect4j expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
channel.connect();
final StringBuilder buffer = new StringBuilder();
Closure closure = new Closure() {
public void run(ExpectState expectState) throws Exception {
buffer.append(expectState.getBuffer());
}
};
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
try {
expect.send(echo -e " + "\"" + new String(clearChars) + "\\n" + new String(clearChars) + "\"" + " | passwd " + userName);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});