001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: berkley $' 006 * '$Date: 2010-04-28 00:12:36 +0000 (Wed, 28 Apr 2010) $' 007 * '$Revision: 24000 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029package org.kepler.ssh; 030 031import java.io.File; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.InputStreamReader; 035import java.io.OutputStream; 036import java.io.OutputStreamWriter; 037import java.io.PipedInputStream; 038import java.io.PipedOutputStream; 039import java.io.UnsupportedEncodingException; 040import java.util.List; 041 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044 045import com.sshtools.common.configuration.SshToolsConnectionProfile; 046import com.sshtools.common.hosts.DialogKnownHostsKeyVerification; 047import com.sshtools.j2ssh.SftpClient; 048import com.sshtools.j2ssh.SshClient; 049import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; 050import com.sshtools.j2ssh.authentication.EKEYXAuthenticationClient; 051import com.sshtools.j2ssh.authentication.GSSAuthenticationClient; 052import com.sshtools.j2ssh.authentication.SshAuthenticationClient; 053import com.sshtools.j2ssh.configuration.SshConnectionProperties; 054import com.sshtools.j2ssh.connection.ChannelInputStream; 055import com.sshtools.j2ssh.session.SessionChannelClient; 056 057/** 058 * This class provides functionality to use an GSISSH session to execute remote 059 * commands and to transfer files on a remote machine. 060 * <p> 061 * To create a gsissh session the code needs a valid proxy certificate and a 062 * valid CA certificate/ trusted certificate for the server. 063 * The code searches for the proxy 064 * certificates and CA certificates at default locations based on the 065 * specifications of underlying jglobus package -- 066 * http://www-unix.globus.org/cog/distribution/1.6.0/docs/configuring.html 067 * By default it looks for proxy certificates in the system's temp directory 068 * and for CA certificates(trusted certificates) in 069 * ${user.home}/.globus/certificates and /etc/grid-security/certificates. 070 * You can alter the default file and search path by creating appropriate 071 * entries in $HOME/.globus/cog.properties. Please refer to 072 * http://www-unix.globus.org/cog/distribution/1.6.0/docs/configuring.html 073 * for more configuration details. 074 * <p> 075 * 076 * You can create several sessions to different sites and use any of them 077 * referring to it with its user@host:port. If port is not specified defaults 078 * to 2222. Once instantiated the GsiSsh object can be used by anyone anytime 079 * to copy file or execute commands without authenticating again. 080 * <p> 081 * 082 * @author Chandrika Sivaramakrishnan 083 * 084 * This uses a modified code of the open source product GSI-SSHTerm. 085 * http://www.ngs.ac.uk/tools/gsisshterm 086 * Since GSI-SSHTerm is a terminal product most of the error scenarios and 087 * user interaction scenarios are handled using GUI popups. This part of 088 * the GSI-SSHTerm code hasn't been modified and hence this code will fail when 089 * user interaction are to be handled through command prompt. For example, 090 * when kepler is run without gui. In such cases use this with an existing 091 * valid proxy certificate 092 */ 093public class GsiSshExec extends RemoteExec { 094 095 // variables specific to GSI SSH 096 protected SshClient sshClient; 097 protected SshConnectionProperties connectionProfile = new SshToolsConnectionProfile(); 098 099 //variables common to SshExec and GsiSshExec 100 private int timeout = 0; // timeout in seconds 101 private boolean forcedCleanUp = false; 102 private static String cleanUpInfoCmd = new String("echo $$; "); 103 // restart timer if stdout has data 104 private boolean timeoutRestartOnStdout = false; 105 // restart timer if stderr has data 106 private boolean timeoutRestartOnStderr = false; 107 108 private boolean pseudoTerminal = false; 109 private String protocolPath = ""; 110 private String cmdLineOptions = ""; 111 private String targetStr=""; 112 private List availableAuthMethods; 113 114 //Logging 115 private static final Log log = LogFactory.getLog(GsiSshExec.class.getName()); 116 private static final boolean isDebugging = log.isDebugEnabled(); 117 118 public GsiSshExec(String user, String host) { 119 //session = SshSessionFactory.getSession(user, host, 22); 120 //session is not cached currently 121 sshClient = GsiSshSessionFactory.getSshClient(user, host, 2222); 122 connectionProfile.setHost(host); 123 connectionProfile.setUsername(user); 124 125 targetStr = connectionProfile.getUsername() + "@" 126 + connectionProfile.getHost(); 127 } 128 129 public GsiSshExec(String user, String host, int port) { 130 sshClient = GsiSshSessionFactory.getSshClient(user, host, port); 131 connectionProfile.setHost(host); 132 connectionProfile.setUsername(user); 133 connectionProfile.setPort(port); 134 135 targetStr = connectionProfile.getUsername() + "@" 136 + connectionProfile.getHost() + ":" 137 + connectionProfile.getPort(); 138 } 139 140 141 public GsiSshExec(String target) { 142 // get USER 143 String user, host; 144 int port = 2222; 145 146 int atPos = target.indexOf('@'); 147 if (atPos >= 0) { 148 user = target.substring(0, target.indexOf('@')); 149 } else { 150 user = System.getProperty("user.name"); 151 } 152 153 // get the HOST and PORT 154 int colonPos = target.indexOf(':'); 155 if (colonPos >= 0 && colonPos > atPos) { 156 host = target.substring(atPos + 1, colonPos); 157 String portStr = target.substring(colonPos + 1); 158 try { 159 port = Integer.parseInt(portStr); 160 } catch (java.lang.NumberFormatException ex) { 161 log 162 .error("The port should be a number or omitted in " 163 + target); 164 } 165 }else{ 166 host = target.substring(atPos + 1); 167 } 168 169 sshClient = GsiSshSessionFactory.getSshClient(user, host, port); 170 connectionProfile.setHost(host); 171 connectionProfile.setUsername(user); 172 connectionProfile.setPort(port); 173 174 targetStr = connectionProfile.getUsername() + "@" 175 + connectionProfile.getHost() + ":" 176 + connectionProfile.getPort(); 177 178 } 179 180 public int copyFrom(String sourcePath, File localPath, boolean recursive) 181 throws SshException { 182 int numberOfCopiedFiles = 0; 183 if(recursive){ 184 throw new SshException("Recursive copy is not supported on grid servers"); 185 } 186 if (!openConnection()) 187 throw new SshException( 188 "Ssh connection could not be opened for copying."); 189 try{ 190 SftpClient sftpclient = new SftpClient(sshClient); 191 if(localPath == null){ 192 sftpclient.get(sourcePath); 193 }else{ 194 String path = localPath.getAbsolutePath(); 195 int index = -1; 196 if(localPath.isDirectory()){ 197 index = sourcePath.lastIndexOf('/'); 198 if(index>-1 && index < sourcePath.length()-1){ 199 path = path + System.getProperty("file.separator") + sourcePath.substring(index+1); 200 } else { 201 path = path + System.getProperty("file.separator") + sourcePath; 202 } 203 } 204 sftpclient.get(sourcePath,path); 205 } 206 numberOfCopiedFiles = 1; 207 }catch(Exception e){ 208 throw new SshException("Exception caught at while copying file: " + sourcePath 209 + "\n" + e); 210 } 211 return 0; 212 } 213 214 /** 215 * Copy _one_ local file to a remote directory Input: file of type File 216 * (which can be a directory) Input must not have wildcards. targetPath is 217 * either a directory or filename 218 * 219 * @return number of files copied successfully SshException is thrown in 220 * case of error. 221 */ 222 protected int _copyTo(File lfile, String targetPath, boolean recursive) 223 throws SshException { 224 225 if (!lfile.exists()) { 226 throw new SshException("File does not exist: " + lfile); 227 } 228 229 // check: recursive traversal of directories enabled? 230 if (lfile.isDirectory()) { 231// if (!recursive) 232// throw new SshException("File " + lfile 233// + " is a directory. Set recursive copy!"); 234 throw new SshException(lfile.getName() 235 + " is a directory. Recursive copy is not supported on grid servers"); 236 } 237 238 if (!openConnection()) 239 throw new SshException( 240 "Ssh connection could not be opened for copying."); 241 242 // at this point we have a living, opened session to the remote machine 243 int numberOfCopiedFiles = 0; 244 try{ 245 String path = lfile.getAbsolutePath(); 246 SftpClient sftpclient = new SftpClient(sshClient); 247 248 //by default preserves file attributes 249 if(targetPath==null || targetPath.trim().equals("")){ 250 sftpclient.put(path); 251 }else{ 252 sftpclient.put(path,targetPath); 253 } 254 // if there is no exception return 1 255 numberOfCopiedFiles = 1; 256 } catch (Exception e) { 257 throw new SshException("Exception caught at while copying file: " + lfile 258 + "\n" + e); 259 } 260 261 return numberOfCopiedFiles; 262 } 263 264 public int executeCmd(String command, OutputStream streamOut, 265 OutputStream streamErr) throws ExecException { 266 return executeCmd(command, streamOut, streamErr, null); 267 } 268 269 public int executeCmd(String command, OutputStream streamOut, 270 OutputStream streamErr, String thirdPartyTarget) 271 throws ExecException { 272 273 int exitCode = -1; 274 SessionChannelClient sessionChannel; 275 _streamReaderThread readerThread; 276 boolean commandSuccess; 277 ChannelInputStream stderrInputStream; 278 279 // get the pwd to the third party if necessary 280 String thirdPartyPwd = getPwdToThirdParty(thirdPartyTarget); 281 // Form the command 282 String updatedCmd = forcedCleanUp ? cleanUpInfoCmd + command : command; 283 284 openConnection(); 285 log.debug("Connected"); 286 287 //int result = copyTo(new File("C:\\Chandrika\\test.txt"),"/home/chandrika",false); 288 //return result; 289 290 try { 291 292 // Create piped stream if password has to be fed 293 PipedInputStream pis = null; 294 PipedOutputStream pos = null; 295 if (thirdPartyPwd != null) { 296 try { 297 pis = new PipedInputStream(); 298 pos = new PipedOutputStream(pis); 299 } catch (IOException ex) { 300 log 301 .error("Error when creating the piped stream for password feededing: " 302 + ex); 303 throw new ExecException( 304 "Error when creating the piped stream for password feededing: " 305 + ex); 306 } 307 // setting pseudo terminal to true so that prompt for password 308 // can be read 309 // SshExec uses ptyexec.c instead of using pseudo terminal 310 pseudoTerminal = true; 311 } 312 313 // Open channel and execute command 314 synchronized (sshClient) { 315 316 // Once authenticated the user's shell can be started 317 // Open a session channel 318 sessionChannel = sshClient.openSessionChannel(); 319 // create pseudo terminal if user has asked for it 320 if (pseudoTerminal) { 321 sessionChannel.requestPseudoTerminal("vt100", 80, 24, 640, 322 480, null); 323 } 324 if(thirdPartyPwd!=null){ 325 sessionChannel.bindInputStream(pis); 326 } 327 stderrInputStream = (ChannelInputStream) sessionChannel 328 .getStderrInputStream(); 329 readerThread = new _streamReaderThread(sessionChannel, 330 sessionChannel.getInputStream(), streamOut, 331 thirdPartyPwd, pos); 332 readerThread.start(); 333 commandSuccess = sessionChannel.executeCommand(updatedCmd); 334 } 335 336 log.debug("boolean command excution result is" + commandSuccess); 337 // command has finished executing. wait for the reader thread to 338 // finish reading output 339 // It will timeout at the latest if the command does not finish 340 // 3 ways to finish: 341 // - command terminates 342 // - timeout 343 // - IOException when reading the command's output or writing 344 // the caller's output 345 readerThread.join(); 346 347 // on timeout finish here with a nice Exception 348 if (readerThread.timeoutHappened()) { 349 log.error("Timeout: " + timeout + "s elapsed for command " 350 + command); 351 if (sessionChannel.isOpen()) { 352 sessionChannel.close(); //closes channels and frees channel 353 } 354 if (forcedCleanUp) { 355 // time for clean-up ;-) 356 kill(readerThread.getProcessID(), true); 357 } 358 throw new ExecTimeoutException(command); 359 } 360 361 // if we cannot process output, still wait for the channel to be 362 // closed 363 // !!! This can lead to hang-up !!! 364 while (!sessionChannel.isClosed()) { 365 try { 366 log.debug("Waiting for channel to close"); 367 Thread.sleep(500); 368 } catch (Exception e) { 369 } 370 } 371 Integer temp = sessionChannel.getExitCode(); 372 if (temp != null) { 373 exitCode = temp.intValue(); 374 } 375 // disconnect for now. 376 //Remove this once session cache is implemented. If session is cached, 377 //should call closeConnection to close the session 378 //closeConnection(); 379 380 if (exitCode != 0 && forcedCleanUp) { 381 kill(readerThread.getProcessID(), true); 382 } 383 //update streamErr with contents from stderrInputStream 384 byte[] b = new byte[1024]; 385 int numread = stderrInputStream.read(b , 0, 1024); 386 while (numread != -1) { 387 streamErr.write(b, 0, numread); 388 numread = stderrInputStream.read(b, 0, 1024); 389 } 390 391 // FORDEBUG 392 /*System.out.println("Output===" + streamOut.toString()); 393 System.out.println("==Checking for error=="); 394 System.out.println("Error== " + streamErr.toString()); 395 System.out.println("Exiting GsiExec " ); 396 */// FORDEBUG 397 398 } catch (Exception e) { 399 throw new ExecException( 400 "Exception occured while executing command " + command 401 + "\n" + e); 402 } 403 return exitCode; 404 } 405 406 public List getAvailableAuthMethods() throws SshException{ 407 try { 408 if(sshClient == null){ 409 return null; 410 } 411 else if (!sshClient.isConnected()){ 412 openConnection(false); 413 }else{ 414 log.debug("Connection already opened"); 415 } 416 synchronized(sshClient){ 417 availableAuthMethods = sshClient.getAvailableAuthMethods(connectionProfile.getUsername()); 418 } 419 return availableAuthMethods; 420 } catch (Exception e) { 421 log.error("Error getting the supported authentication mechanism for " 422 + targetStr + "\n" + e.toString()); 423 throw new SshException("Unable to connect to / get the list of authentication from " 424 +targetStr); 425 } 426 } 427 public boolean openConnection() throws SshException{ 428 return openConnection(true); 429 } 430 public boolean openConnection(boolean authenticate) throws SshException { 431 if (null == sshClient) 432 return false; 433 if (sshClient.isConnected() && sshClient.isAuthenticated()) 434 return true; 435 436 437 //Two step process - 1. Connect 2.authenticate 438 synchronized(sshClient){ 439 //log.debug("BEGIN: Open connection"); 440 if(!sshClient.isConnected()){ 441 try { 442 // Should pass instance of SshToolsConnectionProfile when using gss 443 // authentication 444 //System.out.println("Connecting using sshClient.connect()........"); 445 sshClient.connect(connectionProfile, 446 new DialogKnownHostsKeyVerification(null)); 447 //log.debug("###Connection established successfully"); 448 // //TODO:Run long running job to test if session would be alive 449 } catch (Exception e) { 450 //log.debug("END: Open connection.Error connecting to "+ targetStr); 451 throw new SshException("Error connecting to " + targetStr + " \n" 452 + e); 453 } 454 } 455 /* 456 * Once this code has executed and returned the connection is ready for 457 * authentication 458 */ 459 if(authenticate && !sshClient.isAuthenticated()){ 460 boolean result = authenticate(false); 461 //log.debug("END: Open connection. Returning connection after call to authenticate"); 462 return result; 463 } else{ 464 //log.debug("END: Open connection. Returning connection without authenticating"); 465 return true; 466 } 467 } 468 } 469 470 public boolean authenticate(boolean externalkeyx) throws SshException { 471 boolean authenticated = false; 472 if (null == sshClient) 473 return authenticated; 474 475 SshAuthenticationClient authClient; 476 // If the server supports external key exchange authenticate using that 477 //if(externalkeyx){ 478 if (availableAuthMethods!=null && availableAuthMethods.contains("external-keyx")) { 479 authClient = new EKEYXAuthenticationClient(); 480 authClient.setHostname(connectionProfile.getHost()); 481 authClient.setUsername(connectionProfile.getUsername()); 482 authClient.setProperties(connectionProfile); 483 int result; 484 try { 485 log.debug("Authenticating using external-keyx"); 486 result = sshClient.authenticate(authClient, connectionProfile 487 .getHost()); 488 log.debug("Authentication complete"); 489 490 } catch (Exception e) { 491 throw new SshException("Exception authenticating to " + targetStr 492 + " using external-keyx \n" + e); 493 } 494 if (result == AuthenticationProtocolState.COMPLETE) { 495 log.debug("Authentication to host " + connectionProfile.getHost() 496 + " using external-keyx was successful"); 497 return true; 498 } 499 } 500 501 authClient = new GSSAuthenticationClient(); 502 // username and host name have to be set explicitly even though they 503 // are in properties - bug in GSI-SSHTerm code? 504 authClient.setHostname(connectionProfile.getHost()); 505 authClient.setUsername(connectionProfile.getUsername()); 506 authClient.setProperties(connectionProfile); 507 508 // Authenticate the user 509 // Authentication result could be partial- when does this occur? 510 // TODO:Handle partial authentication 511 int result; 512 try { 513 log.debug("Authenticating using gssapi"); 514 result = sshClient.authenticate(authClient, connectionProfile 515 .getHost()); 516 log.debug("Authenticating using gssapi complete"); 517 } catch (Exception e) { 518 throw new SshException("Exception authenticating to " + targetStr 519 + "\n" + e); 520 } 521 if (result == AuthenticationProtocolState.COMPLETE) { 522 log.debug("Authentication to host " + connectionProfile.getHost() 523 + " using gssapi was successful"); 524 return true; 525 } else { 526 throw new SshException("Authentication failed for: " + targetStr); 527 } 528 529 } 530 531 public void closeConnection(){ 532 533 if (sshClient!=null && sshClient.isConnected()) { 534 sshClient.disconnect(); 535 } 536 } 537 538 public void setForcedCleanUp(boolean forcedCleanUp) { 539 this.forcedCleanUp = forcedCleanUp; 540 } 541 542 public void setTimeout(int seconds, boolean stdout, boolean stderr) { 543 this.timeout = seconds; 544 timeoutRestartOnStdout = stdout; 545 timeoutRestartOnStderr = stderr; 546 547 } 548 549 /** 550 * addIdentity, useless for local exec 551 */ 552 public void addIdentity(String identity) { 553 // do nothing 554 } 555 556 /** 557 * port forwarding not working on local exec 558 */ 559 public void setPortForwardingL(String spec) throws ExecException { 560 // do nothing 561 } 562 563 /** 564 * port forwarding not working on local exec 565 */ 566 public void setPortForwardingR(String spec) throws ExecException { 567 // do nothing 568 } 569 570 @Override 571 public boolean getForcedCleanUp() { 572 return forcedCleanUp; 573 } 574 575 @Override 576 public int getTimeout() { 577 return timeout; 578 } 579 580 public void setTimeout(int seconds) { 581 timeout = seconds; 582 } 583 584 // ////////////Private class/////////////////////// 585 private class _streamReaderThread extends Thread { 586 private InputStreamReader isr; // 'char' reader from the remote command 587 private OutputStreamWriter osw; // 'char' writer to the caller's output 588 // stream 589 590 private boolean cleanUpInfoProcessed = !forcedCleanUp; // false: will 591 // consume first 592 // line for 593 // process ID 594 private String pwd; // the password to be fed to the command 595 private PipedOutputStream pos; // the pipe-in to the stdin of the 596 // remote 597 // command 598 private SessionChannelClient channel; 599 600 private StringBuffer processID; // the remote command's shell's process 601 // id (to kill if needed) 602 private boolean timeoutReached; // becomes true on timeout 603 604 public _streamReaderThread(SessionChannelClient sessionChannel, 605 InputStream in, OutputStream out, String pwd, 606 PipedOutputStream pos) { 607 try { 608 isr = new InputStreamReader(in, "utf-8"); 609 osw = new OutputStreamWriter(out, "utf-8"); 610 } catch (UnsupportedEncodingException ex) { 611 // get the default encoding 612 isr = new InputStreamReader(in); 613 osw = new OutputStreamWriter(out); 614 } 615 616 channel = sessionChannel; 617 this.pwd = pwd; 618 this.pos = pos; 619 } 620 621 public String getProcessID() { 622 return processID.toString(); 623 } 624 625 public boolean timeoutHappened() { 626 return timeoutReached; 627 } 628 629 public void run() { 630 log.debug("In run"); 631 char[] tmp = new char[1024]; 632 boolean checkForPwd = (pwd != null); 633 processID = new StringBuffer(); 634 635 // variables for the timeout checking 636 long start = System.currentTimeMillis(); 637 long current = 0; 638 long maxtime = timeout * 1000L; 639 640 while (true) { // read command's output until termination or 641 // timeout 642 int len = 0; 643 int j = 0; 644 try { 645 while (isr.ready()) { // we do not want to block on read 646 // because we are counting for 647 // timeout 648 //System.out.println("In isr.ready"); 649 len = isr.read(tmp, 0, 1024); 650 651 if (len < 0) { 652 if (isDebugging) 653 log 654 .debug("Read error on stdout stream: " 655 + len); 656 break; // break the reading loop 657 } 658 j = 0; 659 660 // first line is remote process id in case of 661 // forcedCleanUp. Filter here 662 if (!cleanUpInfoProcessed) { 663 // if (isDebugging) 664 // log.debug("cleanup info string: " + new 665 // String(tmp, 0, len)); 666 for (; j < len; j++) { 667 if (tmp[j] == '\n') { 668 cleanUpInfoProcessed = true; // done 669 j++; 670 if (isDebugging) 671 log.debug("Remote process id = " 672 + processID); 673 break; // break the reading loop 674 } 675 processID.append(tmp[j]); 676 } 677 // Note: j<=len here 678 } 679 680 // print the buffer to the output stream 681 osw.write(tmp, j, len - j); 682 osw.flush(); // send it really if someone is polling 683 // it 684 // above us 685 if (timeoutRestartOnStdout) 686 start = System.currentTimeMillis(); // restart 687 // timeout timer 688 //System.out.println(" %%% " 689 // + new String(tmp, j, len - j)); 690 // if (timeoutRestartOnStdout) 691 // start = System.currentTimeMillis(); // restart 692 // // timeout timer 693 String tempStr = new String(tmp, j, len - j); 694 if (tempStr.contains("RSA key fingerprint is") 695 && tempStr.trim().endsWith("(yes/no)?")) { 696 boolean userInput = promptYesNo(tempStr); 697 // boolean userInput = true; 698 log.debug("Prompt for host verification: " 699 + tempStr); 700 if (userInput) { 701 pos.write("yes\n".getBytes()); 702 pos.flush(); 703 log 704 .info("Added destination server to known_hosts of source"); 705 continue; 706 } else { 707 pos.write("no\n".getBytes()); 708 pos.flush(); 709 pos.close(); 710 log 711 .error("Failed to accept RSA key fingerprint"); 712 break; 713 } 714 } 715 716 if (checkForPwd && containsPasswordRequest(tmp, j, len)) { 717 // now feed the password to the process 718 try { 719 720 pos.write(pwd.getBytes()); 721 // log.info("Sent password "); 722 pos.write("\n".getBytes()); 723 // log.info("Sent newline "); 724 pos.flush(); 725 // log.info("Flushed pos "); 726 pos.close(); 727 log.info("Sent password to third party."); 728 } catch (IOException ex) { 729 log 730 .error("Error when feeding the password to the piped stream: " 731 + ex); 732 } catch (Exception e) { 733 log 734 .error("Error when feeding the password to the piped stream: " 735 + e); 736 } 737 checkForPwd = false; 738 } 739 } // end while isr.ready 740 } catch (IOException ex) { 741 log 742 .error("Error on the remote streams. Exiting reader thread: " 743 + ex); 744 break; // exit the loop 745 } 746 if (channel.isClosed()) { 747 System.out.println("Channel is closed"); 748 break; // exit the loop 749 } 750 try { 751 Thread.sleep(500); 752 } catch (Exception e) { 753 } 754 755 // check timeout 756 current = System.currentTimeMillis(); 757 if (timeout > 0 && maxtime < current - start) { 758 log.debug("Reader thread detected timeout: " + timeout 759 + "s elapsed"); 760 timeoutReached = true; 761 break; // exit the loop 762 } 763 } // end of while (true) 764 765 try { 766 osw.close(); 767 } catch (IOException ex) { 768 log.error("Cannot flush and close the output stream: " + ex); 769 } 770 } // end method run() 771 772 /** 773 * Look for one of the strings password/passphrase/passcode in the 774 * char[] array. Return true if found any. Case insensitive search. 775 * Possible bug: we do not find the password text if it is broken into 776 * two in two consecutive calls. 777 */ 778 private boolean containsPasswordRequest(char[] buf, int startPos, 779 int endPos) { 780 // look for strings password/passphrase/passcode 781 int i = startPos; 782 while (i < endPos - 3) { 783 if (Character.toLowerCase(buf[i]) == 'p' 784 && Character.toLowerCase(buf[i + 1]) == 'a' 785 && Character.toLowerCase(buf[i + 2]) == 's' 786 && Character.toLowerCase(buf[i + 3]) == 's') { 787 788 // found "pass", look further for word/code/phrase 789 if (i < endPos - 7 790 && Character.toLowerCase(buf[i + 4]) == 'w' 791 && Character.toLowerCase(buf[i + 5]) == 'o' 792 && Character.toLowerCase(buf[i + 6]) == 'r' 793 && Character.toLowerCase(buf[i + 7]) == 'd') { 794 log.info("PWDSearch: found request for password."); 795 return true; 796 } else if (i < endPos - 7 797 && Character.toLowerCase(buf[i + 4]) == 'c' 798 && Character.toLowerCase(buf[i + 5]) == 'o' 799 && Character.toLowerCase(buf[i + 6]) == 'd' 800 && Character.toLowerCase(buf[i + 7]) == 'e') { 801 log.info("PWDSearch: found request for passcode."); 802 return true; 803 } else if (i < endPos - 9 804 && Character.toLowerCase(buf[i + 4]) == 'p' 805 && Character.toLowerCase(buf[i + 5]) == 'h' 806 && Character.toLowerCase(buf[i + 6]) == 'r' 807 && Character.toLowerCase(buf[i + 7]) == 'a' 808 && Character.toLowerCase(buf[i + 8]) == 's' 809 && Character.toLowerCase(buf[i + 9]) == 'e') { 810 log.info("PWDSearch: found request for passphrase."); 811 return true; 812 } 813 } 814 i = i + 1; 815 } 816 return false; 817 } 818 } // end inner class _streamReaderThread 819 820 private boolean promptYesNo(String str) { 821// Object[] options = { "yes", "no" }; 822// int foo = JOptionPane.showOptionDialog(null, str, "Warning", 823// JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, 824// options, options[0]); 825// return foo == 0; 826 return true; // Used for prompts like confirming machine's rsa finger print 827 } 828 829 private String getPwdToThirdParty(String target) throws ExecException { 830 String pwd = null; 831 if (target == null || target.trim().length() == 0) 832 return null; 833 //check if target is a ssh server 834 ExecInterface temp = ExecFactory.getExecObject(target); 835 if(temp instanceof SshExec){ 836 pwd = SshSession.getPwdToThirdParty(target); 837 } 838 // For grid servers we are currently supporting 839 // grid auth only thro' existing proxy. so return null 840 return pwd; 841 } 842 843 844 845}