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}