diff options
author | Brian Hinz <bphinz@users.sourceforge.net> | 2012-04-05 03:37:50 +0000 |
---|---|---|
committer | Brian Hinz <bphinz@users.sourceforge.net> | 2012-04-05 03:37:50 +0000 |
commit | cf0e312b59e1c0afb4996188afafc8835eec191d (patch) | |
tree | 4a644800acad93b144100955d380991efa3faf1e /java/com/jcraft | |
parent | b3ea375488ddb5ca8f9e90f7be95a269571262ad (diff) | |
download | tigervnc-cf0e312b59e1c0afb4996188afafc8835eec191d.tar.gz tigervnc-cf0e312b59e1c0afb4996188afafc8835eec191d.zip |
adds experimental support for SSH tunneling to the Java client. Has not been tested with large desktop sizes yet. CMakeLists.txt needs some rework.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4882 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'java/com/jcraft')
110 files changed, 19708 insertions, 0 deletions
diff --git a/java/com/jcraft/jsch/Buffer.java b/java/com/jcraft/jsch/Buffer.java new file mode 100644 index 00000000..a3135a15 --- /dev/null +++ b/java/com/jcraft/jsch/Buffer.java @@ -0,0 +1,254 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class Buffer{ + final byte[] tmp=new byte[4]; + byte[] buffer; + int index; + int s; + public Buffer(int size){ + buffer=new byte[size]; + index=0; + s=0; + } + public Buffer(byte[] buffer){ + this.buffer=buffer; + index=0; + s=0; + } + public Buffer(){ this(1024*10*2); } + public void putByte(byte foo){ + buffer[index++]=foo; + } + public void putByte(byte[] foo) { + putByte(foo, 0, foo.length); + } + public void putByte(byte[] foo, int begin, int length) { + System.arraycopy(foo, begin, buffer, index, length); + index+=length; + } + public void putString(byte[] foo){ + putString(foo, 0, foo.length); + } + public void putString(byte[] foo, int begin, int length) { + putInt(length); + putByte(foo, begin, length); + } + public void putInt(int val) { + tmp[0]=(byte)(val >>> 24); + tmp[1]=(byte)(val >>> 16); + tmp[2]=(byte)(val >>> 8); + tmp[3]=(byte)(val); + System.arraycopy(tmp, 0, buffer, index, 4); + index+=4; + } + public void putLong(long val) { + tmp[0]=(byte)(val >>> 56); + tmp[1]=(byte)(val >>> 48); + tmp[2]=(byte)(val >>> 40); + tmp[3]=(byte)(val >>> 32); + System.arraycopy(tmp, 0, buffer, index, 4); + tmp[0]=(byte)(val >>> 24); + tmp[1]=(byte)(val >>> 16); + tmp[2]=(byte)(val >>> 8); + tmp[3]=(byte)(val); + System.arraycopy(tmp, 0, buffer, index+4, 4); + index+=8; + } + void skip(int n) { + index+=n; + } + void putPad(int n) { + while(n>0){ + buffer[index++]=(byte)0; + n--; + } + } + public void putMPInt(byte[] foo){ + int i=foo.length; + if((foo[0]&0x80)!=0){ + i++; + putInt(i); + putByte((byte)0); + } + else{ + putInt(i); + } + putByte(foo); + } + public int getLength(){ + return index-s; + } + public int getOffSet(){ + return s; + } + public void setOffSet(int s){ + this.s=s; + } + public long getLong(){ + long foo = getInt()&0xffffffffL; + foo = ((foo<<32)) | (getInt()&0xffffffffL); + return foo; + } + public int getInt(){ + int foo = getShort(); + foo = ((foo<<16)&0xffff0000) | (getShort()&0xffff); + return foo; + } + public long getUInt(){ + long foo = 0L; + long bar = 0L; + foo = getByte(); + foo = ((foo<<8)&0xff00)|(getByte()&0xff); + bar = getByte(); + bar = ((bar<<8)&0xff00)|(getByte()&0xff); + foo = ((foo<<16)&0xffff0000) | (bar&0xffff); + return foo; + } + int getShort() { + int foo = getByte(); + foo = ((foo<<8)&0xff00)|(getByte()&0xff); + return foo; + } + public int getByte() { + return (buffer[s++]&0xff); + } + public void getByte(byte[] foo) { + getByte(foo, 0, foo.length); + } + void getByte(byte[] foo, int start, int len) { + System.arraycopy(buffer, s, foo, start, len); + s+=len; + } + public int getByte(int len) { + int foo=s; + s+=len; + return foo; + } + public byte[] getMPInt() { + int i=getInt(); // uint32 + if(i<0 || // bigger than 0x7fffffff + i>8*1024){ + // TODO: an exception should be thrown. + i = 8*1024; // the session will be broken, but working around OOME. + } + byte[] foo=new byte[i]; + getByte(foo, 0, i); + return foo; + } + public byte[] getMPIntBits() { + int bits=getInt(); + int bytes=(bits+7)/8; + byte[] foo=new byte[bytes]; + getByte(foo, 0, bytes); + if((foo[0]&0x80)!=0){ + byte[] bar=new byte[foo.length+1]; + bar[0]=0; // ?? + System.arraycopy(foo, 0, bar, 1, foo.length); + foo=bar; + } + return foo; + } + public byte[] getString() { + int i = getInt(); // uint32 + if(i<0 || // bigger than 0x7fffffff + i>256*1024){ + // TODO: an exception should be thrown. + i = 256*1024; // the session will be broken, but working around OOME. + } + byte[] foo=new byte[i]; + getByte(foo, 0, i); + return foo; + } + byte[] getString(int[]start, int[]len) { + int i=getInt(); + start[0]=getByte(i); + len[0]=i; + return buffer; + } + public void reset(){ + index=0; + s=0; + } + public void shift(){ + if(s==0)return; + System.arraycopy(buffer, s, buffer, 0, index-s); + index=index-s; + s=0; + } + void rewind(){ + s=0; + } + + byte getCommand(){ + return buffer[5]; + } + + void checkFreeSize(int n){ + if(buffer.length<index+n){ + byte[] tmp = new byte[buffer.length*2]; + System.arraycopy(buffer, 0, tmp, 0, index); + buffer = tmp; + } + } + +/* + static String[] chars={ + "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f" + }; + static void dump_buffer(){ + int foo; + for(int i=0; i<tmp_buffer_index; i++){ + foo=tmp_buffer[i]&0xff; + System.err.print(chars[(foo>>>4)&0xf]); + System.err.print(chars[foo&0xf]); + if(i%16==15){ + System.err.println(""); + continue; + } + if(i>0 && i%2==1){ + System.err.print(" "); + } + } + System.err.println(""); + } + static void dump(byte[] b){ + dump(b, 0, b.length); + } + static void dump(byte[] b, int s, int l){ + for(int i=s; i<s+l; i++){ + System.err.print(Integer.toHexString(b[i]&0xff)+":"); + } + System.err.println(""); + } +*/ + +} diff --git a/java/com/jcraft/jsch/ChangeLog b/java/com/jcraft/jsch/ChangeLog new file mode 100644 index 00000000..7642c28a --- /dev/null +++ b/java/com/jcraft/jsch/ChangeLog @@ -0,0 +1,794 @@ +ChangeLog of JSch +==================================================================== +Last modified: Thu Feb 2 09:04:04 UTC 2012 + + +Changes since version 0.1.45: +- bugfix: in the agent forwarding mode, "ssh-add -l" on the remote + will freeze. FIXED +- bugfix: requests should not be sent to the closed channel. FIXED +- bugfix: ChannelShell#setAgentForwarding(true) will cause + resource leaks. FIXED +- change: for the efficiency, channel opening will be delayed + in local port forwarding. +- change: added examples/Sudo.java to demonstrate sudo on exec channel. +- change: authentication trials will be failed at 6 failures by the default. +- change: updating copyright messages; 2011 -> 2012 +- feature: added JSch#setIdentityRepository(IdentityRepository irepo) to + integrate with jsch-agent-proxy. + + +Changes since version 0.1.44: +- bugfix: fields referred by multiple threads simultaneously should be + volatile. FIXED +- bugfix: use local window size offered by the remote in sftp put. + FIXED +- bugfix: SftpProgressMonitor#init was not invoked in sftp-put + for input-stream. FIXED +- bugfix: sftp protocol version 3, 4 and 5 should allow only + UTF-8 encoding. FIXED +- bugfix: Channel Subsystem had failed to set X forwarding flag. + FIXED +- bugfix: Channel X11 had leaked some resources. + FIXED +- bugfix: packet compression may break sessions + in some case(transferring deflated data). FIXED +- bugfix: failed to set dev-null for logger + FIXED +- bugfix: even in sftp protocol version 3 session, some sftpd sends data + packets defined in sftp protocol 6 ;-( working around it. FIXED +- bugfix: ChannelSftp file globbing logic had missed + the string "foo\\\*bar" as a pattern. FIXED +- bugfix: sequential accesses to ChannelSftp by multiple threads may + break its I/O channel. + https://bugs.eclipse.org/bugs/show_bug.cgi?id=359184 FIXED +- bugfix: KeyPair.load can not handle private keys cyphered with AES. FIXED +- change: to improve sftp-put performance, send multiple packet at one time. +- change: wait/notify will be used instead of sleep loop + in establishing channel connections. +- change: increasing local window size for sftp get. +- change: updating copyright messages; 2010 -> 2011 +- change: src/com -> src/main/java/com +- feature: key-exchange method "diffie-hellman-group14-sha1" + (RFC4253#section-8.2) +- feature: KeyPair#getPlulicKeyCommment() is added. + + +Changes since version 0.1.43: +- bugfix: hmac-md5-96 and hmac-sha1-96 are broken. FIXED. +- bugfix: working around OOME in parsing broken data from the remote. FIXED. +- bugfix: failed to send very long command for exec channels. FIXED. +- bugfix: in some case, failed to get the response + for remote port-forwarding request. FIXED. +- feature: support for private keys ciphered with aes192-cbc and aes128-cbc. + + +Changes since version 0.1.42: +- bugfix: the remote window size must be in unsigned int. FIXED. +- bugfix: support for EBCDIC environment. FIXED. +- bugfix: data may be written to the closed channel. FIXED. +- bugfix: NPE in closing channels. FIXED. +- bugfix: the private key file may include garbage data before its header. FIXED. +- bugfix: the session down may not be detected during the re-keying process. FIXED. +- change: try keyboard-interactive auth with the given password if UserInfo is not given. +- change: working around the wrong auth method list sent by some SSHD + in the partial auth success. +- change: working around the CPNI-957037 Plain-text Recovery Attack. +- change: in searching for [host]:non-default port in known_hosts, + host:22 should be also checked. +- change: updating copyright messages; 2009 -> 2010 + + +Changes since version 0.1.41: +- bugfix: making exec request during re-keying process will cause + the dead lock for the session. FIXED. + Many thanks for PanLi at Prominic dot NET and www.prominic.net, + US based hosting company. Without their testing JSch with + hundreds of hosts and their bug reports, this problem + was not fixed. +- change: updating copyright messages; 2008 -> 2009 + + +Changes since version 0.1.40: +- bugfix: canceling the remote port-forwarding with the incorrect + bind-address. FIXED. +- bugfix: sftp had missed to close the file in some case. FIXED. +- bugfix: ls(sftp) will throw an exception for the empty directory + in connecting to some sftpd server. FIXED. +- change: dropping the session gently in accepting incorrect packets. +- change: by the default, aes128-ctr will be chosen if it is available + on the local and the remote. +- feature: adding the support for the private key ciphered in AES256. +- feature: new ciphers: aes128-ctr,aes192-ctr,aes256-ctr, + 3des-ctr,arcfour,arcfour128 ,arcfour256 + + +Changes since version 0.1.39: +- bugfix: ProxySOCKS4 had not been functional. FIXED. +- bugfix: NPE at closing the session when it is not opened. FIXED. +- change: JSch#getConfing has become public. + + +Changes since version 0.1.38: +- bugfix: session will be dropped at rekeying. FIXED. +- bugfix: NPE should not be thrown at unexpected session drop. FIXED. +- change: Channel#getSession() may throw JSchExecption. + + +Changes since version 0.1.37: +- bugfix: NPE should not be thrown at unexpected session drop. FIXED. +- bugfix: AIOOBE at Session#connect(). FIXED. +- bugfix: Even if 'true' is given for + Channel#setOutputStream(OutputStream out, boolean dontclose) + as the second paramter, 'out' will be closed. FIXED. +- change: 'examples/Sftp.java' has been modified to demonstrate + ChannelSftp#reaplpath(String path) +- change: setEnv(Hashtable env) for exec and shell channels have been + marked as @deprecated +- feature: setEnv(String name, String value) has been added to exec + and shell channels. +- feature: setEnv(byte[] name, byte[] value) has been added to exec + and shell channels. +- feature: ChannelSftp#realpath(String path) has been added. +- feature: ChannelExec#setCommand(byte[] command) has been added. +- feature: com.jcraft.jsch.ChannelSftp.LsEntry has implemented + java.lang.Comparable +- feature: Session#getServerAliveInterval(), Session#getServerAliveCountMaX() + have been added. + + +Changes since version 0.1.36: +- bugfix: some sftpd will send invalid data in sftp protocol + point of view, and we need to work around such data. FIXED. +- bugfix: the stream forwarding had been broken since 0.1.30. FIXED. +- bugfix: failed to detect 'SSH_MSG_CHANNEL_OPEN_FAILURE'. FIXED. +- bugfix: ChannelSftp will generate the unfavorable absolute pathname + in some case. FIXED. +- bugfix: failed to ignore the invalid public-key file. FIXED. +- change: ignoring empty data sent by 'SSH_MSG_CHANNEL_DATA' and + 'SSH_MSG_CHANNEL_EXTENDED_DATA'. +- change: updating copyright messages; 2007 -> 2008 +- change: build.xml will enable 'javac.debug' option by the default. +- change: added logging messages to IndentityFile and Session class. +- change: followings are deprecated methods, + InputStream ChannelSftp#get(String src, + int mode) + InputStream ChannelSftp#get(String src, + SftpProgressMonitor, + int mode) +- feature: following method is added, + InputStream ChannelSftp#get(String src, + SftpProgressMonitor monitor, + long skip) + + +Changes since version 0.1.35: +- bugfix: ChannelSftp can not handle the local filenames correctly on Windows. FIXED. +- bugfix: '/' must be handled as the file separator on JVM for Windows. FIXED. +- change: the system property + "javax.security.auth.useSubjectCredsOnly" + will be set to "false" for "gssapi-with-mic" + if that property is not given explicitly. +- change: added changes about ChannelSftp#{pwd(), home()} to + ChangeLog; 'Changes since version 0.1.34:' section. + + +Changes since version 0.1.34: +- bugfix: the OutputStream from the channel may make the JVM + lockup in some case. FIXED. + There was a possibility that Channel#connect() may be failed + to initialize its internal without throwing the JSchException. + On such case, the write operation for OutputStream from + that channel will cause the system(JVM) to lock up. +- bugfix: ChannelSftp had problems filename globbing. FIXED. +- bugfix: the message included in SSH_FXP_STATUS must be UTF-8. FIXED. +- change: ChannelSftp supports the filename globbing for + the filename in multi-byte characters. +- change: ChannelSftp will internally handle filenames in UTF-8 encoding. +- change: ChannelSftp#pwd() may throw an SftpException. +- change: ChannelSftp#home() may throw an SftpException. +- feature: following methods have been added in ChannelSftp + String getServerVersion() + String getClientVersion() + void setFilenameEncoding(String encoding) + String getExtension(String key) + +Changes since version 0.1.33: +- bugfix: there had a possibility that the session may be broken + if ciphers for upward/downward streams are different. FIXED. +- bugfix: the authentication method "keyboard-interactive" had + not been tried without UserInfo. FIXED. +- bugfix: ChannelShell#setTerminalMode(byte[] terminal_mode) had + not been functional. FIXED. +- bugfix: the remote port-forwarding to the daemon had been broken + since 0.1.30. FIXED. +- change: the cipher "aes128-cbc" will be used if AES is available. +- change: the interface 'com.jcraft.jsch.ForwardedTCPIPDaemon' has been changed. +- change: the data transfer rate will be improved on some environment. +- feature: ChannelExec can control the size of pty; + ChannelExec#setPtySize(int col, int row, int wp, int hp) is + added. +- feature: the property "CheckCiphers" has been added. + Refer to 'examples/AES.java'. +- feature: Session#setConfig(String key, String value), + JSch#setConfig(String key, String value) have been added. + + +Changes since version 0.1.32: +- bugfix: freeze in 'diffie-hellman-group-exchange-sha1'. FIXED. + By the default, 'diffie-hellman-group1-sha1' will be used + and if you have not chosen 'diffie-hellman-group-exchange-sha1' + explicitly, you don't have to worry about it. +- bugfix: there should be timeout mechanism in opening a socket + for remote port forwarding. FIXED. + At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE' + will be sent to sshd. +- bugfix: there should be timeout mechanism in opening a socket + for X11 forwarding. FIXED. + At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE' + will be sent to sshd. + + +Changes since version 0.1.31: +- bugfix: remote port forwarding will be hanged at its closing. FIXED. +- bugfix: X forwarding channels will be hanged and some resources + will be leaked whenever they are closed. FIXED. +- bugfix: failed to detect "Connection refused". FIXED. +- bugfix: at the failure for keyboard-interactive auth method, + a session will be terminated. FIXED. +- bugfix: due to the cancel for keyboard-interactive auth method, + a session will be terminated. FIXED. +- change: com.jcraft.jsch.jcraft.Compression#uncompress will respect + the argument "start". +- change: "gssapi-with-mic" will choose the default credential. +- feature: Session#setPortForwardingL will return the assigned local + TCP port number; TCP port will be assigned dynamically if lport==0. +- feature: support for SSH_MSG_UNIMPLEMENTED. +- feature: support for PASSWD_CHANGEREQ. + + +Changes since version 0.1.30: +- bugfix: a problem in padding for ciphered private key. + PKCS#5 padding should be used. FIXED. +- bugfix: crash in parsing invalid public key file. FIXED. +- bugfix: a public key may not have a comment. FIXED. +- bugfix: output stream from ChannelSftp#put will hang if it is closed + twice. FIXED. +- feature: agent forwarding. To enable this functionality, + Channel{Exec,Shell,Sftp}#setAgentForwarding(boolean enable) are added. +- feature: ChannelShell#setTerminalMode(byte[] terminal_mode) is added. +- feature: Session#setDaemonThread(boolean true) to run internal threads as + daemon threads. +- feature: an option "PreferredAuthentications" is added. + The default value is "gssapi-with-mic,publickey,keyboard-interactive,password". +- change: if alias-name is given, non-standard TCP port number will not be + saved in 'known_hosts' file. + +Changes since version 0.1.29: +- bugfix: ChannelSftp#cd() will not throw an exception even if + a file is given. FIXED. +- bugfix: ChannelSftp#put() has a bug which will appear in using + on the slow network. FIXED. +- bugfix: ChannelSftp#ls() has a bug which will appear in using + on the slow network. FIXED. +- bugfix: a bug had sneaked in the remote port forwarding. FIXED. +- bugfix: some APIs from JCE have the memory leak on Mac OS X, + so we have re-written the code(com.jcraft.jsch.jcraft.HMAC* + classes) without them. On Mac OS X, such new classes will + be used automatically. FIXED. +- bugfix: the session will be crashed by the long banner message. FIXED. +- change: '/dev/random' will not be referred on Gnu/Linux. +- change: if non-standard TCP port number is used, that number will + be saved in known_hosts file as recent OpenSSH's ssh client does. +- change: Channel#getOutputStream will not use Piped{Output,Input}Stream. +- change: com.jcraft.jsch.HostKeyRepository interface has been + slightly modified. +- feature: Session#setPortForwardingR(String bind_address, ...) has been added. +- feature: the packet compression method 'zlib@openssh.com' has been supported. +- feature: the hashed known_hosts file has been supported. + Refer to 'examples/KnownHosts.java'. +- feature: the authentication method 'gssapi-with-mic' has been + experimentally supported. +- feature: com.jcraft.jsch.Logger interface and + JSch#setLogger(Logger logger) have been added. + Refer to 'examples/Logger.java' for the usage. + + +Changes since version 0.1.28: +- bugfix: ChannelSftp#put will stack in some situations FIXED. +- bugfix: ChannelSftp invoked 'glob_remote' redundantly. FIXED. +- bugfix: ChannelSftp failed to make globbing for some file names. FIXED. +- bugfix: ChannelSftp did not carefully read its input-stream. FIXED. +- bugfix: ChannelSftp#lstat did not try globbing for given path. FIXED. +- bugfix: at closing channel, eof_lcoal and eof_remote did not + become true. FIXED. +- bugfix: IdentityFile did not carefully read file input-streams. FIXED. +- bugfix: KeyPair did not carefully read file input-streams. FIXED. +- bugfix: ProxySOCKS4 did not carefully read file input-streams. FIXED. +- bugfix: ProxySOCKS5 did not carefully read file input-streams. FIXED. +- bugfix: ForwardedTCPIPDaemom may fail in some situation. FIXED. +- bugfix: X forwarding failed to handle the magic-cookie + in some case FIXED. + Thanks to Walter Pfannenmller. +- bugfix: setKnownHosts in KnownHosts.java doesn't read the last + line if linefeed is missing FIXED. + Thanks to Henrik Langos. +- bugfix: With StrictHostKeyChecking set to yes connect() + shouldn't ask. FIXED. + Thanks to Henrik Langos. +- change: Identity#setPassphrase(String passphrase) is replaced with + Identity#setPassphrase(byte[] passphrase). +- change: IdentityFile will clear its internal secrets at finalizing. +- change: KeyPair will clear its internal secrets at finalizing. +- change: KeyPair will clear its internal secrets at finalizing. +- change: MAC#doFinal() is replaced with + MAC#doFile(byte[] buf, int offset) +- change: at TCP socket reading timeout, keep-alive message will be sent + to the remote sshd. To disable this functionality, invoke + explicitly Session.setServerAliveCountMax(0) +- change: PortWatcher stops to use InetAddress.getByAddress(). +- change: in the user authentication, username, password and passphrase + will be encoded in UTF-8. +- change: JSch#addIdentity will check duplicate keys. +- change: SftpException#message has been deleted. +- change: SftpException#getMessage() will return the detailed message. +- feature: IdentityFile#setPassphrase(byte[] passphrase) is added. +- feature: IdentityFile#clear() is added to clear its internal secrets. +- feature: KeyPair#decrypt(byte[] passphrase) is added. +- feature: JSch#addIdentity(String path, byte[] passphrase) is added. +- feature: JSch#getIdentityNames() is added. +- feature: JSch#removeIdentity(String name) is added. +- feature: JSch#removeAllIdentity() is added. +- feature: ChannelSftp#readlink(String path) is added. +- feature: ChannelSftp#getHome() is added. +- feature: Channel#connect(int connectTimeout) is added. +- feature: ChannelShell#setPtyType(String ttype) is added. +- feature: Session#setPassword(byte[] password) is added. +- feature: Session#setHostKeyAlias(String alias) is added. +- feature: KeepAlive is implemented and + Session#setServerAliveInterval(int interval) and + Session#setServerAliveCountMax(int count) are added. +- feature: Session#sendKeepAliveMsg() is added. +- feature: JSchException#getCause() may return a reason. +- feature: SftpException#getCause() may return a reason. +- feature: ChannelExec#setErrStream(OutputStream out, boolean dontclose) + is added. + + +Changes since version 0.1.27: +- bugfix: ChannelSftp#localAbsolutePath did not work correctly. FIXED. +- bugfix: ChannelSftp#chmod did not work for directory. FIXED. +- bugfix: ProxyHTTP had a bug in processing HTTP headers. FIXED. +- bugfix: messages before server's version string should be ignored. FIXED. +- feature: Environment Variable Passing. + + +Changes since version 0.1.26: +- bugfix: there was a session crash bug. That occurrence is rare, but + depends on the thread scheduling. FIXED. +- bugfix: problems in generating remote/local absolute paths on sftp. FIXED. +- bugfix: problems in handling cancel operations for sftp. FIXED. +- bugfix: ChannelX11s were not terminated for a wrong cookie. FIXED. +- bugfix: NoSuchAlgorithmException should be thrown if JCE is not + accessible. FIXED. +- bugfix: ProxyHTTP should check the return code from proxy. FIXED. +- bugfix: server's version string should be checked carefully. FIXED. +- feature: some more improvements on sftp uploading. +- feature: 'getUserName' method is added to Session class. + + +Changes since version 0.1.25: +- bugfix: infinite loop/hang on connection at unexpected error during + key-exchanging operation. FIXED +- bugfix: delays on sftp uploading. FIXED +- bugfix: failed to purge a host-key from its repository in some case. FIXED. +- feature: SOCKS4 proxy + + +Changes since version 0.1.24: +- bugfix: remote port forwarding is not established. FIXED. +- bugfix: failed to parse known_hosts file if it has a long public key blob. + FIXED. +- bugfix: sftp put/get operations keep failing. FIXED. +- bugfix: ChannelShell.setXForwarding always set xforwarding to be true. FIXED. +- change: ChannelShell.setPty is added. +- change: Proxy interface is free from session object. +- change: added examples/ScpToNoneCipher.java to demonstrate NONE Cipher switching. +- feature: added NONE Cipher switching support. +- feature: timeout check will be enabled for proxy connections. + + +Changes since version 0.1.23: +- bugfix: there was resource leak problems in closing local port forwardings. + FIXED. +- bugfix: there was a session crash problems in using aes-cbc cipher. FIXED. +- change: ChannelSession.setPtySize was redefined. +- feature: added SocketFactory for remote port forwarding. + Session.setPortForwardingR(int rport, String host, int lport, + SocketFactory sf) +- feature: added ServerSocketFactory for local port forwarding. + Session.setPortForwardingL(String boundaddress, + int lport, String host, int rport, + ServerSocketFactory ssf) + +Changes since version 0.1.22: +- bugfix: there was a freeze problem at fails on key-exchanging. FIXED. +- bugfix: race-condition forcefully disconnects session in closing channels. + FIXED. + + +Changes since version 0.1.21: +- bugfix: there is a bug in read() method implementation for the + input-stream returned from ChannelSftp.get(). FIXED. +- bugfix: at fail on requesting for the remote port forwarding, + an exception should be thrown. FIXED. +- bugfix: SSH_MSG_CHANNEL_OPEN request for the X11 forwarding should not + be accepted if clients don not expect it. FIXED. +- bugfix: there is a problem in transferring large data(mote than 1GB) + to sshd from recent OpenSSH(3.6.1 or later). FIXED. + For security concerns, those sshd will re-key periodically and + jsch had failed to handle it. +- bugfix: 'exec', 'shell' and 'sftp' channels will fail if the acceptable + packet size by remote sshd is not so large. FIXED. +- bugfix: there are problems in 'InputStream ChannelSftp.get(String)' + and 'OutputStream put(String)'. FIXED. +- feature: added boolean flag 'dontclose' to + * setInputStream(), + * setOutputStream() and + * setExtOutputStream() + methods of Channel class. +- feature: added 'com.jcraft.jsch.ChannelSubsystem' +- feature: allowed to control the compression level in the packet compression. + Refer to 'examples/Compression.java'. +- change: modified 'com/jcraft/jsch/jce/KeyPairGenRSA.java' to be complied + on JDK 1.2. +- change: 'examples/ScpTo.java' and 'examples/ScpFrom.java' will use + 'long' type for the file size instead of 'int'. +- change: 'Identity.getSignature' method will not expect 'session'. +- change: while waiting for socket connection establishment, Thread.join + will be used instead of Thread.sleep. + + +Changes since version 0.1.20: +- known issue: there are problems in 'InputStream ChannelSftp.get(String)' + and 'OutputStream put(String)'. They will be re-implemented + in the next release. +- bugfix: EOF packets should not be sent twice. This bug had crashed + the session on every channel close. FIXED. +- bugfix: at the fail on opening connection to remote sshd, + a misleading exception "invalid server's version string" + had been thrown. FIXED. +- bugfix: fixed a bug in hadling the size of remote channel window. +- bugfix: channels should not be closed even if EOF is received. FIXED. +- bugfix: fixed bugs in file name globbing on sftp. +- change: to transfer packets efficiently, the size of internal buffer + for each channel has been increased. +- change: ChannelSftp.ls will return a vector of + com.jcraft.jsch.ChannelSftp.LsEntry. Sorry for inconveniences. +- feature: added ForwardedTCPIPDaemon. Refer to 'examples/Daemon.java', + which demonstrates to provide network services like inetd. +- feature: ChannelExec.setPty() method to request for assigning pseudo tty. +- feature: added ciphers "aes128-cbc", "aes192-cbc" and "aes256-cbc". + Refer to 'examples/AES.java'. +- feature: local port-forwarding settings can be dynamically deleted + by the bound address. +- feature: added 'Channel.isClosed()'. Channel.getExitStatus() should be + invoked after Channel.isClosed()==true. + + +Changes since version 0.1.19: +- ClassCastException while calling ChannelExec.finalize() method. FIXED. + Thanks to wswiatek at ais dot pl. + + +Changes since version 0.1.18: +- fixed problems related to thread-safety. + Thanks to Eric Meek at cs dot utk dot edu. +- At the lost of the network connectivity to the remote SSHD, clients + connected to the local port were never made aware of the + disconnection. FIXED. +- fixed confusions in handling EOFs from local input stream and + the input stream for remote process. +- 'com.jcraft.jsch.jce.AES128CBC' is added, but it is not be functional in + this release. It will work in the next release. +- Some sshd(Foxit-WAC-Serve) waits for SSH_MSG_NEWKEYS request before + sending it. FIXED. +- fixed a problem in connecting to Cisco Devices. + Thanks to Jason Jeffords at comcast dot net. +- changed the method 'add' of 'HostKeyRepository' interface. +- 'UIKeyborarInteracetive' will ignore empty prompt by sshd. +- added 'sendIgnore()' method to 'Session' class. +- added '-p' for scp command in 'examples/ScpTo.java' to preserve + modification times, access times, and modes from the original file. + + +Changes since version 0.1.17: +- added 'com.jcraft.jsch.HostKeyRepository' interface. + It will allow you to handle host keys in your own repository + (for example, RDB) instead of using 'known_hosts' file. +- added methods to get the finger-print of host keys. + refer to 'examples/KnownHosts.java'. +- improved 'known_hosts' file handling. + - comment lines will be kept. + - SSH1 host keys will be kept. + - each hostname can have multiple host keys. +- fixed a crash bug in processing private keys which have too long key-bits. + + +Changes since version 0.1.16: +- 'com.jcraft.jsch.jce.DHG1' and 'com.jcraft.jsch.jce.DHGEX' are moved to + 'com.jcraft.jsch' package. +- added APIs to handle hostkeys included in 'known_hosts', + JSch.getHostKeys(), + JSch.removeHostKey() +- allowing to set timeout value in opening sockets, + Session.connect(int timeout) + + +Changes since version 0.1.15: +- adding support of setting mtime for remote files on sftp channel. +- setKnownHosts method of JSch class will accept InputStream. +- implementation of Basic password authentication for HTTP proxy. +- fixed a bug in checking which ssh protocol version remote sshd supports +- SSH_MSG_CHANNEL_OPEN_FAILURE will be handled correctly. +- methods in SftpATTRS class has been public. +- working around SIGBLOB bug hidden in older sshd. + + +Changes since version 0.1.14: +- fixed a crash bug in accepting keep-alive messages. +- the parent directory of 'known_hosts' file will be created + if it does not exist. +- the Subsystem channel support was removed. + + +Changes since version 0.1.13: +- added 'setClientVersion' method to Session class. +- fixed hung-up problem on SftpChannel in connecting to + the sshd, which does not support sftp. +- fixed OutOfMemory exception problem in decrypting a private key + with bad passphrase. +- fixed hung-up problem in communicating with the sshd, + whose window size is small. +- RuntimeExceptions will be thrown from jsch APIs. +- SSH_MSG_CHANNEL_SUCCESS and SSH_MSG_CHANNEL_FAILURE requests + have been supported. + + +Changes since version 0.1.12: +- added the partial authentication support. +- allowing to change the passphrase of a private key file + instead of creating a new private key. +- added 'examples/ChangePassphrase.java' +- the interface 'UIKeyboardInteractive' has been modified. + + +Changes since version 0.1.11: +- RSA keypair generation. +- added the method 'getFingerPrint' to KeyPair class, + which will return the finger print of the public key. +- fixed a bug in generating non-ciphered private key. + + +Changes since version 0.1.10: +- fixed a bug in the password authentication, sneaked in + 0.1.9. By this bug, the password authentication had failed every time. + + +Changes since version 0.1.9: +- username and password can be in UTF-8 encoding. +- DSA keypair generation. +- added 'examples/KeyGen.java', which demonstrates + the DSA keypair generation. + + +Changes since version 0.1.8: +- fixed crash problems on the local port forwarding. +- fixed crash problems on the remote port forwarding. +- added setErrStream() and getErrStream() to ChannelExec. +- added keyboard-interactive authentication support. +- modified TripleDESCBC to support IBM's JDK 1.4.1. +- added 'examples/UserAuthKI.java', which demonstrates keyboard-interactive + authentication. + + +Changes since version 0.1.7: +- added APIs for sftp resume downloads and uploads. + The author greatly appreciates + elpernotador(webmaster at unlix dot com dot ar), + who motivated him to hack this functionality. +- 'examples/Sftp.java' demonstrates sftp resume functionality. + Please refer to "put-resume", "put-append", "get-resume" and + "get-append" command. +- added the support of 'window-change' request. +- fixed a freeze bug in 'Inputstream get(String src)' method of 'ChannelSftp' + class. + + +Changes since version 0.1.6: +- added 'int getExitStatus()' method to 'Channel' class. +- fixed crash bugs in closing channels for port forwarding. +- fixed glitches in managing forwarded ports. + + +Changes since version 0.1.5: +- fixed crash bugs in port forwarding. +- modified to use "ssh-rsa" for key-exchanging by the default. +- the port forwarding setting can be canceled dynamically. +- fixed a freeze bug in getting an empty file on sftp channel. + + +Changes since version 0.1.4: +- fixed a bug in managing local window size. + The local window should be consumed by CHANNEL_EXTENDED_DATA packet. +- checking the availability of the ssh session in opening channels. + In some case, ssh session had been freezed. +- java.io.File.separator will be refereed in generating local pathname + on sftp channel. +- absolute local pathname will be handled correctly on sftp channel. + + +Changes since version 0.1.3: +- fixed a serious bug, which had leaked resources related to + ChannelExec. +- added the older SFTP protocol(version 0, 1, 2) support. +- fixed a problem in the authentication step for FSecure's sshd. +- fixed a bug, which had broken Diffie-Hellman key exchange in some case. +- implemented the file name globbing for local files on sftp session. +- fixed a bug in the file name globbing. +- added an interface 'SftpProgressMonitor'. +- modified 'examples/Sftp.java' to demonstrate displaying progress-bar + in transferring files. + + +Changes since version 0.1.2: +- modified 'build.xml' to allow Ant users to compile jsch with debug + support (i.e. line-number tables) by overriding the property + javac.debug on the command line. +- added a property 'StrictHostKeyChecking'. +- added 'UserAuthNone' class to request a list of authentication methods on + remote sshd. +- channels will be managed in each sessions. +- added 'ChannelSubsystem', which allows users to use their own + implementations for subsystems. +- added 'isEOF()' method to 'Channel' class. +- supported key pair files in DOS file format. + + +Changes since version 0.1.1: +- added the file name globbing support on sftp session. +- fixed a bug in the public key authentication. + When there was not a public key in ~/.ssh/, that problem occurred. +- improved the 'setTimeout' method. +- fixed a typo in 'LICENSE.txt' + + +Changes since version 0.1.0: +- added 'rekey' method to 'Session' class for key re-exchanging. +- added 'rekey' and 'compression' command to 'examples/Sftp.java'. +- added new 'get' and 'put' methods to 'ChannelSftp'. + Those methods will operate I/O streams. +- methods in 'ChannelSftp' will throw 'SftpException' +- 'ChannelSftp.Ssh_exp_name' is added for the output of 'ls'. + Thanks to Graeme Vetterlein. +- added 'setTimeout' and 'getTimeout' methods to 'Session' class. +- guess will be done in the algorithm negotiation step. +- FSecure's DSA private key has been supported partially. +- hostkeys will be saved into 'known_hosts' file. +- fixed a bug in 'Util.toBase64' method. +- 'Identity' will reject unrecognized keys. +- 'build.xml' will check if jzlib is available or not. + Thanks to Stefan Bodewig. +- added javadoc target in 'build.xml'. + Thanks to Robert Anderson. + + +Changes since version 0.0.13: +- fixed a bug in connecting to Fsecure's sshd on Windows. +- the license is changed to BSD style. + + +Changes since version 0.0.12: +- fixed a bug in verifying DAS signatures. +- added 'SftpATTR' class, which allow you to get attributes of remote files on + sftp channel, and 'stat', 'lstat' method are added to 'ChannelSftp' class. +- added 'getInputStream' and 'getOutputStream' methods Channel class, which + return passive I/O streams. +- 'setIdentity' method is deleted from 'Session' class and + 'addIdentity' method is added to 'JSch' class +- 'setUserName' method is deleted from 'Session' class and + 'getSession' method of 'JSch' class is changed. +- 'isConnected' method is added to 'Session' class. +- 'UserInfo' interface is changed. + + +Changes since version 0.0.11: +- taking care of remote window size. +- adding 'disconnect' method to 'Channel' and 'Session' classes. +- signal sending support. +- 'mkdir' command for sftp. +- 'fromBase64' method has been moved to Util class and 'toBase64' method has + also been added to that class. +- 'KnownHosts' class for checking host-key in 'known_host' file. +- 'examples/KnownHosts.java' has been added. +- 'setUserName' and 'setPassword' methods have been added to Session class. +- 'UserInfo' interface has been changed. +- The implementation of compression has moved to 'com.jcraft.jsch.jcraft' + package. +- fixed a bug in handling 'SSH_MSG_CHANNEL_REQUET' request. +- fixed a bug in sending multiple requests on a single session. + + +Changes since version 0.0.10: +- Diffie-Hellman key exchange 'diffie-hellman-group1-sha1' is supported. + Refer to 'src/com/jcraft/jsch/jce/DHG1.java'. + Thanks to Mitsugu Kitano, whose feedback was very helpful. +- By the default, 'diffie-hellman-group1-sha1' will be used in the + key exchange step. +- The file attribute on 'SSH File Transfer Protocol' is supported. + Now, we can say JSch supports 'SSH File Transfer Protocol'. +- 'examples/Sftp.java' is updated. + 'chgrp','chown','chmod' commands are supported. + + +Changes since version 0.0.9: +- SSH File Transfer Protocol is supported partially. +- 'examples/Sftp.java' is added. + This example is a tiny sftp command and supports 'cd','put','get','rm',etc. +- 'close' method is added to Channel interface. +- build.xml for examples is added. + Thanks to Ronald Broberg. + + +Changes since version 0.0.8: +- the tunneling through a SOCKS5 proxy is supported. +- 'examples/ScpFrom.java' is added. +- 'com.jcraft.jsch.UserInfo' interface is modified. + + +Changes since version 0.0.7: +- Packet comression is supported. +- 'examples/Compression.java' is added. +- JZlib is included. + + +Changes since version 0.0.6: +- RSA host key is supported. +- RSA public key authentication is supported. + + +Changes since version 0.0.5: +- DSA public key authentication is supported. +- examples/UserAuthPubKey.java is added. +- examples/ScpTo.java is added. + + +Changes since version 0.0.4: +- 3des-cbc is supported. +- hmac-sha1 is supported. +- hmac-md5-96 is supported. +- hmac-sha1-96 is supported. + + +Changes since version 0.0.3: +- port forwarding, similar to the -L option of SSH. +- examples/PortForwardingL.java is added. +- examples/StreamForwarding.java is added. +- examples/Exec.java is renamed as examples/Shell.java +- stream forwarding is added. +- ChannelSftp class is added for implementing filexfer. +- interfaces for jsch users are changed. + + +Changes since version 0.0.2: +- remote exec is supported. +- examples/Exec.java is added. +- build.xml and some scripts for Ant are added. (lbruand) +- Const class is added. (lbruand) + + +Changes since version 0.0.1: +- the tunneling via HTTP proxy is supported. +- port forwarding like option -R of ssh command. + the given port on the remote host will be forwarded to the given host + and port on the local side. diff --git a/java/com/jcraft/jsch/Channel.java b/java/com/jcraft/jsch/Channel.java new file mode 100644 index 00000000..669b575d --- /dev/null +++ b/java/com/jcraft/jsch/Channel.java @@ -0,0 +1,677 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + + +public abstract class Channel implements Runnable{ + + static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91; + static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92; + static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93; + + static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED= 1; + static final int SSH_OPEN_CONNECT_FAILED= 2; + static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE= 3; + static final int SSH_OPEN_RESOURCE_SHORTAGE= 4; + + static int index=0; + private static java.util.Vector pool=new java.util.Vector(); + static Channel getChannel(String type){ + if(type.equals("session")){ + return new ChannelSession(); + } + if(type.equals("shell")){ + return new ChannelShell(); + } + if(type.equals("exec")){ + return new ChannelExec(); + } + if(type.equals("x11")){ + return new ChannelX11(); + } + if(type.equals("auth-agent@openssh.com")){ + return new ChannelAgentForwarding(); + } + if(type.equals("direct-tcpip")){ + return new ChannelDirectTCPIP(); + } + if(type.equals("forwarded-tcpip")){ + return new ChannelForwardedTCPIP(); + } + if(type.equals("sftp")){ + return new ChannelSftp(); + } + if(type.equals("subsystem")){ + return new ChannelSubsystem(); + } + return null; + } + static Channel getChannel(int id, Session session){ + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + Channel c=(Channel)(pool.elementAt(i)); + if(c.id==id && c.session==session) return c; + } + } + return null; + } + static void del(Channel c){ + synchronized(pool){ + pool.removeElement(c); + } + } + + int id; + volatile int recipient=-1; + protected byte[] type=Util.str2byte("foo"); + volatile int lwsize_max=0x100000; + volatile int lwsize=lwsize_max; // local initial window size + volatile int lmpsize=0x4000; // local maximum packet size + + volatile long rwsize=0; // remote initial window size + volatile int rmpsize=0; // remote maximum packet size + + IO io=null; + Thread thread=null; + + volatile boolean eof_local=false; + volatile boolean eof_remote=false; + + volatile boolean close=false; + volatile boolean connected=false; + volatile boolean open_confirmation=false; + + volatile int exitstatus=-1; + + volatile int reply=0; + volatile int connectTimeout=0; + + private Session session; + + int notifyme=0; + + Channel(){ + synchronized(pool){ + id=index++; + pool.addElement(this); + } + } + synchronized void setRecipient(int foo){ + this.recipient=foo; + if(notifyme>0) + notifyAll(); + } + int getRecipient(){ + return recipient; + } + + void init() throws JSchException { + } + + public void connect() throws JSchException{ + connect(0); + } + + public void connect(int connectTimeout) throws JSchException{ + this.connectTimeout=connectTimeout; + try{ + sendChannelOpen(); + start(); + } + catch(Exception e){ + connected=false; + disconnect(); + if(e instanceof JSchException) + throw (JSchException)e; + throw new JSchException(e.toString(), e); + } + } + + public void setXForwarding(boolean foo){ + } + + public void start() throws JSchException{} + + public boolean isEOF() {return eof_remote;} + + void getData(Buffer buf){ + setRecipient(buf.getInt()); + setRemoteWindowSize(buf.getUInt()); + setRemotePacketSize(buf.getInt()); + } + + public void setInputStream(InputStream in){ + io.setInputStream(in, false); + } + public void setInputStream(InputStream in, boolean dontclose){ + io.setInputStream(in, dontclose); + } + public void setOutputStream(OutputStream out){ + io.setOutputStream(out, false); + } + public void setOutputStream(OutputStream out, boolean dontclose){ + io.setOutputStream(out, dontclose); + } + public void setExtOutputStream(OutputStream out){ + io.setExtOutputStream(out, false); + } + public void setExtOutputStream(OutputStream out, boolean dontclose){ + io.setExtOutputStream(out, dontclose); + } + public InputStream getInputStream() throws IOException { + PipedInputStream in= + new MyPipedInputStream( + 32*1024 // this value should be customizable. + ); + io.setOutputStream(new PassiveOutputStream(in), false); + return in; + } + public InputStream getExtInputStream() throws IOException { + PipedInputStream in= + new MyPipedInputStream( + 32*1024 // this value should be customizable. + ); + io.setExtOutputStream(new PassiveOutputStream(in), false); + return in; + } + public OutputStream getOutputStream() throws IOException { + /* + PipedOutputStream out=new PipedOutputStream(); + io.setInputStream(new PassiveInputStream(out + , 32*1024 + ), false); + return out; + */ + + final Channel channel=this; + OutputStream out=new OutputStream(){ + private int dataLen=0; + private Buffer buffer=null; + private Packet packet=null; + private boolean closed=false; + private synchronized void init() throws java.io.IOException{ + buffer=new Buffer(rmpsize); + packet=new Packet(buffer); + + byte[] _buf=buffer.buffer; + if(_buf.length-(14+0)-Session.buffer_margin<=0){ + buffer=null; + packet=null; + throw new IOException("failed to initialize the channel."); + } + + } + byte[] b=new byte[1]; + public void write(int w) throws java.io.IOException{ + b[0]=(byte)w; + write(b, 0, 1); + } + public void write(byte[] buf, int s, int l) throws java.io.IOException{ + if(packet==null){ + init(); + } + + if(closed){ + throw new java.io.IOException("Already closed"); + } + + byte[] _buf=buffer.buffer; + int _bufl=_buf.length; + while(l>0){ + int _l=l; + if(l>_bufl-(14+dataLen)-Session.buffer_margin){ + _l=_bufl-(14+dataLen)-Session.buffer_margin; + } + + if(_l<=0){ + flush(); + continue; + } + + System.arraycopy(buf, s, _buf, 14+dataLen, _l); + dataLen+=_l; + s+=_l; + l-=_l; + } + } + + public void flush() throws java.io.IOException{ + if(closed){ + throw new java.io.IOException("Already closed"); + } + if(dataLen==0) + return; + packet.reset(); + buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buffer.putInt(recipient); + buffer.putInt(dataLen); + buffer.skip(dataLen); + try{ + int foo=dataLen; + dataLen=0; + synchronized(channel){ + if(!channel.close) + getSession().write(packet, channel, foo); + } + } + catch(Exception e){ + close(); + throw new java.io.IOException(e.toString()); + } + + } + public void close() throws java.io.IOException{ + if(packet==null){ + try{ + init(); + } + catch(java.io.IOException e){ + // close should be finished silently. + return; + } + } + if(closed){ + return; + } + if(dataLen>0){ + flush(); + } + channel.eof(); + closed=true; + } + }; + return out; + } + + class MyPipedInputStream extends PipedInputStream{ + MyPipedInputStream() throws IOException{ super(); } + MyPipedInputStream(int size) throws IOException{ + super(); + buffer=new byte[size]; + } + MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); } + MyPipedInputStream(PipedOutputStream out, int size) throws IOException{ + super(out); + buffer=new byte[size]; + } + + /* + * TODO: We should have our own Piped[I/O]Stream implementation. + * Before accepting data, JDK's PipedInputStream will check the existence of + * reader thread, and if it is not alive, the stream will be closed. + * That behavior may cause the problem if multiple threads make access to it. + */ + public synchronized void updateReadSide() throws IOException { + if(available() != 0){ // not empty + return; + } + in = 0; + out = 0; + buffer[in++] = 0; + read(); + } + } + void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; } + void setLocalWindowSize(int foo){ this.lwsize=foo; } + void setLocalPacketSize(int foo){ this.lmpsize=foo; } + synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; } + synchronized void addRemoteWindowSize(int foo){ + this.rwsize+=foo; + if(notifyme>0) + notifyAll(); + } + void setRemotePacketSize(int foo){ this.rmpsize=foo; } + + public void run(){ + } + + void write(byte[] foo) throws IOException { + write(foo, 0, foo.length); + } + void write(byte[] foo, int s, int l) throws IOException { + try{ + io.put(foo, s, l); + }catch(NullPointerException e){} + } + void write_ext(byte[] foo, int s, int l) throws IOException { + try{ + io.put_ext(foo, s, l); + }catch(NullPointerException e){} + } + + void eof_remote(){ + eof_remote=true; + try{ + io.out_close(); + } + catch(NullPointerException e){} + } + + void eof(){ + if(eof_local)return; + eof_local=true; + + try{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF); + buf.putInt(getRecipient()); + synchronized(this){ + if(!close) + getSession().write(packet); + } + } + catch(Exception e){ + //System.err.println("Channel.eof"); + //e.printStackTrace(); + } + /* + if(!isConnected()){ disconnect(); } + */ + } + + /* + http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt + +5.3 Closing a Channel + When a party will no longer send more data to a channel, it SHOULD + send SSH_MSG_CHANNEL_EOF. + + byte SSH_MSG_CHANNEL_EOF + uint32 recipient_channel + + No explicit response is sent to this message. However, the + application may send EOF to whatever is at the other end of the + channel. Note that the channel remains open after this message, and + more data may still be sent in the other direction. This message + does not consume window space and can be sent even if no window space + is available. + + When either party wishes to terminate the channel, it sends + SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST + send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this + message for the channel. The channel is considered closed for a + party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and + the party may then reuse the channel number. A party MAY send + SSH_MSG_CHANNEL_CLOSE without having sent or received + SSH_MSG_CHANNEL_EOF. + + byte SSH_MSG_CHANNEL_CLOSE + uint32 recipient_channel + + This message does not consume window space and can be sent even if no + window space is available. + + It is recommended that any data sent before this message is delivered + to the actual destination, if possible. + */ + + void close(){ + if(close)return; + close=true; + eof_local=eof_remote=true; + + try{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE); + buf.putInt(getRecipient()); + synchronized(this){ + getSession().write(packet); + } + } + catch(Exception e){ + //e.printStackTrace(); + } + } + public boolean isClosed(){ + return close; + } + static void disconnect(Session session){ + Channel[] channels=null; + int count=0; + synchronized(pool){ + channels=new Channel[pool.size()]; + for(int i=0; i<pool.size(); i++){ + try{ + Channel c=((Channel)(pool.elementAt(i))); + if(c.session==session){ + channels[count++]=c; + } + } + catch(Exception e){ + } + } + } + for(int i=0; i<count; i++){ + channels[i].disconnect(); + } + } + + public void disconnect(){ + //System.err.println(this+":disconnect "+io+" "+connected); + //Thread.dumpStack(); + + try{ + + synchronized(this){ + if(!connected){ + return; + } + connected=false; + } + + close(); + + eof_remote=eof_local=true; + + thread=null; + + try{ + if(io!=null){ + io.close(); + } + } + catch(Exception e){ + //e.printStackTrace(); + } + // io=null; + } + finally{ + Channel.del(this); + } + } + + public boolean isConnected(){ + Session _session=this.session; + if(_session!=null){ + return _session.isConnected() && connected; + } + return false; + } + + public void sendSignal(String signal) throws Exception { + RequestSignal request=new RequestSignal(); + request.setSignal(signal); + request.request(getSession(), this); + } + +// public String toString(){ +// return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size; +// } + +/* + class OutputThread extends Thread{ + Channel c; + OutputThread(Channel c){ this.c=c;} + public void run(){c.output_thread();} + } +*/ + + class PassiveInputStream extends MyPipedInputStream{ + PipedOutputStream out; + PassiveInputStream(PipedOutputStream out, int size) throws IOException{ + super(out, size); + this.out=out; + } + PassiveInputStream(PipedOutputStream out) throws IOException{ + super(out); + this.out=out; + } + public void close() throws IOException{ + if(out!=null){ + this.out.close(); + } + out=null; + } + } + class PassiveOutputStream extends PipedOutputStream{ + PassiveOutputStream(PipedInputStream in) throws IOException{ + super(in); + } + } + + void setExitStatus(int status){ exitstatus=status; } + public int getExitStatus(){ return exitstatus; } + + void setSession(Session session){ + this.session=session; + } + + public Session getSession() throws JSchException{ + Session _session=session; + if(_session==null){ + throw new JSchException("session is not available"); + } + return _session; + } + public int getId(){ return id; } + + protected void sendOpenConfirmation() throws Exception{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + buf.putInt(getRecipient()); + buf.putInt(id); + buf.putInt(lwsize); + buf.putInt(lmpsize); + getSession().write(packet); + } + + protected void sendOpenFailure(int reasoncode){ + try{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE); + buf.putInt(getRecipient()); + buf.putInt(reasoncode); + buf.putString(Util.str2byte("open failed")); + buf.putString(Util.empty); + getSession().write(packet); + } + catch(Exception e){ + } + } + + protected Packet genChannelOpenPacket(){ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + // byte SSH_MSG_CHANNEL_OPEN(90) + // string channel type // + // uint32 sender channel // 0 + // uint32 initial window size // 0x100000(65536) + // uint32 maxmum packet size // 0x4000(16384) + packet.reset(); + buf.putByte((byte)90); + buf.putString(this.type); + buf.putInt(this.id); + buf.putInt(this.lwsize); + buf.putInt(this.lmpsize); + return packet; + } + + protected void sendChannelOpen() throws Exception { + Session _session=getSession(); + if(!_session.isConnected()){ + throw new JSchException("session is down"); + } + + Packet packet = genChannelOpenPacket(); + _session.write(packet); + + int retry=10; + long start=System.currentTimeMillis(); + long timeout=connectTimeout; + if(timeout!=0L) retry = 1; + synchronized(this){ + while(this.getRecipient()==-1 && + _session.isConnected() && + retry>0){ + if(timeout>0L){ + if((System.currentTimeMillis()-start)>timeout){ + retry=0; + continue; + } + } + try{ + long t = timeout==0L ? 5000L : timeout; + this.notifyme=1; + wait(t); + } + catch(java.lang.InterruptedException e){ + } + finally{ + this.notifyme=0; + } + retry--; + } + } + if(!_session.isConnected()){ + throw new JSchException("session is down"); + } + if(this.getRecipient()==-1){ // timeout + throw new JSchException("channel is not opened."); + } + if(this.open_confirmation==false){ // SSH_MSG_CHANNEL_OPEN_FAILURE + throw new JSchException("channel is not opened."); + } + connected=true; + } +} diff --git a/java/com/jcraft/jsch/ChannelAgentForwarding.java b/java/com/jcraft/jsch/ChannelAgentForwarding.java new file mode 100644 index 00000000..9788d901 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelAgentForwarding.java @@ -0,0 +1,266 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.util.Vector; + +class ChannelAgentForwarding extends Channel{ + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + + private final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1; + private final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2; + private final byte SSH_AGENTC_RSA_CHALLENGE = 3; + private final byte SSH_AGENT_RSA_RESPONSE = 4; + private final byte SSH_AGENT_FAILURE = 5; + private final byte SSH_AGENT_SUCCESS = 6; + private final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7; + private final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8; + private final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9; + + private final byte SSH2_AGENTC_REQUEST_IDENTITIES=11; + private final byte SSH2_AGENT_IDENTITIES_ANSWER=12; + private final byte SSH2_AGENTC_SIGN_REQUEST=13; + private final byte SSH2_AGENT_SIGN_RESPONSE=14; + private final byte SSH2_AGENTC_ADD_IDENTITY=17; + private final byte SSH2_AGENTC_REMOVE_IDENTITY=18; + private final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES=19; + private final byte SSH2_AGENT_FAILURE=30; + + boolean init=true; + + private Buffer rbuf=null; + private Buffer wbuf=null; + private Packet packet=null; + private Buffer mbuf=null; + + ChannelAgentForwarding(){ + super(); + + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + + type=Util.str2byte("auth-agent@openssh.com"); + rbuf=new Buffer(); + rbuf.reset(); + //wbuf=new Buffer(rmpsize); + //packet=new Packet(wbuf); + mbuf=new Buffer(); + connected=true; + } + + public void run(){ + try{ + sendOpenConfirmation(); + } + catch(Exception e){ + close=true; + disconnect(); + } + } + + void write(byte[] foo, int s, int l) throws java.io.IOException { + + if(packet==null){ + wbuf=new Buffer(rmpsize); + packet=new Packet(wbuf); + } + + rbuf.shift(); + if(rbuf.buffer.length<rbuf.index+l){ + byte[] newbuf=new byte[rbuf.s+l]; + System.arraycopy(rbuf.buffer, 0, newbuf, 0, rbuf.buffer.length); + rbuf.buffer=newbuf; + } + + rbuf.putByte(foo, s, l); + + int mlen=rbuf.getInt(); + if(mlen>rbuf.getLength()){ + rbuf.s-=4; + return; + } + + int typ=rbuf.getByte(); + + Session _session=null; + try{ + _session=getSession(); + } + catch(JSchException e){ + throw new java.io.IOException(e.toString()); + } + + IdentityRepository irepo = _session.jsch.getIdentityRepository(); + UserInfo userinfo=_session.getUserInfo(); + + mbuf.reset(); + + if(typ==SSH2_AGENTC_REQUEST_IDENTITIES){ + mbuf.putByte(SSH2_AGENT_IDENTITIES_ANSWER); + Vector identities = irepo.getIdentities(); + synchronized(identities){ + int count=0; + for(int i=0; i<identities.size(); i++){ + Identity identity=(Identity)(identities.elementAt(i)); + if(identity.getPublicKeyBlob()!=null) + count++; + } + mbuf.putInt(count); + for(int i=0; i<identities.size(); i++){ + Identity identity=(Identity)(identities.elementAt(i)); + byte[] pubkeyblob=identity.getPublicKeyBlob(); + if(pubkeyblob==null) + continue; + mbuf.putString(pubkeyblob); + mbuf.putString(Util.empty); + } + } + } + else if(typ==SSH_AGENTC_REQUEST_RSA_IDENTITIES) { + mbuf.putByte(SSH_AGENT_RSA_IDENTITIES_ANSWER); + mbuf.putInt(0); + } + else if(typ==SSH2_AGENTC_SIGN_REQUEST){ + byte[] blob=rbuf.getString(); + byte[] data=rbuf.getString(); + int flags=rbuf.getInt(); + +// if((flags & 1)!=0){ //SSH_AGENT_OLD_SIGNATURE // old OpenSSH 2.0, 2.1 +// datafellows = SSH_BUG_SIGBLOB; +// } + + Vector identities = irepo.getIdentities(); + Identity identity = null; + synchronized(identities){ + for(int i=0; i<identities.size(); i++){ + Identity _identity=(Identity)(identities.elementAt(i)); + if(_identity.getPublicKeyBlob()==null) + continue; + if(!Util.array_equals(blob, _identity.getPublicKeyBlob())){ + continue; + } + if(_identity.isEncrypted()){ + if(userinfo==null) + continue; + while(_identity.isEncrypted()){ + if(!userinfo.promptPassphrase("Passphrase for "+_identity.getName())){ + break; + } + + String _passphrase=userinfo.getPassphrase(); + if(_passphrase==null){ + break; + } + + byte[] passphrase=Util.str2byte(_passphrase); + try{ + if(_identity.setPassphrase(passphrase)){ + break; + } + } + catch(JSchException e){ + break; + } + } + } + + if(!_identity.isEncrypted()){ + identity=_identity; + break; + } + } + } + + byte[] signature=null; + + if(identity!=null){ + signature=identity.getSignature(data); + } + + if(signature==null){ + mbuf.putByte(SSH2_AGENT_FAILURE); + } + else{ + mbuf.putByte(SSH2_AGENT_SIGN_RESPONSE); + mbuf.putString(signature); + } + } + else if(typ==SSH2_AGENTC_REMOVE_IDENTITY){ + byte[] blob=rbuf.getString(); + irepo.remove(blob); + mbuf.putByte(SSH_AGENT_SUCCESS); + } + else if(typ==SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES){ + mbuf.putByte(SSH_AGENT_SUCCESS); + } + else if(typ==SSH2_AGENTC_REMOVE_ALL_IDENTITIES){ + irepo.removeAll(); + mbuf.putByte(SSH_AGENT_SUCCESS); + } + else if(typ==SSH2_AGENTC_ADD_IDENTITY){ + int fooo = rbuf.getLength(); + byte[] tmp = new byte[fooo]; + rbuf.getByte(tmp); + boolean result = irepo.add(tmp); + mbuf.putByte(result ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); + } + else { + rbuf.skip(rbuf.getLength()-1); + mbuf.putByte(SSH_AGENT_FAILURE); + } + + byte[] response = new byte[mbuf.getLength()]; + mbuf.getByte(response); + send(response); + } + + private void send(byte[] message){ + packet.reset(); + wbuf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + wbuf.putInt(recipient); + wbuf.putInt(4+message.length); + wbuf.putString(message); + + try{ + getSession().write(packet, this, 4+message.length); + } + catch(Exception e){ + } + } + + void eof_remote(){ + super.eof_remote(); + eof(); + } +} diff --git a/java/com/jcraft/jsch/ChannelDirectTCPIP.java b/java/com/jcraft/jsch/ChannelDirectTCPIP.java new file mode 100644 index 00000000..c8af6a2b --- /dev/null +++ b/java/com/jcraft/jsch/ChannelDirectTCPIP.java @@ -0,0 +1,155 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public class ChannelDirectTCPIP extends Channel{ + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + static private final byte[] _type = Util.str2byte("direct-tcpip"); + String host; + int port; + + String originator_IP_address="127.0.0.1"; + int originator_port=0; + + ChannelDirectTCPIP(){ + super(); + type = _type; + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + } + + void init (){ + io=new IO(); + } + + public void connect() throws JSchException{ + try{ + Session _session=getSession(); + if(!_session.isConnected()){ + throw new JSchException("session is down"); + } + + if(io.in!=null){ + thread=new Thread(this); + thread.setName("DirectTCPIP thread "+_session.getHost()); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + catch(Exception e){ + io.close(); + io=null; + Channel.del(this); + if (e instanceof JSchException) { + throw (JSchException) e; + } + } + } + + public void run(){ + + try{ + sendChannelOpen(); + + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + Session _session=getSession(); + int i=0; + + while(isConnected() && + thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14 + -Session.buffer_margin + ); + if(i<=0){ + eof(); + break; + } + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + synchronized(this){ + if(close) + break; + _session.write(packet, this, i); + } + } + } + catch(Exception e){ + } + disconnect(); + } + + public void setInputStream(InputStream in){ + io.setInputStream(in); + } + public void setOutputStream(OutputStream out){ + io.setOutputStream(out); + } + + public void setHost(String host){this.host=host;} + public void setPort(int port){this.port=port;} + public void setOrgIPAddress(String foo){this.originator_IP_address=foo;} + public void setOrgPort(int foo){this.originator_port=foo;} + + protected Packet genChannelOpenPacket(){ + Buffer buf = new Buffer(150); + Packet packet = new Packet(buf); + // byte SSH_MSG_CHANNEL_OPEN(90) + // string channel type // + // uint32 sender channel // 0 + // uint32 initial window size // 0x100000(65536) + // uint32 maxmum packet size // 0x4000(16384) + packet.reset(); + buf.putByte((byte)90); + buf.putString(this.type); + buf.putInt(id); + buf.putInt(lwsize); + buf.putInt(lmpsize); + buf.putString(Util.str2byte(host)); + buf.putInt(port); + buf.putString(Util.str2byte(originator_IP_address)); + buf.putInt(originator_port); + return packet; + } +} diff --git a/java/com/jcraft/jsch/ChannelExec.java b/java/com/jcraft/jsch/ChannelExec.java new file mode 100644 index 00000000..45322b63 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelExec.java @@ -0,0 +1,83 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.*; + +public class ChannelExec extends ChannelSession{ + + byte[] command=new byte[0]; + + public void start() throws JSchException{ + Session _session=getSession(); + try{ + sendRequests(); + Request request=new RequestExec(command); + request.request(_session, this); + } + catch(Exception e){ + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException("ChannelExec", (Throwable)e); + throw new JSchException("ChannelExec"); + } + + if(io.in!=null){ + thread=new Thread(this); + thread.setName("Exec thread "+_session.getHost()); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + public void setCommand(String command){ + this.command=Util.str2byte(command); + } + public void setCommand(byte[] command){ + this.command=command; + } + + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } + + public void setErrStream(java.io.OutputStream out){ + setExtOutputStream(out); + } + public void setErrStream(java.io.OutputStream out, boolean dontclose){ + setExtOutputStream(out, dontclose); + } + public java.io.InputStream getErrStream() throws java.io.IOException { + return getExtInputStream(); + } +} diff --git a/java/com/jcraft/jsch/ChannelForwardedTCPIP.java b/java/com/jcraft/jsch/ChannelForwardedTCPIP.java new file mode 100644 index 00000000..912f1d80 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelForwardedTCPIP.java @@ -0,0 +1,315 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.io.*; + +public class ChannelForwardedTCPIP extends Channel{ + + static java.util.Vector pool=new java.util.Vector(); + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; +//static private final int LOCAL_WINDOW_SIZE_MAX=0x100000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + + static private final int TIMEOUT=10*1000; + + SocketFactory factory=null; + private Socket socket=null; + private ForwardedTCPIPDaemon daemon=null; + String target; + int lport; + int rport; + + ChannelForwardedTCPIP(){ + super(); + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + io=new IO(); + connected=true; + } + + public void run(){ + try{ + if(lport==-1){ + Class c=Class.forName(target); + daemon=(ForwardedTCPIPDaemon)c.newInstance(); + + PipedOutputStream out=new PipedOutputStream(); + io.setInputStream(new PassiveInputStream(out + , 32*1024 + ), false); + + daemon.setChannel(this, getInputStream(), out); + Object[] foo=getPort(getSession(), rport); + daemon.setArg((Object[])foo[3]); + + new Thread(daemon).start(); + } + else{ + socket=(factory==null) ? + Util.createSocket(target, lport, TIMEOUT) : + factory.createSocket(target, lport); + socket.setTcpNoDelay(true); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + } + sendOpenConfirmation(); + } + catch(Exception e){ + sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + close=true; + disconnect(); + return; + } + + thread=Thread.currentThread(); + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + int i=0; + try{ + Session _session = getSession(); + while(thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14 + -Session.buffer_margin + ); + if(i<=0){ + eof(); + break; + } + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + synchronized(this){ + if(close) + break; + _session.write(packet, this, i); + } + } + } + catch(Exception e){ + //System.err.println(e); + } + //thread=null; + //eof(); + disconnect(); + } + + void getData(Buffer buf){ + setRecipient(buf.getInt()); + setRemoteWindowSize(buf.getUInt()); + setRemotePacketSize(buf.getInt()); + byte[] addr=buf.getString(); + int port=buf.getInt(); + byte[] orgaddr=buf.getString(); + int orgport=buf.getInt(); + + /* + System.err.println("addr: "+Util.byte2str(addr)); + System.err.println("port: "+port); + System.err.println("orgaddr: "+Util.byte2str(orgaddr)); + System.err.println("orgport: "+orgport); + */ + + Session _session=null; + try{ + _session=getSession(); + } + catch(JSchException e){ + // session has been already down. + } + + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + Object[] foo=(Object[])(pool.elementAt(i)); + if(foo[0]!=_session) continue; + if(((Integer)foo[1]).intValue()!=port) continue; + this.rport=port; + this.target=(String)foo[2]; + if(foo[3]==null || (foo[3] instanceof Object[])){ this.lport=-1; } + else{ this.lport=((Integer)foo[3]).intValue(); } + if(foo.length>=6){ + this.factory=((SocketFactory)foo[5]); + } + break; + } + if(target==null){ + //System.err.println("??"); + } + } + } + + static Object[] getPort(Session session, int rport){ + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + Object[] bar=(Object[])(pool.elementAt(i)); + if(bar[0]!=session) continue; + if(((Integer)bar[1]).intValue()!=rport) continue; + return bar; + } + return null; + } + } + + static String[] getPortForwarding(Session session){ + java.util.Vector foo=new java.util.Vector(); + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + Object[] bar=(Object[])(pool.elementAt(i)); + if(bar[0]!=session) continue; + if(bar[3]==null){ foo.addElement(bar[1]+":"+bar[2]+":"); } + else{ foo.addElement(bar[1]+":"+bar[2]+":"+bar[3]); } + } + } + String[] bar=new String[foo.size()]; + for(int i=0; i<foo.size(); i++){ + bar[i]=(String)(foo.elementAt(i)); + } + return bar; + } + + static String normalize(String address){ + if(address==null){ return "localhost"; } + else if(address.length()==0 || address.equals("*")){ return ""; } + else{ return address; } + } + + static void addPort(Session session, String _address_to_bind, int port, String target, int lport, SocketFactory factory) throws JSchException{ + String address_to_bind=normalize(_address_to_bind); + synchronized(pool){ + if(getPort(session, port)!=null){ + throw new JSchException("PortForwardingR: remote port "+port+" is already registered."); + } + Object[] foo=new Object[6]; + foo[0]=session; foo[1]=new Integer(port); + foo[2]=target; foo[3]=new Integer(lport); + foo[4]=address_to_bind; + foo[5]=factory; + pool.addElement(foo); + } + } + static void addPort(Session session, String _address_to_bind, int port, String daemon, Object[] arg) throws JSchException{ + String address_to_bind=normalize(_address_to_bind); + synchronized(pool){ + if(getPort(session, port)!=null){ + throw new JSchException("PortForwardingR: remote port "+port+" is already registered."); + } + Object[] foo=new Object[5]; + foo[0]=session; foo[1]=new Integer(port); + foo[2]=daemon; foo[3]=arg; + foo[4]=address_to_bind; + pool.addElement(foo); + } + } + static void delPort(ChannelForwardedTCPIP c){ + Session _session=null; + try{ + _session=c.getSession(); + } + catch(JSchException e){ + // session has been already down. + } + if(_session!=null) + delPort(_session, c.rport); + } + static void delPort(Session session, int rport){ + delPort(session, null, rport); + } + static void delPort(Session session, String address_to_bind, int rport){ + synchronized(pool){ + Object[] foo=null; + for(int i=0; i<pool.size(); i++){ + Object[] bar=(Object[])(pool.elementAt(i)); + if(bar[0]!=session) continue; + if(((Integer)bar[1]).intValue()!=rport) continue; + foo=bar; + break; + } + if(foo==null)return; + pool.removeElement(foo); + if(address_to_bind==null){ + address_to_bind=(String)foo[4]; + } + if(address_to_bind==null){ + address_to_bind="0.0.0.0"; + } + } + + Buffer buf=new Buffer(100); // ?? + Packet packet=new Packet(buf); + + try{ + // byte SSH_MSG_GLOBAL_REQUEST 80 + // string "cancel-tcpip-forward" + // boolean want_reply + // string address_to_bind (e.g. "127.0.0.1") + // uint32 port number to bind + packet.reset(); + buf.putByte((byte) 80/*SSH_MSG_GLOBAL_REQUEST*/); + buf.putString(Util.str2byte("cancel-tcpip-forward")); + buf.putByte((byte)0); + buf.putString(Util.str2byte(address_to_bind)); + buf.putInt(rport); + session.write(packet); + } + catch(Exception e){ +// throw new JSchException(e.toString()); + } + } + static void delPort(Session session){ + int[] rport=null; + int count=0; + synchronized(pool){ + rport=new int[pool.size()]; + for(int i=0; i<pool.size(); i++){ + Object[] bar=(Object[])(pool.elementAt(i)); + if(bar[0]==session) { + rport[count++]=((Integer)bar[1]).intValue(); + } + } + } + for(int i=0; i<count; i++){ + delPort(session, rport[i]); + } + } + + public int getRemotePort(){return rport;} + void setSocketFactory(SocketFactory factory){ + this.factory=factory; + } +} diff --git a/java/com/jcraft/jsch/ChannelSession.java b/java/com/jcraft/jsch/ChannelSession.java new file mode 100644 index 00000000..ffe217ab --- /dev/null +++ b/java/com/jcraft/jsch/ChannelSession.java @@ -0,0 +1,276 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.*; + +class ChannelSession extends Channel{ + private static byte[] _session=Util.str2byte("session"); + + protected boolean agent_forwarding=false; + protected boolean xforwading=false; + protected Hashtable env=null; + + protected boolean pty=false; + + protected String ttype="vt100"; + protected int tcol=80; + protected int trow=24; + protected int twp=640; + protected int thp=480; + protected byte[] terminal_mode=null; + + ChannelSession(){ + super(); + type=_session; + io=new IO(); + } + + /** + * Enable the agent forwarding. + * + * @param enable + */ + public void setAgentForwarding(boolean enable){ + agent_forwarding=enable; + } + + /** + * Enable the X11 forwarding. + * + * @param enable + * @see RFC4254 6.3.1. Requesting X11 Forwarding + */ + public void setXForwarding(boolean enable){ + xforwading=enable; + } + + /** + * @deprecated Use {@link #setEnv(String, String)} or {@link #setEnv(byte[], byte[])} instead. + * @see #setEnv(String, String) + * @see #setEnv(byte[], byte[]) + */ + public void setEnv(Hashtable env){ + synchronized(this){ + this.env=env; + } + } + + /** + * Set the environment variable. + * If <code>name</code> and <code>value</code> are needed to be passed + * to the remote in your faivorite encoding,use + * {@link #setEnv(byte[], byte[])}. + * + * @param name A name for environment variable. + * @param value A value for environment variable. + * @see RFC4254 6.4 Environment Variable Passing + */ + public void setEnv(String name, String value){ + setEnv(Util.str2byte(name), Util.str2byte(value)); + } + + /** + * Set the environment variable. + * + * @param name A name of environment variable. + * @param value A value of environment variable. + * @see #setEnv(String, String) + * @see RFC4254 6.4 Environment Variable Passing + */ + public void setEnv(byte[] name, byte[] value){ + synchronized(this){ + getEnv().put(name, value); + } + } + + private Hashtable getEnv(){ + if(env==null) + env=new Hashtable(); + return env; + } + + /** + * Allocate a Pseudo-Terminal. + * + * @param enable + * @see RFC4254 6.2. Requesting a Pseudo-Terminal + */ + public void setPty(boolean enable){ + pty=enable; + } + + /** + * Set the terminal mode. + * + * @param terminal_mode + */ + public void setTerminalMode(byte[] terminal_mode){ + this.terminal_mode=terminal_mode; + } + + /** + * Change the window dimension interactively. + * + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + * @see RFC4254 6.7. Window Dimension Change Message + */ + public void setPtySize(int col, int row, int wp, int hp){ + setPtyType(this.ttype, col, row, wp, hp); + if(!pty || !isConnected()){ + return; + } + try{ + RequestWindowChange request=new RequestWindowChange(); + request.setSize(col, row, wp, hp); + request.request(getSession(), this); + } + catch(Exception e){ + //System.err.println("ChannelSessio.setPtySize: "+e); + } + } + + /** + * Set the terminal type. + * This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @see #setPtyType(String, int, int, int, int) + */ + public void setPtyType(String ttype){ + setPtyType(ttype, 80, 24, 640, 480); + } + + /** + * Set the terminal type. + * This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + */ + public void setPtyType(String ttype, int col, int row, int wp, int hp){ + this.ttype=ttype; + this.tcol=col; + this.trow=row; + this.twp=wp; + this.thp=hp; + } + + protected void sendRequests() throws Exception{ + Session _session=getSession(); + Request request; + if(agent_forwarding){ + request=new RequestAgentForwarding(); + request.request(_session, this); + } + + if(xforwading){ + request=new RequestX11(); + request.request(_session, this); + } + + if(pty){ + request=new RequestPtyReq(); + ((RequestPtyReq)request).setTType(ttype); + ((RequestPtyReq)request).setTSize(tcol, trow, twp, thp); + if(terminal_mode!=null){ + ((RequestPtyReq)request).setTerminalMode(terminal_mode); + } + request.request(_session, this); + } + + if(env!=null){ + for(Enumeration _env=env.keys(); _env.hasMoreElements();){ + Object name=_env.nextElement(); + Object value=env.get(name); + request=new RequestEnv(); + ((RequestEnv)request).setEnv(toByteArray(name), + toByteArray(value)); + request.request(_session, this); + } + } + } + + private byte[] toByteArray(Object o){ + if(o instanceof String){ + return Util.str2byte((String)o); + } + return (byte[])o; + } + + public void run(){ + //System.err.println(this+":run >"); + + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + int i=-1; + try{ + while(isConnected() && + thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14 + -Session.buffer_margin + ); + if(i==0)continue; + if(i==-1){ + eof(); + break; + } + if(close)break; + //System.out.println("write: "+i); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + getSession().write(packet, this, i); + } + } + catch(Exception e){ + //System.err.println("# ChannelExec.run"); + //e.printStackTrace(); + } + Thread _thread=thread; + if(_thread!=null){ + synchronized(_thread){ _thread.notifyAll(); } + } + thread=null; + //System.err.println(this+":run <"); + } +} diff --git a/java/com/jcraft/jsch/ChannelSftp.java b/java/com/jcraft/jsch/ChannelSftp.java new file mode 100644 index 00000000..66aec550 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelSftp.java @@ -0,0 +1,2651 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +import java.util.Vector; + +public class ChannelSftp extends ChannelSession{ + + static private final int LOCAL_MAXIMUM_PACKET_SIZE=32*1024; + static private final int LOCAL_WINDOW_SIZE_MAX=(64*LOCAL_MAXIMUM_PACKET_SIZE); + + private static final byte SSH_FXP_INIT= 1; + private static final byte SSH_FXP_VERSION= 2; + private static final byte SSH_FXP_OPEN= 3; + private static final byte SSH_FXP_CLOSE= 4; + private static final byte SSH_FXP_READ= 5; + private static final byte SSH_FXP_WRITE= 6; + private static final byte SSH_FXP_LSTAT= 7; + private static final byte SSH_FXP_FSTAT= 8; + private static final byte SSH_FXP_SETSTAT= 9; + private static final byte SSH_FXP_FSETSTAT= 10; + private static final byte SSH_FXP_OPENDIR= 11; + private static final byte SSH_FXP_READDIR= 12; + private static final byte SSH_FXP_REMOVE= 13; + private static final byte SSH_FXP_MKDIR= 14; + private static final byte SSH_FXP_RMDIR= 15; + private static final byte SSH_FXP_REALPATH= 16; + private static final byte SSH_FXP_STAT= 17; + private static final byte SSH_FXP_RENAME= 18; + private static final byte SSH_FXP_READLINK= 19; + private static final byte SSH_FXP_SYMLINK= 20; + private static final byte SSH_FXP_STATUS= 101; + private static final byte SSH_FXP_HANDLE= 102; + private static final byte SSH_FXP_DATA= 103; + private static final byte SSH_FXP_NAME= 104; + private static final byte SSH_FXP_ATTRS= 105; + private static final byte SSH_FXP_EXTENDED= (byte)200; + private static final byte SSH_FXP_EXTENDED_REPLY= (byte)201; + + // pflags + private static final int SSH_FXF_READ= 0x00000001; + private static final int SSH_FXF_WRITE= 0x00000002; + private static final int SSH_FXF_APPEND= 0x00000004; + private static final int SSH_FXF_CREAT= 0x00000008; + private static final int SSH_FXF_TRUNC= 0x00000010; + private static final int SSH_FXF_EXCL= 0x00000020; + + private static final int SSH_FILEXFER_ATTR_SIZE= 0x00000001; + private static final int SSH_FILEXFER_ATTR_UIDGID= 0x00000002; + private static final int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004; + private static final int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008; + private static final int SSH_FILEXFER_ATTR_EXTENDED= 0x80000000; + + public static final int SSH_FX_OK= 0; + public static final int SSH_FX_EOF= 1; + public static final int SSH_FX_NO_SUCH_FILE= 2; + public static final int SSH_FX_PERMISSION_DENIED= 3; + public static final int SSH_FX_FAILURE= 4; + public static final int SSH_FX_BAD_MESSAGE= 5; + public static final int SSH_FX_NO_CONNECTION= 6; + public static final int SSH_FX_CONNECTION_LOST= 7; + public static final int SSH_FX_OP_UNSUPPORTED= 8; +/* + SSH_FX_OK + Indicates successful completion of the operation. + SSH_FX_EOF + indicates end-of-file condition; for SSH_FX_READ it means that no + more data is available in the file, and for SSH_FX_READDIR it + indicates that no more files are contained in the directory. + SSH_FX_NO_SUCH_FILE + is returned when a reference is made to a file which should exist + but doesn't. + SSH_FX_PERMISSION_DENIED + is returned when the authenticated user does not have sufficient + permissions to perform the operation. + SSH_FX_FAILURE + is a generic catch-all error message; it should be returned if an + error occurs for which there is no more specific error code + defined. + SSH_FX_BAD_MESSAGE + may be returned if a badly formatted packet or protocol + incompatibility is detected. + SSH_FX_NO_CONNECTION + is a pseudo-error which indicates that the client has no + connection to the server (it can only be generated locally by the + client, and MUST NOT be returned by servers). + SSH_FX_CONNECTION_LOST + is a pseudo-error which indicates that the connection to the + server has been lost (it can only be generated locally by the + client, and MUST NOT be returned by servers). + SSH_FX_OP_UNSUPPORTED + indicates that an attempt was made to perform an operation which + is not supported for the server (it may be generated locally by + the client if e.g. the version number exchange indicates that a + required feature is not supported by the server, or it may be + returned by the server if the server does not implement an + operation). +*/ + private static final int MAX_MSG_LENGTH = 256* 1024; + + public static final int OVERWRITE=0; + public static final int RESUME=1; + public static final int APPEND=2; + + private boolean interactive=false; + private int seq=1; + private int[] ackid=new int[1]; + + private Buffer buf; + private Packet packet; + + // The followings will be used in file uploading. + private Buffer obuf; + private Packet opacket; + + private int client_version=3; + private int server_version=3; + private String version=String.valueOf(client_version); + + private java.util.Hashtable extensions=null; + private InputStream io_in=null; + +/* +10. Changes from previous protocol versions + The SSH File Transfer Protocol has changed over time, before it's + standardization. The following is a description of the incompatible + changes between different versions. +10.1 Changes between versions 3 and 2 + o The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added. + o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added. + o The SSH_FXP_STATUS message was changed to include fields `error + message' and `language tag'. +10.2 Changes between versions 2 and 1 + o The SSH_FXP_RENAME message was added. +10.3 Changes between versions 1 and 0 + o Implementation changes, no actual protocol changes. +*/ + + private static final String file_separator=java.io.File.separator; + private static final char file_separatorc=java.io.File.separatorChar; + private static boolean fs_is_bs=(byte)java.io.File.separatorChar == '\\'; + + private String cwd; + private String home; + private String lcwd; + + private static final String UTF8="UTF-8"; + private String fEncoding=UTF8; + private boolean fEncoding_is_utf8=true; + + private RequestQueue rq = new RequestQueue(10); + public void setBulkRequests(int bulk_requests) throws JSchException { + if(bulk_requests>0) + rq = new RequestQueue(bulk_requests); + else + throw new JSchException("setBulkRequests: "+ + bulk_requests+" must be greater than 0."); + } + public int getBulkRequests(){ + return rq.size(); + } + + ChannelSftp(){ + super(); + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + } + + void init(){ + } + + public void start() throws JSchException{ + try{ + + PipedOutputStream pos=new PipedOutputStream(); + io.setOutputStream(pos); + PipedInputStream pis=new MyPipedInputStream(pos, rmpsize); + io.setInputStream(pis); + + io_in=io.in; + + if(io_in==null){ + throw new JSchException("channel is down"); + } + + Request request=new RequestSftp(); + request.request(getSession(), this); + + /* + System.err.println("lmpsize: "+lmpsize); + System.err.println("lwsize: "+lwsize); + System.err.println("rmpsize: "+rmpsize); + System.err.println("rwsize: "+rwsize); + */ + + buf=new Buffer(lmpsize); + packet=new Packet(buf); + + obuf=new Buffer(rmpsize); + opacket=new Packet(obuf); + + int i=0; + int length; + int type; + byte[] str; + + // send SSH_FXP_INIT + sendINIT(); + + // receive SSH_FXP_VERSION + Header header=new Header(); + header=header(buf, header); + length=header.length; + if(length > MAX_MSG_LENGTH){ + throw new SftpException(SSH_FX_FAILURE, + "Received message is too long: " + length); + } + type=header.type; // 2 -> SSH_FXP_VERSION + server_version=header.rid; + //System.err.println("SFTP protocol server-version="+server_version); + if(length>0){ + extensions=new java.util.Hashtable(); + // extension data + fill(buf, length); + byte[] extension_name=null; + byte[] extension_data=null; + while(length>0){ + extension_name=buf.getString(); + length-=(4+extension_name.length); + extension_data=buf.getString(); + length-=(4+extension_data.length); + extensions.put(Util.byte2str(extension_name), + Util.byte2str(extension_data)); + } + } + + lcwd=new File(".").getCanonicalPath(); + } + catch(Exception e){ + //System.err.println(e); + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + public void quit(){ disconnect();} + public void exit(){ disconnect();} + public void lcd(String path) throws SftpException{ + path=localAbsolutePath(path); + if((new File(path)).isDirectory()){ + try{ + path=(new File(path)).getCanonicalPath(); + } + catch(Exception e){} + lcwd=path; + return; + } + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory"); + } + + public void cd(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + path=isUnique(path); + + byte[] str=_realpath(path); + SftpATTRS attr=_stat(str); + + if((attr.getFlags()&SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS)==0){ + throw new SftpException(SSH_FX_FAILURE, + "Can't change directory: "+path); + } + if(!attr.isDir()){ + throw new SftpException(SSH_FX_FAILURE, + "Can't change directory: "+path); + } + + setCwd(Util.byte2str(str, fEncoding)); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void put(String src, String dst) throws SftpException{ + put(src, dst, null, OVERWRITE); + } + public void put(String src, String dst, int mode) throws SftpException{ + put(src, dst, null, mode); + } + public void put(String src, String dst, + SftpProgressMonitor monitor) throws SftpException{ + put(src, dst, monitor, OVERWRITE); + } + public void put(String src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=localAbsolutePath(src); + dst=remoteAbsolutePath(dst); + + Vector v=glob_remote(dst); + int vsize=v.size(); + if(vsize!=1){ + if(vsize==0){ + if(isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst=Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + else{ + dst=(String)(v.elementAt(0)); + } + + boolean isRemoteDir=isRemoteDir(dst); + + v=glob_local(src); + vsize=v.size(); + + StringBuffer dstsb=null; + if(isRemoteDir){ + if(!dst.endsWith("/")){ + dst+="/"; + } + dstsb=new StringBuffer(dst); + } + else if(vsize>1){ + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but the destination is missing or a file."); + } + + for(int j=0; j<vsize; j++){ + String _src=(String)(v.elementAt(j)); + String _dst=null; + if(isRemoteDir){ + int i=_src.lastIndexOf(file_separatorc); + if(fs_is_bs){ + int ii=_src.lastIndexOf('/'); + if(ii!=-1 && ii>i) + i=ii; + } + if(i==-1) dstsb.append(_src); + else dstsb.append(_src.substring(i + 1)); + _dst=dstsb.toString(); + dstsb.delete(dst.length(), _dst.length()); + } + else{ + _dst=dst; + } + //System.err.println("_dst "+_dst); + + long size_of_dst=0; + if(mode==RESUME){ + try{ + SftpATTRS attr=_stat(_dst); + size_of_dst=attr.getSize(); + } + catch(Exception eee){ + //System.err.println(eee); + } + long size_of_src=new File(_src).length(); + if(size_of_src<size_of_dst){ + throw new SftpException(SSH_FX_FAILURE, + "failed to resume for "+_dst); + } + if(size_of_src==size_of_dst){ + return; + } + } + + if(monitor!=null){ + monitor.init(SftpProgressMonitor.PUT, _src, _dst, + (new File(_src)).length()); + if(mode==RESUME){ + monitor.count(size_of_dst); + } + } + FileInputStream fis=null; + try{ + fis=new FileInputStream(_src); + _put(fis, _dst, monitor, mode); + } + finally{ + if(fis!=null) { + fis.close(); + } + } + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, e.toString()); + } + } + public void put(InputStream src, String dst) throws SftpException{ + put(src, dst, null, OVERWRITE); + } + public void put(InputStream src, String dst, int mode) throws SftpException{ + put(src, dst, null, mode); + } + public void put(InputStream src, String dst, + SftpProgressMonitor monitor) throws SftpException{ + put(src, dst, monitor, OVERWRITE); + } + public void put(InputStream src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + dst=remoteAbsolutePath(dst); + + Vector v=glob_remote(dst); + int vsize=v.size(); + if(vsize!=1){ + if(vsize==0){ + if(isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst=Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + else{ + dst=(String)(v.elementAt(0)); + } + + if(isRemoteDir(dst)){ + throw new SftpException(SSH_FX_FAILURE, dst+" is a directory"); + } + + if(monitor!=null){ + monitor.init(SftpProgressMonitor.PUT, + "-", dst, + SftpProgressMonitor.UNKNOWN_SIZE); + } + + _put(src, dst, monitor, mode); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, e.toString()); + } + } + + public void _put(InputStream src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + byte[] dstb=Util.str2byte(dst, fEncoding); + long skip=0; + if(mode==RESUME || mode==APPEND){ + try{ + SftpATTRS attr=_stat(dstb); + skip=attr.getSize(); + } + catch(Exception eee){ + //System.err.println(eee); + } + } + if(mode==RESUME && skip>0){ + long skipped=src.skip(skip); + if(skipped<skip){ + throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+dst); + } + } + + if(mode==OVERWRITE){ sendOPENW(dstb); } + else{ sendOPENA(dstb); } + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, "invalid type="+type); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + byte[] handle=buf.getString(); // handle + byte[] data=null; + + boolean dontcopy=true; + + if(!dontcopy){ // This case will not work anymore. + data=new byte[obuf.buffer.length + -(5+13+21+handle.length+Session.buffer_margin + ) + ]; + } + + long offset=0; + if(mode==RESUME || mode==APPEND){ + offset+=skip; + } + + int startid=seq; + int ackcount=0; + int _s=0; + int _datalen=0; + + if(!dontcopy){ // This case will not work anymore. + _datalen=data.length; + } + else{ + data=obuf.buffer; + _s=5+13+21+handle.length; + _datalen=obuf.buffer.length-_s-Session.buffer_margin; + } + + int bulk_requests = rq.size(); + + while(true){ + int nread=0; + int count=0; + int s=_s; + int datalen=_datalen; + + do{ + nread=src.read(data, s, datalen); + if(nread>0){ + s+=nread; + datalen-=nread; + count+=nread; + } + } + while(datalen>0 && nread>0); + if(count<=0)break; + + int foo=count; + while(foo>0){ + if((seq-1)==startid || + ((seq-startid)-ackcount)>=bulk_requests){ + while(((seq-startid)-ackcount)>=bulk_requests){ + if(this.rwsize>=foo) break; + if(checkStatus(ackid, header)){ + int _ackid = ackid[0]; + if(startid>_ackid || _ackid>seq-1){ + if(_ackid==seq){ + System.err.println("ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid); + } + else{ + throw new SftpException(SSH_FX_FAILURE, "ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid); + } + } + ackcount++; + } + else{ + break; + } + } + } + foo-=sendWRITE(handle, offset, data, 0, foo); + } + offset+=count; + if(monitor!=null && !monitor.count(count)){ + break; + } + } + int _ackcount=seq-startid; + while(_ackcount>ackcount){ + if(!checkStatus(null, header)){ + break; + } + ackcount++; + } + if(monitor!=null)monitor.end(); + _sendCLOSE(handle, header); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, e.toString()); + } + } + + public OutputStream put(String dst) throws SftpException{ + return put(dst, (SftpProgressMonitor)null, OVERWRITE); + } + public OutputStream put(String dst, final int mode) throws SftpException{ + return put(dst, (SftpProgressMonitor)null, mode); + } + public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) throws SftpException{ + return put(dst, monitor, mode, 0); + } + public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + dst=remoteAbsolutePath(dst); + dst=isUnique(dst); + + if(isRemoteDir(dst)){ + throw new SftpException(SSH_FX_FAILURE, dst+" is a directory"); + } + + byte[] dstb=Util.str2byte(dst, fEncoding); + + long skip=0; + if(mode==RESUME || mode==APPEND){ + try{ + SftpATTRS attr=_stat(dstb); + skip=attr.getSize(); + } + catch(Exception eee){ + //System.err.println(eee); + } + } + + if(mode==OVERWRITE){ sendOPENW(dstb); } + else{ sendOPENA(dstb); } + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + final byte[] handle=buf.getString(); // handle + + if(mode==RESUME || mode==APPEND){ + offset+=skip; + } + + final long[] _offset=new long[1]; + _offset[0]=offset; + OutputStream out = new OutputStream(){ + private boolean init=true; + private boolean isClosed=false; + private int[] ackid=new int[1]; + private int startid=0; + private int _ackid=0; + private int ackcount=0; + private int writecount=0; + private Header header=new Header(); + + public void write(byte[] d) throws java.io.IOException{ + write(d, 0, d.length); + } + + public void write(byte[] d, int s, int len) throws java.io.IOException{ + if(init){ + startid=seq; + _ackid=seq; + init=false; + } + + if(isClosed){ + throw new IOException("stream already closed"); + } + + try{ + int _len=len; + while(_len>0){ + int sent=sendWRITE(handle, _offset[0], d, s, _len); + writecount++; + _offset[0]+=sent; + s+=sent; + _len-=sent; + if((seq-1)==startid || + io_in.available()>=1024){ + while(io_in.available()>0){ + if(checkStatus(ackid, header)){ + _ackid=ackid[0]; + if(startid>_ackid || _ackid>seq-1){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + ackcount++; + } + else{ + break; + } + } + } + } + if(monitor!=null && !monitor.count(len)){ + close(); + throw new IOException("canceled"); + } + } + catch(IOException e){ throw e; } + catch(Exception e){ throw new IOException(e.toString()); } + } + + byte[] _data=new byte[1]; + public void write(int foo) throws java.io.IOException{ + _data[0]=(byte)foo; + write(_data, 0, 1); + } + + public void flush() throws java.io.IOException{ + + if(isClosed){ + throw new IOException("stream already closed"); + } + + if(!init){ + try{ + while(writecount>ackcount){ + if(!checkStatus(null, header)){ + break; + } + ackcount++; + } + } + catch(SftpException e){ + throw new IOException(e.toString()); + } + } + } + + public void close() throws java.io.IOException{ + if(isClosed){ + return; + } + flush(); + if(monitor!=null)monitor.end(); + try{ _sendCLOSE(handle, header); } + catch(IOException e){ throw e; } + catch(Exception e){ + throw new IOException(e.toString()); + } + isClosed=true; + } + }; + return out; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void get(String src, String dst) throws SftpException{ + get(src, dst, null, OVERWRITE); + } + public void get(String src, String dst, + SftpProgressMonitor monitor) throws SftpException{ + get(src, dst, monitor, OVERWRITE); + } + public void get(String src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + // System.out.println("get: "+src+" "+dst); + + boolean _dstExist = false; + String _dst=null; + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + dst=localAbsolutePath(dst); + + Vector v=glob_remote(src); + int vsize=v.size(); + if(vsize==0){ + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file"); + } + + File dstFile=new File(dst); + boolean isDstDir=dstFile.isDirectory(); + StringBuffer dstsb=null; + if(isDstDir){ + if(!dst.endsWith(file_separator)){ + dst+=file_separator; + } + dstsb=new StringBuffer(dst); + } + else if(vsize>1){ + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but destination is missing or a file."); + } + + for(int j=0; j<vsize; j++){ + String _src=(String)(v.elementAt(j)); + SftpATTRS attr=_stat(_src); + if(attr.isDir()){ + throw new SftpException(SSH_FX_FAILURE, + "not supported to get directory "+_src); + } + + _dst=null; + if(isDstDir){ + int i=_src.lastIndexOf('/'); + if(i==-1) dstsb.append(_src); + else dstsb.append(_src.substring(i + 1)); + _dst=dstsb.toString(); + dstsb.delete(dst.length(), _dst.length()); + } + else{ + _dst=dst; + } + + File _dstFile=new File(_dst); + if(mode==RESUME){ + long size_of_src=attr.getSize(); + long size_of_dst=_dstFile.length(); + if(size_of_dst>size_of_src){ + throw new SftpException(SSH_FX_FAILURE, + "failed to resume for "+_dst); + } + if(size_of_dst==size_of_src){ + return; + } + } + + if(monitor!=null){ + monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize()); + if(mode==RESUME){ + monitor.count(_dstFile.length()); + } + } + + FileOutputStream fos=null; + _dstExist = _dstFile.exists(); + try{ + if(mode==OVERWRITE){ + fos=new FileOutputStream(_dst); + } + else{ + fos=new FileOutputStream(_dst, true); // append + } + // System.err.println("_get: "+_src+", "+_dst); + _get(_src, fos, monitor, mode, new File(_dst).length()); + } + finally{ + if(fos!=null){ + fos.close(); + } + } + } + } + catch(Exception e){ + if(!_dstExist && _dst!=null){ + File _dstFile = new File(_dst); + if(_dstFile.exists() && _dstFile.length()==0){ + _dstFile.delete(); + } + } + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + public void get(String src, OutputStream dst) throws SftpException{ + get(src, dst, null, OVERWRITE, 0); + } + public void get(String src, OutputStream dst, + SftpProgressMonitor monitor) throws SftpException{ + get(src, dst, monitor, OVERWRITE, 0); + } + public void get(String src, OutputStream dst, + SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ +//System.err.println("get: "+src+", "+dst); + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + src=isUnique(src); + + if(monitor!=null){ + SftpATTRS attr=_stat(src); + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + if(mode==RESUME){ + monitor.count(skip); + } + } + _get(src, dst, monitor, mode, skip); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + private void _get(String src, OutputStream dst, + SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ + //System.err.println("_get: "+src+", "+dst); + + byte[] srcb=Util.str2byte(src, fEncoding); + try{ + sendOPENR(srcb); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle=buf.getString(); // filename + + long offset=0; + if(mode==RESUME){ + offset+=skip; + } + + int request_max=1; + rq.init(); + long request_offset=offset; + + int request_len = buf.buffer.length-13; + if(server_version==0){ request_len=1024; } + + loop: + while(true){ + + while(rq.count() < request_max){ + sendREAD(handle, request_offset, request_len, rq); + request_offset += request_len; + } + + header=header(buf, header); + length=header.length; + type=header.type; + + RequestQueue.Request rr = rq.get(header.rid); + + if(type==SSH_FXP_STATUS){ + fill(buf, length); + int i=buf.getInt(); + if(i==SSH_FX_EOF){ + break loop; + } + throwStatusError(buf, i); + } + + if(type!=SSH_FXP_DATA){ + break loop; + } + + buf.rewind(); + fill(buf.buffer, 0, 4); length-=4; + int length_of_data = buf.getInt(); // length of data + + /** + Since sftp protocol version 6, "end-of-file" has been defined, + + byte SSH_FXP_DATA + uint32 request-id + string data + bool end-of-file [optional] + + but some sftpd server will send such a field in the sftp protocol 3 ;-( + */ + int optional_data = length - length_of_data; + + int foo = length_of_data; + while(foo>0){ + int bar=foo; + if(bar>buf.buffer.length){ + bar=buf.buffer.length; + } + int data_len = io_in.read(buf.buffer, 0, bar); + if(data_len<0){ + break loop; + } + + dst.write(buf.buffer, 0, data_len); + + offset+=data_len; + foo-=data_len; + + if(monitor!=null){ + if(!monitor.count(data_len)){ + skip(foo); + if(optional_data>0){ + skip(optional_data); + } + break loop; + } + } + + } + //System.err.println("length: "+length); // length should be 0 + + if(optional_data>0){ + skip(optional_data); + } + + if(length_of_data<rr.length){ // + rq.cancel(header, buf); + sendREAD(handle, rr.offset+length_of_data, (int)(rr.length-length_of_data), rq); + request_offset=rr.offset+rr.length; + } + + if(request_max < rq.size()){ + request_max++; + } + } + dst.flush(); + + if(monitor!=null)monitor.end(); + + rq.cancel(header, buf); + + _sendCLOSE(handle, header); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + + private class RequestQueue { + class Request { + int id; + long offset; + long length; + } + + Request[] rrq=null; + int head, count; + RequestQueue(int size){ + rrq = new Request[size]; + for(int i=0; i<rrq.length; i++){ + rrq[i]=new Request(); + } + init(); + } + + void init(){ + head=count=0; + } + + void add(int id, long offset, int length){ + if(count == 0) head = 0; + int tail = head + count; + if(tail>=rrq.length) tail -= rrq.length; + rrq[tail].id=id; + rrq[tail].offset=offset; + rrq[tail].length=length; + count++; + } + + Request get(int id){ + count -= 1; + int i=head; + head++; + if(head==rrq.length) head=0; + if(rrq[i].id!=id){ + System.err.println("The request is not in order."); + } + rrq[i].id=0; + return rrq[i]; + } + + int count() { + return count; + } + + int size() { + return rrq.length; + } + + void cancel(Header header, Buffer buf) throws IOException { + int _count = count; + for(int i=0; i<_count; i++){ + header=header(buf, header); + int length=header.length; + get(header.rid); + skip(length); + } + } + } + + public InputStream get(String src) throws SftpException{ + return get(src, null, 0L); + } + public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException{ + return get(src, monitor, 0L); + } + + /** + * @deprecated This method will be deleted in the future. + */ + public InputStream get(String src, int mode) throws SftpException{ + return get(src, null, 0L); + } + /** + * @deprecated This method will be deleted in the future. + */ + public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException{ + return get(src, monitor, 0L); + } + public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException{ + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + src=isUnique(src); + + byte[] srcb=Util.str2byte(src, fEncoding); + + SftpATTRS attr=_stat(srcb); + if(monitor!=null){ + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + } + + sendOPENR(srcb); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + final byte[] handle=buf.getString(); // handle + + java.io.InputStream in=new java.io.InputStream(){ + long offset=skip; + boolean closed=false; + int rest_length=0; + byte[] _data=new byte[1]; + byte[] rest_byte=new byte[1024]; + Header header=new Header(); + + public int read() throws java.io.IOException{ + if(closed)return -1; + int i=read(_data, 0, 1); + if (i==-1) { return -1; } + else { + return _data[0]&0xff; + } + } + public int read(byte[] d) throws java.io.IOException{ + if(closed)return -1; + return read(d, 0, d.length); + } + public int read(byte[] d, int s, int len) throws java.io.IOException{ + if(closed)return -1; + if(d==null){throw new NullPointerException();} + if(s<0 || len <0 || s+len>d.length){ + throw new IndexOutOfBoundsException(); + } + if(len==0){ return 0; } + + if(rest_length>0){ + int foo=rest_length; + if(foo>len) foo=len; + System.arraycopy(rest_byte, 0, d, s, foo); + if(foo!=rest_length){ + System.arraycopy(rest_byte, foo, + rest_byte, 0, rest_length-foo); + } + + if(monitor!=null){ + if(!monitor.count(foo)){ + close(); + return -1; + } + } + + rest_length-=foo; + return foo; + } + + if(buf.buffer.length-13<len){ + len=buf.buffer.length-13; + } + if(server_version==0 && len>1024){ + len=1024; + } + + try{sendREAD(handle, offset, len);} + catch(Exception e){ throw new IOException("error"); } + + header=header(buf, header); + rest_length=header.length; + int type=header.type; + int id=header.rid; + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_DATA){ + throw new IOException("error"); + } + if(type==SSH_FXP_STATUS){ + fill(buf, rest_length); + int i=buf.getInt(); + rest_length=0; + if(i==SSH_FX_EOF){ + close(); + return -1; + } + //throwStatusError(buf, i); + throw new IOException("error"); + } + buf.rewind(); + fill(buf.buffer, 0, 4); + int length_of_data = buf.getInt(); rest_length-=4; + + /** + Since sftp protocol version 6, "end-of-file" has been defined, + + byte SSH_FXP_DATA + uint32 request-id + string data + bool end-of-file [optional] + + but some sftpd server will send such a field in the sftp protocol 3 ;-( + */ + int optional_data = rest_length - length_of_data; + + offset += length_of_data; + int foo = length_of_data; + if(foo>0){ + int bar=foo; + if(bar>len){ + bar=len; + } + int i=io_in.read(d, s, bar); + if(i<0){ + return -1; + } + foo-=i; + rest_length=foo; + + if(foo>0){ + if(rest_byte.length<foo){ + rest_byte=new byte[foo]; + } + int _s=0; + int _len=foo; + int j; + while(_len>0){ + j=io_in.read(rest_byte, _s, _len); + if(j<=0)break; + _s+=j; + _len-=j; + } + } + + if(optional_data>0){ + io_in.skip(optional_data); + } + + if(monitor!=null){ + if(!monitor.count(i)){ + close(); + return -1; + } + } + + return i; + } + return 0; // ?? + } + public void close() throws IOException{ + if(closed)return; + closed=true; + if(monitor!=null)monitor.end(); + try{_sendCLOSE(handle, header);} + catch(Exception e){throw new IOException("error");} + } + }; + return in; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public java.util.Vector ls(String path) throws SftpException{ + //System.out.println("ls: "+path); + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + byte[] pattern=null; + java.util.Vector v=new java.util.Vector(); + + int foo=path.lastIndexOf('/'); + String dir=path.substring(0, ((foo==0)?1:foo)); + String _pattern=path.substring(foo+1); + dir=Util.unquote(dir); + + // If pattern has included '*' or '?', we need to convert + // to UTF-8 string before globbing. + byte[][] _pattern_utf8=new byte[1][]; + boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8); + + if(pattern_has_wildcard){ + pattern=_pattern_utf8[0]; + } + else{ + String upath=Util.unquote(path); + //SftpATTRS attr=_lstat(upath); + SftpATTRS attr=_stat(upath); + if(attr.isDir()){ + pattern=null; + dir=upath; + } + else{ + /* + // If we can generage longname by ourself, + // we don't have to use openDIR. + String filename=Util.unquote(_pattern); + String longname=... + v.addElement(new LsEntry(filename, longname, attr)); + return v; + */ + + if(fEncoding_is_utf8){ + pattern=_pattern_utf8[0]; + pattern=Util.unquote(pattern); + } + else{ + _pattern=Util.unquote(_pattern); + pattern=Util.str2byte(_pattern, fEncoding); + } + + } + } + + sendOPENDIR(Util.str2byte(dir, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle=buf.getString(); // handle + + while(true){ + sendREADDIR(handle); + + header=header(buf, header); + length=header.length; + type=header.type; + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + fill(buf, length); + int i=buf.getInt(); + if(i==SSH_FX_EOF) + break; + throwStatusError(buf, i); + } + + buf.rewind(); + fill(buf.buffer, 0, 4); length-=4; + int count=buf.getInt(); + + byte[] str; + int flags; + + buf.reset(); + while(count>0){ + if(length>0){ + buf.shift(); + int j=(buf.buffer.length>(buf.index+length)) ? + length : + (buf.buffer.length-buf.index); + int i=fill(buf.buffer, buf.index, j); + buf.index+=i; + length-=i; + } + byte[] filename=buf.getString(); + byte[] longname=null; + if(server_version<=3){ + longname=buf.getString(); + } + SftpATTRS attrs=SftpATTRS.getATTR(buf); + + boolean find=false; + String f=null; + if(pattern==null){ + find=true; + } + else if(!pattern_has_wildcard){ + find=Util.array_equals(pattern, filename); + } + else{ + byte[] _filename=filename; + if(!fEncoding_is_utf8){ + f=Util.byte2str(_filename, fEncoding); + _filename=Util.str2byte(f, UTF8); + } + find=Util.glob(pattern, _filename); + } + + if(find){ + if(f==null){ + f=Util.byte2str(filename, fEncoding); + } + String l=null; + if(longname==null){ + // TODO: we need to generate long name from attrs + // for the sftp protocol 4(and later). + l=attrs.toString()+" "+f; + } + else{ + l=Util.byte2str(longname, fEncoding); + } + v.addElement(new LsEntry(f, l, attrs)); + } + + count--; + } + } + _sendCLOSE(handle, header); + + /* + if(v.size()==1 && pattern_has_wildcard){ + LsEntry le=(LsEntry)v.elementAt(0); + if(le.getAttrs().isDir()){ + String f=le.getFilename(); + if(isPattern(f)){ + f=Util.quote(f); + } + if(!dir.endsWith("/")){ + dir+="/"; + } + v=null; + return ls(dir+f); + } + } + */ + + return v; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + public String readlink(String path) throws SftpException{ + try{ + if(server_version<3){ + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support symlink operation."); + } + + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + path=isUnique(path); + + sendREADLINK(Util.str2byte(path, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_NAME){ + int count=buf.getInt(); // count + byte[] filename=null; + for(int i=0; i<count; i++){ + filename=buf.getString(); + if(server_version<=3){ + byte[] longname=buf.getString(); + } + SftpATTRS.getATTR(buf); + } + return Util.byte2str(filename, fEncoding); + } + + int i=buf.getInt(); + throwStatusError(buf, i); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + return null; + } + public void symlink(String oldpath, String newpath) throws SftpException{ + if(server_version<3){ + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support symlink operation."); + } + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + oldpath=remoteAbsolutePath(oldpath); + newpath=remoteAbsolutePath(newpath); + + oldpath=isUnique(oldpath); + + if(isPattern(newpath)){ + throw new SftpException(SSH_FX_FAILURE, newpath); + } + newpath=Util.unquote(newpath); + + sendSYMLINK(Util.str2byte(oldpath, fEncoding), + Util.str2byte(newpath, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i=buf.getInt(); + if(i==SSH_FX_OK) return; + throwStatusError(buf, i); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void rename(String oldpath, String newpath) throws SftpException{ + if(server_version<2){ + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support rename operation."); + } + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + oldpath=remoteAbsolutePath(oldpath); + newpath=remoteAbsolutePath(newpath); + + oldpath=isUnique(oldpath); + + Vector v=glob_remote(newpath); + int vsize=v.size(); + if(vsize>=2){ + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + if(vsize==1){ + newpath=(String)(v.elementAt(0)); + } + else{ // vsize==0 + if(isPattern(newpath)) + throw new SftpException(SSH_FX_FAILURE, newpath); + newpath=Util.unquote(newpath); + } + + sendRENAME(Util.str2byte(oldpath, fEncoding), + Util.str2byte(newpath, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i=buf.getInt(); + if(i==SSH_FX_OK) return; + throwStatusError(buf, i); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + public void rm(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + + Header header=new Header(); + + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + sendREMOVE(Util.str2byte(path, fEncoding)); + + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i=buf.getInt(); + if(i!=SSH_FX_OK){ + throwStatusError(buf, i); + } + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + private boolean isRemoteDir(String path){ + try{ + sendSTAT(Util.str2byte(path, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_ATTRS){ + return false; + } + SftpATTRS attr=SftpATTRS.getATTR(buf); + return attr.isDir(); + } + catch(Exception e){} + return false; + } + + public void chgrp(int gid, String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + + SftpATTRS attr=_stat(path); + + attr.setFLAGS(0); + attr.setUIDGID(attr.uid, gid); + _setStat(path, attr); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void chown(int uid, String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + + SftpATTRS attr=_stat(path); + + attr.setFLAGS(0); + attr.setUIDGID(uid, attr.gid); + _setStat(path, attr); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void chmod(int permissions, String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + + SftpATTRS attr=_stat(path); + + attr.setFLAGS(0); + attr.setPERMISSIONS(permissions); + _setStat(path, attr); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void setMtime(String path, int mtime) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + + SftpATTRS attr=_stat(path); + + attr.setFLAGS(0); + attr.setACMODTIME(attr.getATime(), mtime); + _setStat(path, attr); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void rmdir(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + + Header header=new Header(); + + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + sendRMDIR(Util.str2byte(path, fEncoding)); + + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i=buf.getInt(); + if(i!=SSH_FX_OK){ + throwStatusError(buf, i); + } + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void mkdir(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + sendMKDIR(Util.str2byte(path, fEncoding), null); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i=buf.getInt(); + if(i==SSH_FX_OK) return; + throwStatusError(buf, i); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public SftpATTRS stat(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + path=isUnique(path); + + return _stat(path); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + //return null; + } + + private SftpATTRS _stat(byte[] path) throws SftpException{ + try{ + + sendSTAT(path); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_ATTRS){ + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + throw new SftpException(SSH_FX_FAILURE, ""); + } + SftpATTRS attr=SftpATTRS.getATTR(buf); + return attr; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + //return null; + } + + private SftpATTRS _stat(String path) throws SftpException{ + return _stat(Util.str2byte(path, fEncoding)); + } + + public SftpATTRS lstat(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + path=isUnique(path); + + return _lstat(path); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + private SftpATTRS _lstat(String path) throws SftpException{ + try{ + sendLSTAT(Util.str2byte(path, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_ATTRS){ + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + throw new SftpException(SSH_FX_FAILURE, ""); + } + SftpATTRS attr=SftpATTRS.getATTR(buf); + return attr; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + private byte[] _realpath(String path) throws SftpException, IOException, Exception{ + sendREALPATH(Util.str2byte(path, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i; + if(type==SSH_FXP_STATUS){ + i=buf.getInt(); + throwStatusError(buf, i); + } + i=buf.getInt(); // count + + byte[] str=null; + while(i-->0){ + str=buf.getString(); // absolute path; + if(server_version<=3){ + byte[] lname=buf.getString(); // long filename + } + SftpATTRS attr=SftpATTRS.getATTR(buf); // dummy attribute + } + return str; + } + + public void setStat(String path, SftpATTRS attr) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j<vsize; j++){ + path=(String)(v.elementAt(j)); + _setStat(path, attr); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + private void _setStat(String path, SftpATTRS attr) throws SftpException{ + try{ + sendSETSTAT(Util.str2byte(path, fEncoding), attr); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i=buf.getInt(); + if(i!=SSH_FX_OK){ + throwStatusError(buf, i); + } + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public String pwd() throws SftpException{ return getCwd(); } + public String lpwd(){ return lcwd; } + public String version(){ return version; } + public String getHome() throws SftpException { + if(home==null){ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + byte[] _home=_realpath(""); + home=Util.byte2str(_home, fEncoding); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + return home; + } + + private String getCwd() throws SftpException{ + if(cwd==null) + cwd=getHome(); + return cwd; + } + + private void setCwd(String cwd){ + this.cwd=cwd; + } + + private void read(byte[] buf, int s, int l) throws IOException, SftpException{ + int i=0; + while(l>0){ + i=io_in.read(buf, s, l); + if(i<=0){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + s+=i; + l-=i; + } + } + + private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException{ + header=header(buf, header); + int length=header.length; + int type=header.type; + if(ackid!=null) + ackid[0]=header.rid; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i=buf.getInt(); + if(i!=SSH_FX_OK){ + throwStatusError(buf, i); + } + return true; + } + private boolean _sendCLOSE(byte[] handle, Header header) throws Exception{ + sendCLOSE(handle); + return checkStatus(null, header); + } + + private void sendINIT() throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_INIT, 5); + buf.putInt(3); // version 3 + getSession().write(packet, this, 5+4); + } + + private void sendREALPATH(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_REALPATH, path); + } + private void sendSTAT(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_STAT, path); + } + private void sendLSTAT(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_LSTAT, path); + } + private void sendFSTAT(byte[] handle) throws Exception{ + sendPacketPath(SSH_FXP_FSTAT, handle); + } + private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_SETSTAT, 9+path.length+attr.length()); + buf.putInt(seq++); + buf.putString(path); // path + attr.dump(buf); + getSession().write(packet, this, 9+path.length+attr.length()+4); + } + private void sendREMOVE(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_REMOVE, path); + } + private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_MKDIR, 9+path.length+(attr!=null?attr.length():4)); + buf.putInt(seq++); + buf.putString(path); // path + if(attr!=null) attr.dump(buf); + else buf.putInt(0); + getSession().write(packet, this, 9+path.length+(attr!=null?attr.length():4)+4); + } + private void sendRMDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_RMDIR, path); + } + private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception{ + sendPacketPath(SSH_FXP_SYMLINK, p1, p2); + } + private void sendREADLINK(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_READLINK, path); + } + private void sendOPENDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_OPENDIR, path); + } + private void sendREADDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_READDIR, path); + } + private void sendRENAME(byte[] p1, byte[] p2) throws Exception{ + sendPacketPath(SSH_FXP_RENAME, p1, p2); + } + private void sendCLOSE(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_CLOSE, path); + } + private void sendOPENR(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_READ); + } + private void sendOPENW(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_TRUNC); + } + private void sendOPENA(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_WRITE|/*SSH_FXF_APPEND|*/SSH_FXF_CREAT); + } + private void sendOPEN(byte[] path, int mode) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_OPEN, 17+path.length); + buf.putInt(seq++); + buf.putString(path); + buf.putInt(mode); + buf.putInt(0); // attrs + getSession().write(packet, this, 17+path.length+4); + } + private void sendPacketPath(byte fxp, byte[] path) throws Exception{ + packet.reset(); + putHEAD(fxp, 9+path.length); + buf.putInt(seq++); + buf.putString(path); // path + getSession().write(packet, this, 9+path.length+4); + } + private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception{ + packet.reset(); + putHEAD(fxp, 13+p1.length+p2.length); + buf.putInt(seq++); + buf.putString(p1); + buf.putString(p2); + getSession().write(packet, this, 13+p1.length+p2.length+4); + } + + private int sendWRITE(byte[] handle, long offset, + byte[] data, int start, int length) throws Exception{ + int _length=length; + opacket.reset(); + if(obuf.buffer.length<obuf.index+13+21+handle.length+length+Session.buffer_margin){ + _length=obuf.buffer.length-(obuf.index+13+21+handle.length+Session.buffer_margin); + // System.err.println("_length="+_length+" length="+length); + } + + putHEAD(obuf, SSH_FXP_WRITE, 21+handle.length+_length); // 14 + obuf.putInt(seq++); // 4 + obuf.putString(handle); // 4+handle.length + obuf.putLong(offset); // 8 + if(obuf.buffer!=data){ + obuf.putString(data, start, _length); // 4+_length + } + else{ + obuf.putInt(_length); + obuf.skip(_length); + } + getSession().write(opacket, this, 21+handle.length+_length+4); + return _length; + } + private void sendREAD(byte[] handle, long offset, int length) throws Exception{ + sendREAD(handle, offset, length, null); + } + private void sendREAD(byte[] handle, long offset, int length, + RequestQueue rrq) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_READ, 21+handle.length); + buf.putInt(seq++); + buf.putString(handle); + buf.putLong(offset); + buf.putInt(length); + getSession().write(packet, this, 21+handle.length+4); + if(rrq!=null){ + rrq.add(seq-1, offset, length); + } + } + + private void putHEAD(Buffer buf, byte type, int length) throws Exception{ + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(length+4); + buf.putInt(length); + buf.putByte(type); + } + + private void putHEAD(byte type, int length) throws Exception{ + putHEAD(buf, type, length); + } + + private Vector glob_remote(String _path) throws Exception{ + Vector v=new Vector(); + int i=0; + + int foo=_path.lastIndexOf('/'); + if(foo<0){ // it is not absolute path. + v.addElement(Util.unquote(_path)); + return v; + } + + String dir=_path.substring(0, ((foo==0)?1:foo)); + String _pattern=_path.substring(foo+1); + + dir=Util.unquote(dir); + + byte[] pattern=null; + byte[][] _pattern_utf8=new byte[1][]; + boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8); + + if(!pattern_has_wildcard){ + if(!dir.equals("/")) + dir+="/"; + v.addElement(dir+Util.unquote(_pattern)); + return v; + } + + pattern=_pattern_utf8[0]; + + sendOPENDIR(Util.str2byte(dir, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + i=buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle=buf.getString(); // filename + String pdir=null; // parent directory + + while(true){ + sendREADDIR(handle); + header=header(buf, header); + length=header.length; + type=header.type; + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + fill(buf, length); + break; + } + + buf.rewind(); + fill(buf.buffer, 0, 4); length-=4; + int count=buf.getInt(); + + byte[] str; + int flags; + + buf.reset(); + while(count>0){ + if(length>0){ + buf.shift(); + int j=(buf.buffer.length>(buf.index+length)) ? length : (buf.buffer.length-buf.index); + i=io_in.read(buf.buffer, buf.index, j); + if(i<=0)break; + buf.index+=i; + length-=i; + } + + byte[] filename=buf.getString(); + //System.err.println("filename: "+new String(filename)); + if(server_version<=3){ + str=buf.getString(); // longname + } + SftpATTRS attrs=SftpATTRS.getATTR(buf); + + byte[] _filename=filename; + String f=null; + boolean found=false; + + if(!fEncoding_is_utf8){ + f=Util.byte2str(filename, fEncoding); + _filename=Util.str2byte(f, UTF8); + } + found=Util.glob(pattern, _filename); + + if(found){ + if(f==null){ + f=Util.byte2str(filename, fEncoding); + } + if(pdir==null){ + pdir=dir; + if(!pdir.endsWith("/")){ + pdir+="/"; + } + } + v.addElement(pdir+f); + } + count--; + } + } + if(_sendCLOSE(handle, header)) + return v; + return null; + } + + private boolean isPattern(byte[] path){ + int length=path.length; + int i=0; + while(i<length){ + if(path[i]=='*' || path[i]=='?') + return true; + if(path[i]=='\\' && (i+1)<length) + i++; + i++; + } + return false; + } + + private Vector glob_local(String _path) throws Exception{ +//System.err.println("glob_local: "+_path); + Vector v=new Vector(); + byte[] path=Util.str2byte(_path, UTF8); + int i=path.length-1; + while(i>=0){ + if(path[i]!='*' && path[i]!='?'){ + i--; + continue; + } + if(!fs_is_bs && + i>0 && path[i-1]=='\\'){ + i--; + if(i>0 && path[i-1]=='\\'){ + i--; + i--; + continue; + } + } + break; + } + + if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} + + while(i>=0){ + if(path[i]==file_separatorc || + (fs_is_bs && path[i]=='/')){ // On Windows, '/' is also the separator. + break; + } + i--; + } + + if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} + + byte[] dir; + if(i==0){dir=new byte[]{(byte)file_separatorc};} + else{ + dir=new byte[i]; + System.arraycopy(path, 0, dir, 0, i); + } + + byte[] pattern=new byte[path.length-i-1]; + System.arraycopy(path, i+1, pattern, 0, pattern.length); + +//System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern)); + try{ + String[] children=(new File(Util.byte2str(dir, UTF8))).list(); + String pdir=Util.byte2str(dir)+file_separator; + for(int j=0; j<children.length; j++){ +//System.err.println("children: "+children[j]); + if(Util.glob(pattern, Util.str2byte(children[j], UTF8))){ + v.addElement(pdir+children[j]); + } + } + } + catch(Exception e){ + } + return v; + } + + private void throwStatusError(Buffer buf, int i) throws SftpException{ + if(server_version>=3 && // WindRiver's sftp will send invalid + buf.getLength()>=4){ // SSH_FXP_STATUS packet. + byte[] str=buf.getString(); + //byte[] tag=buf.getString(); + throw new SftpException(i, Util.byte2str(str, UTF8)); + } + else{ + throw new SftpException(i, "Failure"); + } + } + + private static boolean isLocalAbsolutePath(String path){ + return (new File(path)).isAbsolute(); + } + + public void disconnect(){ + super.disconnect(); + } + + private boolean isPattern(String path, byte[][] utf8){ + byte[] _path=Util.str2byte(path, UTF8); + if(utf8!=null) + utf8[0]=_path; + return isPattern(_path); + } + + private boolean isPattern(String path){ + return isPattern(path, null); + } + + private void fill(Buffer buf, int len) throws IOException{ + buf.reset(); + fill(buf.buffer, 0, len); + buf.skip(len); + } + + private int fill(byte[] buf, int s, int len) throws IOException{ + int i=0; + int foo=s; + while(len>0){ + i=io_in.read(buf, s, len); + if(i<=0){ + throw new IOException("inputstream is closed"); + //return (s-foo)==0 ? i : s-foo; + } + s+=i; + len-=i; + } + return s-foo; + } + private void skip(long foo) throws IOException{ + while(foo>0){ + long bar=io_in.skip(foo); + if(bar<=0) + break; + foo-=bar; + } + } + + class Header{ + int length; + int type; + int rid; + } + private Header header(Buffer buf, Header header) throws IOException{ + buf.rewind(); + int i=fill(buf.buffer, 0, 9); + header.length=buf.getInt()-5; + header.type=buf.getByte()&0xff; + header.rid=buf.getInt(); + return header; + } + + private String remoteAbsolutePath(String path) throws SftpException{ + if(path.charAt(0)=='/') return path; + String cwd=getCwd(); +// if(cwd.equals(getHome())) return path; + if(cwd.endsWith("/")) return cwd+path; + return cwd+"/"+path; + } + + private String localAbsolutePath(String path){ + if(isLocalAbsolutePath(path)) return path; + if(lcwd.endsWith(file_separator)) return lcwd+path; + return lcwd+file_separator+path; + } + + /** + * This method will check if the given string can be expanded to the + * unique string. If it can be expanded to mutiple files, SftpException + * will be thrown. + * @return the returned string is unquoted. + */ + private String isUnique(String path) throws SftpException, Exception{ + Vector v=glob_remote(path); + if(v.size()!=1){ + throw new SftpException(SSH_FX_FAILURE, path+" is not unique: "+v.toString()); + } + return (String)(v.elementAt(0)); + } + + public int getServerVersion() throws SftpException{ + if(!isConnected()){ + throw new SftpException(SSH_FX_FAILURE, "The channel is not connected."); + } + return server_version; + } + + public void setFilenameEncoding(String encoding) throws SftpException{ + int sversion=getServerVersion(); + if(3 <= sversion && sversion <= 5 && + !encoding.equals(UTF8)){ + throw new SftpException(SSH_FX_FAILURE, + "The encoding can not be changed for this sftp server."); + } + if(encoding.equals(UTF8)){ + encoding=UTF8; + } + fEncoding=encoding; + fEncoding_is_utf8=fEncoding.equals(UTF8); + } + + public String getExtension(String key){ + if(extensions==null) + return null; + return (String)extensions.get(key); + } + + public String realpath(String path) throws SftpException{ + try{ + byte[] _path=_realpath(remoteAbsolutePath(path)); + return Util.byte2str(_path, fEncoding); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public class LsEntry implements Comparable{ + private String filename; + private String longname; + private SftpATTRS attrs; + LsEntry(String filename, String longname, SftpATTRS attrs){ + setFilename(filename); + setLongname(longname); + setAttrs(attrs); + } + public String getFilename(){return filename;}; + void setFilename(String filename){this.filename = filename;}; + public String getLongname(){return longname;}; + void setLongname(String longname){this.longname = longname;}; + public SftpATTRS getAttrs(){return attrs;}; + void setAttrs(SftpATTRS attrs) {this.attrs = attrs;}; + public String toString(){ return longname; } + public int compareTo(Object o) throws ClassCastException{ + if(o instanceof LsEntry){ + return filename.compareTo(((LsEntry)o).getFilename()); + } + throw new ClassCastException("a decendent of LsEntry must be given."); + } + } +} diff --git a/java/com/jcraft/jsch/ChannelShell.java b/java/com/jcraft/jsch/ChannelShell.java new file mode 100644 index 00000000..eeb24a39 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelShell.java @@ -0,0 +1,70 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.*; + +public class ChannelShell extends ChannelSession{ + + ChannelShell(){ + super(); + pty=true; + } + + public void start() throws JSchException{ + Session _session=getSession(); + try{ + sendRequests(); + + Request request=new RequestShell(); + request.request(_session, this); + } + catch(Exception e){ + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException("ChannelShell", (Throwable)e); + throw new JSchException("ChannelShell"); + } + + if(io.in!=null){ + thread=new Thread(this); + thread.setName("Shell for "+_session.host); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } +} diff --git a/java/com/jcraft/jsch/ChannelSubsystem.java b/java/com/jcraft/jsch/ChannelSubsystem.java new file mode 100644 index 00000000..3f33bf3e --- /dev/null +++ b/java/com/jcraft/jsch/ChannelSubsystem.java @@ -0,0 +1,83 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class ChannelSubsystem extends ChannelSession{ + boolean xforwading=false; + boolean pty=false; + boolean want_reply=true; + String subsystem=""; + public void setXForwarding(boolean foo){ xforwading=foo; } + public void setPty(boolean foo){ pty=foo; } + public void setWantReply(boolean foo){ want_reply=foo; } + public void setSubsystem(String foo){ subsystem=foo; } + public void start() throws JSchException{ + Session _session=getSession(); + try{ + Request request; + if(xforwading){ + request=new RequestX11(); + request.request(_session, this); + } + if(pty){ + request=new RequestPtyReq(); + request.request(_session, this); + } + request=new RequestSubsystem(); + ((RequestSubsystem)request).request(_session, this, subsystem, want_reply); + } + catch(Exception e){ + if(e instanceof JSchException){ throw (JSchException)e; } + if(e instanceof Throwable) + throw new JSchException("ChannelSubsystem", (Throwable)e); + throw new JSchException("ChannelSubsystem"); + } + if(io.in!=null){ + thread=new Thread(this); + thread.setName("Subsystem for "+_session.host); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } + + public void setErrStream(java.io.OutputStream out){ + setExtOutputStream(out); + } + public java.io.InputStream getErrStream() throws java.io.IOException { + return getExtInputStream(); + } +} diff --git a/java/com/jcraft/jsch/ChannelX11.java b/java/com/jcraft/jsch/ChannelX11.java new file mode 100644 index 00000000..393728c0 --- /dev/null +++ b/java/com/jcraft/jsch/ChannelX11.java @@ -0,0 +1,273 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; + +class ChannelX11 extends Channel{ + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + + static private final int TIMEOUT=10*1000; + + private static String host="127.0.0.1"; + private static int port=6000; + + private boolean init=true; + + static byte[] cookie=null; + private static byte[] cookie_hex=null; + + private static java.util.Hashtable faked_cookie_pool=new java.util.Hashtable(); + private static java.util.Hashtable faked_cookie_hex_pool=new java.util.Hashtable(); + + private static byte[] table={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39, + 0x61,0x62,0x63,0x64,0x65,0x66}; + + private Socket socket = null; + + static int revtable(byte foo){ + for(int i=0; i<table.length; i++){ + if(table[i]==foo)return i; + } + return 0; + } + static void setCookie(String foo){ + cookie_hex=Util.str2byte(foo); + cookie=new byte[16]; + for(int i=0; i<16; i++){ + cookie[i]=(byte)(((revtable(cookie_hex[i*2])<<4)&0xf0) | + ((revtable(cookie_hex[i*2+1]))&0xf)); + } + } + static void setHost(String foo){ host=foo; } + static void setPort(int foo){ port=foo; } + static byte[] getFakedCookie(Session session){ + synchronized(faked_cookie_hex_pool){ + byte[] foo=(byte[])faked_cookie_hex_pool.get(session); + if(foo==null){ + Random random=Session.random; + foo=new byte[16]; + synchronized(random){ + random.fill(foo, 0, 16); + } +/* +System.err.print("faked_cookie: "); +for(int i=0; i<foo.length; i++){ + System.err.print(Integer.toHexString(foo[i]&0xff)+":"); +} +System.err.println(""); +*/ + faked_cookie_pool.put(session, foo); + byte[] bar=new byte[32]; + for(int i=0; i<16; i++){ + bar[2*i]=table[(foo[i]>>>4)&0xf]; + bar[2*i+1]=table[(foo[i])&0xf]; + } + faked_cookie_hex_pool.put(session, bar); + foo=bar; + } + return foo; + } + } + + static void removeFakedCookie(Session session){ + synchronized(faked_cookie_hex_pool){ + faked_cookie_hex_pool.remove(session); + faked_cookie_pool.remove(session); + } + } + + ChannelX11(){ + super(); + + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + + type=Util.str2byte("x11"); + + connected=true; + /* + try{ + socket=Util.createSocket(host, port, TIMEOUT); + socket.setTcpNoDelay(true); + io=new IO(); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + } + catch(Exception e){ + //System.err.println(e); + } + */ + } + + public void run(){ + + try{ + socket=Util.createSocket(host, port, TIMEOUT); + socket.setTcpNoDelay(true); + io=new IO(); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + sendOpenConfirmation(); + } + catch(Exception e){ + sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + close=true; + disconnect(); + return; + } + + thread=Thread.currentThread(); + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + int i=0; + try{ + while(thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14-Session.buffer_margin); + if(i<=0){ + eof(); + break; + } + if(close)break; + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + getSession().write(packet, this, i); + } + } + catch(Exception e){ + //System.err.println(e); + } + disconnect(); + } + + private byte[] cache=new byte[0]; + private byte[] addCache(byte[] foo, int s, int l){ + byte[] bar=new byte[cache.length+l]; + System.arraycopy(foo, s, bar, cache.length, l); + if(cache.length>0) + System.arraycopy(cache, 0, bar, 0, cache.length); + cache=bar; + return cache; + } + + void write(byte[] foo, int s, int l) throws java.io.IOException { + //if(eof_local)return; + + if(init){ + + Session _session=null; + try{ + _session=getSession(); + } + catch(JSchException e){ + throw new java.io.IOException(e.toString()); + } + + foo=addCache(foo, s, l); + s=0; + l=foo.length; + + if(l<9) + return; + + int plen=(foo[s+6]&0xff)*256+(foo[s+7]&0xff); + int dlen=(foo[s+8]&0xff)*256+(foo[s+9]&0xff); + + if((foo[s]&0xff)==0x42){ + } + else if((foo[s]&0xff)==0x6c){ + plen=((plen>>>8)&0xff)|((plen<<8)&0xff00); + dlen=((dlen>>>8)&0xff)|((dlen<<8)&0xff00); + } + else{ + // ?? + } + + if(l<12+plen+((-plen)&3)+dlen) + return; + + byte[] bar=new byte[dlen]; + System.arraycopy(foo, s+12+plen+((-plen)&3), bar, 0, dlen); + byte[] faked_cookie=null; + + synchronized(faked_cookie_pool){ + faked_cookie=(byte[])faked_cookie_pool.get(_session); + } + + /* +System.err.print("faked_cookie: "); +for(int i=0; i<faked_cookie.length; i++){ + System.err.print(Integer.toHexString(faked_cookie[i]&0xff)+":"); +} +System.err.println(""); +System.err.print("bar: "); +for(int i=0; i<bar.length; i++){ + System.err.print(Integer.toHexString(bar[i]&0xff)+":"); +} +System.err.println(""); + */ + + if(equals(bar, faked_cookie)){ + if(cookie!=null) + System.arraycopy(cookie, 0, foo, s+12+plen+((-plen)&3), dlen); + } + else{ + //System.err.println("wrong cookie"); + thread=null; + eof(); + io.close(); + disconnect(); + } + init=false; + io.put(foo, s, l); + cache=null; + return; + } + io.put(foo, s, l); + } + + private static boolean equals(byte[] foo, byte[] bar){ + if(foo.length!=bar.length)return false; + for(int i=0; i<foo.length; i++){ + if(foo[i]!=bar[i])return false; + } + return true; + } +} diff --git a/java/com/jcraft/jsch/Cipher.java b/java/com/jcraft/jsch/Cipher.java new file mode 100644 index 00000000..cc7084e6 --- /dev/null +++ b/java/com/jcraft/jsch/Cipher.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Cipher{ + static int ENCRYPT_MODE=0; + static int DECRYPT_MODE=1; + int getIVSize(); + int getBlockSize(); + void init(int mode, byte[] key, byte[] iv) throws Exception; + void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception; + boolean isCBC(); +} diff --git a/java/com/jcraft/jsch/CipherNone.java b/java/com/jcraft/jsch/CipherNone.java new file mode 100644 index 00000000..61f836ab --- /dev/null +++ b/java/com/jcraft/jsch/CipherNone.java @@ -0,0 +1,42 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class CipherNone implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/Compression.java b/java/com/jcraft/jsch/Compression.java new file mode 100644 index 00000000..7ae79220 --- /dev/null +++ b/java/com/jcraft/jsch/Compression.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Compression{ + static public final int INFLATER=0; + static public final int DEFLATER=1; + void init(int type, int level); + byte[] compress(byte[] buf, int start, int[] len); + byte[] uncompress(byte[] buf, int start, int[] len); +} diff --git a/java/com/jcraft/jsch/DH.java b/java/com/jcraft/jsch/DH.java new file mode 100644 index 00000000..963f13c2 --- /dev/null +++ b/java/com/jcraft/jsch/DH.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface DH{ + void init() throws Exception; + void setP(byte[] p); + void setG(byte[] g); + byte[] getE() throws Exception; + void setF(byte[] f); + byte[] getK() throws Exception; +} diff --git a/java/com/jcraft/jsch/DHG1.java b/java/com/jcraft/jsch/DHG1.java new file mode 100644 index 00000000..9b9a860e --- /dev/null +++ b/java/com/jcraft/jsch/DHG1.java @@ -0,0 +1,310 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHG1 extends KeyExchange{ + + static final byte[] g={ 2 }; + static final byte[] p={ +(byte)0x00, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, +(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34, +(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1, +(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74, +(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22, +(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD, +(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B, +(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37, +(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45, +(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6, +(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B, +(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED, +(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5, +(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6, +(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE6,(byte)0x53,(byte)0x81, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF +}; + + private static final int SSH_MSG_KEXDH_INIT= 30; + private static final int SSH_MSG_KEXDH_REPLY= 31; + + static final int RSA=0; + static final int DSS=1; + private int type=0; + + private int state; + + DH dh; +// HASH sha; + +// byte[] K; +// byte[] H; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + +// byte[] K_S; + + byte[] e; + + private Buffer buf; + private Packet packet; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + +// sha=new SHA1(); +// sha.init(); + try{ + Class c=Class.forName(session.getConfig("sha-1")); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + dh=(DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ + //System.err.println(e); + throw e; + } + + dh.setP(p); + dh.setG(g); + + // The client responds with: + // byte SSH_MSG_KEXDH_INIT(30) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEXDH_INIT); + buf.putMPInt(e); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXDH_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEXDH_REPLY"); + } + + state=SSH_MSG_KEXDH_REPLY; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + + switch(state){ + case SSH_MSG_KEXDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEXDH_REPLY(31) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=31){ + System.err.println("type: must be 31 "+j); + return false; + } + + K_S=_buf.getString(); + // K_S is server_key_blob, which includes .... + // string ssh-dss + // impint p of dsa + // impint q of dsa + // impint g of dsa + // impint pub_key of dsa + //System.err.print("K_S: "); //dump(K_S, 0, K_S.length); + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + /* +for(int ii=0; ii<sig_of_H.length;ii++){ + System.err.print(Integer.toHexString(sig_of_H[ii]&0xff)); + System.err.print(": "); +} +System.err.println(""); + */ + + dh.setF(f); + K=dh.getK(); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + H=sha.digest(); + //System.err.print("H -> "); //dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result=false; + + if(alg.equals("ssh-rsa")){ + byte[] tmp; + byte[] ee; + byte[] n; + + type=RSA; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + ee=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + n=tmp; + +// SignatureRSA sig=new SignatureRSA(); +// sig.init(); + + SignatureRSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.rsa")); + sig=(SignatureRSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(ee, n); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_rsa_verify: signature "+result); + } + + } + else if(alg.equals("ssh-dss")){ + byte[] q=null; + byte[] tmp; + byte[] p; + byte[] g; + + type=DSS; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + p=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + q=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + g=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + f=tmp; +// SignatureDSA sig=new SignatureDSA(); +// sig.init(); + SignatureDSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.dss")); + sig=(SignatureDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + sig.setPubKey(f, p, q, g); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_dss_verify: signature "+result); + } + + } + else{ + System.err.println("unknown alg"); + } + state=STATE_END; + return result; + } + return false; + } + + public String getKeyType(){ + if(type==DSS) return "DSA"; + return "RSA"; + } + + public int getState(){return state; } +} diff --git a/java/com/jcraft/jsch/DHG14.java b/java/com/jcraft/jsch/DHG14.java new file mode 100644 index 00000000..8ef5fb79 --- /dev/null +++ b/java/com/jcraft/jsch/DHG14.java @@ -0,0 +1,310 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHG14 extends KeyExchange{ + + static final byte[] g={ 2 }; + static final byte[] p={ +(byte)0x00, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, +(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34, +(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1, +(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74, +(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22, +(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD, +(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B, +(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37, +(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45, +(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6, +(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B, +(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED, +(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5, +(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6, +(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE4,(byte)0x5B,(byte)0x3D, +(byte)0xC2,(byte)0x00,(byte)0x7C,(byte)0xB8,(byte)0xA1,(byte)0x63,(byte)0xBF,(byte)0x05, +(byte)0x98,(byte)0xDA,(byte)0x48,(byte)0x36,(byte)0x1C,(byte)0x55,(byte)0xD3,(byte)0x9A, +(byte)0x69,(byte)0x16,(byte)0x3F,(byte)0xA8,(byte)0xFD,(byte)0x24,(byte)0xCF,(byte)0x5F, +(byte)0x83,(byte)0x65,(byte)0x5D,(byte)0x23,(byte)0xDC,(byte)0xA3,(byte)0xAD,(byte)0x96, +(byte)0x1C,(byte)0x62,(byte)0xF3,(byte)0x56,(byte)0x20,(byte)0x85,(byte)0x52,(byte)0xBB, +(byte)0x9E,(byte)0xD5,(byte)0x29,(byte)0x07,(byte)0x70,(byte)0x96,(byte)0x96,(byte)0x6D, +(byte)0x67,(byte)0x0C,(byte)0x35,(byte)0x4E,(byte)0x4A,(byte)0xBC,(byte)0x98,(byte)0x04, +(byte)0xF1,(byte)0x74,(byte)0x6C,(byte)0x08,(byte)0xCA,(byte)0x18,(byte)0x21,(byte)0x7C, +(byte)0x32,(byte)0x90,(byte)0x5E,(byte)0x46,(byte)0x2E,(byte)0x36,(byte)0xCE,(byte)0x3B, +(byte)0xE3,(byte)0x9E,(byte)0x77,(byte)0x2C,(byte)0x18,(byte)0x0E,(byte)0x86,(byte)0x03, +(byte)0x9B,(byte)0x27,(byte)0x83,(byte)0xA2,(byte)0xEC,(byte)0x07,(byte)0xA2,(byte)0x8F, +(byte)0xB5,(byte)0xC5,(byte)0x5D,(byte)0xF0,(byte)0x6F,(byte)0x4C,(byte)0x52,(byte)0xC9, +(byte)0xDE,(byte)0x2B,(byte)0xCB,(byte)0xF6,(byte)0x95,(byte)0x58,(byte)0x17,(byte)0x18, +(byte)0x39,(byte)0x95,(byte)0x49,(byte)0x7C,(byte)0xEA,(byte)0x95,(byte)0x6A,(byte)0xE5, +(byte)0x15,(byte)0xD2,(byte)0x26,(byte)0x18,(byte)0x98,(byte)0xFA,(byte)0x05,(byte)0x10, +(byte)0x15,(byte)0x72,(byte)0x8E,(byte)0x5A,(byte)0x8A,(byte)0xAC,(byte)0xAA,(byte)0x68, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF +}; + + private static final int SSH_MSG_KEXDH_INIT= 30; + private static final int SSH_MSG_KEXDH_REPLY= 31; + + static final int RSA=0; + static final int DSS=1; + private int type=0; + + private int state; + + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + + try{ + Class c=Class.forName(session.getConfig("sha-1")); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + dh=(DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ + //System.err.println(e); + throw e; + } + + dh.setP(p); + dh.setG(g); + // The client responds with: + // byte SSH_MSG_KEXDH_INIT(30) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + packet.reset(); + buf.putByte((byte)SSH_MSG_KEXDH_INIT); + buf.putMPInt(e); + + if(V_S==null){ // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXDH_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEXDH_REPLY"); + } + + state=SSH_MSG_KEXDH_REPLY; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + + switch(state){ + case SSH_MSG_KEXDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEXDH_REPLY(31) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=31){ + System.err.println("type: must be 31 "+j); + return false; + } + + K_S=_buf.getString(); + // K_S is server_key_blob, which includes .... + // string ssh-dss + // impint p of dsa + // impint q of dsa + // impint g of dsa + // impint pub_key of dsa + //System.err.print("K_S: "); //dump(K_S, 0, K_S.length); + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + + dh.setF(f); + K=dh.getK(); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + H=sha.digest(); + //System.err.print("H -> "); //dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result=false; + + if(alg.equals("ssh-rsa")){ + byte[] tmp; + byte[] ee; + byte[] n; + + type=RSA; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + ee=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + n=tmp; + + SignatureRSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.rsa")); + sig=(SignatureRSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(ee, n); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_rsa_verify: signature "+result); + } + + } + else if(alg.equals("ssh-dss")){ + byte[] q=null; + byte[] tmp; + byte[] p; + byte[] g; + + type=DSS; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + p=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + q=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + g=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + f=tmp; + + SignatureDSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.dss")); + sig=(SignatureDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + sig.setPubKey(f, p, q, g); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_dss_verify: signature "+result); + } + + } + else{ + System.err.println("unknown alg"); + } + state=STATE_END; + return result; + } + return false; + } + + public String getKeyType(){ + if(type==DSS) return "DSA"; + return "RSA"; + } + + public int getState(){return state; } +} diff --git a/java/com/jcraft/jsch/DHGEX.java b/java/com/jcraft/jsch/DHGEX.java new file mode 100644 index 00000000..23fa9ebf --- /dev/null +++ b/java/com/jcraft/jsch/DHGEX.java @@ -0,0 +1,340 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHGEX extends KeyExchange{ + + private static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; + private static final int SSH_MSG_KEX_DH_GEX_INIT= 32; + private static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; + private static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; + + static int min=1024; + +// static int min=512; + static int preferred=1024; + static int max=1024; + +// static int preferred=1024; +// static int max=2000; + + static final int RSA=0; + static final int DSS=1; + private int type=0; + + private int state; + +// com.jcraft.jsch.DH dh; + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + private Buffer buf; + private Packet packet; + + private byte[] p; + private byte[] g; + private byte[] e; + //private byte[] f; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + + try{ + Class c=Class.forName(session.getConfig("sha-1")); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + dh=(com.jcraft.jsch.DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ +// System.err.println(e); + throw e; + } + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_REQUEST); + buf.putInt(min); + buf.putInt(preferred); + buf.putInt(max); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_REQUEST("+min+"<"+preferred+"<"+max+") sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_GROUP"); + } + + state=SSH_MSG_KEX_DH_GEX_GROUP; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + switch(state){ + case SSH_MSG_KEX_DH_GEX_GROUP: + // byte SSH_MSG_KEX_DH_GEX_GROUP(31) + // mpint p, safe prime + // mpint g, generator for subgroup in GF (p) + _buf.getInt(); + _buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_GROUP){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "+j); + return false; + } + + p=_buf.getMPInt(); + g=_buf.getMPInt(); + /* +for(int iii=0; iii<p.length; iii++){ +System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+","); +} +System.err.println(""); +for(int iii=0; iii<g.length; iii++){ +System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+","); +} + */ + dh.setP(p); + dh.setG(g); + + // The client responds with: + // byte SSH_MSG_KEX_DH_GEX_INIT(32) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_INIT); + buf.putMPInt(e); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_REPLY"); + } + + state=SSH_MSG_KEX_DH_GEX_REPLY; + return true; + //break; + + case SSH_MSG_KEX_DH_GEX_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_DH_GEX_REPLY(33) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_REPLY){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "+j); + return false; + } + + K_S=_buf.getString(); + // K_S is server_key_blob, which includes .... + // string ssh-dss + // impint p of dsa + // impint q of dsa + // impint g of dsa + // impint pub_key of dsa + //System.err.print("K_S: "); dump(K_S, 0, K_S.length); + + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + + dh.setF(f); + K=dh.getK(); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // uint32 min, minimal size in bits of an acceptable group + // uint32 n, preferred size in bits of the group the server should send + // uint32 max, maximal size in bits of an acceptable group + // mpint p, safe prime + // mpint g, generator for subgroup + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putInt(min); buf.putInt(preferred); buf.putInt(max); + buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + + H=sha.digest(); + + // System.err.print("H -> "); dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result=false; + if(alg.equals("ssh-rsa")){ + byte[] tmp; + byte[] ee; + byte[] n; + + type=RSA; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + ee=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + n=tmp; + +// SignatureRSA sig=new SignatureRSA(); +// sig.init(); + + SignatureRSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.rsa")); + sig=(SignatureRSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(ee, n); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_rsa_verify: signature "+result); + } + + } + else if(alg.equals("ssh-dss")){ + byte[] q=null; + byte[] tmp; + + type=DSS; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + p=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + q=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + g=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + f=tmp; + +// SignatureDSA sig=new SignatureDSA(); +// sig.init(); + + SignatureDSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.dss")); + sig=(SignatureDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(f, p, q, g); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_dss_verify: signature "+result); + } + + } + else{ + System.err.println("unknown alg"); + } + state=STATE_END; + return result; + } + return false; + } + + public String getKeyType(){ + if(type==DSS) return "DSA"; + return "RSA"; + } + + public int getState(){return state; } +} diff --git a/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java new file mode 100644 index 00000000..4176aef4 --- /dev/null +++ b/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java @@ -0,0 +1,36 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; +import java.io.*; + +public interface ForwardedTCPIPDaemon extends Runnable{ + void setChannel(ChannelForwardedTCPIP channel, InputStream in, OutputStream out); + void setArg(Object[] arg); +} diff --git a/java/com/jcraft/jsch/GSSContext.java b/java/com/jcraft/jsch/GSSContext.java new file mode 100644 index 00000000..23c58ee3 --- /dev/null +++ b/java/com/jcraft/jsch/GSSContext.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface GSSContext{ + public void create(String user, String host) throws JSchException; + public boolean isEstablished(); + public byte[] init(byte[] token, int s, int l) throws JSchException; + public byte[] getMIC(byte[] message, int s, int l); + public void dispose(); +} diff --git a/java/com/jcraft/jsch/HASH.java b/java/com/jcraft/jsch/HASH.java new file mode 100644 index 00000000..6bfff886 --- /dev/null +++ b/java/com/jcraft/jsch/HASH.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface HASH{ + void init() throws Exception; + int getBlockSize(); + void update(byte[] foo, int start, int len) throws Exception; + byte[] digest() throws Exception; +} diff --git a/java/com/jcraft/jsch/HostKey.java b/java/com/jcraft/jsch/HostKey.java new file mode 100644 index 00000000..6401ad26 --- /dev/null +++ b/java/com/jcraft/jsch/HostKey.java @@ -0,0 +1,104 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class HostKey{ + private static final byte[] sshdss=Util.str2byte("ssh-dss"); + private static final byte[] sshrsa=Util.str2byte("ssh-rsa"); + + protected static final int GUESS=0; + public static final int SSHDSS=1; + public static final int SSHRSA=2; + static final int UNKNOWN=3; + + protected String host; + protected int type; + protected byte[] key; + + public HostKey(String host, byte[] key) throws JSchException { + this(host, GUESS, key); + } + + public HostKey(String host, int type, byte[] key) throws JSchException { + this.host=host; + if(type==GUESS){ + if(key[8]=='d'){ this.type=SSHDSS; } + else if(key[8]=='r'){ this.type=SSHRSA; } + else { throw new JSchException("invalid key type");} + } + else{ + this.type=type; + } + this.key=key; + } + + public String getHost(){ return host; } + public String getType(){ + if(type==SSHDSS){ return Util.byte2str(sshdss); } + if(type==SSHRSA){ return Util.byte2str(sshrsa);} + return "UNKNOWN"; + } + public String getKey(){ + return Util.byte2str(Util.toBase64(key, 0, key.length)); + } + public String getFingerPrint(JSch jsch){ + HASH hash=null; + try{ + Class c=Class.forName(jsch.getConfig("md5")); + hash=(HASH)(c.newInstance()); + } + catch(Exception e){ System.err.println("getFingerPrint: "+e); } + return Util.getFingerPrint(hash, key); + } + + boolean isMatched(String _host){ + return isIncluded(_host); + } + + private boolean isIncluded(String _host){ + int i=0; + String hosts=this.host; + int hostslen=hosts.length(); + int hostlen=_host.length(); + int j; + while(i<hostslen){ + j=hosts.indexOf(',', i); + if(j==-1){ + if(hostlen!=hostslen-i) return false; + return hosts.regionMatches(true, i, _host, 0, hostlen); + } + if(hostlen==(j-i)){ + if(hosts.regionMatches(true, i, _host, 0, hostlen)) return true; + } + i=j+1; + } + return false; + } +} diff --git a/java/com/jcraft/jsch/HostKeyRepository.java b/java/com/jcraft/jsch/HostKeyRepository.java new file mode 100644 index 00000000..adfe633c --- /dev/null +++ b/java/com/jcraft/jsch/HostKeyRepository.java @@ -0,0 +1,44 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface HostKeyRepository{ + final int OK=0; + final int NOT_INCLUDED=1; + final int CHANGED=2; + + int check(String host, byte[] key); + void add(HostKey hostkey, UserInfo ui); + void remove(String host, String type); + void remove(String host, String type, byte[] key); + String getKnownHostsRepositoryID(); + HostKey[] getHostKey(); + HostKey[] getHostKey(String host, String type); +} diff --git a/java/com/jcraft/jsch/IO.java b/java/com/jcraft/jsch/IO.java new file mode 100644 index 00000000..65535ba3 --- /dev/null +++ b/java/com/jcraft/jsch/IO.java @@ -0,0 +1,132 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public class IO{ + InputStream in; + OutputStream out; + OutputStream out_ext; + + private boolean in_dontclose=false; + private boolean out_dontclose=false; + private boolean out_ext_dontclose=false; + + void setOutputStream(OutputStream out){ this.out=out; } + void setOutputStream(OutputStream out, boolean dontclose){ + this.out_dontclose=dontclose; + setOutputStream(out); + } + void setExtOutputStream(OutputStream out){ this.out_ext=out; } + void setExtOutputStream(OutputStream out, boolean dontclose){ + this.out_ext_dontclose=dontclose; + setExtOutputStream(out); + } + void setInputStream(InputStream in){ this.in=in; } + void setInputStream(InputStream in, boolean dontclose){ + this.in_dontclose=dontclose; + setInputStream(in); + } + + public void put(Packet p) throws IOException, java.net.SocketException { + out.write(p.buffer.buffer, 0, p.buffer.index); + out.flush(); + } + void put(byte[] array, int begin, int length) throws IOException { + out.write(array, begin, length); + out.flush(); + } + void put_ext(byte[] array, int begin, int length) throws IOException { + out_ext.write(array, begin, length); + out_ext.flush(); + } + + int getByte() throws IOException { + return in.read(); + } + + void getByte(byte[] array) throws IOException { + getByte(array, 0, array.length); + } + + void getByte(byte[] array, int begin, int length) throws IOException { + do{ + int completed = in.read(array, begin, length); + if(completed<0){ + throw new IOException("End of IO Stream Read"); + } + begin+=completed; + length-=completed; + } + while (length>0); + } + + void out_close(){ + try{ + if(out!=null && !out_dontclose) out.close(); + out=null; + } + catch(Exception ee){} + } + + public void close(){ + try{ + if(in!=null && !in_dontclose) in.close(); + in=null; + } + catch(Exception ee){} + + out_close(); + + try{ + if(out_ext!=null && !out_ext_dontclose) out_ext.close(); + out_ext=null; + } + catch(Exception ee){} + } + + /* + public void finalize() throws Throwable{ + try{ + if(in!=null) in.close(); + } + catch(Exception ee){} + try{ + if(out!=null) out.close(); + } + catch(Exception ee){} + try{ + if(out_ext!=null) out_ext.close(); + } + catch(Exception ee){} + } + */ +} diff --git a/java/com/jcraft/jsch/Identity.java b/java/com/jcraft/jsch/Identity.java new file mode 100644 index 00000000..2f8cb80e --- /dev/null +++ b/java/com/jcraft/jsch/Identity.java @@ -0,0 +1,41 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Identity{ + public boolean setPassphrase(byte[] passphrase) throws JSchException; + public byte[] getPublicKeyBlob(); + public byte[] getSignature(byte[] data); + public boolean decrypt(); + public String getAlgName(); + public String getName(); + public boolean isEncrypted(); + public void clear(); +} diff --git a/java/com/jcraft/jsch/IdentityFile.java b/java/com/jcraft/jsch/IdentityFile.java new file mode 100644 index 00000000..0427dab4 --- /dev/null +++ b/java/com/jcraft/jsch/IdentityFile.java @@ -0,0 +1,955 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +class IdentityFile implements Identity{ + String identity; + byte[] key; + byte[] iv; + private JSch jsch; + private HASH hash; + private byte[] encoded_data; + + private Cipher cipher; + + // DSA + private byte[] P_array; + private byte[] Q_array; + private byte[] G_array; + private byte[] pub_array; + private byte[] prv_array; + + // RSA + private byte[] n_array; // modulus + private byte[] e_array; // public exponent + private byte[] d_array; // private exponent + +// private String algname="ssh-dss"; + private String algname="ssh-rsa"; + + private static final int ERROR=0; + private static final int RSA=1; + private static final int DSS=2; + private static final int UNKNOWN=3; + + private static final int OPENSSH=0; + private static final int FSECURE=1; + private static final int PUTTY=2; + + private int type=ERROR; + private int keytype=OPENSSH; + + private byte[] publickeyblob=null; + + private boolean encrypted=true; + + static IdentityFile newInstance(String prvfile, String pubfile, JSch jsch) throws JSchException{ + byte[] prvkey=null; + byte[] pubkey=null; + + File file=null; + FileInputStream fis=null; + try{ + file=new File(prvfile); + fis=new FileInputStream(prvfile); + prvkey=new byte[(int)(file.length())]; + int len=0; + while(true){ + int i=fis.read(prvkey, len, prvkey.length-len); + if(i<=0) + break; + len+=i; + } + fis.close(); + } + catch(Exception e){ + try{ if(fis!=null) fis.close();} + catch(Exception ee){} + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + + String _pubfile=pubfile; + if(pubfile==null){ + _pubfile=prvfile+".pub"; + } + + try{ + file=new File(_pubfile); + fis = new FileInputStream(_pubfile); + pubkey=new byte[(int)(file.length())]; + int len=0; + while(true){ + int i=fis.read(pubkey, len, pubkey.length-len); + if(i<=0) + break; + len+=i; + } + fis.close(); + } + catch(Exception e){ + try{ if(fis!=null) fis.close();} + catch(Exception ee){} + if(pubfile!=null){ + // The pubfile is explicitry given, but not accessible. + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + return newInstance(prvfile, prvkey, pubkey, jsch); + } + + static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{ + try{ + return new IdentityFile(name, prvkey, pubkey, jsch); + } + finally{ + Util.bzero(prvkey); + } + } + + private IdentityFile(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{ + this.identity=name; + this.jsch=jsch; + + // prvkey from "ssh-add" command on the remote. + if(pubkey==null && + prvkey!=null && + (prvkey.length>11 && + prvkey[0]==0 && prvkey[1]==0 && prvkey[2]==0 && prvkey[3]==7)){ + + Buffer buf=new Buffer(prvkey); + String _type = new String(buf.getString()); // ssh-rsa + + if(_type.equals("ssh-rsa")){ + type=RSA; + n_array=buf.getString(); + e_array=buf.getString(); + d_array=buf.getString(); + buf.getString(); + buf.getString(); + buf.getString(); + this.identity += new String(buf.getString()); + } + else if(_type.equals("ssh-dss")){ + type=DSS; + P_array=buf.getString(); + Q_array=buf.getString(); + G_array=buf.getString(); + pub_array=buf.getString(); + prv_array=buf.getString(); + this.identity += new String(buf.getString()); + } + else{ + throw new JSchException("privatekey: invalid key "+new String(prvkey, 4, 7)); + } + encoded_data=prvkey; + encrypted=false; + keytype=OPENSSH; + return; + } + + /* TODO: IdentityFile should use KeyPair. + * The following logic exists also in KeyPair. It is redundant. + */ + try{ + Class c; + c=Class.forName((String)jsch.getConfig("3des-cbc")); + cipher=(Cipher)(c.newInstance()); + key=new byte[cipher.getBlockSize()]; // 24 + iv=new byte[cipher.getIVSize()]; // 8 + c=Class.forName((String)jsch.getConfig("md5")); + hash=(HASH)(c.newInstance()); + hash.init(); + + byte[] buf=prvkey; + int len=buf.length; + + int i=0; + + while(i<len){ + if(buf[i] == '-' && i+4<len && + buf[i+1] == '-' && buf[i+2] == '-' && + buf[i+3] == '-' && buf[i+4] == '-'){ + break; + } + i++; + } + + while(i<len){ + if(buf[i]=='B'&& i+3<len && buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){ + i+=6; + if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSS; } + else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; } + else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure + type=UNKNOWN; + keytype=FSECURE; + } + else{ + //System.err.println("invalid format: "+identity); + throw new JSchException("invalid privatekey: "+identity); + } + i+=3; + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='2'&& buf[i+5]=='5'&& buf[i+6]=='6'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes256-cbc"))){ + c=Class.forName((String)jsch.getConfig("aes256-cbc")); + cipher=(Cipher)(c.newInstance()); + key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes256-cbc is not available "+identity); + } + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='1'&& buf[i+5]=='9'&& buf[i+6]=='2'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes192-cbc"))){ + c=Class.forName((String)jsch.getConfig("aes192-cbc")); + cipher=(Cipher)(c.newInstance()); + key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes192-cbc is not available "+identity); + } + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='1'&& buf[i+5]=='2'&& buf[i+6]=='8'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes128-cbc"))){ + c=Class.forName((String)jsch.getConfig("aes128-cbc")); + cipher=(Cipher)(c.newInstance()); + key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes128-cbc is not available "+identity); + } + continue; + } + if(buf[i]=='C'&& i+3<len && buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){ + i+=4; + for(int ii=0; ii<iv.length; ii++){ + iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+ + (a2b(buf[i++])&0xf)); + } + continue; + } + if(buf[i]==0x0d && i+1<len && buf[i+1]==0x0a){ + i++; + continue; + } + if(buf[i]==0x0a && i+1<len){ + if(buf[i+1]==0x0a){ i+=2; break; } + if(buf[i+1]==0x0d && + i+2<len && buf[i+2]==0x0a){ + i+=3; break; + } + boolean inheader=false; + for(int j=i+1; j<len; j++){ + if(buf[j]==0x0a) break; + //if(buf[j]==0x0d) break; + if(buf[j]==':'){inheader=true; break;} + } + if(!inheader){ + i++; + encrypted=false; // no passphrase + break; + } + } + i++; + } + + if(type==ERROR){ + throw new JSchException("invalid privatekey: "+identity); + } + + int start=i; + while(i<len){ + if(buf[i]==0x0a){ + boolean xd=(buf[i-1]==0x0d); + System.arraycopy(buf, i+1, + buf, + i-(xd ? 1 : 0), + len-i-1-(xd ? 1 : 0) + ); + if(xd)len--; + len--; + continue; + } + if(buf[i]=='-'){ break; } + i++; + } + encoded_data=Util.fromBase64(buf, start, i-start); + + if(encoded_data.length>4 && // FSecure + encoded_data[0]==(byte)0x3f && + encoded_data[1]==(byte)0x6f && + encoded_data[2]==(byte)0xf9 && + encoded_data[3]==(byte)0xeb){ + + Buffer _buf=new Buffer(encoded_data); + _buf.getInt(); // 0x3f6ff9be + _buf.getInt(); + byte[]_type=_buf.getString(); + //System.err.println("type: "+new String(_type)); + byte[] _cipher=_buf.getString(); + String cipher=Util.byte2str(_cipher); + //System.err.println("cipher: "+cipher); + if(cipher.equals("3des-cbc")){ + _buf.getInt(); + byte[] foo=new byte[encoded_data.length-_buf.getOffSet()]; + _buf.getByte(foo); + encoded_data=foo; + encrypted=true; + throw new JSchException("unknown privatekey format: "+identity); + } + else if(cipher.equals("none")){ + _buf.getInt(); + //_buf.getInt(); + + encrypted=false; + + byte[] foo=new byte[encoded_data.length-_buf.getOffSet()]; + _buf.getByte(foo); + encoded_data=foo; + } + + } + + if(pubkey==null){ + return; + } + + buf=pubkey; + len=buf.length; + + if(buf.length>4 && // FSecure's public key + buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){ + i=0; + do{i++;}while(len>i && buf[i]!=0x0a); + if(len<=i) return; + while(i<len){ + if(buf[i]==0x0a){ + boolean inheader=false; + for(int j=i+1; j<len; j++){ + if(buf[j]==0x0a) break; + if(buf[j]==':'){inheader=true; break;} + } + if(!inheader){ + i++; + break; + } + } + i++; + } + if(len<=i) return; + + start=i; + while(i<len){ + if(buf[i]==0x0a){ + System.arraycopy(buf, i+1, buf, i, len-i-1); + len--; + continue; + } + if(buf[i]=='-'){ break; } + i++; + } + publickeyblob=Util.fromBase64(buf, start, i-start); + + if(type==UNKNOWN && publickeyblob.length>8){ + if(publickeyblob[8]=='d'){ + type=DSS; + } + else if(publickeyblob[8]=='r'){ + type=RSA; + } + } + } + else{ + if(buf[0]!='s'|| buf[1]!='s'|| buf[2]!='h'|| buf[3]!='-') return; + i=0; + while(i<len){ if(buf[i]==' ')break; i++;} i++; + if(i>=len) return; + start=i; + while(i<len){ if(buf[i]==' ' || buf[i]=='\n')break; i++;} + publickeyblob=Util.fromBase64(buf, start, i-start); + if(publickeyblob.length<4+7){ // It must start with "ssh-XXX". + if(JSch.getLogger().isEnabled(Logger.WARN)){ + JSch.getLogger().log(Logger.WARN, + "failed to parse the public key"); + } + publickeyblob=null; + } + } + } + catch(Exception e){ + //System.err.println("IdentityFile: "+e); + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + public String getAlgName(){ + if(type==RSA) return "ssh-rsa"; + return "ssh-dss"; + } + + public boolean setPassphrase(byte[] _passphrase) throws JSchException{ + /* + hash is MD5 + h(0) <- hash(passphrase, iv); + h(n) <- hash(h(n-1), passphrase, iv); + key <- (h(0),...,h(n))[0,..,key.length]; + */ + try{ + if(encrypted){ + if(_passphrase==null) return false; + byte[] passphrase=_passphrase; + int hsize=hash.getBlockSize(); + byte[] hn=new byte[key.length/hsize*hsize+ + (key.length%hsize==0?0:hsize)]; + byte[] tmp=null; + if(keytype==OPENSSH){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + hash.update(iv, 0, iv.length > 8 ? 8: iv.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + else if(keytype==FSECURE){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + Util.bzero(passphrase); + } + if(decrypt()){ + encrypted=false; + return true; + } + P_array=Q_array=G_array=pub_array=prv_array=null; + return false; + } + catch(Exception e){ + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + public byte[] getPublicKeyBlob(){ + if(publickeyblob!=null) return publickeyblob; + if(type==RSA) return getPublicKeyBlob_rsa(); + return getPublicKeyBlob_dss(); + } + + byte[] getPublicKeyBlob_rsa(){ + if(e_array==null) return null; + Buffer buf=new Buffer("ssh-rsa".length()+4+ + e_array.length+4+ + n_array.length+4); + buf.putString(Util.str2byte("ssh-rsa")); + buf.putString(e_array); + buf.putString(n_array); + return buf.buffer; + } + + byte[] getPublicKeyBlob_dss(){ + if(P_array==null) return null; + Buffer buf=new Buffer("ssh-dss".length()+4+ + P_array.length+4+ + Q_array.length+4+ + G_array.length+4+ + pub_array.length+4); + buf.putString(Util.str2byte("ssh-dss")); + buf.putString(P_array); + buf.putString(Q_array); + buf.putString(G_array); + buf.putString(pub_array); + return buf.buffer; + } + + public byte[] getSignature(byte[] data){ + if(type==RSA) return getSignature_rsa(data); + return getSignature_dss(data); + } + + byte[] getSignature_rsa(byte[] data){ + try{ + Class c=Class.forName((String)jsch.getConfig("signature.rsa")); + SignatureRSA rsa=(SignatureRSA)(c.newInstance()); + + rsa.init(); + rsa.setPrvKey(d_array, n_array); + + rsa.update(data); + byte[] sig = rsa.sign(); + Buffer buf=new Buffer("ssh-rsa".length()+4+ + sig.length+4); + buf.putString(Util.str2byte("ssh-rsa")); + buf.putString(sig); + return buf.buffer; + } + catch(Exception e){ + } + return null; + } + + byte[] getSignature_dss(byte[] data){ +/* + byte[] foo; + int i; + System.err.print("P "); + foo=P_array; + for(i=0; i<foo.length; i++){ + System.err.print(Integer.toHexString(foo[i]&0xff)+":"); + } + System.err.println(""); + System.err.print("Q "); + foo=Q_array; + for(i=0; i<foo.length; i++){ + System.err.print(Integer.toHexString(foo[i]&0xff)+":"); + } + System.err.println(""); + System.err.print("G "); + foo=G_array; + for(i=0; i<foo.length; i++){ + System.err.print(Integer.toHexString(foo[i]&0xff)+":"); + } + System.err.println(""); +*/ + + try{ + Class c=Class.forName((String)jsch.getConfig("signature.dss")); + SignatureDSA dsa=(SignatureDSA)(c.newInstance()); + dsa.init(); + dsa.setPrvKey(prv_array, P_array, Q_array, G_array); + + dsa.update(data); + byte[] sig = dsa.sign(); + Buffer buf=new Buffer("ssh-dss".length()+4+ + sig.length+4); + buf.putString(Util.str2byte("ssh-dss")); + buf.putString(sig); + return buf.buffer; + } + catch(Exception e){ + //System.err.println("e "+e); + } + return null; + } + + public boolean decrypt(){ + if(type==RSA) return decrypt_rsa(); + return decrypt_dss(); + } + + boolean decrypt_rsa(){ + byte[] p_array; + byte[] q_array; + byte[] dmp1_array; + byte[] dmq1_array; + byte[] iqmp_array; + + try{ + byte[] plain; + if(encrypted){ + if(keytype==OPENSSH){ + cipher.init(Cipher.DECRYPT_MODE, key, iv); + plain=new byte[encoded_data.length]; + cipher.update(encoded_data, 0, encoded_data.length, plain, 0); + } + else if(keytype==FSECURE){ + for(int i=0; i<iv.length; i++)iv[i]=0; + cipher.init(Cipher.DECRYPT_MODE, key, iv); + plain=new byte[encoded_data.length]; + cipher.update(encoded_data, 0, encoded_data.length, plain, 0); + } + else{ + return false; + } + } + else{ + if(n_array!=null) return true; + plain=encoded_data; + } + + if(keytype==FSECURE){ // FSecure + Buffer buf=new Buffer(plain); + int foo=buf.getInt(); + if(plain.length!=foo+4){ + return false; + } + e_array=buf.getMPIntBits(); + d_array=buf.getMPIntBits(); + n_array=buf.getMPIntBits(); + byte[] u_array=buf.getMPIntBits(); + p_array=buf.getMPIntBits(); + q_array=buf.getMPIntBits(); + return true; + } + + int index=0; + int length=0; + + if(plain[index]!=0x30)return false; + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + +//System.err.println("int: len="+length); +//System.err.print(Integer.toHexString(plain[index-1]&0xff)+":"); +//System.err.println(""); + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + n_array=new byte[length]; + System.arraycopy(plain, index, n_array, 0, length); + index+=length; +/* +System.err.println("int: N len="+length); +for(int i=0; i<n_array.length; i++){ +System.err.print(Integer.toHexString(n_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + e_array=new byte[length]; + System.arraycopy(plain, index, e_array, 0, length); + index+=length; +/* +System.err.println("int: E len="+length); +for(int i=0; i<e_array.length; i++){ +System.err.print(Integer.toHexString(e_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + d_array=new byte[length]; + System.arraycopy(plain, index, d_array, 0, length); + index+=length; +/* +System.err.println("int: D len="+length); +for(int i=0; i<d_array.length; i++){ +System.err.print(Integer.toHexString(d_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + p_array=new byte[length]; + System.arraycopy(plain, index, p_array, 0, length); + index+=length; +/* +System.err.println("int: P len="+length); +for(int i=0; i<p_array.length; i++){ +System.err.print(Integer.toHexString(p_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + q_array=new byte[length]; + System.arraycopy(plain, index, q_array, 0, length); + index+=length; +/* +System.err.println("int: q len="+length); +for(int i=0; i<q_array.length; i++){ +System.err.print(Integer.toHexString(q_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + dmp1_array=new byte[length]; + System.arraycopy(plain, index, dmp1_array, 0, length); + index+=length; +/* +System.err.println("int: dmp1 len="+length); +for(int i=0; i<dmp1_array.length; i++){ +System.err.print(Integer.toHexString(dmp1_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + dmq1_array=new byte[length]; + System.arraycopy(plain, index, dmq1_array, 0, length); + index+=length; +/* +System.err.println("int: dmq1 len="+length); +for(int i=0; i<dmq1_array.length; i++){ +System.err.print(Integer.toHexString(dmq1_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + iqmp_array=new byte[length]; + System.arraycopy(plain, index, iqmp_array, 0, length); + index+=length; +/* +System.err.println("int: iqmp len="+length); +for(int i=0; i<iqmp_array.length; i++){ +System.err.print(Integer.toHexString(iqmp_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + } + catch(Exception e){ + //System.err.println(e); + return false; + } + return true; + } + + boolean decrypt_dss(){ + try{ + byte[] plain; + if(encrypted){ + if(keytype==OPENSSH){ + cipher.init(Cipher.DECRYPT_MODE, key, iv); + plain=new byte[encoded_data.length]; + cipher.update(encoded_data, 0, encoded_data.length, plain, 0); +/* +for(int i=0; i<plain.length; i++){ +System.err.print(Integer.toHexString(plain[i]&0xff)+":"); +} +System.err.println(""); +*/ + } + else if(keytype==FSECURE){ + for(int i=0; i<iv.length; i++)iv[i]=0; + cipher.init(Cipher.DECRYPT_MODE, key, iv); + plain=new byte[encoded_data.length]; + cipher.update(encoded_data, 0, encoded_data.length, plain, 0); + } + else{ + return false; + } + } + else{ + if(P_array!=null) return true; + plain=encoded_data; + } + + if(keytype==FSECURE){ // FSecure + Buffer buf=new Buffer(plain); + int foo=buf.getInt(); + if(plain.length!=foo+4){ + return false; + } + P_array=buf.getMPIntBits(); + G_array=buf.getMPIntBits(); + Q_array=buf.getMPIntBits(); + pub_array=buf.getMPIntBits(); + prv_array=buf.getMPIntBits(); + return true; + } + + int index=0; + int length=0; + if(plain[index]!=0x30)return false; + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + P_array=new byte[length]; + System.arraycopy(plain, index, P_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + Q_array=new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + G_array=new byte[length]; + System.arraycopy(plain, index, G_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + pub_array=new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index+=length; + } + catch(Exception e){ + //System.err.println(e); + //e.printStackTrace(); + return false; + } + return true; + } + + public boolean isEncrypted(){ + return encrypted; + } + + public String getName(){ + return identity; + } + + private byte a2b(byte c){ + if('0'<=c&&c<='9') return (byte)(c-'0'); + if('a'<=c&&c<='z') return (byte)(c-'a'+10); + return (byte)(c-'A'+10); + } + + public boolean equals(Object o){ + if(!(o instanceof IdentityFile)) return super.equals(o); + IdentityFile foo=(IdentityFile)o; + return getName().equals(foo.getName()); + } + + public void clear(){ + Util.bzero(encoded_data); + Util.bzero(prv_array); + Util.bzero(d_array); + Util.bzero(key); + Util.bzero(iv); + } + + public void finalize (){ + clear(); + } +} diff --git a/java/com/jcraft/jsch/IdentityRepository.java b/java/com/jcraft/jsch/IdentityRepository.java new file mode 100644 index 00000000..1f75a014 --- /dev/null +++ b/java/com/jcraft/jsch/IdentityRepository.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +public interface IdentityRepository { + public Vector getIdentities(); + public boolean add(byte[] identity); + public boolean remove(byte[] blob); + public void removeAll(); +} diff --git a/java/com/jcraft/jsch/JSch.java b/java/com/jcraft/jsch/JSch.java new file mode 100644 index 00000000..20c39e5c --- /dev/null +++ b/java/com/jcraft/jsch/JSch.java @@ -0,0 +1,316 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.util.Vector; + +public class JSch{ + public static final String VERSION = "0.1.46"; + + static java.util.Hashtable config=new java.util.Hashtable(); + static{ +// config.put("kex", "diffie-hellman-group-exchange-sha1"); + config.put("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1"); + config.put("server_host_key", "ssh-rsa,ssh-dss"); +// config.put("server_host_key", "ssh-dss,ssh-rsa"); + + config.put("cipher.s2c", + "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc"); + config.put("cipher.c2s", + "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc"); + + config.put("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha1-96,hmac-md5-96"); + config.put("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha1-96,hmac-md5-96"); + config.put("compression.s2c", "none"); + // config.put("compression.s2c", "zlib@openssh.com,zlib,none"); + config.put("compression.c2s", "none"); + // config.put("compression.c2s", "zlib@openssh.com,zlib,none"); + + config.put("lang.s2c", ""); + config.put("lang.c2s", ""); + + config.put("compression_level", "6"); + + config.put("diffie-hellman-group-exchange-sha1", + "com.jcraft.jsch.DHGEX"); + config.put("diffie-hellman-group1-sha1", + "com.jcraft.jsch.DHG1"); + config.put("diffie-hellman-group14-sha1", + "com.jcraft.jsch.DHG14"); + + config.put("dh", "com.jcraft.jsch.jce.DH"); + config.put("3des-cbc", "com.jcraft.jsch.jce.TripleDESCBC"); + config.put("blowfish-cbc", "com.jcraft.jsch.jce.BlowfishCBC"); + config.put("hmac-sha1", "com.jcraft.jsch.jce.HMACSHA1"); + config.put("hmac-sha1-96", "com.jcraft.jsch.jce.HMACSHA196"); + config.put("hmac-md5", "com.jcraft.jsch.jce.HMACMD5"); + config.put("hmac-md5-96", "com.jcraft.jsch.jce.HMACMD596"); + config.put("sha-1", "com.jcraft.jsch.jce.SHA1"); + config.put("md5", "com.jcraft.jsch.jce.MD5"); + config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA"); + config.put("signature.rsa", "com.jcraft.jsch.jce.SignatureRSA"); + config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA"); + config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA"); + config.put("random", "com.jcraft.jsch.jce.Random"); + + config.put("none", "com.jcraft.jsch.CipherNone"); + + config.put("aes128-cbc", "com.jcraft.jsch.jce.AES128CBC"); + config.put("aes192-cbc", "com.jcraft.jsch.jce.AES192CBC"); + config.put("aes256-cbc", "com.jcraft.jsch.jce.AES256CBC"); + + config.put("aes128-ctr", "com.jcraft.jsch.jce.AES128CTR"); + config.put("aes192-ctr", "com.jcraft.jsch.jce.AES192CTR"); + config.put("aes256-ctr", "com.jcraft.jsch.jce.AES256CTR"); + config.put("3des-ctr", "com.jcraft.jsch.jce.TripleDESCTR"); + config.put("arcfour", "com.jcraft.jsch.jce.ARCFOUR"); + config.put("arcfour128", "com.jcraft.jsch.jce.ARCFOUR128"); + config.put("arcfour256", "com.jcraft.jsch.jce.ARCFOUR256"); + + config.put("userauth.none", "com.jcraft.jsch.UserAuthNone"); + config.put("userauth.password", "com.jcraft.jsch.UserAuthPassword"); + config.put("userauth.keyboard-interactive", "com.jcraft.jsch.UserAuthKeyboardInteractive"); + config.put("userauth.publickey", "com.jcraft.jsch.UserAuthPublicKey"); + config.put("userauth.gssapi-with-mic", "com.jcraft.jsch.UserAuthGSSAPIWithMIC"); + config.put("gssapi-with-mic.krb5", "com.jcraft.jsch.jgss.GSSContextKrb5"); + + config.put("zlib", "com.jcraft.jsch.jcraft.Compression"); + config.put("zlib@openssh.com", "com.jcraft.jsch.jcraft.Compression"); + + config.put("StrictHostKeyChecking", "ask"); + config.put("HashKnownHosts", "no"); + //config.put("HashKnownHosts", "yes"); + config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password"); + + config.put("CheckCiphers", "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256"); + config.put("CheckKexes", "diffie-hellman-group14-sha1"); + + config.put("MaxAuthTries", "6"); + } + + private java.util.Vector sessionPool = new java.util.Vector(); + + private IdentityRepository identityRepository = + new LocalIdentityRepository(this); + + public synchronized void setIdentityRepository(IdentityRepository identityRepository){ + this.identityRepository = identityRepository; + } + + synchronized IdentityRepository getIdentityRepository(){ + return this.identityRepository; + } + + private HostKeyRepository known_hosts=null; + + private static final Logger DEVNULL=new Logger(){ + public boolean isEnabled(int level){return false;} + public void log(int level, String message){} + }; + static Logger logger=DEVNULL; + + public JSch(){ + + try{ + String osname=(String)(System.getProperties().get("os.name")); + if(osname!=null && osname.equals("Mac OS X")){ + config.put("hmac-sha1", "com.jcraft.jsch.jcraft.HMACSHA1"); + config.put("hmac-md5", "com.jcraft.jsch.jcraft.HMACMD5"); + config.put("hmac-md5-96", "com.jcraft.jsch.jcraft.HMACMD596"); + config.put("hmac-sha1-96", "com.jcraft.jsch.jcraft.HMACSHA196"); + } + } + catch(Exception e){ + } + + } + + public Session getSession(String username, String host) throws JSchException { return getSession(username, host, 22); } + public Session getSession(String username, String host, int port) throws JSchException { + if(username==null){ + throw new JSchException("username must not be null."); + } + if(host==null){ + throw new JSchException("host must not be null."); + } + Session s=new Session(this); + s.setUserName(username); + s.setHost(host); + s.setPort(port); + return s; + } + + protected void addSession(Session session){ + synchronized(sessionPool){ + sessionPool.addElement(session); + } + } + + protected boolean removeSession(Session session){ + synchronized(sessionPool){ + return sessionPool.remove(session); + } + } + public void setHostKeyRepository(HostKeyRepository hkrepo){ + known_hosts=hkrepo; + } + + public void setKnownHosts(String filename) throws JSchException{ + if(known_hosts==null) known_hosts=new KnownHosts(this); + if(known_hosts instanceof KnownHosts){ + synchronized(known_hosts){ + ((KnownHosts)known_hosts).setKnownHosts(filename); + } + } + } + + public void setKnownHosts(InputStream stream) throws JSchException{ + if(known_hosts==null) known_hosts=new KnownHosts(this); + if(known_hosts instanceof KnownHosts){ + synchronized(known_hosts){ + ((KnownHosts)known_hosts).setKnownHosts(stream); + } + } + } + + public HostKeyRepository getHostKeyRepository(){ + if(known_hosts==null) known_hosts=new KnownHosts(this); + return known_hosts; + } + + public void addIdentity(String prvkey) throws JSchException{ + addIdentity(prvkey, (byte[])null); + } + + public void addIdentity(String prvkey, String passphrase) throws JSchException{ + byte[] _passphrase=null; + if(passphrase!=null){ + _passphrase=Util.str2byte(passphrase); + } + addIdentity(prvkey, _passphrase); + if(_passphrase!=null) + Util.bzero(_passphrase); + } + + public void addIdentity(String prvkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(prvkey, null, this); + addIdentity(identity, passphrase); + } + public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(prvkey, pubkey, this); + addIdentity(identity, passphrase); + } + + public void addIdentity(String name, byte[]prvkey, byte[]pubkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(name, prvkey, pubkey, this); + addIdentity(identity, passphrase); + } + + public void addIdentity(Identity identity, byte[] passphrase) throws JSchException{ + if(passphrase!=null){ + try{ + byte[] goo=new byte[passphrase.length]; + System.arraycopy(passphrase, 0, goo, 0, passphrase.length); + passphrase=goo; + identity.setPassphrase(passphrase); + } + finally{ + Util.bzero(passphrase); + } + } + + if(identityRepository instanceof LocalIdentityRepository){ + ((LocalIdentityRepository)identityRepository).add(identity); + } + else { + // TODO + } + } + + /** + * @deprecated use JSch#removeIdentity(Identity identity) + */ + public void removeIdentity(String name) throws JSchException{ + Vector identities = identityRepository.getIdentities(); + for(int i=0; i<identities.size(); i++){ + Identity identity=(Identity)(identities.elementAt(i)); + if(!identity.getName().equals(name)) + continue; + identityRepository.remove(identity.getPublicKeyBlob()); + break; + } + } + + public void removeIdentity(Identity identity) throws JSchException{ + identityRepository.remove(identity.getPublicKeyBlob()); + } + + public Vector getIdentityNames() throws JSchException{ + Vector foo=new Vector(); + Vector identities = identityRepository.getIdentities(); + for(int i=0; i<identities.size(); i++){ + Identity identity=(Identity)(identities.elementAt(i)); + foo.addElement(identity.getName()); + } + return foo; + } + + public void removeAllIdentity() throws JSchException{ + identityRepository.removeAll(); + } + + public static String getConfig(String key){ + synchronized(config){ + return (String)(config.get(key)); + } + } + + public static void setConfig(java.util.Hashtable newconf){ + synchronized(config){ + for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) { + String key=(String)(e.nextElement()); + config.put(key, (String)(newconf.get(key))); + } + } + } + + public static void setConfig(String key, String value){ + config.put(key, value); + } + + public static void setLogger(Logger logger){ + if(logger==null) logger=DEVNULL; + JSch.logger=logger; + } + static Logger getLogger(){ + return logger; + } +} diff --git a/java/com/jcraft/jsch/JSchAuthCancelException.java b/java/com/jcraft/jsch/JSchAuthCancelException.java new file mode 100644 index 00000000..65e71f19 --- /dev/null +++ b/java/com/jcraft/jsch/JSchAuthCancelException.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class JSchAuthCancelException extends JSchException{ + //private static final long serialVersionUID=3204965907117900987L; + String method; + JSchAuthCancelException () { + super(); + } + JSchAuthCancelException (String s) { + super(s); + this.method=s; + } + public String getMethod(){ + return method; + } +} diff --git a/java/com/jcraft/jsch/JSchException.java b/java/com/jcraft/jsch/JSchException.java new file mode 100644 index 00000000..1e9056ce --- /dev/null +++ b/java/com/jcraft/jsch/JSchException.java @@ -0,0 +1,48 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class JSchException extends Exception{ + //private static final long serialVersionUID=-1319309923966731989L; + private Throwable cause=null; + public JSchException () { + super(); + } + public JSchException (String s) { + super(s); + } + public JSchException (String s, Throwable e) { + super(s); + this.cause=e; + } + public Throwable getCause(){ + return this.cause; + } +} diff --git a/java/com/jcraft/jsch/JSchPartialAuthException.java b/java/com/jcraft/jsch/JSchPartialAuthException.java new file mode 100644 index 00000000..aa7ac9e4 --- /dev/null +++ b/java/com/jcraft/jsch/JSchPartialAuthException.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class JSchPartialAuthException extends JSchException{ + //private static final long serialVersionUID=-378849862323360367L; + String methods; + public JSchPartialAuthException () { + super(); + } + public JSchPartialAuthException (String s) { + super(s); + this.methods=s; + } + public String getMethods(){ + return methods; + } +} diff --git a/java/com/jcraft/jsch/KeyExchange.java b/java/com/jcraft/jsch/KeyExchange.java new file mode 100644 index 00000000..ef089ba3 --- /dev/null +++ b/java/com/jcraft/jsch/KeyExchange.java @@ -0,0 +1,159 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public abstract class KeyExchange{ + + static final int PROPOSAL_KEX_ALGS=0; + static final int PROPOSAL_SERVER_HOST_KEY_ALGS=1; + static final int PROPOSAL_ENC_ALGS_CTOS=2; + static final int PROPOSAL_ENC_ALGS_STOC=3; + static final int PROPOSAL_MAC_ALGS_CTOS=4; + static final int PROPOSAL_MAC_ALGS_STOC=5; + static final int PROPOSAL_COMP_ALGS_CTOS=6; + static final int PROPOSAL_COMP_ALGS_STOC=7; + static final int PROPOSAL_LANG_CTOS=8; + static final int PROPOSAL_LANG_STOC=9; + static final int PROPOSAL_MAX=10; + + //static String kex_algs="diffie-hellman-group-exchange-sha1"+ + // ",diffie-hellman-group1-sha1"; + +//static String kex="diffie-hellman-group-exchange-sha1"; + static String kex="diffie-hellman-group1-sha1"; + static String server_host_key="ssh-rsa,ssh-dss"; + static String enc_c2s="blowfish-cbc"; + static String enc_s2c="blowfish-cbc"; + static String mac_c2s="hmac-md5"; // hmac-md5,hmac-sha1,hmac-ripemd160, + // hmac-sha1-96,hmac-md5-96 + static String mac_s2c="hmac-md5"; +//static String comp_c2s="none"; // zlib +//static String comp_s2c="none"; + static String lang_c2s=""; + static String lang_s2c=""; + + public static final int STATE_END=0; + + protected Session session=null; + protected HASH sha=null; + protected byte[] K=null; + protected byte[] H=null; + protected byte[] K_S=null; + + public abstract void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception; + public abstract boolean next(Buffer buf) throws Exception; + public abstract String getKeyType(); + public abstract int getState(); + + /* + void dump(byte[] foo){ + for(int i=0; i<foo.length; i++){ + if((foo[i]&0xf0)==0)System.err.print("0"); + System.err.print(Integer.toHexString(foo[i]&0xff)); + if(i%16==15){System.err.println(""); continue;} + if(i%2==1)System.err.print(" "); + } + } + */ + + protected static String[] guess(byte[]I_S, byte[]I_C){ + String[] guess=new String[PROPOSAL_MAX]; + Buffer sb=new Buffer(I_S); sb.setOffSet(17); + Buffer cb=new Buffer(I_C); cb.setOffSet(17); + + for(int i=0; i<PROPOSAL_MAX; i++){ + byte[] sp=sb.getString(); // server proposal + byte[] cp=cb.getString(); // client proposal + int j=0; + int k=0; + + loop: + while(j<cp.length){ + while(j<cp.length && cp[j]!=',')j++; + if(k==j) return null; + String algorithm=Util.byte2str(cp, k, j-k); + int l=0; + int m=0; + while(l<sp.length){ + while(l<sp.length && sp[l]!=',')l++; + if(m==l) return null; + if(algorithm.equals(Util.byte2str(sp, m, l-m))){ + guess[i]=algorithm; + break loop; + } + l++; + m=l; + } + j++; + k=j; + } + if(j==0){ + guess[i]=""; + } + else if(guess[i]==null){ + return null; + } + } + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "kex: server->client"+ + " "+guess[PROPOSAL_ENC_ALGS_STOC]+ + " "+guess[PROPOSAL_MAC_ALGS_STOC]+ + " "+guess[PROPOSAL_COMP_ALGS_STOC]); + JSch.getLogger().log(Logger.INFO, + "kex: client->server"+ + " "+guess[PROPOSAL_ENC_ALGS_CTOS]+ + " "+guess[PROPOSAL_MAC_ALGS_CTOS]+ + " "+guess[PROPOSAL_COMP_ALGS_CTOS]); + } + +// for(int i=0; i<PROPOSAL_MAX; i++){ +// System.err.println("guess: ["+guess[i]+"]"); +// } + + return guess; + } + + public String getFingerPrint(){ + HASH hash=null; + try{ + Class c=Class.forName(session.getConfig("md5")); + hash=(HASH)(c.newInstance()); + } + catch(Exception e){ System.err.println("getFingerPrint: "+e); } + return Util.getFingerPrint(hash, getHostKey()); + } + byte[] getK(){ return K; } + byte[] getH(){ return H; } + HASH getHash(){ return sha; } + byte[] getHostKey(){ return K_S; } +} diff --git a/java/com/jcraft/jsch/KeyPair.java b/java/com/jcraft/jsch/KeyPair.java new file mode 100644 index 00000000..b3f681cb --- /dev/null +++ b/java/com/jcraft/jsch/KeyPair.java @@ -0,0 +1,729 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.File; + +public abstract class KeyPair{ + public static final int ERROR=0; + public static final int DSA=1; + public static final int RSA=2; + public static final int UNKNOWN=3; + + static final int VENDOR_OPENSSH=0; + static final int VENDOR_FSECURE=1; + int vendor=VENDOR_OPENSSH; + + private static final byte[] cr=Util.str2byte("\n"); + + public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException{ + return genKeyPair(jsch, type, 1024); + } + public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException{ + KeyPair kpair=null; + if(type==DSA){ kpair=new KeyPairDSA(jsch); } + else if(type==RSA){ kpair=new KeyPairRSA(jsch); } + if(kpair!=null){ + kpair.generate(key_size); + } + return kpair; + } + + abstract void generate(int key_size) throws JSchException; + + abstract byte[] getBegin(); + abstract byte[] getEnd(); + abstract int getKeySize(); + + public String getPublicKeyComment(){ + return publicKeyComment; + } + private String publicKeyComment = ""; + + JSch jsch=null; + private Cipher cipher; + private HASH hash; + private Random random; + + private byte[] passphrase; + + public KeyPair(JSch jsch){ + this.jsch=jsch; + } + + static byte[][] header={Util.str2byte("Proc-Type: 4,ENCRYPTED"), + Util.str2byte("DEK-Info: DES-EDE3-CBC,")}; + + abstract byte[] getPrivateKey(); + + public void writePrivateKey(java.io.OutputStream out){ + byte[] plain=getPrivateKey(); + byte[][] _iv=new byte[1][]; + byte[] encoded=encrypt(plain, _iv); + if(encoded!=plain) + Util.bzero(plain); + byte[] iv=_iv[0]; + byte[] prv=Util.toBase64(encoded, 0, encoded.length); + + try{ + out.write(getBegin()); out.write(cr); + if(passphrase!=null){ + out.write(header[0]); out.write(cr); + out.write(header[1]); + for(int i=0; i<iv.length; i++){ + out.write(b2a((byte)((iv[i]>>>4)&0x0f))); + out.write(b2a((byte)(iv[i]&0x0f))); + } + out.write(cr); + out.write(cr); + } + int i=0; + while(i<prv.length){ + if(i+64<prv.length){ + out.write(prv, i, 64); + out.write(cr); + i+=64; + continue; + } + out.write(prv, i, prv.length-i); + out.write(cr); + break; + } + out.write(getEnd()); out.write(cr); + //out.close(); + } + catch(Exception e){ + } + } + + private static byte[] space=Util.str2byte(" "); + + abstract byte[] getKeyTypeName(); + public abstract int getKeyType(); + + public byte[] getPublicKeyBlob(){ return publickeyblob; } + + public void writePublicKey(java.io.OutputStream out, String comment){ + byte[] pubblob=getPublicKeyBlob(); + byte[] pub=Util.toBase64(pubblob, 0, pubblob.length); + try{ + out.write(getKeyTypeName()); out.write(space); + out.write(pub, 0, pub.length); out.write(space); + out.write(Util.str2byte(comment)); + out.write(cr); + } + catch(Exception e){ + } + } + + public void writePublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{ + FileOutputStream fos=new FileOutputStream(name); + writePublicKey(fos, comment); + fos.close(); + } + + public void writeSECSHPublicKey(java.io.OutputStream out, String comment){ + byte[] pubblob=getPublicKeyBlob(); + byte[] pub=Util.toBase64(pubblob, 0, pubblob.length); + try{ + out.write(Util.str2byte("---- BEGIN SSH2 PUBLIC KEY ----")); out.write(cr); + out.write(Util.str2byte("Comment: \""+comment+"\"")); out.write(cr); + int index=0; + while(index<pub.length){ + int len=70; + if((pub.length-index)<len)len=pub.length-index; + out.write(pub, index, len); out.write(cr); + index+=len; + } + out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr); + } + catch(Exception e){ + } + } + + public void writeSECSHPublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{ + FileOutputStream fos=new FileOutputStream(name); + writeSECSHPublicKey(fos, comment); + fos.close(); + } + + + public void writePrivateKey(String name) throws java.io.FileNotFoundException, java.io.IOException{ + FileOutputStream fos=new FileOutputStream(name); + writePrivateKey(fos); + fos.close(); + } + + public String getFingerPrint(){ + if(hash==null) hash=genHash(); + byte[] kblob=getPublicKeyBlob(); + if(kblob==null) return null; + return getKeySize()+" "+Util.getFingerPrint(hash, kblob); + } + + private byte[] encrypt(byte[] plain, byte[][] _iv){ + if(passphrase==null) return plain; + + if(cipher==null) cipher=genCipher(); + byte[] iv=_iv[0]=new byte[cipher.getIVSize()]; + + if(random==null) random=genRandom(); + random.fill(iv, 0, iv.length); + + byte[] key=genKey(passphrase, iv); + byte[] encoded=plain; + + // PKCS#5Padding + { + //int bsize=cipher.getBlockSize(); + int bsize=cipher.getIVSize(); + byte[] foo=new byte[(encoded.length/bsize+1)*bsize]; + System.arraycopy(encoded, 0, foo, 0, encoded.length); + int padding=bsize-encoded.length%bsize; + for(int i=foo.length-1; (foo.length-padding)<=i; i--){ + foo[i]=(byte)padding; + } + encoded=foo; + } + + try{ + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + cipher.update(encoded, 0, encoded.length, encoded, 0); + } + catch(Exception e){ + //System.err.println(e); + } + Util.bzero(key); + return encoded; + } + + abstract boolean parse(byte[] data); + + private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv){ + /* + if(iv==null){ // FSecure + iv=new byte[8]; + for(int i=0; i<iv.length; i++)iv[i]=0; + } + */ + try{ + byte[] key=genKey(passphrase, iv); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Util.bzero(key); + byte[] plain=new byte[data.length]; + cipher.update(data, 0, data.length, plain, 0); + return plain; + } + catch(Exception e){ + //System.err.println(e); + } + return null; + } + + int writeSEQUENCE(byte[] buf, int index, int len){ + buf[index++]=0x30; + index=writeLength(buf, index, len); + return index; + } + int writeINTEGER(byte[] buf, int index, byte[] data){ + buf[index++]=0x02; + index=writeLength(buf, index, data.length); + System.arraycopy(data, 0, buf, index, data.length); + index+=data.length; + return index; + } + + int countLength(int len){ + int i=1; + if(len<=0x7f) return i; + while(len>0){ + len>>>=8; + i++; + } + return i; + } + + int writeLength(byte[] data, int index, int len){ + int i=countLength(len)-1; + if(i==0){ + data[index++]=(byte)len; + return index; + } + data[index++]=(byte)(0x80|i); + int j=index+i; + while(i>0){ + data[index+i-1]=(byte)(len&0xff); + len>>>=8; + i--; + } + return j; + } + + private Random genRandom(){ + if(random==null){ + try{ + Class c=Class.forName(jsch.getConfig("random")); + random=(Random)(c.newInstance()); + } + catch(Exception e){ System.err.println("connect: random "+e); } + } + return random; + } + + private HASH genHash(){ + try{ + Class c=Class.forName(jsch.getConfig("md5")); + hash=(HASH)(c.newInstance()); + hash.init(); + } + catch(Exception e){ + } + return hash; + } + private Cipher genCipher(){ + try{ + Class c; + c=Class.forName(jsch.getConfig("3des-cbc")); + cipher=(Cipher)(c.newInstance()); + } + catch(Exception e){ + } + return cipher; + } + + /* + hash is MD5 + h(0) <- hash(passphrase, iv); + h(n) <- hash(h(n-1), passphrase, iv); + key <- (h(0),...,h(n))[0,..,key.length]; + */ + synchronized byte[] genKey(byte[] passphrase, byte[] iv){ + if(cipher==null) cipher=genCipher(); + if(hash==null) hash=genHash(); + + byte[] key=new byte[cipher.getBlockSize()]; + int hsize=hash.getBlockSize(); + byte[] hn=new byte[key.length/hsize*hsize+ + (key.length%hsize==0?0:hsize)]; + try{ + byte[] tmp=null; + if(vendor==VENDOR_OPENSSH){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + hash.update(iv, 0, iv.length > 8 ? 8: iv.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + else if(vendor==VENDOR_FSECURE){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + } + catch(Exception e){ + System.err.println(e); + } + return key; + } + + public void setPassphrase(String passphrase){ + if(passphrase==null || passphrase.length()==0){ + setPassphrase((byte[])null); + } + else{ + setPassphrase(Util.str2byte(passphrase)); + } + } + public void setPassphrase(byte[] passphrase){ + if(passphrase!=null && passphrase.length==0) + passphrase=null; + this.passphrase=passphrase; + } + + private boolean encrypted=false; + private byte[] data=null; + private byte[] iv=null; + private byte[] publickeyblob=null; + + public boolean isEncrypted(){ return encrypted; } + public boolean decrypt(String _passphrase){ + if(_passphrase==null || _passphrase.length()==0){ + return !encrypted; + } + return decrypt(Util.str2byte(_passphrase)); + } + public boolean decrypt(byte[] _passphrase){ + if(!encrypted){ + return true; + } + if(_passphrase==null){ + return !encrypted; + } + byte[] bar=new byte[_passphrase.length]; + System.arraycopy(_passphrase, 0, bar, 0, bar.length); + _passphrase=bar; + byte[] foo=decrypt(data, _passphrase, iv); + Util.bzero(_passphrase); + if(parse(foo)){ + encrypted=false; + } + return !encrypted; + } + + public static KeyPair load(JSch jsch, String prvkey) throws JSchException{ + String pubkey=prvkey+".pub"; + if(!new File(pubkey).exists()){ + pubkey=null; + } + return load(jsch, prvkey, pubkey); + } + public static KeyPair load(JSch jsch, String prvkey, String pubkey) throws JSchException{ + + byte[] iv=new byte[8]; // 8 + boolean encrypted=true; + byte[] data=null; + + byte[] publickeyblob=null; + + int type=ERROR; + int vendor=VENDOR_OPENSSH; + String publicKeyComment = ""; + Cipher cipher=null; + + try{ + File file=new File(prvkey); + FileInputStream fis=new FileInputStream(prvkey); + byte[] buf=new byte[(int)(file.length())]; + int len=0; + while(true){ + int i=fis.read(buf, len, buf.length-len); + if(i<=0) + break; + len+=i; + } + fis.close(); + + int i=0; + + while(i<len){ + if(buf[i] == '-' && i+4<len && + buf[i+1] == '-' && buf[i+2] == '-' && + buf[i+3] == '-' && buf[i+4] == '-'){ + break; + } + i++; + } + + while(i<len){ + if(buf[i]=='B'&& i+3<len && buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){ + i+=6; + if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; } + else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; } + else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure + type=UNKNOWN; + vendor=VENDOR_FSECURE; + } + else{ + throw new JSchException("invalid privatekey: "+prvkey); + } + i+=3; + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='2'&& buf[i+5]=='5'&& buf[i+6]=='6'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes256-cbc"))){ + Class c=Class.forName((String)jsch.getConfig("aes256-cbc")); + cipher=(Cipher)(c.newInstance()); + // key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes256-cbc is not available "+prvkey); + } + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='1'&& buf[i+5]=='9'&& buf[i+6]=='2'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes192-cbc"))){ + Class c=Class.forName((String)jsch.getConfig("aes192-cbc")); + cipher=(Cipher)(c.newInstance()); + // key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes192-cbc is not available "+prvkey); + } + continue; + } + if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && + buf[i+4]=='1'&& buf[i+5]=='2'&& buf[i+6]=='8'&& buf[i+7]=='-'){ + i+=8; + if(Session.checkCipher((String)jsch.getConfig("aes128-cbc"))){ + Class c=Class.forName((String)jsch.getConfig("aes128-cbc")); + cipher=(Cipher)(c.newInstance()); + // key=new byte[cipher.getBlockSize()]; + iv=new byte[cipher.getIVSize()]; + } + else{ + throw new JSchException("privatekey: aes128-cbc is not available "+prvkey); + } + continue; + } + if(buf[i]=='C'&& i+3<len && buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){ + i+=4; + for(int ii=0; ii<iv.length; ii++){ + iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf)); + } + continue; + } + if(buf[i]==0x0d && i+1<buf.length && buf[i+1]==0x0a){ + i++; + continue; + } + if(buf[i]==0x0a && i+1<buf.length){ + if(buf[i+1]==0x0a){ i+=2; break; } + if(buf[i+1]==0x0d && + i+2<buf.length && buf[i+2]==0x0a){ + i+=3; break; + } + boolean inheader=false; + for(int j=i+1; j<buf.length; j++){ + if(buf[j]==0x0a) break; + //if(buf[j]==0x0d) break; + if(buf[j]==':'){inheader=true; break;} + } + if(!inheader){ + i++; + encrypted=false; // no passphrase + break; + } + } + i++; + } + + if(type==ERROR){ + throw new JSchException("invalid privatekey: "+prvkey); + } + + int start=i; + while(i<len){ + if(buf[i]==0x0a){ + boolean xd=(buf[i-1]==0x0d); + System.arraycopy(buf, i+1, + buf, + i-(xd ? 1 : 0), + len-i-1-(xd ? 1 : 0) + ); + if(xd)len--; + len--; + continue; + } + if(buf[i]=='-'){ break; } + i++; + } + data=Util.fromBase64(buf, start, i-start); + + if(data.length>4 && // FSecure + data[0]==(byte)0x3f && + data[1]==(byte)0x6f && + data[2]==(byte)0xf9 && + data[3]==(byte)0xeb){ + + Buffer _buf=new Buffer(data); + _buf.getInt(); // 0x3f6ff9be + _buf.getInt(); + byte[]_type=_buf.getString(); + //System.err.println("type: "+new String(_type)); + String _cipher=Util.byte2str(_buf.getString()); + //System.err.println("cipher: "+_cipher); + if(_cipher.equals("3des-cbc")){ + _buf.getInt(); + byte[] foo=new byte[data.length-_buf.getOffSet()]; + _buf.getByte(foo); + data=foo; + encrypted=true; + throw new JSchException("unknown privatekey format: "+prvkey); + } + else if(_cipher.equals("none")){ + _buf.getInt(); + _buf.getInt(); + + encrypted=false; + + byte[] foo=new byte[data.length-_buf.getOffSet()]; + _buf.getByte(foo); + data=foo; + } + } + + if(pubkey!=null){ + try{ + file=new File(pubkey); + fis=new FileInputStream(pubkey); + buf=new byte[(int)(file.length())]; + len=0; + while(true){ + i=fis.read(buf, len, buf.length-len); + if(i<=0) + break; + len+=i; + } + fis.close(); + + if(buf.length>4 && // FSecure's public key + buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){ + + boolean valid=true; + i=0; + do{i++;}while(buf.length>i && buf[i]!=0x0a); + if(buf.length<=i) {valid=false;} + + while(valid){ + if(buf[i]==0x0a){ + boolean inheader=false; + for(int j=i+1; j<buf.length; j++){ + if(buf[j]==0x0a) break; + if(buf[j]==':'){inheader=true; break;} + } + if(!inheader){ + i++; + break; + } + } + i++; + } + if(buf.length<=i){valid=false;} + + start=i; + while(valid && i<len){ + if(buf[i]==0x0a){ + System.arraycopy(buf, i+1, buf, i, len-i-1); + len--; + continue; + } + if(buf[i]=='-'){ break; } + i++; + } + if(valid){ + publickeyblob=Util.fromBase64(buf, start, i-start); + if(type==UNKNOWN){ + if(publickeyblob[8]=='d'){ type=DSA; } + else if(publickeyblob[8]=='r'){ type=RSA; } + } + } + } + else{ + if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-'){ + i=0; + while(i<len){ if(buf[i]==' ')break; i++;} i++; + if(i<len){ + start=i; + while(i<len){ if(buf[i]==' ')break; i++;} + publickeyblob=Util.fromBase64(buf, start, i-start); + } + if(i++<len){ + int s=i; + while(i<len){ if(buf[i]=='\n')break; i++;} + if(i<len){ + publicKeyComment = new String(buf, s, i-s); + } + } + } + } + } + catch(Exception ee){ + } + } + } + catch(Exception e){ + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + + KeyPair kpair=null; + if(type==DSA){ kpair=new KeyPairDSA(jsch); } + else if(type==RSA){ kpair=new KeyPairRSA(jsch); } + + if(kpair!=null){ + kpair.encrypted=encrypted; + kpair.publickeyblob=publickeyblob; + kpair.vendor=vendor; + kpair.publicKeyComment=publicKeyComment; + kpair.cipher=cipher; + + if(encrypted){ + kpair.iv=iv; + kpair.data=data; + } + else{ + if(kpair.parse(data)){ + return kpair; + } + else{ + throw new JSchException("invalid privatekey: "+prvkey); + } + } + } + + return kpair; + } + + static private byte a2b(byte c){ + if('0'<=c&&c<='9') return (byte)(c-'0'); + return (byte)(c-'a'+10); + } + static private byte b2a(byte c){ + if(0<=c&&c<=9) return (byte)(c+'0'); + return (byte)(c-10+'A'); + } + + public void dispose(){ + Util.bzero(passphrase); + } + + public void finalize (){ + dispose(); + } +} diff --git a/java/com/jcraft/jsch/KeyPairDSA.java b/java/com/jcraft/jsch/KeyPairDSA.java new file mode 100644 index 00000000..f65d8c00 --- /dev/null +++ b/java/com/jcraft/jsch/KeyPairDSA.java @@ -0,0 +1,221 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class KeyPairDSA extends KeyPair{ + private byte[] P_array; + private byte[] Q_array; + private byte[] G_array; + private byte[] pub_array; + private byte[] prv_array; + + //private int key_size=0; + private int key_size=1024; + + public KeyPairDSA(JSch jsch){ + super(jsch); + } + + void generate(int key_size) throws JSchException{ + this.key_size=key_size; + try{ + Class c=Class.forName(jsch.getConfig("keypairgen.dsa")); + KeyPairGenDSA keypairgen=(KeyPairGenDSA)(c.newInstance()); + keypairgen.init(key_size); + P_array=keypairgen.getP(); + Q_array=keypairgen.getQ(); + G_array=keypairgen.getG(); + pub_array=keypairgen.getY(); + prv_array=keypairgen.getX(); + + keypairgen=null; + } + catch(Exception e){ + //System.err.println("KeyPairDSA: "+e); + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + private static final byte[] begin=Util.str2byte("-----BEGIN DSA PRIVATE KEY-----"); + private static final byte[] end=Util.str2byte("-----END DSA PRIVATE KEY-----"); + + byte[] getBegin(){ return begin; } + byte[] getEnd(){ return end; } + + byte[] getPrivateKey(){ + int content= + 1+countLength(1) + 1 + // INTEGER + 1+countLength(P_array.length) + P_array.length + // INTEGER P + 1+countLength(Q_array.length) + Q_array.length + // INTEGER Q + 1+countLength(G_array.length) + G_array.length + // INTEGER G + 1+countLength(pub_array.length) + pub_array.length + // INTEGER pub + 1+countLength(prv_array.length) + prv_array.length; // INTEGER prv + + int total= + 1+countLength(content)+content; // SEQUENCE + + byte[] plain=new byte[total]; + int index=0; + index=writeSEQUENCE(plain, index, content); + index=writeINTEGER(plain, index, new byte[1]); // 0 + index=writeINTEGER(plain, index, P_array); + index=writeINTEGER(plain, index, Q_array); + index=writeINTEGER(plain, index, G_array); + index=writeINTEGER(plain, index, pub_array); + index=writeINTEGER(plain, index, prv_array); + return plain; + } + + boolean parse(byte[] plain){ + try{ + + if(vendor==VENDOR_FSECURE){ + if(plain[0]!=0x30){ // FSecure + Buffer buf=new Buffer(plain); + buf.getInt(); + P_array=buf.getMPIntBits(); + G_array=buf.getMPIntBits(); + Q_array=buf.getMPIntBits(); + pub_array=buf.getMPIntBits(); + prv_array=buf.getMPIntBits(); + return true; + } + return false; + } + + int index=0; + int length=0; + + if(plain[index]!=0x30)return false; + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + P_array=new byte[length]; + System.arraycopy(plain, index, P_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + Q_array=new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + G_array=new byte[length]; + System.arraycopy(plain, index, G_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + pub_array=new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index+=length; + } + catch(Exception e){ + //System.err.println(e); + //e.printStackTrace(); + return false; + } + return true; + } + + public byte[] getPublicKeyBlob(){ + byte[] foo=super.getPublicKeyBlob(); + if(foo!=null) return foo; + + if(P_array==null) return null; + + Buffer buf=new Buffer(sshdss.length+4+ + P_array.length+4+ + Q_array.length+4+ + G_array.length+4+ + pub_array.length+4); + buf.putString(sshdss); + buf.putString(P_array); + buf.putString(Q_array); + buf.putString(G_array); + buf.putString(pub_array); + return buf.buffer; + } + + private static final byte[] sshdss=Util.str2byte("ssh-dss"); + byte[] getKeyTypeName(){return sshdss;} + public int getKeyType(){return DSA;} + + public int getKeySize(){return key_size; } + public void dispose(){ + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/java/com/jcraft/jsch/KeyPairGenDSA.java b/java/com/jcraft/jsch/KeyPairGenDSA.java new file mode 100644 index 00000000..f6507f24 --- /dev/null +++ b/java/com/jcraft/jsch/KeyPairGenDSA.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KeyPairGenDSA{ + void init(int key_size) throws Exception; + byte[] getX(); + byte[] getY(); + byte[] getP(); + byte[] getQ(); + byte[] getG(); +} diff --git a/java/com/jcraft/jsch/KeyPairGenRSA.java b/java/com/jcraft/jsch/KeyPairGenRSA.java new file mode 100644 index 00000000..3a849074 --- /dev/null +++ b/java/com/jcraft/jsch/KeyPairGenRSA.java @@ -0,0 +1,43 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KeyPairGenRSA{ + void init(int key_size) throws Exception; + byte[] getD(); + byte[] getE(); + byte[] getN(); + + byte[] getC(); + byte[] getEP(); + byte[] getEQ(); + byte[] getP(); + byte[] getQ(); +} diff --git a/java/com/jcraft/jsch/KeyPairRSA.java b/java/com/jcraft/jsch/KeyPairRSA.java new file mode 100644 index 00000000..dfc202f9 --- /dev/null +++ b/java/com/jcraft/jsch/KeyPairRSA.java @@ -0,0 +1,320 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class KeyPairRSA extends KeyPair{ + private byte[] prv_array; + private byte[] pub_array; + private byte[] n_array; + + private byte[] p_array; // prime p + private byte[] q_array; // prime q + private byte[] ep_array; // prime exponent p + private byte[] eq_array; // prime exponent q + private byte[] c_array; // coefficient + + //private int key_size=0; + private int key_size=1024; + + public KeyPairRSA(JSch jsch){ + super(jsch); + } + + void generate(int key_size) throws JSchException{ + this.key_size=key_size; + try{ + Class c=Class.forName(jsch.getConfig("keypairgen.rsa")); + KeyPairGenRSA keypairgen=(KeyPairGenRSA)(c.newInstance()); + keypairgen.init(key_size); + pub_array=keypairgen.getE(); + prv_array=keypairgen.getD(); + n_array=keypairgen.getN(); + + p_array=keypairgen.getP(); + q_array=keypairgen.getQ(); + ep_array=keypairgen.getEP(); + eq_array=keypairgen.getEQ(); + c_array=keypairgen.getC(); + + keypairgen=null; + } + catch(Exception e){ + //System.err.println("KeyPairRSA: "+e); + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + private static final byte[] begin=Util.str2byte("-----BEGIN RSA PRIVATE KEY-----"); + private static final byte[] end=Util.str2byte("-----END RSA PRIVATE KEY-----"); + + byte[] getBegin(){ return begin; } + byte[] getEnd(){ return end; } + + byte[] getPrivateKey(){ + int content= + 1+countLength(1) + 1 + // INTEGER + 1+countLength(n_array.length) + n_array.length + // INTEGER N + 1+countLength(pub_array.length) + pub_array.length + // INTEGER pub + 1+countLength(prv_array.length) + prv_array.length+ // INTEGER prv + 1+countLength(p_array.length) + p_array.length+ // INTEGER p + 1+countLength(q_array.length) + q_array.length+ // INTEGER q + 1+countLength(ep_array.length) + ep_array.length+ // INTEGER ep + 1+countLength(eq_array.length) + eq_array.length+ // INTEGER eq + 1+countLength(c_array.length) + c_array.length; // INTEGER c + + int total= + 1+countLength(content)+content; // SEQUENCE + + byte[] plain=new byte[total]; + int index=0; + index=writeSEQUENCE(plain, index, content); + index=writeINTEGER(plain, index, new byte[1]); // 0 + index=writeINTEGER(plain, index, n_array); + index=writeINTEGER(plain, index, pub_array); + index=writeINTEGER(plain, index, prv_array); + index=writeINTEGER(plain, index, p_array); + index=writeINTEGER(plain, index, q_array); + index=writeINTEGER(plain, index, ep_array); + index=writeINTEGER(plain, index, eq_array); + index=writeINTEGER(plain, index, c_array); + return plain; + } + + boolean parse(byte [] plain){ + /* + byte[] p_array; + byte[] q_array; + byte[] dmp1_array; + byte[] dmq1_array; + byte[] iqmp_array; + */ + try{ + int index=0; + int length=0; + + if(vendor==VENDOR_FSECURE){ + if(plain[index]!=0x30){ // FSecure + Buffer buf=new Buffer(plain); + pub_array=buf.getMPIntBits(); + prv_array=buf.getMPIntBits(); + n_array=buf.getMPIntBits(); + byte[] u_array=buf.getMPIntBits(); + p_array=buf.getMPIntBits(); + q_array=buf.getMPIntBits(); + return true; + } + return false; + } + + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + +//System.err.println("int: len="+length); +//System.err.print(Integer.toHexString(plain[index-1]&0xff)+":"); +//System.err.println(""); + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + n_array=new byte[length]; + System.arraycopy(plain, index, n_array, 0, length); + index+=length; +/* +System.err.println("int: N len="+length); +for(int i=0; i<n_array.length; i++){ +System.err.print(Integer.toHexString(n_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + pub_array=new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index+=length; +/* +System.err.println("int: E len="+length); +for(int i=0; i<pub_array.length; i++){ +System.err.print(Integer.toHexString(pub_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index+=length; +/* +System.err.println("int: prv len="+length); +for(int i=0; i<prv_array.length; i++){ +System.err.print(Integer.toHexString(prv_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + p_array=new byte[length]; + System.arraycopy(plain, index, p_array, 0, length); + index+=length; +/* +System.err.println("int: P len="+length); +for(int i=0; i<p_array.length; i++){ +System.err.print(Integer.toHexString(p_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + q_array=new byte[length]; + System.arraycopy(plain, index, q_array, 0, length); + index+=length; +/* +System.err.println("int: q len="+length); +for(int i=0; i<q_array.length; i++){ +System.err.print(Integer.toHexString(q_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + ep_array=new byte[length]; + System.arraycopy(plain, index, ep_array, 0, length); + index+=length; +/* +System.err.println("int: ep len="+length); +for(int i=0; i<ep_array.length; i++){ +System.err.print(Integer.toHexString(ep_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + eq_array=new byte[length]; + System.arraycopy(plain, index, eq_array, 0, length); + index+=length; +/* +System.err.println("int: eq len="+length); +for(int i=0; i<eq_array.length; i++){ +System.err.print(Integer.toHexString(eq_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + c_array=new byte[length]; + System.arraycopy(plain, index, c_array, 0, length); + index+=length; +/* +System.err.println("int: c len="+length); +for(int i=0; i<c_array.length; i++){ +System.err.print(Integer.toHexString(c_array[i]&0xff)+":"); +} +System.err.println(""); +*/ + } + catch(Exception e){ + //System.err.println(e); + return false; + } + return true; + } + + + public byte[] getPublicKeyBlob(){ + byte[] foo=super.getPublicKeyBlob(); + if(foo!=null) return foo; + + if(pub_array==null) return null; + + Buffer buf=new Buffer(sshrsa.length+4+ + pub_array.length+4+ + n_array.length+4); + buf.putString(sshrsa); + buf.putString(pub_array); + buf.putString(n_array); + return buf.buffer; + } + + private static final byte[] sshrsa=Util.str2byte("ssh-rsa"); + byte[] getKeyTypeName(){return sshrsa;} + public int getKeyType(){return RSA;} + + public int getKeySize(){return key_size; } + public void dispose(){ + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/java/com/jcraft/jsch/KnownHosts.java b/java/com/jcraft/jsch/KnownHosts.java new file mode 100644 index 00000000..ac76477e --- /dev/null +++ b/java/com/jcraft/jsch/KnownHosts.java @@ -0,0 +1,506 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public +class KnownHosts implements HostKeyRepository{ + private static final String _known_hosts="known_hosts"; + + /* + static final int SSHDSS=0; + static final int SSHRSA=1; + static final int UNKNOWN=2; + */ + + private JSch jsch=null; + private String known_hosts=null; + private java.util.Vector pool=null; + + private MAC hmacsha1=null; + + KnownHosts(JSch jsch){ + super(); + this.jsch=jsch; + pool=new java.util.Vector(); + } + + void setKnownHosts(String foo) throws JSchException{ + try{ + known_hosts=foo; + FileInputStream fis=new FileInputStream(foo); + setKnownHosts(fis); + } + catch(FileNotFoundException e){ + } + } + void setKnownHosts(InputStream foo) throws JSchException{ + pool.removeAllElements(); + StringBuffer sb=new StringBuffer(); + byte i; + int j; + boolean error=false; + try{ + InputStream fis=foo; + String host; + String key=null; + int type; + byte[] buf=new byte[1024]; + int bufl=0; +loop: + while(true){ + bufl=0; + while(true){ + j=fis.read(); + if(j==-1){ + if(bufl==0){ break loop; } + else{ break; } + } + if(j==0x0d){ continue; } + if(j==0x0a){ break; } + if(buf.length<=bufl){ + if(bufl>1024*10) break; // too long... + byte[] newbuf=new byte[buf.length*2]; + System.arraycopy(buf, 0, newbuf, 0, buf.length); + buf=newbuf; + } + buf[bufl++]=(byte)j; + } + + j=0; + while(j<bufl){ + i=buf[j]; + if(i==' '||i=='\t'){ j++; continue; } + if(i=='#'){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + break; + } + if(j>=bufl){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + sb.setLength(0); + while(j<bufl){ + i=buf[j++]; + if(i==0x20 || i=='\t'){ break; } + sb.append((char)i); + } + host=sb.toString(); + if(j>=bufl || host.length()==0){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + sb.setLength(0); + type=-1; + while(j<bufl){ + i=buf[j++]; + if(i==0x20 || i=='\t'){ break; } + sb.append((char)i); + } + if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; } + else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; } + else { j=bufl; } + if(j>=bufl){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + sb.setLength(0); + while(j<bufl){ + i=buf[j++]; + if(i==0x0d){ continue; } + if(i==0x0a){ break; } + sb.append((char)i); + } + key=sb.toString(); + if(key.length()==0){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + //System.err.println(host); + //System.err.println("|"+key+"|"); + + HostKey hk = null; + hk = new HashedHostKey(host, type, + Util.fromBase64(Util.str2byte(key), 0, + key.length())); + pool.addElement(hk); + } + fis.close(); + if(error){ + throw new JSchException("KnownHosts: invalid format"); + } + } + catch(Exception e){ + if(e instanceof JSchException) + throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + private void addInvalidLine(String line) throws JSchException { + HostKey hk = new HostKey(line, HostKey.UNKNOWN, null); + pool.addElement(hk); + } + String getKnownHostsFile(){ return known_hosts; } + public String getKnownHostsRepositoryID(){ return known_hosts; } + + public int check(String host, byte[] key){ + int result=NOT_INCLUDED; + if(host==null){ + return result; + } + + int type=getType(key); + HostKey hk; + + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + hk=(HostKey)(pool.elementAt(i)); + if(hk.isMatched(host) && hk.type==type){ + if(Util.array_equals(hk.key, key)){ + return OK; + } + else{ + result=CHANGED; + } + } + } + } + + if(result==NOT_INCLUDED && + host.startsWith("[") && + host.indexOf("]:")>1 + ){ + return check(host.substring(1, host.indexOf("]:")), key); + } + + return result; + } + public void add(HostKey hostkey, UserInfo userinfo){ + int type=hostkey.type; + String host=hostkey.getHost(); + byte[] key=hostkey.key; + + HostKey hk=null; + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + hk=(HostKey)(pool.elementAt(i)); + if(hk.isMatched(host) && hk.type==type){ +/* + if(Util.array_equals(hk.key, key)){ return; } + if(hk.host.equals(host)){ + hk.key=key; + return; + } + else{ + hk.host=deleteSubString(hk.host, host); + break; + } +*/ + } + } + } + + hk=hostkey; + + pool.addElement(hk); + + String bar=getKnownHostsRepositoryID(); + if(bar!=null){ + boolean foo=true; + File goo=new File(bar); + if(!goo.exists()){ + foo=false; + if(userinfo!=null){ + foo=userinfo.promptYesNo(bar+" does not exist.\n"+ + "Are you sure you want to create it?" + ); + goo=goo.getParentFile(); + if(foo && goo!=null && !goo.exists()){ + foo=userinfo.promptYesNo("The parent directory "+goo+" does not exist.\n"+ + "Are you sure you want to create it?" + ); + if(foo){ + if(!goo.mkdirs()){ + userinfo.showMessage(goo+" has not been created."); + foo=false; + } + else{ + userinfo.showMessage(goo+" has been succesfully created.\nPlease check its access permission."); + } + } + } + if(goo==null)foo=false; + } + } + if(foo){ + try{ + sync(bar); + } + catch(Exception e){ System.err.println("sync known_hosts: "+e); } + } + } + } + + public HostKey[] getHostKey(){ + return getHostKey(null, null); + } + public HostKey[] getHostKey(String host, String type){ + synchronized(pool){ + int count=0; + for(int i=0; i<pool.size(); i++){ + HostKey hk=(HostKey)pool.elementAt(i); + if(hk.type==HostKey.UNKNOWN) continue; + if(host==null || + (hk.isMatched(host) && + (type==null || hk.getType().equals(type)))){ + count++; + } + } + if(count==0)return null; + HostKey[] foo=new HostKey[count]; + int j=0; + for(int i=0; i<pool.size(); i++){ + HostKey hk=(HostKey)pool.elementAt(i); + if(hk.type==HostKey.UNKNOWN) continue; + if(host==null || + (hk.isMatched(host) && + (type==null || hk.getType().equals(type)))){ + foo[j++]=hk; + } + } + return foo; + } + } + public void remove(String host, String type){ + remove(host, type, null); + } + public void remove(String host, String type, byte[] key){ + boolean sync=false; + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + HostKey hk=(HostKey)(pool.elementAt(i)); + if(host==null || + (hk.isMatched(host) && + (type==null || (hk.getType().equals(type) && + (key==null || Util.array_equals(key, hk.key)))))){ + String hosts=hk.getHost(); + if(hosts.equals(host) || + ((hk instanceof HashedHostKey) && + ((HashedHostKey)hk).isHashed())){ + pool.removeElement(hk); + } + else{ + hk.host=deleteSubString(hosts, host); + } + sync=true; + } + } + } + if(sync){ + try{sync();}catch(Exception e){}; + } + } + + protected void sync() throws IOException { + if(known_hosts!=null) + sync(known_hosts); + } + protected synchronized void sync(String foo) throws IOException { + if(foo==null) return; + FileOutputStream fos=new FileOutputStream(foo); + dump(fos); + fos.close(); + } + + private static final byte[] space={(byte)0x20}; + private static final byte[] cr=Util.str2byte("\n"); + void dump(OutputStream out) throws IOException { + try{ + HostKey hk; + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + hk=(HostKey)(pool.elementAt(i)); + //hk.dump(out); + String host=hk.getHost(); + String type=hk.getType(); + if(type.equals("UNKNOWN")){ + out.write(Util.str2byte(host)); + out.write(cr); + continue; + } + out.write(Util.str2byte(host)); + out.write(space); + out.write(Util.str2byte(type)); + out.write(space); + out.write(Util.str2byte(hk.getKey())); + out.write(cr); + } + } + } + catch(Exception e){ + System.err.println(e); + } + } + private int getType(byte[] key){ + if(key[8]=='d') return HostKey.SSHDSS; + if(key[8]=='r') return HostKey.SSHRSA; + return HostKey.UNKNOWN; + } + private String deleteSubString(String hosts, String host){ + int i=0; + int hostlen=host.length(); + int hostslen=hosts.length(); + int j; + while(i<hostslen){ + j=hosts.indexOf(',', i); + if(j==-1) break; + if(!host.equals(hosts.substring(i, j))){ + i=j+1; + continue; + } + return hosts.substring(0, i)+hosts.substring(j+1); + } + if(hosts.endsWith(host) && hostslen-i==hostlen){ + return hosts.substring(0, (hostlen==hostslen) ? 0 :hostslen-hostlen-1); + } + return hosts; + } + + private synchronized MAC getHMACSHA1(){ + if(hmacsha1==null){ + try{ + Class c=Class.forName(jsch.getConfig("hmac-sha1")); + hmacsha1=(MAC)(c.newInstance()); + } + catch(Exception e){ + System.err.println("hmacsha1: "+e); + } + } + return hmacsha1; + } + + HostKey createHashedHostKey(String host, byte[]key) throws JSchException { + HashedHostKey hhk=new HashedHostKey(host, key); + hhk.hash(); + return hhk; + } + class HashedHostKey extends HostKey{ + private static final String HASH_MAGIC="|1|"; + private static final String HASH_DELIM="|"; + + private boolean hashed=false; + byte[] salt=null; + byte[] hash=null; + + + HashedHostKey(String host, byte[] key) throws JSchException { + this(host, GUESS, key); + } + HashedHostKey(String host, int type, byte[] key) throws JSchException { + super(host, type, key); + if(this.host.startsWith(HASH_MAGIC) && + this.host.substring(HASH_MAGIC.length()).indexOf(HASH_DELIM)>0){ + String data=this.host.substring(HASH_MAGIC.length()); + String _salt=data.substring(0, data.indexOf(HASH_DELIM)); + String _hash=data.substring(data.indexOf(HASH_DELIM)+1); + salt=Util.fromBase64(Util.str2byte(_salt), 0, _salt.length()); + hash=Util.fromBase64(Util.str2byte(_hash), 0, _hash.length()); + if(salt.length!=20 || // block size of hmac-sha1 + hash.length!=20){ + salt=null; + hash=null; + return; + } + hashed=true; + } + } + + boolean isMatched(String _host){ + if(!hashed){ + return super.isMatched(_host); + } + MAC macsha1=getHMACSHA1(); + try{ + synchronized(macsha1){ + macsha1.init(salt); + byte[] foo=Util.str2byte(_host); + macsha1.update(foo, 0, foo.length); + byte[] bar=new byte[macsha1.getBlockSize()]; + macsha1.doFinal(bar, 0); + return Util.array_equals(hash, bar); + } + } + catch(Exception e){ + System.out.println(e); + } + return false; + } + + boolean isHashed(){ + return hashed; + } + + void hash(){ + if(hashed) + return; + MAC macsha1=getHMACSHA1(); + if(salt==null){ + Random random=Session.random; + synchronized(random){ + salt=new byte[macsha1.getBlockSize()]; + random.fill(salt, 0, salt.length); + } + } + try{ + synchronized(macsha1){ + macsha1.init(salt); + byte[] foo=Util.str2byte(host); + macsha1.update(foo, 0, foo.length); + hash=new byte[macsha1.getBlockSize()]; + macsha1.doFinal(hash, 0); + } + } + catch(Exception e){ + } + host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+ + HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length)); + hashed=true; + } + } +} diff --git a/java/com/jcraft/jsch/LICENSE.txt b/java/com/jcraft/jsch/LICENSE.txt new file mode 100644 index 00000000..81c8eacf --- /dev/null +++ b/java/com/jcraft/jsch/LICENSE.txt @@ -0,0 +1,30 @@ +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002-2012 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/java/com/jcraft/jsch/LocalIdentityRepository.java b/java/com/jcraft/jsch/LocalIdentityRepository.java new file mode 100644 index 00000000..be814152 --- /dev/null +++ b/java/com/jcraft/jsch/LocalIdentityRepository.java @@ -0,0 +1,90 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +class LocalIdentityRepository implements IdentityRepository { + + private Vector identities = new Vector(); + private JSch jsch; + + LocalIdentityRepository(JSch jsch){ + this.jsch = jsch; + } + + public synchronized Vector getIdentities() { + Vector v = new Vector(); + for(int i=0; i<identities.size(); i++){ + v.addElement(identities.elementAt(i)); + } + return v; + } + + public synchronized void add(Identity identity) { + if(!identities.contains(identity)) { + identities.addElement(identity); + } + } + + public synchronized boolean add(byte[] identity) { + try{ + Identity _identity = + IdentityFile.newInstance("from remote:", identity, null, jsch); + identities.addElement(_identity); + return true; + } + catch(JSchException e){ + return false; + } + } + + public synchronized boolean remove(byte[] blob) { + if(blob == null) return false; + for(int i=0; i<identities.size(); i++) { + Identity _identity = (Identity)(identities.elementAt(i)); + byte[] _blob = _identity.getPublicKeyBlob(); + if(_blob == null || !Util.array_equals(blob, _blob)) + continue; + identities.removeElement(_identity); + _identity.clear(); + return true; + } + return false; + } + + public synchronized void removeAll() { + for(int i=0; i<identities.size(); i++) { + Identity identity=(Identity)(identities.elementAt(i)); + identity.clear(); + } + identities.removeAllElements(); + } +} diff --git a/java/com/jcraft/jsch/Logger.java b/java/com/jcraft/jsch/Logger.java new file mode 100644 index 00000000..1263722b --- /dev/null +++ b/java/com/jcraft/jsch/Logger.java @@ -0,0 +1,54 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Logger{ + + public final int DEBUG=0; + public final int INFO=1; + public final int WARN=2; + public final int ERROR=3; + public final int FATAL=4; + + public boolean isEnabled(int level); + + public void log(int level, String message); + + /* + public final Logger SIMPLE_LOGGER=new Logger(){ + public boolean isEnabled(int level){return true;} + public void log(int level, String message){System.err.println(message);} + }; + final Logger DEVNULL=new Logger(){ + public boolean isEnabled(int level){return false;} + public void log(int level, String message){} + }; + */ +} diff --git a/java/com/jcraft/jsch/MAC.java b/java/com/jcraft/jsch/MAC.java new file mode 100644 index 00000000..0475ce2c --- /dev/null +++ b/java/com/jcraft/jsch/MAC.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface MAC{ + String getName(); + int getBlockSize(); + void init(byte[] key) throws Exception; + void update(byte[] foo, int start, int len); + void update(int foo); + void doFinal(byte[] buf, int offset); +} diff --git a/java/com/jcraft/jsch/Packet.java b/java/com/jcraft/jsch/Packet.java new file mode 100644 index 00000000..9c441577 --- /dev/null +++ b/java/com/jcraft/jsch/Packet.java @@ -0,0 +1,115 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class Packet{ + + private static Random random=null; + static void setRandom(Random foo){ random=foo;} + + Buffer buffer; + byte[] ba4=new byte[4]; + public Packet(Buffer buffer){ + this.buffer=buffer; + } + public void reset(){ + buffer.index=5; + } + void padding(int bsize){ + int len=buffer.index; + int pad=(-len)&(bsize-1); + if(pad<bsize){ + pad+=bsize; + } + len=len+pad-4; + ba4[0]=(byte)(len>>>24); + ba4[1]=(byte)(len>>>16); + ba4[2]=(byte)(len>>>8); + ba4[3]=(byte)(len); + System.arraycopy(ba4, 0, buffer.buffer, 0, 4); + buffer.buffer[4]=(byte)pad; + synchronized(random){ + random.fill(buffer.buffer, buffer.index, pad); + } + buffer.skip(pad); + //buffer.putPad(pad); +/* +for(int i=0; i<buffer.index; i++){ +System.err.print(Integer.toHexString(buffer.buffer[i]&0xff)+":"); +} +System.err.println(""); +*/ + } + + int shift(int len, int bsize, int mac){ + int s=len+5+9; + int pad=(-s)&(bsize-1); + if(pad<bsize)pad+=bsize; + s+=pad; + s+=mac; + s+=32; // margin for deflater; deflater may inflate data + + /**/ + if(buffer.buffer.length<s+buffer.index-5-9-len){ + byte[] foo=new byte[s+buffer.index-5-9-len]; + System.arraycopy(buffer.buffer, 0, foo, 0, buffer.buffer.length); + buffer.buffer=foo; + } + /**/ + +//if(buffer.buffer.length<len+5+9) +// System.err.println("buffer.buffer.length="+buffer.buffer.length+" len+5+9="+(len+5+9)); + +//if(buffer.buffer.length<s) +// System.err.println("buffer.buffer.length="+buffer.buffer.length+" s="+(s)); + + System.arraycopy(buffer.buffer, + len+5+9, + buffer.buffer, s, buffer.index-5-9-len); + + buffer.index=10; + buffer.putInt(len); + buffer.index=len+5+9; + return s; + } + void unshift(byte command, int recipient, int s, int len){ + System.arraycopy(buffer.buffer, + s, + buffer.buffer, 5+9, len); + buffer.buffer[5]=command; + buffer.index=6; + buffer.putInt(recipient); + buffer.putInt(len); + buffer.index=len+5+9; + } + Buffer getBuffer(){ + return buffer; + } +} diff --git a/java/com/jcraft/jsch/PortWatcher.java b/java/com/jcraft/jsch/PortWatcher.java new file mode 100644 index 00000000..508e16bd --- /dev/null +++ b/java/com/jcraft/jsch/PortWatcher.java @@ -0,0 +1,194 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.io.*; + +class PortWatcher implements Runnable{ + private static java.util.Vector pool=new java.util.Vector(); + private static InetAddress anyLocalAddress=null; + static{ + // 0.0.0.0 +/* + try{ anyLocalAddress=InetAddress.getByAddress(new byte[4]); } + catch(UnknownHostException e){ + } +*/ + try{ anyLocalAddress=InetAddress.getByName("0.0.0.0"); } + catch(UnknownHostException e){ + } + } + + Session session; + int lport; + int rport; + String host; + InetAddress boundaddress; + Runnable thread; + ServerSocket ss; + + static String[] getPortForwarding(Session session){ + java.util.Vector foo=new java.util.Vector(); + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + PortWatcher p=(PortWatcher)(pool.elementAt(i)); + if(p.session==session){ + foo.addElement(p.lport+":"+p.host+":"+p.rport); + } + } + } + String[] bar=new String[foo.size()]; + for(int i=0; i<foo.size(); i++){ + bar[i]=(String)(foo.elementAt(i)); + } + return bar; + } + static PortWatcher getPort(Session session, String address, int lport) throws JSchException{ + InetAddress addr; + try{ + addr=InetAddress.getByName(address); + } + catch(UnknownHostException uhe){ + throw new JSchException("PortForwardingL: invalid address "+address+" specified.", uhe); + } + synchronized(pool){ + for(int i=0; i<pool.size(); i++){ + PortWatcher p=(PortWatcher)(pool.elementAt(i)); + if(p.session==session && p.lport==lport){ + if(/*p.boundaddress.isAnyLocalAddress() ||*/ + (anyLocalAddress!=null && p.boundaddress.equals(anyLocalAddress)) || + p.boundaddress.equals(addr)) + return p; + } + } + return null; + } + } + static PortWatcher addPort(Session session, String address, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{ + if(getPort(session, address, lport)!=null){ + throw new JSchException("PortForwardingL: local port "+ address+":"+lport+" is already registered."); + } + PortWatcher pw=new PortWatcher(session, address, lport, host, rport, ssf); + pool.addElement(pw); + return pw; + } + static void delPort(Session session, String address, int lport) throws JSchException{ + PortWatcher pw=getPort(session, address, lport); + if(pw==null){ + throw new JSchException("PortForwardingL: local port "+address+":"+lport+" is not registered."); + } + pw.delete(); + pool.removeElement(pw); + } + static void delPort(Session session){ + synchronized(pool){ + PortWatcher[] foo=new PortWatcher[pool.size()]; + int count=0; + for(int i=0; i<pool.size(); i++){ + PortWatcher p=(PortWatcher)(pool.elementAt(i)); + if(p.session==session) { + p.delete(); + foo[count++]=p; + } + } + for(int i=0; i<count; i++){ + PortWatcher p=foo[i]; + pool.removeElement(p); + } + } + } + PortWatcher(Session session, + String address, int lport, + String host, int rport, + ServerSocketFactory factory) throws JSchException{ + this.session=session; + this.lport=lport; + this.host=host; + this.rport=rport; + try{ + boundaddress=InetAddress.getByName(address); + ss=(factory==null) ? + new ServerSocket(lport, 0, boundaddress) : + factory.createServerSocket(lport, 0, boundaddress); + } + catch(Exception e){ + //System.err.println(e); + String message="PortForwardingL: local port "+address+":"+lport+" cannot be bound."; + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + if(lport==0){ + int assigned=ss.getLocalPort(); + if(assigned!=-1) + this.lport=assigned; + } + } + + public void run(){ + thread=this; + try{ + while(thread!=null){ + Socket socket=ss.accept(); + socket.setTcpNoDelay(true); + InputStream in=socket.getInputStream(); + OutputStream out=socket.getOutputStream(); + ChannelDirectTCPIP channel=new ChannelDirectTCPIP(); + channel.init(); + channel.setInputStream(in); + channel.setOutputStream(out); + session.addChannel(channel); + ((ChannelDirectTCPIP)channel).setHost(host); + ((ChannelDirectTCPIP)channel).setPort(rport); + ((ChannelDirectTCPIP)channel).setOrgIPAddress(socket.getInetAddress().getHostAddress()); + ((ChannelDirectTCPIP)channel).setOrgPort(socket.getPort()); + channel.connect(); + if(channel.exitstatus!=-1){ + } + } + } + catch(Exception e){ + //System.err.println("! "+e); + } + + delete(); + } + + void delete(){ + thread=null; + try{ + if(ss!=null)ss.close(); + ss=null; + } + catch(Exception e){ + } + } +} diff --git a/java/com/jcraft/jsch/Proxy.java b/java/com/jcraft/jsch/Proxy.java new file mode 100644 index 00000000..7d05caa8 --- /dev/null +++ b/java/com/jcraft/jsch/Proxy.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.Socket; +public interface Proxy{ + void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception; + InputStream getInputStream(); + OutputStream getOutputStream(); + Socket getSocket(); + void close(); +} diff --git a/java/com/jcraft/jsch/ProxyHTTP.java b/java/com/jcraft/jsch/ProxyHTTP.java new file mode 100644 index 00000000..df23114a --- /dev/null +++ b/java/com/jcraft/jsch/ProxyHTTP.java @@ -0,0 +1,180 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; + +public class ProxyHTTP implements Proxy{ + private static int DEFAULTPORT=80; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + + private String user; + private String passwd; + + public ProxyHTTP(String proxy_host){ + int port=DEFAULTPORT; + String host=proxy_host; + if(proxy_host.indexOf(':')!=-1){ + try{ + host=proxy_host.substring(0, proxy_host.indexOf(':')); + port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1)); + } + catch(Exception e){ + } + } + this.proxy_host=host; + this.proxy_port=port; + } + public ProxyHTTP(String proxy_host, int proxy_port){ + this.proxy_host=proxy_host; + this.proxy_port=proxy_port; + } + public void setUserPasswd(String user, String passwd){ + this.user=user; + this.passwd=passwd; + } + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{ + try{ + if(socket_factory==null){ + socket=Util.createSocket(proxy_host, proxy_port, timeout); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(proxy_host, proxy_port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + if(timeout>0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + out.write(Util.str2byte("CONNECT "+host+":"+port+" HTTP/1.0\r\n")); + + if(user!=null && passwd!=null){ + byte[] code=Util.str2byte(user+":"+passwd); + code=Util.toBase64(code, 0, code.length); + out.write(Util.str2byte("Proxy-Authorization: Basic ")); + out.write(code); + out.write(Util.str2byte("\r\n")); + } + + out.write(Util.str2byte("\r\n")); + out.flush(); + + int foo=0; + + StringBuffer sb=new StringBuffer(); + while(foo>=0){ + foo=in.read(); if(foo!=13){sb.append((char)foo); continue;} + foo=in.read(); if(foo!=10){continue;} + break; + } + if(foo<0){ + throw new IOException(); + } + + String response=sb.toString(); + String reason="Unknow reason"; + int code=-1; + try{ + foo=response.indexOf(' '); + int bar=response.indexOf(' ', foo+1); + code=Integer.parseInt(response.substring(foo+1, bar)); + reason=response.substring(bar+1); + } + catch(Exception e){ + } + if(code!=200){ + throw new IOException("proxy error: "+reason); + } + + /* + while(foo>=0){ + foo=in.read(); if(foo!=13) continue; + foo=in.read(); if(foo!=10) continue; + foo=in.read(); if(foo!=13) continue; + foo=in.read(); if(foo!=10) continue; + break; + } + */ + + int count=0; + while(true){ + count=0; + while(foo>=0){ + foo=in.read(); if(foo!=13){count++; continue;} + foo=in.read(); if(foo!=10){continue;} + break; + } + if(foo<0){ + throw new IOException(); + } + if(count==0)break; + } + } + catch(RuntimeException e){ + throw e; + } + catch(Exception e){ + try{ if(socket!=null)socket.close(); } + catch(Exception eee){ + } + String message="ProxyHTTP: "+e.toString(); + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + } + public InputStream getInputStream(){ return in; } + public OutputStream getOutputStream(){ return out; } + public Socket getSocket(){ return socket; } + public void close(){ + try{ + if(in!=null)in.close(); + if(out!=null)out.close(); + if(socket!=null)socket.close(); + } + catch(Exception e){ + } + in=null; + out=null; + socket=null; + } + public static int getDefaultPort(){ + return DEFAULTPORT; + } +} diff --git a/java/com/jcraft/jsch/ProxySOCKS4.java b/java/com/jcraft/jsch/ProxySOCKS4.java new file mode 100644 index 00000000..cb506165 --- /dev/null +++ b/java/com/jcraft/jsch/ProxySOCKS4.java @@ -0,0 +1,212 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + This file depends on following documents, + - SOCKS: A protocol for TCP proxy across firewalls, Ying-Da Lee + http://www.socks.nec.com/protocol/socks4.protocol + */ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; + +public class ProxySOCKS4 implements Proxy{ + private static int DEFAULTPORT=1080; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + private String user; + private String passwd; + + public ProxySOCKS4(String proxy_host){ + int port=DEFAULTPORT; + String host=proxy_host; + if(proxy_host.indexOf(':')!=-1){ + try{ + host=proxy_host.substring(0, proxy_host.indexOf(':')); + port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1)); + } + catch(Exception e){ + } + } + this.proxy_host=host; + this.proxy_port=port; + } + public ProxySOCKS4(String proxy_host, int proxy_port){ + this.proxy_host=proxy_host; + this.proxy_port=proxy_port; + } + public void setUserPasswd(String user, String passwd){ + this.user=user; + this.passwd=passwd; + } + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{ + try{ + if(socket_factory==null){ + socket=Util.createSocket(proxy_host, proxy_port, timeout); + //socket=new Socket(proxy_host, proxy_port); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(proxy_host, proxy_port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + if(timeout>0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + +/* + 1) CONNECT + + The client connects to the SOCKS server and sends a CONNECT request when + it wants to establish a connection to an application server. The client + includes in the request packet the IP address and the port number of the + destination host, and userid, in the following format. + + +----+----+----+----+----+----+----+----+----+----+....+----+ + | VN | CD | DSTPORT | DSTIP | USERID |NULL| + +----+----+----+----+----+----+----+----+----+----+....+----+ + # of bytes: 1 1 2 4 variable 1 + + VN is the SOCKS protocol version number and should be 4. CD is the + SOCKS command code and should be 1 for CONNECT request. NULL is a byte + of all zero bits. +*/ + + index=0; + buf[index++]=4; + buf[index++]=1; + + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + try{ + InetAddress addr=InetAddress.getByName(host); + byte[] byteAddress = addr.getAddress(); + for (int i = 0; i < byteAddress.length; i++) { + buf[index++]=byteAddress[i]; + } + } + catch(UnknownHostException uhe){ + throw new JSchException("ProxySOCKS4: "+uhe.toString(), uhe); + } + + if(user!=null){ + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index+=user.length(); + } + buf[index++]=0; + out.write(buf, 0, index); + +/* + The SOCKS server checks to see whether such a request should be granted + based on any combination of source IP address, destination IP address, + destination port number, the userid, and information it may obtain by + consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS + server makes a connection to the specified port of the destination host. + A reply packet is sent to the client when this connection is established, + or when the request is rejected or the operation fails. + + +----+----+----+----+----+----+----+----+ + | VN | CD | DSTPORT | DSTIP | + +----+----+----+----+----+----+----+----+ + # of bytes: 1 1 2 4 + + VN is the version of the reply code and should be 0. CD is the result + code with one of the following values: + + 90: request granted + 91: request rejected or failed + 92: request rejected becasue SOCKS server cannot connect to + identd on the client + 93: request rejected because the client program and identd + report different user-ids + + The remaining fields are ignored. +*/ + + int len=8; + int s=0; + while(s<len){ + int i=in.read(buf, s, len-s); + if(i<=0){ + throw new JSchException("ProxySOCKS4: stream is closed"); + } + s+=i; + } + if(buf[0]!=0){ + throw new JSchException("ProxySOCKS4: server returns VN "+buf[0]); + } + if(buf[1]!=90){ + try{ socket.close(); } + catch(Exception eee){ + } + String message="ProxySOCKS4: server returns CD "+buf[1]; + throw new JSchException(message); + } + } + catch(RuntimeException e){ + throw e; + } + catch(Exception e){ + try{ if(socket!=null)socket.close(); } + catch(Exception eee){ + } + throw new JSchException("ProxySOCKS4: "+e.toString()); + } + } + public InputStream getInputStream(){ return in; } + public OutputStream getOutputStream(){ return out; } + public Socket getSocket(){ return socket; } + public void close(){ + try{ + if(in!=null)in.close(); + if(out!=null)out.close(); + if(socket!=null)socket.close(); + } + catch(Exception e){ + } + in=null; + out=null; + socket=null; + } + public static int getDefaultPort(){ + return DEFAULTPORT; + } +} diff --git a/java/com/jcraft/jsch/ProxySOCKS5.java b/java/com/jcraft/jsch/ProxySOCKS5.java new file mode 100644 index 00000000..7960135a --- /dev/null +++ b/java/com/jcraft/jsch/ProxySOCKS5.java @@ -0,0 +1,349 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + This file depends on following documents, + - RFC 1928 SOCKS Protocol Verseion 5 + - RFC 1929 Username/Password Authentication for SOCKS V5. + */ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; + +public class ProxySOCKS5 implements Proxy{ + private static int DEFAULTPORT=1080; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + private String user; + private String passwd; + + public ProxySOCKS5(String proxy_host){ + int port=DEFAULTPORT; + String host=proxy_host; + if(proxy_host.indexOf(':')!=-1){ + try{ + host=proxy_host.substring(0, proxy_host.indexOf(':')); + port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1)); + } + catch(Exception e){ + } + } + this.proxy_host=host; + this.proxy_port=port; + } + public ProxySOCKS5(String proxy_host, int proxy_port){ + this.proxy_host=proxy_host; + this.proxy_port=proxy_port; + } + public void setUserPasswd(String user, String passwd){ + this.user=user; + this.passwd=passwd; + } + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{ + try{ + if(socket_factory==null){ + socket=Util.createSocket(proxy_host, proxy_port, timeout); + //socket=new Socket(proxy_host, proxy_port); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(proxy_host, proxy_port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + if(timeout>0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + +/* + +----+----------+----------+ + |VER | NMETHODS | METHODS | + +----+----------+----------+ + | 1 | 1 | 1 to 255 | + +----+----------+----------+ + + The VER field is set to X'05' for this version of the protocol. The + NMETHODS field contains the number of method identifier octets that + appear in the METHODS field. + + The values currently defined for METHOD are: + + o X'00' NO AUTHENTICATION REQUIRED + o X'01' GSSAPI + o X'02' USERNAME/PASSWORD + o X'03' to X'7F' IANA ASSIGNED + o X'80' to X'FE' RESERVED FOR PRIVATE METHODS + o X'FF' NO ACCEPTABLE METHODS +*/ + + buf[index++]=5; + + buf[index++]=2; + buf[index++]=0; // NO AUTHENTICATION REQUIRED + buf[index++]=2; // USERNAME/PASSWORD + + out.write(buf, 0, index); + +/* + The server selects from one of the methods given in METHODS, and + sends a METHOD selection message: + + +----+--------+ + |VER | METHOD | + +----+--------+ + | 1 | 1 | + +----+--------+ +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + + boolean check=false; + switch((buf[1])&0xff){ + case 0: // NO AUTHENTICATION REQUIRED + check=true; + break; + case 2: // USERNAME/PASSWORD + if(user==null || passwd==null)break; + +/* + Once the SOCKS V5 server has started, and the client has selected the + Username/Password Authentication protocol, the Username/Password + subnegotiation begins. This begins with the client producing a + Username/Password request: + + +----+------+----------+------+----------+ + |VER | ULEN | UNAME | PLEN | PASSWD | + +----+------+----------+------+----------+ + | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + +----+------+----------+------+----------+ + + The VER field contains the current version of the subnegotiation, + which is X'01'. The ULEN field contains the length of the UNAME field + that follows. The UNAME field contains the username as known to the + source operating system. The PLEN field contains the length of the + PASSWD field that follows. The PASSWD field contains the password + association with the given UNAME. +*/ + index=0; + buf[index++]=1; + buf[index++]=(byte)(user.length()); + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index+=user.length(); + buf[index++]=(byte)(passwd.length()); + System.arraycopy(Util.str2byte(passwd), 0, buf, index, passwd.length()); + index+=passwd.length(); + + out.write(buf, 0, index); + +/* + The server verifies the supplied UNAME and PASSWD, and sends the + following response: + + +----+--------+ + |VER | STATUS | + +----+--------+ + | 1 | 1 | + +----+--------+ + + A STATUS field of X'00' indicates success. If the server returns a + `failure' (STATUS value other than X'00') status, it MUST close the + connection. +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + if(buf[1]==0) + check=true; + break; + default: + } + + if(!check){ + try{ socket.close(); } + catch(Exception eee){ + } + throw new JSchException("fail in SOCKS5 proxy"); + } + +/* + The SOCKS request is formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o CMD + o CONNECT X'01' + o BIND X'02' + o UDP ASSOCIATE X'03' + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o DST.ADDR desired destination address + o DST.PORT desired destination port in network octet + order +*/ + + index=0; + buf[index++]=5; + buf[index++]=1; // CONNECT + buf[index++]=0; + + byte[] hostb=Util.str2byte(host); + int len=hostb.length; + buf[index++]=3; // DOMAINNAME + buf[index++]=(byte)(len); + System.arraycopy(hostb, 0, buf, index, len); + index+=len; + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + out.write(buf, 0, index); + +/* + The SOCKS request information is sent by the client as soon as it has + established a connection to the SOCKS server, and completed the + authentication negotiations. The server evaluates the request, and + returns a reply formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o REP Reply field: + o X'00' succeeded + o X'01' general SOCKS server failure + o X'02' connection not allowed by ruleset + o X'03' Network unreachable + o X'04' Host unreachable + o X'05' Connection refused + o X'06' TTL expired + o X'07' Command not supported + o X'08' Address type not supported + o X'09' to X'FF' unassigned + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o BND.ADDR server bound address + o BND.PORT server bound port in network octet order +*/ + + //in.read(buf, 0, 4); + fill(in, buf, 4); + + if(buf[1]!=0){ + try{ socket.close(); } + catch(Exception eee){ + } + throw new JSchException("ProxySOCKS5: server returns "+buf[1]); + } + + switch(buf[3]&0xff){ + case 1: + //in.read(buf, 0, 6); + fill(in, buf, 6); + break; + case 3: + //in.read(buf, 0, 1); + fill(in, buf, 1); + //in.read(buf, 0, buf[0]+2); + fill(in, buf, (buf[0]&0xff)+2); + break; + case 4: + //in.read(buf, 0, 18); + fill(in, buf, 18); + break; + default: + } + } + catch(RuntimeException e){ + throw e; + } + catch(Exception e){ + try{ if(socket!=null)socket.close(); } + catch(Exception eee){ + } + String message="ProxySOCKS5: "+e.toString(); + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + } + public InputStream getInputStream(){ return in; } + public OutputStream getOutputStream(){ return out; } + public Socket getSocket(){ return socket; } + public void close(){ + try{ + if(in!=null)in.close(); + if(out!=null)out.close(); + if(socket!=null)socket.close(); + } + catch(Exception e){ + } + in=null; + out=null; + socket=null; + } + public static int getDefaultPort(){ + return DEFAULTPORT; + } + private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException{ + int s=0; + while(s<len){ + int i=in.read(buf, s, len-s); + if(i<=0){ + throw new JSchException("ProxySOCKS5: stream is closed"); + } + s+=i; + } + } +} diff --git a/java/com/jcraft/jsch/README b/java/com/jcraft/jsch/README new file mode 100644 index 00000000..a52886e5 --- /dev/null +++ b/java/com/jcraft/jsch/README @@ -0,0 +1,221 @@ + + JSch + + Java Secure Channel + by ymnk@jcraft.com, JCraft,Inc. + + http://www.jcraft.com/jsch/ + +Last modified: Wed Nov 1 14:43:31 UTC 2006 + + +Description +=========== +JSch is a pure Java implementation of SSH2. JSch allows you to +connect to an sshd server and use port forwarding, X11 forwarding, +file transfer, etc., and you can integrate its functionality +into your own Java programs. JSch is licensed under BSD style license. + + +Documentation +============= +* README files all over the source tree have info related to the stuff + in the directories. +* ChangeLog: what changed from the previous version? + + +Directories & Files in the Source Tree +====================================== +* src/com/ has source trees of JSch +* example/ has some samples, which demonstrate the usages. +* tools/ has scripts for Ant. + + +Why JSch? +========== +Our intension in developing this stuff is to enable users of our pure +java X servers, WiredX(http://wiredx.net/) and WeirdX, to enjoy secure X +sessions. Our efforts have mostly targeted the SSH2 protocol in relation +to X Window System and X11 forwarding. Of course, we are also interested in +adding other functionality - port forward, file transfer, terminal emulation, etc. + + +Features +======== +* JSch is in pure Java, but it depends on JavaTM Cryptography + Extension (JCE). JSch is know to work with: + o J2SE 1.4.0 or later (no additional libraries required). + o J2SE 1.3 and Sun's JCE reference implementation that can be + obtained at http://java.sun.com/products/jce/ + o J2SE 1.2.2 and later and Bouncycastle's JCE implementation that + can be obtained at http://www.bouncycastle.org/ +* SSH2 protocol support. +* Key exchange: diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1 +* Cipher: blowfish-cbc,3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc + 3des-ctr,aes128-ctr,aes192-ctr,aes256-ctc, + arcfour,arcfour128,arcfour256 +* MAC: hmac-md5,hmac-md5-96,hmac-sha1,hmac-sha1-96 +* Host key type: ssh-dss, ssh-rsa +* Userauth: password +* Userauth: publickey(DSA,RSA) +* Userauth: keyboard-interactive +* Userauth: gssapi-with-mic +* X11 forwarding. +* xauth spoofing. +* connection through HTTP proxy. +* connection through SOCKS5, SOCKS4 proxy. +* port forwarding. +* stream forwarding. +* signal sending. + The unofficial patch for sshd of openssh will be find in the thread + http://marc.theaimsgroup.com/?l=openssh-unix-dev&m=104295745607575&w=2 +* envrironment variable passing. +* remote exec. +* generating DSA and RSA key pairs. +* SSH File Transfer Protocol(version 0, 1, 2, 3) +* partial authentication +* packet compression: zlib, zlib@openssh.com + JZlib(http://www.jcraft.com/jzlib/) has been used. +* hashed known_hosts file. +* NONE Cipher switching. + http://www.psc.edu/networking/projects/hpn-ssh/none.php +* JSch is licensed under BSD style license(refer to LICENSE.txt). + + +How To Try +========== +This archive does not include java byte code, so please compile +the source code by your self. + $ cd jsch-?.?.?/src + $ javac com/jcraft/jsch/*java com/jcraft/jsch/jce/*java com/jcraft/jzlib/*.java +'/examples/' directory has included some samples to demonstrate what +JSch can do. Please refer to '/examples/README' file. + + +AES cipher +========== +JSch supports aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr, +aes256-ctr but you require AES support in your J2SE to choose some of them. +If you are using Sun's J2SE, J2SE 1.4.2 or later is required. +And then, J2SE 1.4.2(or later) does not support aes256 by the default, +because of 'import control restrictions of some countries'. +We have confirmed that by applying + "Java Cryptography Extension (JCE) + Unlimited Strength Jurisdiction Policy Files 1.4.2" +on + http://java.sun.com/j2se/1.4.2/download.html#docs +we can enjoy 'aes256-cbc,aes256-ctr'. + + +Stream Forwarding +================= +JSch has a unique functionality, Stream Forwarding. +Stream Forwarding allows you to plug Java I/O streams directly into a remote TCP +port without assigning and opening a local TCP port. +In port forwarding, as with the -L option of ssh command, you have to assign +and open a local TCP port and that port is also accessible by crackers +on localhost. In some case, that local TCP port may be plugged to a +secret port via SSH session. +A sample program, /example/StreamForwarding.java , demonstrates +this functionality. + + +Generating Authentication Keys +============================== +JSch allows you to generate DSA and RSA key pairs, which are in OpenSSH format. +Please refer to 'examples/KeyGen.java'. + + +Packet Compression +================== +According to the draft from IETF sesch working group, the packet +compression can be applied to each data stream directions; from sshd +server to ssh client and from ssh client to sshd server. So, jsch +allows you to choose which data stream direction will be compressed or not. +For example, in X11 forwarding session, the packet compression for data +stream from sshd to ssh client will save the network traffic, but +usually the traffic from ssh client to sshd is light, so by omitting +the compression for this direction, you may be able to save some CPU time. +Please refer to a sample program 'examples/Compression.java'. + + +Property +======== +By setting properties, you can control the behavior of jsch. +Here is an example of enabling the packet compression, + + Session session=jsch.getSession(user, host, 22); + java.util.Properties config=new java.util.Properties(); + config.put("compression.s2c", "zlib,none"); + config.put("compression.c2s", "zlib,none"); + session.setConfig(config); + session.connect(); + +Current release has supported following properties, +* compression.s2c: zlib, none + default: none + Specifies whether to use compression for the data stream + from sshd to jsch. If "zlib,none" is given and the remote sshd does + not allow the packet compression, compression will not be done. +* compression.c2s: zlib, none + default: none + Specifies whether to use compression for the data stream + from jsch to sshd. +* StrictHostKeyChecking: ask | yes | no + default: ask + If this property is set to ``yes'', jsch will never automatically add + host keys to the $HOME/.ssh/known_hosts file, and refuses to connect + to hosts whose host key has changed. This property forces the user + to manually add all new hosts. If this property is set to ``no'', + jsch will automatically add new host keys to the user known hosts + files. If this property is set to ``ask'', new host keys will be + added to the user known host files only after the user has confirmed + that is what they really want to do, and jsch will refuse to connect + to hosts whose host key has changed. + + +TODO +==== +* re-implementation with java.nio. +* replacing cipher, hash by JCE with pure Java code. +* SSH File Transfer Protocol version 4. +* error handling. + + +Copyrights & Disclaimers +======================== +JSch is copyrighted by ymnk, JCraft,Inc. and is licensed through BSD style license. +Read the LICENSE.txt file for the complete license. + + +Credits and Acknowledgments +============================ +JSch has been developed by ymnk@jcraft.com and it can not be hacked +without several help. +* First of all, we want to thank JCE team at Sun Microsystems. + For long time, we had planed to implement SSH2 in pure Java, + but we had hesitated to do because tons of work must be done for + implementing ciphers, hashes, etc., from the scratch. + Thanks to newly added functionalities to J2SE 1.4.0, we could + start this project. +* We appreciate the OpenSSH project. + The options '-ddd' of sshd, '---vvv' of ssh and the compile options + '-DPACKET_DEBUG', '-DDEBUG_KEXDH' and '-DDEBUG_KEX' were very + useful in debugging JSch. +* We appreciate IETF sesch working group and SSH Communications Security Corp. + Without the standardization of the protocol, we could not get the + chance to implement JSch. +* We appreciate Seigo Haruyama(http://www.unixuser.org/~haruyama/), + who are interpreting drafts of SSH2 protocol in Japanese. + His works were very useful for us to understand the technical terms + in our native language. +* We also appreciate SourceForge.net's awesome service to the + Open Source Community. + + +If you have any comments, suggestions and questions, write us +at jsch@jcraft.com + + +``SSH is a registered trademark and Secure Shell is a trademark of +SSH Communications Security Corp (www.ssh.com)''. diff --git a/java/com/jcraft/jsch/Random.java b/java/com/jcraft/jsch/Random.java new file mode 100644 index 00000000..879b7770 --- /dev/null +++ b/java/com/jcraft/jsch/Random.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Random{ + void fill(byte[] foo, int start, int len); +} diff --git a/java/com/jcraft/jsch/Request.java b/java/com/jcraft/jsch/Request.java new file mode 100644 index 00000000..94f9b013 --- /dev/null +++ b/java/com/jcraft/jsch/Request.java @@ -0,0 +1,69 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +abstract class Request{ + private boolean reply=false; + private Session session=null; + private Channel channel=null; + void request(Session session, Channel channel) throws Exception{ + this.session=session; + this.channel=channel; + if(channel.connectTimeout>0){ + setReply(true); + } + } + boolean waitForReply(){ return reply; } + void setReply(boolean reply){ this.reply=reply; } + void write(Packet packet) throws Exception{ + if(reply){ + channel.reply=-1; + } + session.write(packet); + if(reply){ + long start=System.currentTimeMillis(); + long timeout=channel.connectTimeout; + while(channel.isConnected() && channel.reply==-1){ + try{Thread.sleep(10);} + catch(Exception ee){ + } + if(timeout>0L && + (System.currentTimeMillis()-start)>timeout){ + channel.reply=0; + throw new JSchException("channel request: timeout"); + } + } + + if(channel.reply==0){ + throw new JSchException("failed to send channel request"); + } + } + } +} diff --git a/java/com/jcraft/jsch/RequestAgentForwarding.java b/java/com/jcraft/jsch/RequestAgentForwarding.java new file mode 100644 index 00000000..66d328cd --- /dev/null +++ b/java/com/jcraft/jsch/RequestAgentForwarding.java @@ -0,0 +1,53 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestAgentForwarding extends Request{ + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + setReply(false); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "auth-agent-req@openssh.com" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("auth-agent-req@openssh.com")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + write(packet); + session.agent_forwarding=true; + } +} diff --git a/java/com/jcraft/jsch/RequestEnv.java b/java/com/jcraft/jsch/RequestEnv.java new file mode 100644 index 00000000..cccda4df --- /dev/null +++ b/java/com/jcraft/jsch/RequestEnv.java @@ -0,0 +1,54 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestEnv extends Request{ + byte[] name=new byte[0]; + byte[] value=new byte[0]; + void setEnv(byte[] name, byte[] value){ + this.name=name; + this.value=value; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("env")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(name); + buf.putString(value); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestExec.java b/java/com/jcraft/jsch/RequestExec.java new file mode 100644 index 00000000..318d99ad --- /dev/null +++ b/java/com/jcraft/jsch/RequestExec.java @@ -0,0 +1,58 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestExec extends Request{ + private byte[] command=new byte[0]; + RequestExec(byte[] command){ + this.command=command; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "exec" + // boolean want reply // 0 + // string command + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("exec")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.checkFreeSize(4+command.length); + buf.putString(command); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestPtyReq.java b/java/com/jcraft/jsch/RequestPtyReq.java new file mode 100644 index 00000000..86bf4f13 --- /dev/null +++ b/java/com/jcraft/jsch/RequestPtyReq.java @@ -0,0 +1,78 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestPtyReq extends Request{ + private String ttype="vt100"; + private int tcol=80; + private int trow=24; + private int twp=640; + private int thp=480; + + private byte[] terminal_mode=Util.empty; + + void setCode(String cookie){ + } + + void setTType(String ttype){ + this.ttype=ttype; + } + + void setTerminalMode(byte[] terminal_mode){ + this.terminal_mode=terminal_mode; + } + + void setTSize(int tcol, int trow, int twp, int thp){ + this.tcol=tcol; + this.trow=trow; + this.twp=twp; + this.thp=thp; + } + + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("pty-req")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(ttype)); + buf.putInt(tcol); + buf.putInt(trow); + buf.putInt(twp); + buf.putInt(thp); + buf.putString(terminal_mode); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestSftp.java b/java/com/jcraft/jsch/RequestSftp.java new file mode 100644 index 00000000..483c296b --- /dev/null +++ b/java/com/jcraft/jsch/RequestSftp.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class RequestSftp extends Request{ + RequestSftp(){ + setReply(true); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte("sftp")); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestShell.java b/java/com/jcraft/jsch/RequestShell.java new file mode 100644 index 00000000..e037ba9e --- /dev/null +++ b/java/com/jcraft/jsch/RequestShell.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestShell extends Request{ + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "shell" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("shell")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestSignal.java b/java/com/jcraft/jsch/RequestSignal.java new file mode 100644 index 00000000..a6926177 --- /dev/null +++ b/java/com/jcraft/jsch/RequestSignal.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestSignal extends Request{ + private String signal="KILL"; + public void setSignal(String foo){ signal=foo; } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("signal")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(signal)); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestSubsystem.java b/java/com/jcraft/jsch/RequestSubsystem.java new file mode 100644 index 00000000..b6fee4f4 --- /dev/null +++ b/java/com/jcraft/jsch/RequestSubsystem.java @@ -0,0 +1,53 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class RequestSubsystem extends Request{ + private String subsystem=null; + public void request(Session session, Channel channel, String subsystem, boolean want_reply) throws Exception{ + setReply(want_reply); + this.subsystem=subsystem; + this.request(session, channel); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(subsystem)); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestWindowChange.java b/java/com/jcraft/jsch/RequestWindowChange.java new file mode 100644 index 00000000..43600ac4 --- /dev/null +++ b/java/com/jcraft/jsch/RequestWindowChange.java @@ -0,0 +1,68 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestWindowChange extends Request{ + int width_columns=80; + int height_rows=24; + int width_pixels=640; + int height_pixels=480; + void setSize(int col, int row, int wp, int hp){ + this.width_columns=col; + this.height_rows=row; + this.width_pixels=wp; + this.height_pixels=hp; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + //byte SSH_MSG_CHANNEL_REQUEST + //uint32 recipient_channel + //string "window-change" + //boolean FALSE + //uint32 terminal width, columns + //uint32 terminal height, rows + //uint32 terminal width, pixels + //uint32 terminal height, pixels + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("window-change")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putInt(width_columns); + buf.putInt(height_rows); + buf.putInt(width_pixels); + buf.putInt(height_pixels); + write(packet); + } +} diff --git a/java/com/jcraft/jsch/RequestX11.java b/java/com/jcraft/jsch/RequestX11.java new file mode 100644 index 00000000..3bdaca9f --- /dev/null +++ b/java/com/jcraft/jsch/RequestX11.java @@ -0,0 +1,63 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestX11 extends Request{ + public void setCookie(String cookie){ + ChannelX11.cookie=Util.str2byte(cookie); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "x11-req" + // boolean want reply // 0 + // boolean single connection + // string x11 authentication protocol // "MIT-MAGIC-COOKIE-1". + // string x11 authentication cookie + // uint32 x11 screen number + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("x11-req")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putByte((byte)0); + buf.putString(Util.str2byte("MIT-MAGIC-COOKIE-1")); + buf.putString(ChannelX11.getFakedCookie(session)); + buf.putInt(0); + write(packet); + + session.x11_forwarding=true; + } +} diff --git a/java/com/jcraft/jsch/ServerSocketFactory.java b/java/com/jcraft/jsch/ServerSocketFactory.java new file mode 100644 index 00000000..682b4c4f --- /dev/null +++ b/java/com/jcraft/jsch/ServerSocketFactory.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.io.*; + +public interface ServerSocketFactory{ + public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException; +} diff --git a/java/com/jcraft/jsch/Session.java b/java/com/jcraft/jsch/Session.java new file mode 100644 index 00000000..962f52fa --- /dev/null +++ b/java/com/jcraft/jsch/Session.java @@ -0,0 +1,2054 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; + +public class Session implements Runnable{ + + // http://ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-01.txt + static final int SSH_MSG_DISCONNECT= 1; + static final int SSH_MSG_IGNORE= 2; + static final int SSH_MSG_UNIMPLEMENTED= 3; + static final int SSH_MSG_DEBUG= 4; + static final int SSH_MSG_SERVICE_REQUEST= 5; + static final int SSH_MSG_SERVICE_ACCEPT= 6; + static final int SSH_MSG_KEXINIT= 20; + static final int SSH_MSG_NEWKEYS= 21; + static final int SSH_MSG_KEXDH_INIT= 30; + static final int SSH_MSG_KEXDH_REPLY= 31; + static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; + static final int SSH_MSG_KEX_DH_GEX_INIT= 32; + static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; + static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; + static final int SSH_MSG_GLOBAL_REQUEST= 80; + static final int SSH_MSG_REQUEST_SUCCESS= 81; + static final int SSH_MSG_REQUEST_FAILURE= 82; + static final int SSH_MSG_CHANNEL_OPEN= 90; + static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91; + static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92; + static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93; + static final int SSH_MSG_CHANNEL_DATA= 94; + static final int SSH_MSG_CHANNEL_EXTENDED_DATA= 95; + static final int SSH_MSG_CHANNEL_EOF= 96; + static final int SSH_MSG_CHANNEL_CLOSE= 97; + static final int SSH_MSG_CHANNEL_REQUEST= 98; + static final int SSH_MSG_CHANNEL_SUCCESS= 99; + static final int SSH_MSG_CHANNEL_FAILURE= 100; + + private static final int PACKET_MAX_SIZE = 256 * 1024; + + private byte[] V_S; // server version + private byte[] V_C=Util.str2byte("SSH-2.0-JSCH-"+JSch.VERSION); // client version + + private byte[] I_C; // the payload of the client's SSH_MSG_KEXINIT + private byte[] I_S; // the payload of the server's SSH_MSG_KEXINIT + private byte[] K_S; // the host key + + private byte[] session_id; + + private byte[] IVc2s; + private byte[] IVs2c; + private byte[] Ec2s; + private byte[] Es2c; + private byte[] MACc2s; + private byte[] MACs2c; + + private int seqi=0; + private int seqo=0; + + String[] guess=null; + private Cipher s2ccipher; + private Cipher c2scipher; + private MAC s2cmac; + private MAC c2smac; + //private byte[] mac_buf; + private byte[] s2cmac_result1; + private byte[] s2cmac_result2; + + private Compression deflater; + private Compression inflater; + + private IO io; + private Socket socket; + private int timeout=0; + + private volatile boolean isConnected=false; + + private boolean isAuthed=false; + + private Thread connectThread=null; + private Object lock=new Object(); + + boolean x11_forwarding=false; + boolean agent_forwarding=false; + + InputStream in=null; + OutputStream out=null; + + static Random random; + + Buffer buf; + Packet packet; + + SocketFactory socket_factory=null; + + static final int buffer_margin = 32 + // maximum padding length + 20 + // maximum mac length + 32; // margin for deflater; deflater may inflate data + + private java.util.Hashtable config=null; + + private Proxy proxy=null; + private UserInfo userinfo; + + private String hostKeyAlias=null; + private int serverAliveInterval=0; + private int serverAliveCountMax=1; + + protected boolean daemon_thread=false; + + private long kex_start_time=0L; + + int max_auth_tries = 6; + int auth_failures = 0; + + String host="127.0.0.1"; + int port=22; + + String username=null; + byte[] password=null; + + JSch jsch; + + Session(JSch jsch) throws JSchException{ + super(); + this.jsch=jsch; + buf=new Buffer(); + packet=new Packet(buf); + } + + public void connect() throws JSchException{ + connect(timeout); + } + + public void connect(int connectTimeout) throws JSchException{ + if(isConnected){ + throw new JSchException("session is already connected"); + } + + io=new IO(); + if(random==null){ + try{ + Class c=Class.forName(getConfig("random")); + random=(Random)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + } + Packet.setRandom(random); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Connecting to "+host+" port "+port); + } + + try { + int i, j; + + if(proxy==null){ + InputStream in; + OutputStream out; + if(socket_factory==null){ + socket=Util.createSocket(host, port, connectTimeout); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(host, port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + //if(timeout>0){ socket.setSoTimeout(timeout); } + socket.setTcpNoDelay(true); + io.setInputStream(in); + io.setOutputStream(out); + } + else{ + synchronized(proxy){ + proxy.connect(socket_factory, host, port, connectTimeout); + io.setInputStream(proxy.getInputStream()); + io.setOutputStream(proxy.getOutputStream()); + socket=proxy.getSocket(); + } + } + + if(connectTimeout>0 && socket!=null){ + socket.setSoTimeout(connectTimeout); + } + + isConnected=true; + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Connection established"); + } + + jsch.addSession(this); + + { + // Some Cisco devices will miss to read '\n' if it is sent separately. + byte[] foo=new byte[V_C.length+1]; + System.arraycopy(V_C, 0, foo, 0, V_C.length); + foo[foo.length-1]=(byte)'\n'; + io.put(foo, 0, foo.length); + } + + while(true){ + i=0; + j=0; + while(i<buf.buffer.length){ + j=io.getByte(); + if(j<0)break; + buf.buffer[i]=(byte)j; i++; + if(j==10)break; + } + if(j<0){ + throw new JSchException("connection is closed by foreign host"); + } + + if(buf.buffer[i-1]==10){ // 0x0a + i--; + if(i>0 && buf.buffer[i-1]==13){ // 0x0d + i--; + } + } + + if(i<=3 || + ((i!=buf.buffer.length) && + (buf.buffer[0]!='S'||buf.buffer[1]!='S'|| + buf.buffer[2]!='H'||buf.buffer[3]!='-'))){ + // It must not start with 'SSH-' + //System.err.println(new String(buf.buffer, 0, i); + continue; + } + + if(i==buf.buffer.length || + i<7 || // SSH-1.99 or SSH-2.0 + (buf.buffer[4]=='1' && buf.buffer[6]!='9') // SSH-1.5 + ){ + throw new JSchException("invalid server's version string"); + } + break; + } + + V_S=new byte[i]; System.arraycopy(buf.buffer, 0, V_S, 0, i); + //System.err.println("V_S: ("+i+") ["+new String(V_S)+"]"); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Remote version string: "+Util.byte2str(V_S)); + JSch.getLogger().log(Logger.INFO, + "Local version string: "+Util.byte2str(V_C)); + } + + send_kexinit(); + + buf=read(buf); + if(buf.getCommand()!=SSH_MSG_KEXINIT){ + in_kex=false; + throw new JSchException("invalid protocol: "+buf.getCommand()); + } + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXINIT received"); + } + + KeyExchange kex=receive_kexinit(buf); + + while(true){ + buf=read(buf); + if(kex.getState()==buf.getCommand()){ + kex_start_time=System.currentTimeMillis(); + boolean result=kex.next(buf); + if(!result){ + //System.err.println("verify: "+result); + in_kex=false; + throw new JSchException("verify: "+result); + } + } + else{ + in_kex=false; + throw new JSchException("invalid protocol(kex): "+buf.getCommand()); + } + if(kex.getState()==KeyExchange.STATE_END){ + break; + } + } + + try{ checkHost(host, port, kex); } + catch(JSchException ee){ + in_kex=false; + throw ee; + } + + send_newkeys(); + + // receive SSH_MSG_NEWKEYS(21) + buf=read(buf); + //System.err.println("read: 21 ? "+buf.getCommand()); + if(buf.getCommand()==SSH_MSG_NEWKEYS){ + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_NEWKEYS received"); + } + + receive_newkeys(buf, kex); + } + else{ + in_kex=false; + throw new JSchException("invalid protocol(newkyes): "+buf.getCommand()); + } + + try{ + String s = getConfig("MaxAuthTries"); + if(s!=null){ + max_auth_tries = Integer.parseInt(s); + } + } + catch(NumberFormatException e){ + throw new JSchException("MaxAuthTries: "+getConfig("MaxAuthTries"), e); + } + + boolean auth=false; + boolean auth_cancel=false; + + UserAuth ua=null; + try{ + Class c=Class.forName(getConfig("userauth.none")); + ua=(UserAuth)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + + auth=ua.start(this); + + String cmethods=getConfig("PreferredAuthentications"); + + String[] cmethoda=Util.split(cmethods, ","); + + String smethods=null; + if(!auth){ + smethods=((UserAuthNone)ua).getMethods(); + if(smethods!=null){ + smethods=smethods.toLowerCase(); + } + else{ + // methods: publickey,password,keyboard-interactive + //smethods="publickey,password,keyboard-interactive"; + smethods=cmethods; + } + } + + String[] smethoda=Util.split(smethods, ","); + + int methodi=0; + + loop: + while(true){ + + while(!auth && + cmethoda!=null && methodi<cmethoda.length){ + + String method=cmethoda[methodi++]; + boolean acceptable=false; + for(int k=0; k<smethoda.length; k++){ + if(smethoda[k].equals(method)){ + acceptable=true; + break; + } + } + if(!acceptable){ + continue; + } + + //System.err.println(" method: "+method); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + String str="Authentications that can continue: "; + for(int k=methodi-1; k<cmethoda.length; k++){ + str+=cmethoda[k]; + if(k+1<cmethoda.length) + str+=","; + } + JSch.getLogger().log(Logger.INFO, + str); + JSch.getLogger().log(Logger.INFO, + "Next authentication method: "+method); + } + + ua=null; + try{ + Class c=null; + if(getConfig("userauth."+method)!=null){ + c=Class.forName(getConfig("userauth."+method)); + ua=(UserAuth)(c.newInstance()); + } + } + catch(Exception e){ + if(JSch.getLogger().isEnabled(Logger.WARN)){ + JSch.getLogger().log(Logger.WARN, + "failed to load "+method+" method"); + } + } + + if(ua!=null){ + auth_cancel=false; + try{ + auth=ua.start(this); + if(auth && + JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Authentication succeeded ("+method+")."); + } + } + catch(JSchAuthCancelException ee){ + auth_cancel=true; + } + catch(JSchPartialAuthException ee){ + String tmp = smethods; + smethods=ee.getMethods(); + smethoda=Util.split(smethods, ","); + if(!tmp.equals(smethods)){ + methodi=0; + } + //System.err.println("PartialAuth: "+methods); + auth_cancel=false; + continue loop; + } + catch(RuntimeException ee){ + throw ee; + } + catch(Exception ee){ + //System.err.println("ee: "+ee); // SSH_MSG_DISCONNECT: 2 Too many authentication failures + break loop; + } + } + } + break; + } + + if(!auth){ + if(auth_failures >= max_auth_tries){ + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Login trials exceeds "+max_auth_tries); + } + } + if(auth_cancel) + throw new JSchException("Auth cancel"); + throw new JSchException("Auth fail"); + } + + if(connectTimeout>0 || timeout>0){ + socket.setSoTimeout(timeout); + } + + isAuthed=true; + + synchronized(lock){ + if(isConnected){ + connectThread=new Thread(this); + connectThread.setName("Connect thread "+host+" session"); + if(daemon_thread){ + connectThread.setDaemon(daemon_thread); + } + connectThread.start(); + } + else{ + // The session has been already down and + // we don't have to start new thread. + } + } + } + catch(Exception e) { + in_kex=false; + if(isConnected){ + try{ + packet.reset(); + buf.putByte((byte)SSH_MSG_DISCONNECT); + buf.putInt(3); + buf.putString(Util.str2byte(e.toString())); + buf.putString(Util.str2byte("en")); + write(packet); + disconnect(); + } + catch(Exception ee){ + } + } + isConnected=false; + //e.printStackTrace(); + if(e instanceof RuntimeException) throw (RuntimeException)e; + if(e instanceof JSchException) throw (JSchException)e; + throw new JSchException("Session.connect: "+e); + } + finally{ + Util.bzero(this.password); + this.password=null; + } + } + + private KeyExchange receive_kexinit(Buffer buf) throws Exception { + int j=buf.getInt(); + if(j!=buf.getLength()){ // packet was compressed and + buf.getByte(); // j is the size of deflated packet. + I_S=new byte[buf.index-5]; + } + else{ + I_S=new byte[j-1-buf.getByte()]; + } + System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length); + + if(!in_kex){ // We are in rekeying activated by the remote! + send_kexinit(); + } + + guess=KeyExchange.guess(I_S, I_C); + if(guess==null){ + throw new JSchException("Algorithm negotiation fail"); + } + + if(!isAuthed && + (guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS].equals("none") || + (guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC].equals("none")))){ + throw new JSchException("NONE Cipher should not be chosen before authentification is successed."); + } + + KeyExchange kex=null; + try{ + Class c=Class.forName(getConfig(guess[KeyExchange.PROPOSAL_KEX_ALGS])); + kex=(KeyExchange)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + + kex.init(this, V_S, V_C, I_S, I_C); + return kex; + } + + private boolean in_kex=false; + public void rekey() throws Exception { + send_kexinit(); + } + private void send_kexinit() throws Exception { + if(in_kex) + return; + + String cipherc2s=getConfig("cipher.c2s"); + String ciphers2c=getConfig("cipher.s2c"); + + String[] not_available_ciphers=checkCiphers(getConfig("CheckCiphers")); + if(not_available_ciphers!=null && not_available_ciphers.length>0){ + cipherc2s=Util.diffString(cipherc2s, not_available_ciphers); + ciphers2c=Util.diffString(ciphers2c, not_available_ciphers); + if(cipherc2s==null || ciphers2c==null){ + throw new JSchException("There are not any available ciphers."); + } + } + + String kex=getConfig("kex"); + String[] not_available_kexes=checkKexes(getConfig("CheckKexes")); + if(not_available_kexes!=null && not_available_kexes.length>0){ + kex=Util.diffString(kex, not_available_kexes); + if(kex==null){ + throw new JSchException("There are not any available kexes."); + } + } + + in_kex=true; + kex_start_time=System.currentTimeMillis(); + + // byte SSH_MSG_KEXINIT(20) + // byte[16] cookie (random bytes) + // string kex_algorithms + // string server_host_key_algorithms + // string encryption_algorithms_client_to_server + // string encryption_algorithms_server_to_client + // string mac_algorithms_client_to_server + // string mac_algorithms_server_to_client + // string compression_algorithms_client_to_server + // string compression_algorithms_server_to_client + // string languages_client_to_server + // string languages_server_to_client + Buffer buf = new Buffer(); // send_kexinit may be invoked + Packet packet = new Packet(buf); // by user thread. + packet.reset(); + buf.putByte((byte) SSH_MSG_KEXINIT); + synchronized(random){ + random.fill(buf.buffer, buf.index, 16); buf.skip(16); + } + buf.putString(Util.str2byte(kex)); + buf.putString(Util.str2byte(getConfig("server_host_key"))); + buf.putString(Util.str2byte(cipherc2s)); + buf.putString(Util.str2byte(ciphers2c)); + buf.putString(Util.str2byte(getConfig("mac.c2s"))); + buf.putString(Util.str2byte(getConfig("mac.s2c"))); + buf.putString(Util.str2byte(getConfig("compression.c2s"))); + buf.putString(Util.str2byte(getConfig("compression.s2c"))); + buf.putString(Util.str2byte(getConfig("lang.c2s"))); + buf.putString(Util.str2byte(getConfig("lang.s2c"))); + buf.putByte((byte)0); + buf.putInt(0); + + buf.setOffSet(5); + I_C=new byte[buf.getLength()]; + buf.getByte(I_C); + + write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXINIT sent"); + } + } + + private void send_newkeys() throws Exception { + // send SSH_MSG_NEWKEYS(21) + packet.reset(); + buf.putByte((byte)SSH_MSG_NEWKEYS); + write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_NEWKEYS sent"); + } + } + + private void checkHost(String chost, int port, KeyExchange kex) throws JSchException { + String shkc=getConfig("StrictHostKeyChecking"); + + if(hostKeyAlias!=null){ + chost=hostKeyAlias; + } + + //System.err.println("shkc: "+shkc); + + byte[] K_S=kex.getHostKey(); + String key_type=kex.getKeyType(); + String key_fprint=kex.getFingerPrint(); + + if(hostKeyAlias==null && port!=22){ + chost=("["+chost+"]:"+port); + } + +// hostkey=new HostKey(chost, K_S); + + HostKeyRepository hkr=jsch.getHostKeyRepository(); + int i=0; + synchronized(hkr){ + i=hkr.check(chost, K_S); + } + + boolean insert=false; + + if((shkc.equals("ask") || shkc.equals("yes")) && + i==HostKeyRepository.CHANGED){ + String file=null; + synchronized(hkr){ + file=hkr.getKnownHostsRepositoryID(); + } + if(file==null){file="known_hosts";} + + boolean b=false; + + if(userinfo!=null){ + String message= +"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"+ +"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n"+ +"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n"+ +"It is also possible that the "+key_type+" host key has just been changed.\n"+ +"The fingerprint for the "+key_type+" key sent by the remote host is\n"+ +key_fprint+".\n"+ +"Please contact your system administrator.\n"+ +"Add correct host key in "+file+" to get rid of this message."; + + if(shkc.equals("ask")){ + b=userinfo.promptYesNo(message+ + "\nDo you want to delete the old key and insert the new key?"); + } + else{ // shkc.equals("yes") + userinfo.showMessage(message); + } + } + + if(!b){ + throw new JSchException("HostKey has been changed: "+chost); + } + + synchronized(hkr){ + hkr.remove(chost, + (key_type.equals("DSA") ? "ssh-dss" : "ssh-rsa"), + null); + insert=true; + } + } + + if((shkc.equals("ask") || shkc.equals("yes")) && + (i!=HostKeyRepository.OK) && !insert){ + if(shkc.equals("yes")){ + throw new JSchException("reject HostKey: "+host); + } + //System.err.println("finger-print: "+key_fprint); + if(userinfo!=null){ + boolean foo=userinfo.promptYesNo( +"The authenticity of host '"+host+"' can't be established.\n"+ +key_type+" key fingerprint is "+key_fprint+".\n"+ +"Are you sure you want to continue connecting?" + ); + if(!foo){ + throw new JSchException("reject HostKey: "+host); + } + insert=true; + } + else{ + if(i==HostKeyRepository.NOT_INCLUDED) + throw new JSchException("UnknownHostKey: "+host+". "+key_type+" key fingerprint is "+key_fprint); + else + throw new JSchException("HostKey has been changed: "+host); + } + } + + if(shkc.equals("no") && + HostKeyRepository.NOT_INCLUDED==i){ + insert=true; + } + + if(i==HostKeyRepository.OK && + JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Host '"+host+"' is known and mathces the "+key_type+" host key"); + } + + if(insert && + JSch.getLogger().isEnabled(Logger.WARN)){ + JSch.getLogger().log(Logger.WARN, + "Permanently added '"+host+"' ("+key_type+") to the list of known hosts."); + } + + String hkh=getConfig("HashKnownHosts"); + if(hkh.equals("yes") && (hkr instanceof KnownHosts)){ + hostkey=((KnownHosts)hkr).createHashedHostKey(chost, K_S); + } + else{ + hostkey=new HostKey(chost, K_S); + } + + if(insert){ + synchronized(hkr){ + hkr.add(hostkey, userinfo); + } + + } + + } + +//public void start(){ (new Thread(this)).start(); } + + public Channel openChannel(String type) throws JSchException{ + if(!isConnected){ + throw new JSchException("session is down"); + } + try{ + Channel channel=Channel.getChannel(type); + addChannel(channel); + channel.init(); + return channel; + } + catch(Exception e){ + //e.printStackTrace(); + } + return null; + } + + // encode will bin invoked in write with synchronization. + public void encode(Packet packet) throws Exception{ +//System.err.println("encode: "+packet.buffer.getCommand()); +//System.err.println(" "+packet.buffer.index); +//if(packet.buffer.getCommand()==96){ +//Thread.dumpStack(); +//} + if(deflater!=null){ + compress_len[0]=packet.buffer.index; + packet.buffer.buffer=deflater.compress(packet.buffer.buffer, + 5, compress_len); + packet.buffer.index=compress_len[0]; + } + if(c2scipher!=null){ + //packet.padding(c2scipher.getIVSize()); + packet.padding(c2scipher_size); + int pad=packet.buffer.buffer[4]; + synchronized(random){ + random.fill(packet.buffer.buffer, packet.buffer.index-pad, pad); + } + } + else{ + packet.padding(8); + } + + if(c2smac!=null){ + c2smac.update(seqo); + c2smac.update(packet.buffer.buffer, 0, packet.buffer.index); + c2smac.doFinal(packet.buffer.buffer, packet.buffer.index); + } + if(c2scipher!=null){ + byte[] buf=packet.buffer.buffer; + c2scipher.update(buf, 0, packet.buffer.index, buf, 0); + } + if(c2smac!=null){ + packet.buffer.skip(c2smac.getBlockSize()); + } + } + + int[] uncompress_len=new int[1]; + int[] compress_len=new int[1]; + + private int s2ccipher_size=8; + private int c2scipher_size=8; + public Buffer read(Buffer buf) throws Exception{ + int j=0; + while(true){ + buf.reset(); + io.getByte(buf.buffer, buf.index, s2ccipher_size); + buf.index+=s2ccipher_size; + if(s2ccipher!=null){ + s2ccipher.update(buf.buffer, 0, s2ccipher_size, buf.buffer, 0); + } + j=((buf.buffer[0]<<24)&0xff000000)| + ((buf.buffer[1]<<16)&0x00ff0000)| + ((buf.buffer[2]<< 8)&0x0000ff00)| + ((buf.buffer[3] )&0x000000ff); + // RFC 4253 6.1. Maximum Packet Length + if(j<5 || j>PACKET_MAX_SIZE){ + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE); + } + int need = j+4-s2ccipher_size; + //if(need<0){ + // throw new IOException("invalid data"); + //} + if((buf.index+need)>buf.buffer.length){ + byte[] foo=new byte[buf.index+need]; + System.arraycopy(buf.buffer, 0, foo, 0, buf.index); + buf.buffer=foo; + } + + if((need%s2ccipher_size)!=0){ + String message="Bad packet length "+need; + if(JSch.getLogger().isEnabled(Logger.FATAL)){ + JSch.getLogger().log(Logger.FATAL, message); + } + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-s2ccipher_size); + } + + if(need>0){ + io.getByte(buf.buffer, buf.index, need); buf.index+=(need); + if(s2ccipher!=null){ + s2ccipher.update(buf.buffer, s2ccipher_size, need, buf.buffer, s2ccipher_size); + } + } + + if(s2cmac!=null){ + s2cmac.update(seqi); + s2cmac.update(buf.buffer, 0, buf.index); + + s2cmac.doFinal(s2cmac_result1, 0); + io.getByte(s2cmac_result2, 0, s2cmac_result2.length); + if(!java.util.Arrays.equals(s2cmac_result1, s2cmac_result2)){ + if(need > PACKET_MAX_SIZE){ + throw new IOException("MAC Error"); + } + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-need); + continue; + } + } + + seqi++; + + if(inflater!=null){ + //inflater.uncompress(buf); + int pad=buf.buffer[4]; + uncompress_len[0]=buf.index-5-pad; + byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len); + if(foo!=null){ + buf.buffer=foo; + buf.index=5+uncompress_len[0]; + } + else{ + System.err.println("fail in inflater"); + break; + } + } + + int type=buf.getCommand()&0xff; + //System.err.println("read: "+type); + if(type==SSH_MSG_DISCONNECT){ + buf.rewind(); + buf.getInt();buf.getShort(); + int reason_code=buf.getInt(); + byte[] description=buf.getString(); + byte[] language_tag=buf.getString(); + throw new JSchException("SSH_MSG_DISCONNECT: "+ + reason_code+ + " "+Util.byte2str(description)+ + " "+Util.byte2str(language_tag)); + //break; + } + else if(type==SSH_MSG_IGNORE){ + } + else if(type==SSH_MSG_UNIMPLEMENTED){ + buf.rewind(); + buf.getInt();buf.getShort(); + int reason_id=buf.getInt(); + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Received SSH_MSG_UNIMPLEMENTED for "+reason_id); + } + } + else if(type==SSH_MSG_DEBUG){ + buf.rewind(); + buf.getInt();buf.getShort(); +/* + byte always_display=(byte)buf.getByte(); + byte[] message=buf.getString(); + byte[] language_tag=buf.getString(); + System.err.println("SSH_MSG_DEBUG:"+ + " "+Util.byte2str(message)+ + " "+Util.byte2str(language_tag)); +*/ + } + else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST){ + buf.rewind(); + buf.getInt();buf.getShort(); + Channel c=Channel.getChannel(buf.getInt(), this); + if(c==null){ + } + else{ + c.addRemoteWindowSize(buf.getInt()); + } + } + else if(type==UserAuth.SSH_MSG_USERAUTH_SUCCESS){ + isAuthed=true; + if(inflater==null && deflater==null){ + String method; + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } + break; + } + else{ + break; + } + } + buf.rewind(); + return buf; + } + + private void start_discard(Buffer buf, Cipher cipher, MAC mac, + int packet_length, int discard) throws JSchException, IOException{ + MAC discard_mac = null; + + if(!cipher.isCBC()){ + throw new JSchException("Packet corrupt"); + } + + if(packet_length!=PACKET_MAX_SIZE && mac != null){ + discard_mac = mac; + } + + discard -= buf.index; + + while(discard>0){ + buf.reset(); + int len = discard>buf.buffer.length ? buf.buffer.length : discard; + io.getByte(buf.buffer, 0, len); + if(discard_mac!=null){ + discard_mac.update(buf.buffer, 0, len); + } + discard -= len; + } + + if(discard_mac!=null){ + discard_mac.doFinal(buf.buffer, 0); + } + + throw new JSchException("Packet corrupt"); + } + + byte[] getSessionId(){ + return session_id; + } + + private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception { + updateKeys(kex); + in_kex=false; + } + private void updateKeys(KeyExchange kex) throws Exception{ + byte[] K=kex.getK(); + byte[] H=kex.getH(); + HASH hash=kex.getHash(); + +// String[] guess=kex.guess; + + if(session_id==null){ + session_id=new byte[H.length]; + System.arraycopy(H, 0, session_id, 0, H.length); + } + + /* + Initial IV client to server: HASH (K || H || "A" || session_id) + Initial IV server to client: HASH (K || H || "B" || session_id) + Encryption key client to server: HASH (K || H || "C" || session_id) + Encryption key server to client: HASH (K || H || "D" || session_id) + Integrity key client to server: HASH (K || H || "E" || session_id) + Integrity key server to client: HASH (K || H || "F" || session_id) + */ + + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte((byte)0x41); + buf.putByte(session_id); + hash.update(buf.buffer, 0, buf.index); + IVc2s=hash.digest(); + + int j=buf.index-session_id.length-1; + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + IVs2c=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Ec2s=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Es2c=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACc2s=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACs2c=hash.digest(); + + try{ + Class c; + String method; + + method=guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC]; + c=Class.forName(getConfig(method)); + s2ccipher=(Cipher)(c.newInstance()); + while(s2ccipher.getBlockSize()>Es2c.length){ + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte(Es2c); + hash.update(buf.buffer, 0, buf.index); + byte[] foo=hash.digest(); + byte[] bar=new byte[Es2c.length+foo.length]; + System.arraycopy(Es2c, 0, bar, 0, Es2c.length); + System.arraycopy(foo, 0, bar, Es2c.length, foo.length); + Es2c=bar; + } + s2ccipher.init(Cipher.DECRYPT_MODE, Es2c, IVs2c); + s2ccipher_size=s2ccipher.getIVSize(); + + method=guess[KeyExchange.PROPOSAL_MAC_ALGS_STOC]; + c=Class.forName(getConfig(method)); + s2cmac=(MAC)(c.newInstance()); + s2cmac.init(MACs2c); + //mac_buf=new byte[s2cmac.getBlockSize()]; + s2cmac_result1=new byte[s2cmac.getBlockSize()]; + s2cmac_result2=new byte[s2cmac.getBlockSize()]; + + method=guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS]; + c=Class.forName(getConfig(method)); + c2scipher=(Cipher)(c.newInstance()); + while(c2scipher.getBlockSize()>Ec2s.length){ + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte(Ec2s); + hash.update(buf.buffer, 0, buf.index); + byte[] foo=hash.digest(); + byte[] bar=new byte[Ec2s.length+foo.length]; + System.arraycopy(Ec2s, 0, bar, 0, Ec2s.length); + System.arraycopy(foo, 0, bar, Ec2s.length, foo.length); + Ec2s=bar; + } + c2scipher.init(Cipher.ENCRYPT_MODE, Ec2s, IVc2s); + c2scipher_size=c2scipher.getIVSize(); + + method=guess[KeyExchange.PROPOSAL_MAC_ALGS_CTOS]; + c=Class.forName(getConfig(method)); + c2smac=(MAC)(c.newInstance()); + c2smac.init(MACc2s); + + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } + catch(Exception e){ + if(e instanceof JSchException) + throw e; + throw new JSchException(e.toString(), e); + //System.err.println("updatekeys: "+e); + } + } + + /*public*/ /*synchronized*/ void write(Packet packet, Channel c, int length) throws Exception{ + long t = getTimeout(); + while(true){ + if(in_kex){ + if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){ + throw new JSchException("timeout in wating for rekeying process."); + } + try{Thread.sleep(10);} + catch(java.lang.InterruptedException e){}; + continue; + } + synchronized(c){ + + if(c.rwsize<length){ + try{ + c.notifyme++; + c.wait(100); + } + catch(java.lang.InterruptedException e){ + } + finally{ + c.notifyme--; + } + } + + if(c.rwsize>=length){ + c.rwsize-=length; + break; + } + + } + if(c.close || !c.isConnected()){ + throw new IOException("channel is broken"); + } + + boolean sendit=false; + int s=0; + byte command=0; + int recipient=-1; + synchronized(c){ + if(c.rwsize>0){ + long len=c.rwsize; + if(len>length){ + len=length; + } + if(len!=length){ + s=packet.shift((int)len, + (c2scipher!=null ? c2scipher_size : 8), + (c2smac!=null ? c2smac.getBlockSize() : 0)); + } + command=packet.buffer.getCommand(); + recipient=c.getRecipient(); + length-=len; + c.rwsize-=len; + sendit=true; + } + } + if(sendit){ + _write(packet); + if(length==0){ + return; + } + packet.unshift(command, recipient, s, length); + } + + synchronized(c){ + if(in_kex){ + continue; + } + if(c.rwsize>=length){ + c.rwsize-=length; + break; + } + + //try{ + //System.out.println("1wait: "+c.rwsize); + // c.notifyme++; + // c.wait(100); + //} + //catch(java.lang.InterruptedException e){ + //} + //finally{ + // c.notifyme--; + //} + } + } + _write(packet); + } + + public void write(Packet packet) throws Exception{ + // System.err.println("in_kex="+in_kex+" "+(packet.buffer.getCommand())); + long t = getTimeout(); + while(in_kex){ + if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){ + throw new JSchException("timeout in wating for rekeying process."); + } + byte command=packet.buffer.getCommand(); + //System.err.println("command: "+command); + if(command==SSH_MSG_KEXINIT || + command==SSH_MSG_NEWKEYS || + command==SSH_MSG_KEXDH_INIT || + command==SSH_MSG_KEXDH_REPLY || + command==SSH_MSG_KEX_DH_GEX_GROUP || + command==SSH_MSG_KEX_DH_GEX_INIT || + command==SSH_MSG_KEX_DH_GEX_REPLY || + command==SSH_MSG_KEX_DH_GEX_REQUEST || + command==SSH_MSG_DISCONNECT){ + break; + } + try{Thread.sleep(10);} + catch(java.lang.InterruptedException e){}; + } + _write(packet); + } + + private void _write(Packet packet) throws Exception{ + synchronized(lock){ + encode(packet); + if(io!=null){ + io.put(packet); + seqo++; + } + } + } + + Runnable thread; + public void run(){ + thread=this; + + byte[] foo; + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + int i=0; + Channel channel; + int[] start=new int[1]; + int[] length=new int[1]; + KeyExchange kex=null; + + int stimeout=0; + try{ + while(isConnected && + thread!=null){ + try{ + buf=read(buf); + stimeout=0; + } + catch(InterruptedIOException/*SocketTimeoutException*/ ee){ + if(!in_kex && stimeout<serverAliveCountMax){ + sendKeepAliveMsg(); + stimeout++; + continue; + } + else if(in_kex && stimeout<serverAliveCountMax){ + stimeout++; + continue; + } + throw ee; + } + + int msgType=buf.getCommand()&0xff; + + if(kex!=null && kex.getState()==msgType){ + kex_start_time=System.currentTimeMillis(); + boolean result=kex.next(buf); + if(!result){ + throw new JSchException("verify: "+result); + } + continue; + } + + switch(msgType){ + case SSH_MSG_KEXINIT: +//System.err.println("KEXINIT"); + kex=receive_kexinit(buf); + break; + + case SSH_MSG_NEWKEYS: +//System.err.println("NEWKEYS"); + send_newkeys(); + receive_newkeys(buf, kex); + kex=null; + break; + + case SSH_MSG_CHANNEL_DATA: + buf.getInt(); + buf.getByte(); + buf.getByte(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + foo=buf.getString(start, length); + if(channel==null){ + break; + } + + if(length[0]==0){ + break; + } + +try{ + channel.write(foo, start[0], length[0]); +} +catch(Exception e){ +//System.err.println(e); + try{channel.disconnect();}catch(Exception ee){} +break; +} + int len=length[0]; + channel.setLocalWindowSize(channel.lwsize-len); + if(channel.lwsize<channel.lwsize_max/2){ + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST); + buf.putInt(channel.getRecipient()); + buf.putInt(channel.lwsize_max-channel.lwsize); + synchronized(channel){ + if(!channel.close) + write(packet); + } + channel.setLocalWindowSize(channel.lwsize_max); + } + break; + + case SSH_MSG_CHANNEL_EXTENDED_DATA: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + buf.getInt(); // data_type_code == 1 + foo=buf.getString(start, length); + //System.err.println("stderr: "+new String(foo,start[0],length[0])); + if(channel==null){ + break; + } + + if(length[0]==0){ + break; + } + + channel.write_ext(foo, start[0], length[0]); + + len=length[0]; + channel.setLocalWindowSize(channel.lwsize-len); + if(channel.lwsize<channel.lwsize_max/2){ + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST); + buf.putInt(channel.getRecipient()); + buf.putInt(channel.lwsize_max-channel.lwsize); + synchronized(channel){ + if(!channel.close) + write(packet); + } + channel.setLocalWindowSize(channel.lwsize_max); + } + break; + + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel==null){ + break; + } + channel.addRemoteWindowSize(buf.getInt()); + break; + + case SSH_MSG_CHANNEL_EOF: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel!=null){ + //channel.eof_remote=true; + //channel.eof(); + channel.eof_remote(); + } + /* + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_EOF); + buf.putInt(channel.getRecipient()); + write(packet); + */ + break; + case SSH_MSG_CHANNEL_CLOSE: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel!=null){ +// channel.close(); + channel.disconnect(); + } + /* + if(Channel.pool.size()==0){ + thread=null; + } + */ + break; + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel==null){ + //break; + } + int r=buf.getInt(); + long rws=buf.getUInt(); + int rps=buf.getInt(); + + channel.setRemoteWindowSize(rws); + channel.setRemotePacketSize(rps); + channel.open_confirmation=true; + channel.setRecipient(r); + break; + case SSH_MSG_CHANNEL_OPEN_FAILURE: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel==null){ + //break; + } + int reason_code=buf.getInt(); + //foo=buf.getString(); // additional textual information + //foo=buf.getString(); // language tag + channel.setExitStatus(reason_code); + channel.close=true; + channel.eof_remote=true; + channel.setRecipient(0); + break; + case SSH_MSG_CHANNEL_REQUEST: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + foo=buf.getString(); + boolean reply=(buf.getByte()!=0); + channel=Channel.getChannel(i, this); + if(channel!=null){ + byte reply_type=(byte)SSH_MSG_CHANNEL_FAILURE; + if((Util.byte2str(foo)).equals("exit-status")){ + i=buf.getInt(); // exit-status + channel.setExitStatus(i); + reply_type=(byte)SSH_MSG_CHANNEL_SUCCESS; + } + if(reply){ + packet.reset(); + buf.putByte(reply_type); + buf.putInt(channel.getRecipient()); + write(packet); + } + } + else{ + } + break; + case SSH_MSG_CHANNEL_OPEN: + buf.getInt(); + buf.getShort(); + foo=buf.getString(); + String ctyp=Util.byte2str(foo); + if(!"forwarded-tcpip".equals(ctyp) && + !("x11".equals(ctyp) && x11_forwarding) && + !("auth-agent@openssh.com".equals(ctyp) && agent_forwarding)){ + //System.err.println("Session.run: CHANNEL OPEN "+ctyp); + //throw new IOException("Session.run: CHANNEL OPEN "+ctyp); + packet.reset(); + buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE); + buf.putInt(buf.getInt()); + buf.putInt(Channel.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + buf.putString(Util.empty); + buf.putString(Util.empty); + write(packet); + } + else{ + channel=Channel.getChannel(ctyp); + addChannel(channel); + channel.getData(buf); + channel.init(); + + Thread tmp=new Thread(channel); + tmp.setName("Channel "+ctyp+" "+host); + if(daemon_thread){ + tmp.setDaemon(daemon_thread); + } + tmp.start(); + break; + } + case SSH_MSG_CHANNEL_SUCCESS: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel==null){ + break; + } + channel.reply=1; + break; + case SSH_MSG_CHANNEL_FAILURE: + buf.getInt(); + buf.getShort(); + i=buf.getInt(); + channel=Channel.getChannel(i, this); + if(channel==null){ + break; + } + channel.reply=0; + break; + case SSH_MSG_GLOBAL_REQUEST: + buf.getInt(); + buf.getShort(); + foo=buf.getString(); // request name + reply=(buf.getByte()!=0); + if(reply){ + packet.reset(); + buf.putByte((byte)SSH_MSG_REQUEST_FAILURE); + write(packet); + } + break; + case SSH_MSG_REQUEST_FAILURE: + case SSH_MSG_REQUEST_SUCCESS: + Thread t=grr.getThread(); + if(t!=null){ + grr.setReply(msgType==SSH_MSG_REQUEST_SUCCESS? 1 : 0); + t.interrupt(); + } + break; + default: + //System.err.println("Session.run: unsupported type "+msgType); + throw new IOException("Unknown SSH message type "+msgType); + } + } + } + catch(Exception e){ + in_kex=false; + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Caught an exception, leaving main loop due to " + e.getMessage()); + } + //System.err.println("# Session.run"); + //e.printStackTrace(); + } + try{ + disconnect(); + } + catch(NullPointerException e){ + //System.err.println("@1"); + //e.printStackTrace(); + } + catch(Exception e){ + //System.err.println("@2"); + //e.printStackTrace(); + } + isConnected=false; + } + + public void disconnect(){ + if(!isConnected) return; + //System.err.println(this+": disconnect"); + //Thread.dumpStack(); + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Disconnecting from "+host+" port "+port); + } + /* + for(int i=0; i<Channel.pool.size(); i++){ + try{ + Channel c=((Channel)(Channel.pool.elementAt(i))); + if(c.session==this) c.eof(); + } + catch(Exception e){ + } + } + */ + + Channel.disconnect(this); + + isConnected=false; + + PortWatcher.delPort(this); + ChannelForwardedTCPIP.delPort(this); + ChannelX11.removeFakedCookie(this); + + synchronized(lock){ + if(connectThread!=null){ + Thread.yield(); + connectThread.interrupt(); + connectThread=null; + } + } + thread=null; + try{ + if(io!=null){ + if(io.in!=null) io.in.close(); + if(io.out!=null) io.out.close(); + if(io.out_ext!=null) io.out_ext.close(); + } + if(proxy==null){ + if(socket!=null) + socket.close(); + } + else{ + synchronized(proxy){ + proxy.close(); + } + proxy=null; + } + } + catch(Exception e){ +// e.printStackTrace(); + } + io=null; + socket=null; +// synchronized(jsch.pool){ +// jsch.pool.removeElement(this); +// } + + jsch.removeSession(this); + + //System.gc(); + } + + public int setPortForwardingL(int lport, String host, int rport) throws JSchException{ + return setPortForwardingL("127.0.0.1", lport, host, rport); + } + public int setPortForwardingL(String boundaddress, int lport, String host, int rport) throws JSchException{ + return setPortForwardingL(boundaddress, lport, host, rport, null); + } + public int setPortForwardingL(String boundaddress, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{ + PortWatcher pw=PortWatcher.addPort(this, boundaddress, lport, host, rport, ssf); + Thread tmp=new Thread(pw); + tmp.setName("PortWatcher Thread for "+host); + if(daemon_thread){ + tmp.setDaemon(daemon_thread); + } + tmp.start(); + return pw.lport; + } + public void delPortForwardingL(int lport) throws JSchException{ + delPortForwardingL("127.0.0.1", lport); + } + public void delPortForwardingL(String boundaddress, int lport) throws JSchException{ + PortWatcher.delPort(this, boundaddress, lport); + } + public String[] getPortForwardingL() throws JSchException{ + return PortWatcher.getPortForwarding(this); + } + + public void setPortForwardingR(int rport, String host, int lport) throws JSchException{ + setPortForwardingR(null, rport, host, lport, (SocketFactory)null); + } + public void setPortForwardingR(String bind_address, int rport, String host, int lport) throws JSchException{ + setPortForwardingR(bind_address, rport, host, lport, (SocketFactory)null); + } + public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) throws JSchException{ + setPortForwardingR(null, rport, host, lport, sf); + } + public void setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) throws JSchException{ + ChannelForwardedTCPIP.addPort(this, bind_address, rport, host, lport, sf); + setPortForwarding(bind_address, rport); + } + + public void setPortForwardingR(int rport, String daemon) throws JSchException{ + setPortForwardingR(null, rport, daemon, null); + } + public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException{ + setPortForwardingR(null, rport, daemon, arg); + } + public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) throws JSchException{ + ChannelForwardedTCPIP.addPort(this, bind_address, rport, daemon, arg); + setPortForwarding(bind_address, rport); + } + + private class GlobalRequestReply{ + private Thread thread=null; + private int reply=-1; + void setThread(Thread thread){ + this.thread=thread; + this.reply=-1; + } + Thread getThread(){ return thread; } + void setReply(int reply){ this.reply=reply; } + int getReply(){ return this.reply; } + } + private GlobalRequestReply grr=new GlobalRequestReply(); + private void setPortForwarding(String bind_address, int rport) throws JSchException{ + synchronized(grr){ + Buffer buf=new Buffer(100); // ?? + Packet packet=new Packet(buf); + + String address_to_bind=ChannelForwardedTCPIP.normalize(bind_address); + + grr.setThread(Thread.currentThread()); + + try{ + // byte SSH_MSG_GLOBAL_REQUEST 80 + // string "tcpip-forward" + // boolean want_reply + // string address_to_bind + // uint32 port number to bind + packet.reset(); + buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); + buf.putString(Util.str2byte("tcpip-forward")); + buf.putByte((byte)1); + buf.putString(Util.str2byte(address_to_bind)); + buf.putInt(rport); + write(packet); + } + catch(Exception e){ + grr.setThread(null); + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + + int count = 0; + int reply = grr.getReply(); + while(count < 10 && reply == -1){ + try{ Thread.sleep(1000); } + catch(Exception e){ + } + count++; + reply = grr.getReply(); + } + grr.setThread(null); + if(reply != 1){ + throw new JSchException("remote port forwarding failed for listen port "+rport); + } + } + } + public void delPortForwardingR(int rport) throws JSchException{ + ChannelForwardedTCPIP.delPort(this, rport); + } + + private void initDeflater(String method) throws JSchException{ + if(method.equals("none")){ + deflater=null; + return; + } + String foo=getConfig(method); + if(foo!=null){ + if(method.equals("zlib") || + (isAuthed && method.equals("zlib@openssh.com"))){ + try{ + Class c=Class.forName(foo); + deflater=(Compression)(c.newInstance()); + int level=6; + try{ level=Integer.parseInt(getConfig("compression_level"));} + catch(Exception ee){ } + deflater.init(Compression.DEFLATER, level); + } + catch(Exception ee){ + throw new JSchException(ee.toString(), ee); + //System.err.println(foo+" isn't accessible."); + } + } + } + } + private void initInflater(String method) throws JSchException{ + if(method.equals("none")){ + inflater=null; + return; + } + String foo=getConfig(method); + if(foo!=null){ + if(method.equals("zlib") || + (isAuthed && method.equals("zlib@openssh.com"))){ + try{ + Class c=Class.forName(foo); + inflater=(Compression)(c.newInstance()); + inflater.init(Compression.INFLATER, 0); + } + catch(Exception ee){ + throw new JSchException(ee.toString(), ee); + //System.err.println(foo+" isn't accessible."); + } + } + } + } + + void addChannel(Channel channel){ + channel.setSession(this); + } + + public void setProxy(Proxy proxy){ this.proxy=proxy; } + public void setHost(String host){ this.host=host; } + public void setPort(int port){ this.port=port; } + void setUserName(String username){ this.username=username; } + public void setUserInfo(UserInfo userinfo){ this.userinfo=userinfo; } + public UserInfo getUserInfo(){ return userinfo; } + public void setInputStream(InputStream in){ this.in=in; } + public void setOutputStream(OutputStream out){ this.out=out; } + public void setX11Host(String host){ ChannelX11.setHost(host); } + public void setX11Port(int port){ ChannelX11.setPort(port); } + public void setX11Cookie(String cookie){ ChannelX11.setCookie(cookie); } + public void setPassword(String password){ + if(password!=null) + this.password=Util.str2byte(password); + } + public void setPassword(byte[] password){ + if(password!=null){ + this.password=new byte[password.length]; + System.arraycopy(password, 0, this.password, 0, password.length); + } + } + + public void setConfig(java.util.Properties newconf){ + setConfig((java.util.Hashtable)newconf); + } + + public void setConfig(java.util.Hashtable newconf){ + synchronized(lock){ + if(config==null) + config=new java.util.Hashtable(); + for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) { + String key=(String)(e.nextElement()); + config.put(key, (String)(newconf.get(key))); + } + } + } + + public void setConfig(String key, String value){ + synchronized(lock){ + if(config==null){ + config=new java.util.Hashtable(); + } + config.put(key, value); + } + } + + public String getConfig(String key){ + Object foo=null; + if(config!=null){ + foo=config.get(key); + if(foo instanceof String) return (String)foo; + } + foo=jsch.getConfig(key); + if(foo instanceof String) return (String)foo; + return null; + } + + public void setSocketFactory(SocketFactory sfactory){ + socket_factory=sfactory; + } + public boolean isConnected(){ return isConnected; } + public int getTimeout(){ return timeout; } + public void setTimeout(int timeout) throws JSchException { + if(socket==null){ + if(timeout<0){ + throw new JSchException("invalid timeout value"); + } + this.timeout=timeout; + return; + } + try{ + socket.setSoTimeout(timeout); + this.timeout=timeout; + } + catch(Exception e){ + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + public String getServerVersion(){ + return Util.byte2str(V_S); + } + public String getClientVersion(){ + return Util.byte2str(V_C); + } + public void setClientVersion(String cv){ + V_C=Util.str2byte(cv); + } + + public void sendIgnore() throws Exception{ + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_IGNORE); + write(packet); + } + + private static final byte[] keepalivemsg=Util.str2byte("keepalive@jcraft.com"); + public void sendKeepAliveMsg() throws Exception{ + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_GLOBAL_REQUEST); + buf.putString(keepalivemsg); + buf.putByte((byte)1); + write(packet); + } + + private HostKey hostkey=null; + public HostKey getHostKey(){ return hostkey; } + public String getHost(){return host;} + public String getUserName(){return username;} + public int getPort(){return port;} + public void setHostKeyAlias(String hostKeyAlias){ + this.hostKeyAlias=hostKeyAlias; + } + public String getHostKeyAlias(){ + return hostKeyAlias; + } + + public void setServerAliveInterval(int interval) throws JSchException { + setTimeout(interval); + this.serverAliveInterval=interval; + } + public void setServerAliveCountMax(int count){ + this.serverAliveCountMax=count; + } + + public int getServerAliveInterval(){ + return this.serverAliveInterval; + } + public int getServerAliveCountMax(){ + return this.serverAliveCountMax; + } + + public void setDaemonThread(boolean enable){ + this.daemon_thread=enable; + } + + private String[] checkCiphers(String ciphers){ + if(ciphers==null || ciphers.length()==0) + return null; + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "CheckCiphers: "+ciphers); + } + + java.util.Vector result=new java.util.Vector(); + String[] _ciphers=Util.split(ciphers, ","); + for(int i=0; i<_ciphers.length; i++){ + if(!checkCipher(getConfig(_ciphers[i]))){ + result.addElement(_ciphers[i]); + } + } + if(result.size()==0) + return null; + String[] foo=new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + for(int i=0; i<foo.length; i++){ + JSch.getLogger().log(Logger.INFO, + foo[i]+" is not available."); + } + } + + return foo; + } + + static boolean checkCipher(String cipher){ + try{ + Class c=Class.forName(cipher); + Cipher _c=(Cipher)(c.newInstance()); + _c.init(Cipher.ENCRYPT_MODE, + new byte[_c.getBlockSize()], + new byte[_c.getIVSize()]); + return true; + } + catch(Exception e){ + return false; + } + } + + private String[] checkKexes(String kexes){ + if(kexes==null || kexes.length()==0) + return null; + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "CheckKexes: "+kexes); + } + + java.util.Vector result=new java.util.Vector(); + String[] _kexes=Util.split(kexes, ","); + for(int i=0; i<_kexes.length; i++){ + if(!checkKex(this, getConfig(_kexes[i]))){ + result.addElement(_kexes[i]); + } + } + if(result.size()==0) + return null; + String[] foo=new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + for(int i=0; i<foo.length; i++){ + JSch.getLogger().log(Logger.INFO, + foo[i]+" is not available."); + } + } + + return foo; + } + + static boolean checkKex(Session s, String kex){ + try{ + Class c=Class.forName(kex); + KeyExchange _c=(KeyExchange)(c.newInstance()); + _c.init(s ,null, null, null, null); + return true; + } + catch(Exception e){ return false; } + } +} diff --git a/java/com/jcraft/jsch/SftpATTRS.java b/java/com/jcraft/jsch/SftpATTRS.java new file mode 100644 index 00000000..e89b2435 --- /dev/null +++ b/java/com/jcraft/jsch/SftpATTRS.java @@ -0,0 +1,263 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/* + uint32 flags + uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE + uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS + uint32 atime present only if flag SSH_FILEXFER_ACMODTIME + uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME + uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED + string extended_type + string extended_data + ... more extended data (extended_type - extended_data pairs), + so that number of pairs equals extended_count +*/ +public class SftpATTRS { + + static final int S_ISUID = 04000; // set user ID on execution + static final int S_ISGID = 02000; // set group ID on execution + static final int S_ISVTX = 01000; // sticky bit ****** NOT DOCUMENTED ***** + + static final int S_IRUSR = 00400; // read by owner + static final int S_IWUSR = 00200; // write by owner + static final int S_IXUSR = 00100; // execute/search by owner + static final int S_IREAD = 00400; // read by owner + static final int S_IWRITE= 00200; // write by owner + static final int S_IEXEC = 00100; // execute/search by owner + + static final int S_IRGRP = 00040; // read by group + static final int S_IWGRP = 00020; // write by group + static final int S_IXGRP = 00010; // execute/search by group + + static final int S_IROTH = 00004; // read by others + static final int S_IWOTH = 00002; // write by others + static final int S_IXOTH = 00001; // execute/search by others + + private static final int pmask = 0xFFF; + + public String getPermissionsString() { + StringBuffer buf = new StringBuffer(10); + + if(isDir()) buf.append('d'); + else if(isLink()) buf.append('l'); + else buf.append('-'); + + if((permissions & S_IRUSR)!=0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWUSR)!=0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_ISUID)!=0) buf.append('s'); + else if ((permissions & S_IXUSR)!=0) buf.append('x'); + else buf.append('-'); + + if((permissions & S_IRGRP)!=0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWGRP)!=0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_ISGID)!=0) buf.append('s'); + else if((permissions & S_IXGRP)!=0) buf.append('x'); + else buf.append('-'); + + if((permissions & S_IROTH) != 0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWOTH) != 0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_IXOTH) != 0) buf.append('x'); + else buf.append('-'); + return (buf.toString()); + } + + public String getAtimeString(){ + SimpleDateFormat locale=new SimpleDateFormat(); + return (locale.format(new Date(atime))); + } + + public String getMtimeString(){ + Date date= new Date(((long)mtime)*1000); + return (date.toString()); + } + + public static final int SSH_FILEXFER_ATTR_SIZE= 0x00000001; + public static final int SSH_FILEXFER_ATTR_UIDGID= 0x00000002; + public static final int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004; + public static final int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008; + public static final int SSH_FILEXFER_ATTR_EXTENDED= 0x80000000; + + static final int S_IFDIR=0x4000; + static final int S_IFLNK=0xa000; + + int flags=0; + long size; + int uid; + int gid; + int permissions; + int atime; + int mtime; + String[] extended=null; + + private SftpATTRS(){ + } + + static SftpATTRS getATTR(Buffer buf){ + SftpATTRS attr=new SftpATTRS(); + attr.flags=buf.getInt(); + if((attr.flags&SSH_FILEXFER_ATTR_SIZE)!=0){ attr.size=buf.getLong(); } + if((attr.flags&SSH_FILEXFER_ATTR_UIDGID)!=0){ + attr.uid=buf.getInt(); attr.gid=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ + attr.permissions=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ + attr.atime=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ + attr.mtime=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){ + int count=buf.getInt(); + if(count>0){ + attr.extended=new String[count*2]; + for(int i=0; i<count; i++){ + attr.extended[i*2]=Util.byte2str(buf.getString()); + attr.extended[i*2+1]=Util.byte2str(buf.getString()); + } + } + } + return attr; + } + + int length(){ + int len=4; + + if((flags&SSH_FILEXFER_ATTR_SIZE)!=0){ len+=8; } + if((flags&SSH_FILEXFER_ATTR_UIDGID)!=0){ len+=8; } + if((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ len+=4; } + if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ len+=8; } + if((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){ + len+=4; + int count=extended.length/2; + if(count>0){ + for(int i=0; i<count; i++){ + len+=4; len+=extended[i*2].length(); + len+=4; len+=extended[i*2+1].length(); + } + } + } + return len; + } + + void dump(Buffer buf){ + buf.putInt(flags); + if((flags&SSH_FILEXFER_ATTR_SIZE)!=0){ buf.putLong(size); } + if((flags&SSH_FILEXFER_ATTR_UIDGID)!=0){ + buf.putInt(uid); buf.putInt(gid); + } + if((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ + buf.putInt(permissions); + } + if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ buf.putInt(atime); } + if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ buf.putInt(mtime); } + if((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){ + int count=extended.length/2; + if(count>0){ + for(int i=0; i<count; i++){ + buf.putString(Util.str2byte(extended[i*2])); + buf.putString(Util.str2byte(extended[i*2+1])); + } + } + } + } + void setFLAGS(int flags){ + this.flags=flags; + } + public void setSIZE(long size){ + flags|=SSH_FILEXFER_ATTR_SIZE; + this.size=size; + } + public void setUIDGID(int uid, int gid){ + flags|=SSH_FILEXFER_ATTR_UIDGID; + this.uid=uid; + this.gid=gid; + } + public void setACMODTIME(int atime, int mtime){ + flags|=SSH_FILEXFER_ATTR_ACMODTIME; + this.atime=atime; + this.mtime=mtime; + } + public void setPERMISSIONS(int permissions){ + flags|=SSH_FILEXFER_ATTR_PERMISSIONS; + permissions=(this.permissions&~pmask)|(permissions&pmask); + this.permissions=permissions; + } + + public boolean isDir(){ + return ((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0 && + ((permissions&S_IFDIR)==S_IFDIR)); + } + public boolean isLink(){ + return ((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0 && + ((permissions&S_IFLNK)==S_IFLNK)); + } + public int getFlags() { return flags; } + public long getSize() { return size; } + public int getUId() { return uid; } + public int getGId() { return gid; } + public int getPermissions() { return permissions; } + public int getATime() { return atime; } + public int getMTime() { return mtime; } + public String[] getExtended() { return extended; } + + public String toString() { + return (getPermissionsString()+" "+getUId()+" "+getGId()+" "+getSize()+" "+getMtimeString()); + } + /* + public String toString(){ + return (((flags&SSH_FILEXFER_ATTR_SIZE)!=0) ? ("size:"+size+" ") : "")+ + (((flags&SSH_FILEXFER_ATTR_UIDGID)!=0) ? ("uid:"+uid+",gid:"+gid+" ") : "")+ + (((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0) ? ("permissions:0x"+Integer.toHexString(permissions)+" ") : "")+ + (((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0) ? ("atime:"+atime+",mtime:"+mtime+" ") : "")+ + (((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0) ? ("extended:?"+" ") : ""); + } + */ +} diff --git a/java/com/jcraft/jsch/SftpException.java b/java/com/jcraft/jsch/SftpException.java new file mode 100644 index 00000000..6a6c1ff8 --- /dev/null +++ b/java/com/jcraft/jsch/SftpException.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class SftpException extends Exception{ + //private static final long serialVersionUID=-5616888495583253811L; + public int id; + private Throwable cause=null; + public SftpException (int id, String message) { + super(message); + this.id=id; + } + public SftpException (int id, String message, Throwable e) { + super(message); + this.id=id; + this.cause=e; + } + public String toString(){ + return id+": "+getMessage(); + } + public Throwable getCause(){ + return this.cause; + } +} diff --git a/java/com/jcraft/jsch/SftpProgressMonitor.java b/java/com/jcraft/jsch/SftpProgressMonitor.java new file mode 100644 index 00000000..bd91688d --- /dev/null +++ b/java/com/jcraft/jsch/SftpProgressMonitor.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface SftpProgressMonitor{ + public static final int PUT=0; + public static final int GET=1; + public static final long UNKNOWN_SIZE = -1L; + void init(int op, String src, String dest, long max); + boolean count(long count); + void end(); +} diff --git a/java/com/jcraft/jsch/SignatureDSA.java b/java/com/jcraft/jsch/SignatureDSA.java new file mode 100644 index 00000000..6f44b7ff --- /dev/null +++ b/java/com/jcraft/jsch/SignatureDSA.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface SignatureDSA{ + void init() throws Exception; + void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception; + void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception; + void update(byte[] H) throws Exception; + boolean verify(byte[] sig) throws Exception; + byte[] sign() throws Exception; +} diff --git a/java/com/jcraft/jsch/SignatureRSA.java b/java/com/jcraft/jsch/SignatureRSA.java new file mode 100644 index 00000000..e8e61059 --- /dev/null +++ b/java/com/jcraft/jsch/SignatureRSA.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface SignatureRSA{ + void init() throws Exception; + void setPubKey(byte[] e, byte[] n) throws Exception; + void setPrvKey(byte[] d, byte[] n) throws Exception; + void update(byte[] H) throws Exception; + boolean verify(byte[] sig) throws Exception; + byte[] sign() throws Exception; +} diff --git a/java/com/jcraft/jsch/SocketFactory.java b/java/com/jcraft/jsch/SocketFactory.java new file mode 100644 index 00000000..aaac0dce --- /dev/null +++ b/java/com/jcraft/jsch/SocketFactory.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.io.*; + +public interface SocketFactory{ + public Socket createSocket(String host, int port)throws IOException, + UnknownHostException; + public InputStream getInputStream(Socket socket)throws IOException; + public OutputStream getOutputStream(Socket socket)throws IOException; +} diff --git a/java/com/jcraft/jsch/UIKeyboardInteractive.java b/java/com/jcraft/jsch/UIKeyboardInteractive.java new file mode 100644 index 00000000..23af9c31 --- /dev/null +++ b/java/com/jcraft/jsch/UIKeyboardInteractive.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface UIKeyboardInteractive{ + String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo); +} diff --git a/java/com/jcraft/jsch/UserAuth.java b/java/com/jcraft/jsch/UserAuth.java new file mode 100644 index 00000000..085a9508 --- /dev/null +++ b/java/com/jcraft/jsch/UserAuth.java @@ -0,0 +1,53 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public abstract class UserAuth{ + protected static final int SSH_MSG_USERAUTH_REQUEST= 50; + protected static final int SSH_MSG_USERAUTH_FAILURE= 51; + protected static final int SSH_MSG_USERAUTH_SUCCESS= 52; + protected static final int SSH_MSG_USERAUTH_BANNER= 53; + protected static final int SSH_MSG_USERAUTH_INFO_REQUEST= 60; + protected static final int SSH_MSG_USERAUTH_INFO_RESPONSE= 61; + protected static final int SSH_MSG_USERAUTH_PK_OK= 60; + + protected UserInfo userinfo; + protected Packet packet; + protected Buffer buf; + protected String username; + + public boolean start(Session session) throws Exception{ + this.userinfo=session.getUserInfo(); + this.packet=session.packet; + this.buf=packet.getBuffer(); + this.username=session.getUserName(); + return true; + } +} diff --git a/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java b/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java new file mode 100644 index 00000000..15856cbc --- /dev/null +++ b/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java @@ -0,0 +1,227 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING +NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class UserAuthGSSAPIWithMIC extends UserAuth { + private static final int SSH_MSG_USERAUTH_GSSAPI_RESPONSE= 60; + private static final int SSH_MSG_USERAUTH_GSSAPI_TOKEN= 61; + private static final int SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE=63; + private static final int SSH_MSG_USERAUTH_GSSAPI_ERROR= 64; + private static final int SSH_MSG_USERAUTH_GSSAPI_ERRTOK= 65; + private static final int SSH_MSG_USERAUTH_GSSAPI_MIC= 66; + + private static final byte[][] supported_oid={ + // OID 1.2.840.113554.1.2.2 in DER + {(byte)0x6,(byte)0x9,(byte)0x2a,(byte)0x86,(byte)0x48, + (byte)0x86,(byte)0xf7,(byte)0x12,(byte)0x1,(byte)0x2, + (byte)0x2} + }; + + private static final String[] supported_method={ + "gssapi-with-mic.krb5" + }; + + public boolean start(Session session)throws Exception{ + super.start(session); + + byte[] _username=Util.str2byte(username); + + packet.reset(); + + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name(in ISO-10646 UTF-8 encoding) + // string service name(in US-ASCII) + // string "gssapi"(US-ASCII) + // uint32 n, the number of OIDs client supports + // string[n] mechanism OIDS + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("gssapi-with-mic")); + buf.putInt(supported_oid.length); + for(int i=0; i<supported_oid.length; i++){ + buf.putString(supported_oid[i]); + } + session.write(packet); + + String method=null; + int command; + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_FAILURE){ + return false; + } + + if(command==SSH_MSG_USERAUTH_GSSAPI_RESPONSE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] message=buf.getString(); + + for(int i=0; i<supported_oid.length; i++){ + if(Util.array_equals(message, supported_oid[i])){ + method=supported_method[i]; + break; + } + } + + if(method==null){ + return false; + } + + break; // success + } + + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue; + } + return false; + } + + GSSContext context=null; + try{ + Class c=Class.forName(session.getConfig(method)); + context=(GSSContext)(c.newInstance()); + } + catch(Exception e){ + return false; + } + + try{ + context.create(username, session.host); + } + catch(JSchException e){ + return false; + } + + byte[] token=new byte[0]; + + while(!context.isEstablished()){ + try{ + token=context.init(token, 0, token.length); + } + catch(JSchException e){ + // TODO + // ERRTOK should be sent? + // byte SSH_MSG_USERAUTH_GSSAPI_ERRTOK + // string error token + return false; + } + + if(token!=null){ + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_GSSAPI_TOKEN); + buf.putString(token); + session.write(packet); + } + + if(!context.isEstablished()){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + if(command==SSH_MSG_USERAUTH_GSSAPI_ERROR){ + // uint32 major_status + // uint32 minor_status + // string message + // string language tag + + buf=session.read(buf); + command=buf.getCommand()&0xff; + //return false; + } + else if(command==SSH_MSG_USERAUTH_GSSAPI_ERRTOK){ + // string error token + + buf=session.read(buf); + command=buf.getCommand()&0xff; + //return false; + } + + if(command==SSH_MSG_USERAUTH_FAILURE){ + return false; + } + + buf.getInt(); buf.getByte(); buf.getByte(); + token=buf.getString(); + } + } + + Buffer mbuf=new Buffer(); + // string session identifier + // byte SSH_MSG_USERAUTH_REQUEST + // string user name + // string service + // string "gssapi-with-mic" + mbuf.putString(session.getSessionId()); + mbuf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + mbuf.putString(_username); + mbuf.putString(Util.str2byte("ssh-connection")); + mbuf.putString(Util.str2byte("gssapi-with-mic")); + + byte[] mic=context.getMIC(mbuf.buffer, 0, mbuf.getLength()); + + if(mic==null){ + return false; + } + + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_GSSAPI_MIC); + buf.putString(mic); + session.write(packet); + + context.dispose(); + + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + } + return false; + } +} + + diff --git a/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java b/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java new file mode 100644 index 00000000..29947965 --- /dev/null +++ b/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java @@ -0,0 +1,203 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class UserAuthKeyboardInteractive extends UserAuth{ + public boolean start(Session session) throws Exception{ + super.start(session); + + if(userinfo!=null && !(userinfo instanceof UIKeyboardInteractive)){ + return false; + } + + String dest=username+"@"+session.host; + if(session.port!=22){ + dest+=(":"+session.port); + } + byte[] password=session.password; + + boolean cancel=false; + + byte[] _username=null; + _username=Util.str2byte(username); + + while(true){ + + if(session.auth_failures >= session.max_auth_tries){ + return false; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name (ISO-10646 UTF-8, as defined in [RFC-2279]) + // string service name (US-ASCII) "ssh-userauth" ? "ssh-connection" + // string "keyboard-interactive" (US-ASCII) + // string language tag (as defined in [RFC-3066]) + // string submethods (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + //buf.putString("ssh-userauth".getBytes()); + buf.putString(Util.str2byte("keyboard-interactive")); + buf.putString(Util.empty); + buf.putString(Util.empty); + session.write(packet); + + boolean firsttime=true; + loop: + while(true){ + buf=session.read(buf); + int command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop; + } + if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); +// System.err.println(new String(foo)+ +// " partial_success:"+(partial_success!=0)); + + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + + if(firsttime){ + return false; + //throw new JSchException("USERAUTH KI is not supported"); + //cancel=true; // ?? + } + session.auth_failures++; + break; + } + if(command==SSH_MSG_USERAUTH_INFO_REQUEST){ + firsttime=false; + buf.getInt(); buf.getByte(); buf.getByte(); + String name=Util.byte2str(buf.getString()); + String instruction=Util.byte2str(buf.getString()); + String languate_tag=Util.byte2str(buf.getString()); + int num=buf.getInt(); + String[] prompt=new String[num]; + boolean[] echo=new boolean[num]; + for(int i=0; i<num; i++){ + prompt[i]=Util.byte2str(buf.getString()); + echo[i]=(buf.getByte()!=0); + } + + byte[][] response=null; + + if(password!=null && + prompt.length==1 && + !echo[0] && + prompt[0].toLowerCase().startsWith("password:")){ + response=new byte[1][]; + response[0]=password; + password=null; + } + else if(num>0 + ||(name.length()>0 || instruction.length()>0) + ){ + if(userinfo!=null){ + UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo; + String[] _response=kbi.promptKeyboardInteractive(dest, + name, + instruction, + prompt, + echo); + if(_response!=null){ + response=new byte[_response.length][]; + for(int i=0; i<_response.length; i++){ + response[i]=Util.str2byte(_response[i]); + } + } + } + } + + // byte SSH_MSG_USERAUTH_INFO_RESPONSE(61) + // int num-responses + // string response[1] (ISO-10646 UTF-8) + // ... + // string response[num-responses] (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_INFO_RESPONSE); + if(num>0 && + (response==null || // cancel + num!=response.length)){ + + if(response==null){ + // working around the bug in OpenSSH ;-< + buf.putInt(num); + for(int i=0; i<num; i++){ + buf.putString(Util.empty); + } + } + else{ + buf.putInt(0); + } + + if(response==null) + cancel=true; + } + else{ + buf.putInt(num); + for(int i=0; i<num; i++){ + buf.putString(response[i]); + } + } + session.write(packet); + /* + if(cancel) + break; + */ + continue loop; + } + //throw new JSchException("USERAUTH fail ("+command+")"); + return false; + } + if(cancel){ + throw new JSchAuthCancelException("keyboard-interactive"); + //break; + } + } + //return false; + } +} diff --git a/java/com/jcraft/jsch/UserAuthNone.java b/java/com/jcraft/jsch/UserAuthNone.java new file mode 100644 index 00000000..b3b0b077 --- /dev/null +++ b/java/com/jcraft/jsch/UserAuthNone.java @@ -0,0 +1,129 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class UserAuthNone extends UserAuth{ + private static final int SSH_MSG_SERVICE_ACCEPT= 6; + private String methods=null; + + public boolean start(Session session) throws Exception{ + super.start(session); + + + // send + // byte SSH_MSG_SERVICE_REQUEST(5) + // string service name "ssh-userauth" + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_SERVICE_REQUEST); + buf.putString(Util.str2byte("ssh-userauth")); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_SERVICE_REQUEST sent"); + } + + // receive + // byte SSH_MSG_SERVICE_ACCEPT(6) + // string service name + buf=session.read(buf); + int command=buf.getCommand(); + + boolean result=(command==SSH_MSG_SERVICE_ACCEPT); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_SERVICE_ACCEPT received"); + } + if(!result) + return false; + + byte[] _username=null; + _username=Util.str2byte(username); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "none" + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("none")); + session.write(packet); + + loop: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + try{ + userinfo.showMessage(message); + } + catch(RuntimeException ee){ + } + } + continue loop; + } + if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + methods=Util.byte2str(foo); +//System.err.println("UserAuthNONE: "+methods+ +// " partial_success:"+(partial_success!=0)); +// if(partial_success!=0){ +// throw new JSchPartialAuthException(new String(foo)); +// } + + break; + } + else{ +// System.err.println("USERAUTH fail ("+command+")"); + throw new JSchException("USERAUTH fail ("+command+")"); + } + } + //throw new JSchException("USERAUTH fail"); + return false; + } + String getMethods(){ + return methods; + } +} diff --git a/java/com/jcraft/jsch/UserAuthPassword.java b/java/com/jcraft/jsch/UserAuthPassword.java new file mode 100644 index 00000000..9b5837f5 --- /dev/null +++ b/java/com/jcraft/jsch/UserAuthPassword.java @@ -0,0 +1,193 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class UserAuthPassword extends UserAuth{ + private final int SSH_MSG_USERAUTH_PASSWD_CHANGEREQ=60; + + public boolean start(Session session) throws Exception{ + super.start(session); + + byte[] password=session.password; + String dest=username+"@"+session.host; + if(session.port!=22){ + dest+=(":"+session.port); + } + + try{ + + while(true){ + + if(session.auth_failures >= session.max_auth_tries){ + return false; + } + + if(password==null){ + if(userinfo==null){ + //throw new JSchException("USERAUTH fail"); + return false; + } + if(!userinfo.promptPassword("Password for "+dest)){ + throw new JSchAuthCancelException("password"); + //break; + } + + String _password=userinfo.getPassword(); + if(_password==null){ + throw new JSchAuthCancelException("password"); + //break; + } + password=Util.str2byte(_password); + } + + byte[] _username=null; + _username=Util.str2byte(username); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen FALSE + // string plaintext password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte)0); + buf.putString(password); + session.write(packet); + + loop: + while(true){ + buf=session.read(buf); + int command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop; + } + if(command==SSH_MSG_USERAUTH_PASSWD_CHANGEREQ){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] instruction=buf.getString(); + byte[] tag=buf.getString(); + if(userinfo==null || + !(userinfo instanceof UIKeyboardInteractive)){ + if(userinfo!=null){ + userinfo.showMessage("Password must be changed."); + } + return false; + } + + UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo; + String[] response; + String name="Password Change Required"; + String[] prompt={"New Password: "}; + boolean[] echo={false}; + response=kbi.promptKeyboardInteractive(dest, + name, + Util.byte2str(instruction), + prompt, + echo); + if(response==null){ + throw new JSchAuthCancelException("password"); + } + + byte[] newpassword=Util.str2byte(response[0]); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen TRUE + // string plaintext old password (ISO-10646 UTF-8) + // string plaintext new password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte)1); + buf.putString(password); + buf.putString(newpassword); + Util.bzero(newpassword); + response=null; + session.write(packet); + continue loop; + } + if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + break; + } + else{ + //System.err.println("USERAUTH fail ("+buf.getCommand()+")"); +// throw new JSchException("USERAUTH fail ("+buf.getCommand()+")"); + return false; + } + } + + if(password!=null){ + Util.bzero(password); + password=null; + } + + } + + } + finally{ + if(password!=null){ + Util.bzero(password); + password=null; + } + } + + //throw new JSchException("USERAUTH fail"); + //return false; + } +} diff --git a/java/com/jcraft/jsch/UserAuthPublicKey.java b/java/com/jcraft/jsch/UserAuthPublicKey.java new file mode 100644 index 00000000..64e8e720 --- /dev/null +++ b/java/com/jcraft/jsch/UserAuthPublicKey.java @@ -0,0 +1,228 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +class UserAuthPublicKey extends UserAuth{ + + public boolean start(Session session) throws Exception{ + super.start(session); + + Vector identities=session.jsch.getIdentityRepository().getIdentities(); + + byte[] passphrase=null; + byte[] _username=null; + + int command; + + synchronized(identities){ + if(identities.size()<=0){ + return false; + } + + _username=Util.str2byte(username); + + for(int i=0; i<identities.size(); i++){ + + if(session.auth_failures >= session.max_auth_tries){ + return false; + } + + Identity identity=(Identity)(identities.elementAt(i)); + byte[] pubkeyblob=identity.getPublicKeyBlob(); + +//System.err.println("UserAuthPublicKey: "+identity+" "+pubkeyblob); + + if(pubkeyblob!=null){ + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen FALSE + // string plaintext password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte)0); + buf.putString(Util.str2byte(identity.getAlgName())); + buf.putString(pubkeyblob); + session.write(packet); + + loop1: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_PK_OK){ + break; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + break; + } + else if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop1; + } + else{ + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); + break; + } + } + + if(command!=SSH_MSG_USERAUTH_PK_OK){ + continue; + } + } + +//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + + int count=5; + while(true){ + if((identity.isEncrypted() && passphrase==null)){ + if(userinfo==null) throw new JSchException("USERAUTH fail"); + if(identity.isEncrypted() && + !userinfo.promptPassphrase("Passphrase for "+identity.getName())){ + throw new JSchAuthCancelException("publickey"); + //throw new JSchException("USERAUTH cancel"); + //break; + } + String _passphrase=userinfo.getPassphrase(); + if(_passphrase!=null){ + passphrase=Util.str2byte(_passphrase); + } + } + + if(!identity.isEncrypted() || passphrase!=null){ + if(identity.setPassphrase(passphrase)) + break; + } + Util.bzero(passphrase); + passphrase=null; + count--; + if(count==0)break; + } + + Util.bzero(passphrase); + passphrase=null; +//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + + if(identity.isEncrypted()) continue; + if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob(); + +//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob); + + if(pubkeyblob==null) continue; + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen TRUE + // string plaintext password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte)1); + buf.putString(Util.str2byte(identity.getAlgName())); + buf.putString(pubkeyblob); + +// byte[] tmp=new byte[buf.index-5]; +// System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length); +// buf.putString(signature); + + byte[] sid=session.getSessionId(); + int sidlen=sid.length; + byte[] tmp=new byte[4+sidlen+buf.index-5]; + tmp[0]=(byte)(sidlen>>>24); + tmp[1]=(byte)(sidlen>>>16); + tmp[2]=(byte)(sidlen>>>8); + tmp[3]=(byte)(sidlen); + System.arraycopy(sid, 0, tmp, 4, sidlen); + System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5); + byte[] signature=identity.getSignature(tmp); + if(signature==null){ // for example, too long key length. + break; + } + buf.putString(signature); + session.write(packet); + + loop2: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + else if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop2; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + break; + } + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); + break; + } + } + } + return false; + } +} diff --git a/java/com/jcraft/jsch/UserInfo.java b/java/com/jcraft/jsch/UserInfo.java new file mode 100644 index 00000000..22552ede --- /dev/null +++ b/java/com/jcraft/jsch/UserInfo.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface UserInfo{ + String getPassphrase(); + String getPassword(); + boolean promptPassword(String message); + boolean promptPassphrase(String message); + boolean promptYesNo(String message); + void showMessage(String message); +} diff --git a/java/com/jcraft/jsch/Util.java b/java/com/jcraft/jsch/Util.java new file mode 100644 index 00000000..df51b2da --- /dev/null +++ b/java/com/jcraft/jsch/Util.java @@ -0,0 +1,474 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; +import java.net.Socket; + +class Util{ + + private static final byte[] b64 =Util.str2byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); + private static byte val(byte foo){ + if(foo == '=') return 0; + for(int j=0; j<b64.length; j++){ + if(foo==b64[j]) return (byte)j; + } + return 0; + } + static byte[] fromBase64(byte[] buf, int start, int length){ + byte[] foo=new byte[length]; + int j=0; + for (int i=start;i<start+length;i+=4){ + foo[j]=(byte)((val(buf[i])<<2)|((val(buf[i+1])&0x30)>>>4)); + if(buf[i+2]==(byte)'='){ j++; break;} + foo[j+1]=(byte)(((val(buf[i+1])&0x0f)<<4)|((val(buf[i+2])&0x3c)>>>2)); + if(buf[i+3]==(byte)'='){ j+=2; break;} + foo[j+2]=(byte)(((val(buf[i+2])&0x03)<<6)|(val(buf[i+3])&0x3f)); + j+=3; + } + byte[] bar=new byte[j]; + System.arraycopy(foo, 0, bar, 0, j); + return bar; + } + static byte[] toBase64(byte[] buf, int start, int length){ + + byte[] tmp=new byte[length*2]; + int i,j,k; + + int foo=(length/3)*3+start; + i=0; + for(j=start; j<foo; j+=3){ + k=(buf[j]>>>2)&0x3f; + tmp[i++]=b64[k]; + k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f; + tmp[i++]=b64[k]; + k=(buf[j+1]&0x0f)<<2|(buf[j+2]>>>6)&0x03; + tmp[i++]=b64[k]; + k=buf[j+2]&0x3f; + tmp[i++]=b64[k]; + } + + foo=(start+length)-foo; + if(foo==1){ + k=(buf[j]>>>2)&0x3f; + tmp[i++]=b64[k]; + k=((buf[j]&0x03)<<4)&0x3f; + tmp[i++]=b64[k]; + tmp[i++]=(byte)'='; + tmp[i++]=(byte)'='; + } + else if(foo==2){ + k=(buf[j]>>>2)&0x3f; + tmp[i++]=b64[k]; + k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f; + tmp[i++]=b64[k]; + k=((buf[j+1]&0x0f)<<2)&0x3f; + tmp[i++]=b64[k]; + tmp[i++]=(byte)'='; + } + byte[] bar=new byte[i]; + System.arraycopy(tmp, 0, bar, 0, i); + return bar; + +// return sun.misc.BASE64Encoder().encode(buf); + } + + static String[] split(String foo, String split){ + if(foo==null) + return null; + byte[] buf=Util.str2byte(foo); + java.util.Vector bar=new java.util.Vector(); + int start=0; + int index; + while(true){ + index=foo.indexOf(split, start); + if(index>=0){ + bar.addElement(Util.byte2str(buf, start, index-start)); + start=index+1; + continue; + } + bar.addElement(Util.byte2str(buf, start, buf.length-start)); + break; + } + String[] result=new String[bar.size()]; + for(int i=0; i<result.length; i++){ + result[i]=(String)(bar.elementAt(i)); + } + return result; + } + static boolean glob(byte[] pattern, byte[] name){ + return glob0(pattern, 0, name, 0); + } + static private boolean glob0(byte[] pattern, int pattern_index, + byte[] name, int name_index){ + if(name.length>0 && name[0]=='.'){ + if(pattern.length>0 && pattern[0]=='.'){ + if(pattern.length==2 && pattern[1]=='*') return true; + return glob(pattern, pattern_index+1, name, name_index+1); + } + return false; + } + return glob(pattern, pattern_index, name, name_index); + } + static private boolean glob(byte[] pattern, int pattern_index, + byte[] name, int name_index){ + //System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", "+name_index); + + int patternlen=pattern.length; + if(patternlen==0) + return false; + + int namelen=name.length; + int i=pattern_index; + int j=name_index; + + while(i<patternlen && j<namelen){ + if(pattern[i]=='\\'){ + if(i+1==patternlen) + return false; + i++; + if(pattern[i]!=name[j]) + return false; + i+=skipUTF8Char(pattern[i]); + j+=skipUTF8Char(name[j]); + continue; + } + + if(pattern[i]=='*'){ + while(i<patternlen){ + if(pattern[i]=='*'){ + i++; + continue; + } + break; + } + if(patternlen==i) + return true; + + byte foo=pattern[i]; + if(foo=='?'){ + while(j<namelen){ + if(glob(pattern, i, name, j)){ + return true; + } + j+=skipUTF8Char(name[j]); + } + return false; + } + else if(foo=='\\'){ + if(i+1==patternlen) + return false; + i++; + foo=pattern[i]; + while(j<namelen){ + if(foo==name[j]){ + if(glob(pattern, i+skipUTF8Char(foo), + name, j+skipUTF8Char(name[j]))){ + return true; + } + } + j+=skipUTF8Char(name[j]); + } + return false; + } + + while(j<namelen){ + if(foo==name[j]){ + if(glob(pattern, i, name, j)){ + return true; + } + } + j+=skipUTF8Char(name[j]); + } + return false; + } + + if(pattern[i]=='?'){ + i++; + j+=skipUTF8Char(name[j]); + continue; + } + + if(pattern[i]!=name[j]) + return false; + + i+=skipUTF8Char(pattern[i]); + j+=skipUTF8Char(name[j]); + + if(!(j<namelen)){ // name is end + if(!(i<patternlen)){ // pattern is end + return true; + } + if(pattern[i]=='*'){ + break; + } + } + continue; + } + + if(i==patternlen && j==namelen) + return true; + + if(!(j<namelen) && // name is end + pattern[i]=='*'){ + boolean ok=true; + while(i<patternlen){ + if(pattern[i++]!='*'){ + ok=false; + break; + } + } + return ok; + } + + return false; + } + + static String quote(String path){ + byte[] _path=str2byte(path); + int count=0; + for(int i=0;i<_path.length; i++){ + byte b=_path[i]; + if(b=='\\' || b=='?' || b=='*') + count++; + } + if(count==0) + return path; + byte[] _path2=new byte[_path.length+count]; + for(int i=0, j=0; i<_path.length; i++){ + byte b=_path[i]; + if(b=='\\' || b=='?' || b=='*'){ + _path2[j++]='\\'; + } + _path2[j++]=b; + } + return byte2str(_path2); + } + + static String unquote(String path){ + byte[] foo=str2byte(path); + byte[] bar=unquote(foo); + if(foo.length==bar.length) + return path; + return byte2str(bar); + } + static byte[] unquote(byte[] path){ + int pathlen=path.length; + int i=0; + while(i<pathlen){ + if(path[i]=='\\'){ + if(i+1==pathlen) + break; + System.arraycopy(path, i+1, path, i, path.length-(i+1)); + pathlen--; + i++; + continue; + } + i++; + } + if(pathlen==path.length) + return path; + byte[] foo=new byte[pathlen]; + System.arraycopy(path, 0, foo, 0, pathlen); + return foo; + } + + private static String[] chars={ + "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f" + }; + static String getFingerPrint(HASH hash, byte[] data){ + try{ + hash.init(); + hash.update(data, 0, data.length); + byte[] foo=hash.digest(); + StringBuffer sb=new StringBuffer(); + int bar; + for(int i=0; i<foo.length;i++){ + bar=foo[i]&0xff; + sb.append(chars[(bar>>>4)&0xf]); + sb.append(chars[(bar)&0xf]); + if(i+1<foo.length) + sb.append(":"); + } + return sb.toString(); + } + catch(Exception e){ + return "???"; + } + } + static boolean array_equals(byte[] foo, byte bar[]){ + int i=foo.length; + if(i!=bar.length) return false; + for(int j=0; j<i; j++){ if(foo[j]!=bar[j]) return false; } + //try{while(true){i--; if(foo[i]!=bar[i])return false;}}catch(Exception e){} + return true; + } + static Socket createSocket(String host, int port, int timeout) throws JSchException{ + Socket socket=null; + if(timeout==0){ + try{ + socket=new Socket(host, port); + return socket; + } + catch(Exception e){ + String message=e.toString(); + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + } + final String _host=host; + final int _port=port; + final Socket[] sockp=new Socket[1]; + final Exception[] ee=new Exception[1]; + String message=""; + Thread tmp=new Thread(new Runnable(){ + public void run(){ + sockp[0]=null; + try{ + sockp[0]=new Socket(_host, _port); + } + catch(Exception e){ + ee[0]=e; + if(sockp[0]!=null && sockp[0].isConnected()){ + try{ + sockp[0].close(); + } + catch(Exception eee){} + } + sockp[0]=null; + } + } + }); + tmp.setName("Opening Socket "+host); + tmp.start(); + try{ + tmp.join(timeout); + message="timeout: "; + } + catch(java.lang.InterruptedException eee){ + } + if(sockp[0]!=null && sockp[0].isConnected()){ + socket=sockp[0]; + } + else{ + message+="socket is not established"; + if(ee[0]!=null){ + message=ee[0].toString(); + } + tmp.interrupt(); + tmp=null; + throw new JSchException(message); + } + return socket; + } + + static byte[] str2byte(String str, String encoding){ + if(str==null) + return null; + try{ return str.getBytes(encoding); } + catch(java.io.UnsupportedEncodingException e){ + return str.getBytes(); + } + } + + static byte[] str2byte(String str){ + return str2byte(str, "UTF-8"); + } + + static String byte2str(byte[] str, String encoding){ + return byte2str(str, 0, str.length, encoding); + } + + static String byte2str(byte[] str, int s, int l, String encoding){ + try{ return new String(str, s, l, encoding); } + catch(java.io.UnsupportedEncodingException e){ + return new String(str, s, l); + } + } + + static String byte2str(byte[] str){ + return byte2str(str, 0, str.length, "UTF-8"); + } + + static String byte2str(byte[] str, int s, int l){ + return byte2str(str, s, l, "UTF-8"); + } + + static final byte[] empty = str2byte(""); + + /* + static byte[] char2byte(char[] foo){ + int len=0; + for(int i=0; i<foo.length; i++){ + if((foo[i]&0xff00)==0) len++; + else len+=2; + } + byte[] bar=new byte[len]; + for(int i=0, j=0; i<foo.length; i++){ + if((foo[i]&0xff00)==0){ + bar[j++]=(byte)foo[i]; + } + else{ + bar[j++]=(byte)(foo[i]>>>8); + bar[j++]=(byte)foo[i]; + } + } + return bar; + } + */ + static void bzero(byte[] foo){ + if(foo==null) + return; + for(int i=0; i<foo.length; i++) + foo[i]=0; + } + + static String diffString(String str, String[] not_available){ + String[] stra=Util.split(str, ","); + String result=null; + loop: + for(int i=0; i<stra.length; i++){ + for(int j=0; j<not_available.length; j++){ + if(stra[i].equals(not_available[j])){ + continue loop; + } + } + if(result==null){ result=stra[i]; } + else{ result=result+","+stra[i]; } + } + return result; + } + + private static int skipUTF8Char(byte b){ + if((byte)(b&0x80)==0) return 1; + if((byte)(b&0xe0)==(byte)0xc0) return 2; + if((byte)(b&0xf0)==(byte)0xe0) return 3; + return 1; + } +} diff --git a/java/com/jcraft/jsch/jce/AES128CBC.java b/java/com/jcraft/jsch/jce/AES128CBC.java new file mode 100644 index 00000000..8c2b43b5 --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES128CBC.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES128CBC implements Cipher{ + private static final int ivsize=16; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + + public boolean isCBC(){return true; } +} diff --git a/java/com/jcraft/jsch/jce/AES128CTR.java b/java/com/jcraft/jsch/jce/AES128CTR.java new file mode 100644 index 00000000..470a896b --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES128CTR.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES128CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/AES192CBC.java b/java/com/jcraft/jsch/jce/AES192CBC.java new file mode 100644 index 00000000..615d4943 --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES192CBC.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES192CBC implements Cipher{ + private static final int ivsize=16; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/java/com/jcraft/jsch/jce/AES192CTR.java b/java/com/jcraft/jsch/jce/AES192CTR.java new file mode 100644 index 00000000..74090bf9 --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES192CTR.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES192CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/AES256CBC.java b/java/com/jcraft/jsch/jce/AES256CBC.java new file mode 100644 index 00000000..9018a204 --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES256CBC.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES256CBC implements Cipher{ + private static final int ivsize=16; + private static final int bsize=32; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/java/com/jcraft/jsch/jce/AES256CTR.java b/java/com/jcraft/jsch/jce/AES256CTR.java new file mode 100644 index 00000000..b7472e96 --- /dev/null +++ b/java/com/jcraft/jsch/jce/AES256CTR.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES256CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=32; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/ARCFOUR.java b/java/com/jcraft/jsch/jce/ARCFOUR.java new file mode 100644 index 00000000..9f6537c3 --- /dev/null +++ b/java/com/jcraft/jsch/jce/ARCFOUR.java @@ -0,0 +1,68 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class ARCFOUR implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/ARCFOUR128.java b/java/com/jcraft/jsch/jce/ARCFOUR128.java new file mode 100644 index 00000000..d8b46137 --- /dev/null +++ b/java/com/jcraft/jsch/jce/ARCFOUR128.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class ARCFOUR128 implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + private static final int skip=1536; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + byte[] tmp; + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + byte[] foo=new byte[1]; + for(int i=0; i<skip; i++){ + cipher.update(foo, 0, 1, foo, 0); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/ARCFOUR256.java b/java/com/jcraft/jsch/jce/ARCFOUR256.java new file mode 100644 index 00000000..a4a9f690 --- /dev/null +++ b/java/com/jcraft/jsch/jce/ARCFOUR256.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class ARCFOUR256 implements Cipher{ + private static final int ivsize=8; + private static final int bsize=32; + private static final int skip=1536; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + byte[] tmp; + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + byte[] foo=new byte[1]; + for(int i=0; i<skip; i++){ + cipher.update(foo, 0, 1, foo, 0); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jce/BlowfishCBC.java b/java/com/jcraft/jsch/jce/BlowfishCBC.java new file mode 100644 index 00000000..3853ab72 --- /dev/null +++ b/java/com/jcraft/jsch/jce/BlowfishCBC.java @@ -0,0 +1,71 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class BlowfishCBC implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; +// if(padding) pad="PKCS5Padding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); + cipher=javax.crypto.Cipher.getInstance("Blowfish/CBC/"+pad); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); + } + catch(Exception e){ + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/java/com/jcraft/jsch/jce/DH.java b/java/com/jcraft/jsch/jce/DH.java new file mode 100644 index 00000000..da03c1de --- /dev/null +++ b/java/com/jcraft/jsch/jce/DH.java @@ -0,0 +1,91 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class DH implements com.jcraft.jsch.DH{ + BigInteger p; + BigInteger g; + BigInteger e; // my public key + byte[] e_array; + BigInteger f; // your public key + BigInteger K; // shared secret key + byte[] K_array; + + private KeyPairGenerator myKpairGen; + private KeyAgreement myKeyAgree; + public void init() throws Exception{ + myKpairGen=KeyPairGenerator.getInstance("DH"); +// myKpairGen=KeyPairGenerator.getInstance("DiffieHellman"); + myKeyAgree=KeyAgreement.getInstance("DH"); +// myKeyAgree=KeyAgreement.getInstance("DiffieHellman"); + } + public byte[] getE() throws Exception{ + if(e==null){ + DHParameterSpec dhSkipParamSpec=new DHParameterSpec(p, g); + myKpairGen.initialize(dhSkipParamSpec); + KeyPair myKpair=myKpairGen.generateKeyPair(); + myKeyAgree.init(myKpair.getPrivate()); +// BigInteger x=((javax.crypto.interfaces.DHPrivateKey)(myKpair.getPrivate())).getX(); + byte[] myPubKeyEnc=myKpair.getPublic().getEncoded(); + e=((javax.crypto.interfaces.DHPublicKey)(myKpair.getPublic())).getY(); + e_array=e.toByteArray(); + } + return e_array; + } + public byte[] getK() throws Exception{ + if(K==null){ + KeyFactory myKeyFac=KeyFactory.getInstance("DH"); + DHPublicKeySpec keySpec=new DHPublicKeySpec(f, p, g); + PublicKey yourPubKey=myKeyFac.generatePublic(keySpec); + myKeyAgree.doPhase(yourPubKey, true); + byte[] mySharedSecret=myKeyAgree.generateSecret(); + K=new BigInteger(mySharedSecret); + K_array=K.toByteArray(); + +//System.err.println("K.signum(): "+K.signum()+ +// " "+Integer.toHexString(mySharedSecret[0]&0xff)+ +// " "+Integer.toHexString(K_array[0]&0xff)); + + K_array=mySharedSecret; + } + return K_array; + } + public void setP(byte[] p){ setP(new BigInteger(p)); } + public void setG(byte[] g){ setG(new BigInteger(g)); } + public void setF(byte[] f){ setF(new BigInteger(f)); } + void setP(BigInteger p){this.p=p;} + void setG(BigInteger g){this.g=g;} + void setF(BigInteger f){this.f=f;} +} diff --git a/java/com/jcraft/jsch/jce/HMACMD5.java b/java/com/jcraft/jsch/jce/HMACMD5.java new file mode 100644 index 00000000..def3747b --- /dev/null +++ b/java/com/jcraft/jsch/jce/HMACMD5.java @@ -0,0 +1,75 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.MAC; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class HMACMD5 implements MAC{ + private static final String name="hmac-md5"; + private static final int BSIZE=16; + private Mac mac; + public int getBlockSize(){return BSIZE;}; + public void init(byte[] key) throws Exception{ + if(key.length>BSIZE){ + byte[] tmp=new byte[BSIZE]; + System.arraycopy(key, 0, tmp, 0, BSIZE); + key=tmp; + } + + SecretKeySpec skey=new SecretKeySpec(key, "HmacMD5"); + mac=Mac.getInstance("HmacMD5"); + mac.init(skey); + } + + private final byte[] tmp=new byte[4]; + public void update(int i){ + tmp[0]=(byte)(i>>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + public void update(byte foo[], int s, int l){ + mac.update(foo, s, l); + } + public void doFinal(byte[] buf, int offset){ + try{ + mac.doFinal(buf, offset); + } + catch(ShortBufferException e){ + } + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jce/HMACMD596.java b/java/com/jcraft/jsch/jce/HMACMD596.java new file mode 100644 index 00000000..a296a5df --- /dev/null +++ b/java/com/jcraft/jsch/jce/HMACMD596.java @@ -0,0 +1,77 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.MAC; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class HMACMD596 implements MAC{ + private static final String name="hmac-md5-96"; + private static final int bsize=12; + private Mac mac; + public int getBlockSize(){return bsize;}; + public void init(byte[] key) throws Exception{ + if(key.length>16){ + byte[] tmp=new byte[16]; + System.arraycopy(key, 0, tmp, 0, 16); + key=tmp; + } + SecretKeySpec skey=new SecretKeySpec(key, "HmacMD5"); + mac=Mac.getInstance("HmacMD5"); + mac.init(skey); + } + private final byte[] tmp=new byte[4]; + public void update(int i){ + tmp[0]=(byte)(i>>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + + public void update(byte foo[], int s, int l){ + mac.update(foo, s, l); + } + + private final byte[] _buf16=new byte[16]; + public void doFinal(byte[] buf, int offset){ + try{ + mac.doFinal(_buf16, 0); + } + catch(ShortBufferException e){ + } + System.arraycopy(_buf16, 0, buf, offset, 12); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jce/HMACSHA1.java b/java/com/jcraft/jsch/jce/HMACSHA1.java new file mode 100644 index 00000000..13feaabf --- /dev/null +++ b/java/com/jcraft/jsch/jce/HMACSHA1.java @@ -0,0 +1,75 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.MAC; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class HMACSHA1 implements MAC{ + private static final String name="hmac-sha1"; + private static final int bsize=20; + private Mac mac; + public int getBlockSize(){return bsize;}; + public void init(byte[] key) throws Exception{ + if(key.length>bsize){ + byte[] tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, bsize); + key=tmp; + } + SecretKeySpec skey=new SecretKeySpec(key, "HmacSHA1"); + mac=Mac.getInstance("HmacSHA1"); + mac.init(skey); + } + private final byte[] tmp=new byte[4]; + public void update(int i){ + tmp[0]=(byte)(i>>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + + public void update(byte foo[], int s, int l){ + mac.update(foo, s, l); + } + + public void doFinal(byte[] buf, int offset){ + try{ + mac.doFinal(buf, offset); + } + catch(ShortBufferException e){ + } + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jce/HMACSHA196.java b/java/com/jcraft/jsch/jce/HMACSHA196.java new file mode 100644 index 00000000..e8791762 --- /dev/null +++ b/java/com/jcraft/jsch/jce/HMACSHA196.java @@ -0,0 +1,76 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.MAC; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class HMACSHA196 implements MAC{ + private static final String name="hmac-sha1-96"; + private static final int bsize=12; + private Mac mac; + public int getBlockSize(){return bsize;}; + public void init(byte[] key) throws Exception{ + if(key.length>20){ + byte[] tmp=new byte[20]; + System.arraycopy(key, 0, tmp, 0, 20); + key=tmp; + } + SecretKeySpec skey=new SecretKeySpec(key, "HmacSHA1"); + mac=Mac.getInstance("HmacSHA1"); + mac.init(skey); + } + private final byte[] tmp=new byte[4]; + public void update(int i){ + tmp[0]=(byte)(i>>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + public void update(byte foo[], int s, int l){ + mac.update(foo, s, l); + } + + private final byte[] _buf20=new byte[20]; + public void doFinal(byte[] buf, int offset){ + try{ + mac.doFinal(_buf20, 0); + } + catch(ShortBufferException e){ + } + System.arraycopy(_buf20, 0, buf, offset, 12); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jce/KeyPairGenDSA.java b/java/com/jcraft/jsch/jce/KeyPairGenDSA.java new file mode 100644 index 00000000..67ad54eb --- /dev/null +++ b/java/com/jcraft/jsch/jce/KeyPairGenDSA.java @@ -0,0 +1,62 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; +import java.security.interfaces.*; + +public class KeyPairGenDSA implements com.jcraft.jsch.KeyPairGenDSA{ + byte[] x; // private + byte[] y; // public + byte[] p; + byte[] q; + byte[] g; + + public void init(int key_size) throws Exception{ + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); + keyGen.initialize(key_size, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + PublicKey pubKey=pair.getPublic(); + PrivateKey prvKey=pair.getPrivate(); + + x=((DSAPrivateKey)prvKey).getX().toByteArray(); + y=((DSAPublicKey)pubKey).getY().toByteArray(); + + DSAParams params=((DSAKey)prvKey).getParams(); + p=params.getP().toByteArray(); + q=params.getQ().toByteArray(); + g=params.getG().toByteArray(); + } + public byte[] getX(){return x;} + public byte[] getY(){return y;} + public byte[] getP(){return p;} + public byte[] getQ(){return q;} + public byte[] getG(){return g;} +} diff --git a/java/com/jcraft/jsch/jce/KeyPairGenRSA.java b/java/com/jcraft/jsch/jce/KeyPairGenRSA.java new file mode 100644 index 00000000..543f0f79 --- /dev/null +++ b/java/com/jcraft/jsch/jce/KeyPairGenRSA.java @@ -0,0 +1,72 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; +import java.security.interfaces.*; + +public class KeyPairGenRSA implements com.jcraft.jsch.KeyPairGenRSA{ + byte[] d; // private + byte[] e; // public + byte[] n; + + byte[] c; // coefficient + byte[] ep; // exponent p + byte[] eq; // exponent q + byte[] p; // prime p + byte[] q; // prime q + + public void init(int key_size) throws Exception{ + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(key_size, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + + PublicKey pubKey=pair.getPublic(); + PrivateKey prvKey=pair.getPrivate(); + + d=((RSAPrivateKey)prvKey).getPrivateExponent().toByteArray(); + e=((RSAPublicKey)pubKey).getPublicExponent().toByteArray(); + n=((RSAPrivateKey)prvKey).getModulus().toByteArray(); + + c=((RSAPrivateCrtKey)prvKey).getCrtCoefficient().toByteArray(); + ep=((RSAPrivateCrtKey)prvKey).getPrimeExponentP().toByteArray(); + eq=((RSAPrivateCrtKey)prvKey).getPrimeExponentQ().toByteArray(); + p=((RSAPrivateCrtKey)prvKey).getPrimeP().toByteArray(); + q=((RSAPrivateCrtKey)prvKey).getPrimeQ().toByteArray(); + } + public byte[] getD(){return d;} + public byte[] getE(){return e;} + public byte[] getN(){return n;} + public byte[] getC(){return c;} + public byte[] getEP(){return ep;} + public byte[] getEQ(){return eq;} + public byte[] getP(){return p;} + public byte[] getQ(){return q;} +} diff --git a/java/com/jcraft/jsch/jce/MD5.java b/java/com/jcraft/jsch/jce/MD5.java new file mode 100644 index 00000000..538ae340 --- /dev/null +++ b/java/com/jcraft/jsch/jce/MD5.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; + +import java.security.*; + +public class MD5 implements HASH{ + MessageDigest md; + public int getBlockSize(){return 16;} + public void init() throws Exception{ + try{ md=MessageDigest.getInstance("MD5"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception{ + md.update(foo, start, len); + } + public byte[] digest() throws Exception{ + return md.digest(); + } +} diff --git a/java/com/jcraft/jsch/jce/Random.java b/java/com/jcraft/jsch/jce/Random.java new file mode 100644 index 00000000..8668a01a --- /dev/null +++ b/java/com/jcraft/jsch/jce/Random.java @@ -0,0 +1,81 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.SecureRandom; + +public class Random implements com.jcraft.jsch.Random{ + private byte[] tmp=new byte[16]; + private SecureRandom random=null; + public Random(){ + + // We hope that 'new SecureRandom()' will use NativePRNG algorithm + // on Sun's Java5 for GNU/Linux and Solaris. + // It seems NativePRNG refers to /dev/urandom and it must not be blocked, + // but NativePRNG is slower than SHA1PRNG ;-< + // TIPS: By adding option '-Djava.security.egd=file:/dev/./urandom' + // SHA1PRNG will be used instead of NativePRNG. + // On MacOSX, 'new SecureRandom()' will use NativePRNG algorithm and + // it is also slower than SHA1PRNG. + // On Windows, 'new SecureRandom()' will use SHA1PRNG algorithm. + random=new SecureRandom(); + + /* + try{ + random=SecureRandom.getInstance("SHA1PRNG"); + return; + } + catch(java.security.NoSuchAlgorithmException e){ + // System.err.println(e); + } + + // The following code is for IBM's JCE + try{ + random=SecureRandom.getInstance("IBMSecureRandom"); + return; + } + catch(java.security.NoSuchAlgorithmException ee){ + //System.err.println(ee); + } + */ + } + public void fill(byte[] foo, int start, int len){ + /* + // This case will not become true in our usage. + if(start==0 && foo.length==len){ + random.nextBytes(foo); + return; + } + */ + if(len>tmp.length){ tmp=new byte[len]; } + random.nextBytes(tmp); + System.arraycopy(tmp, 0, foo, start, len); + } +} diff --git a/java/com/jcraft/jsch/jce/SHA1.java b/java/com/jcraft/jsch/jce/SHA1.java new file mode 100644 index 00000000..08fce4b0 --- /dev/null +++ b/java/com/jcraft/jsch/jce/SHA1.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; + +import java.security.*; + +public class SHA1 implements HASH{ + MessageDigest md; + public int getBlockSize(){return 20;} + public void init() throws Exception{ + try{ md=MessageDigest.getInstance("SHA-1"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception{ + md.update(foo, start, len); + } + public byte[] digest() throws Exception{ + return md.digest(); + } +} diff --git a/java/com/jcraft/jsch/jce/SignatureDSA.java b/java/com/jcraft/jsch/jce/SignatureDSA.java new file mode 100644 index 00000000..262dfc5e --- /dev/null +++ b/java/com/jcraft/jsch/jce/SignatureDSA.java @@ -0,0 +1,147 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; + +public class SignatureDSA implements com.jcraft.jsch.SignatureDSA{ + + java.security.Signature signature; + KeyFactory keyFactory; + + public void init() throws Exception{ + signature=java.security.Signature.getInstance("SHA1withDSA"); + keyFactory=KeyFactory.getInstance("DSA"); + } + public void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception{ + DSAPublicKeySpec dsaPubKeySpec = + new DSAPublicKeySpec(new BigInteger(y), + new BigInteger(p), + new BigInteger(q), + new BigInteger(g)); + PublicKey pubKey=keyFactory.generatePublic(dsaPubKeySpec); + signature.initVerify(pubKey); + } + public void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception{ + DSAPrivateKeySpec dsaPrivKeySpec = + new DSAPrivateKeySpec(new BigInteger(x), + new BigInteger(p), + new BigInteger(q), + new BigInteger(g)); + PrivateKey prvKey = keyFactory.generatePrivate(dsaPrivKeySpec); + signature.initSign(prvKey); + } + public byte[] sign() throws Exception{ + byte[] sig=signature.sign(); +/* +System.err.print("sign["+sig.length+"] "); +for(int i=0; i<sig.length;i++){ +System.err.print(Integer.toHexString(sig[i]&0xff)+":"); +} +System.err.println(""); +*/ + // sig is in ASN.1 + // SEQUENCE::={ r INTEGER, s INTEGER } + int len=0; + int index=3; + len=sig[index++]&0xff; +//System.err.println("! len="+len); + byte[] r=new byte[len]; + System.arraycopy(sig, index, r, 0, r.length); + index=index+len+1; + len=sig[index++]&0xff; +//System.err.println("!! len="+len); + byte[] s=new byte[len]; + System.arraycopy(sig, index, s, 0, s.length); + + byte[] result=new byte[40]; + + // result must be 40 bytes, but length of r and s may not be 20 bytes + + System.arraycopy(r, (r.length>20)?1:0, + result, (r.length>20)?0:20-r.length, + (r.length>20)?20:r.length); + System.arraycopy(s, (s.length>20)?1:0, + result, (s.length>20)?20:40-s.length, + (s.length>20)?20:s.length); + +// System.arraycopy(sig, (sig[3]==20?4:5), result, 0, 20); +// System.arraycopy(sig, sig.length-20, result, 20, 20); + + return result; + } + public void update(byte[] foo) throws Exception{ + signature.update(foo); + } + public boolean verify(byte[] sig) throws Exception{ + int i=0; + int j=0; + byte[] tmp; + + if(sig[0]==0 && sig[1]==0 && sig[2]==0){ + j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)| + ((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff); + i+=j; + j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)| + ((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff); + tmp=new byte[j]; + System.arraycopy(sig, i, tmp, 0, j); sig=tmp; + } + + // ASN.1 + int frst=((sig[0]&0x80)!=0?1:0); + int scnd=((sig[20]&0x80)!=0?1:0); + //System.err.println("frst: "+frst+", scnd: "+scnd); + + int length=sig.length+6+frst+scnd; + tmp=new byte[length]; + tmp[0]=(byte)0x30; tmp[1]=(byte)0x2c; + tmp[1]+=frst; tmp[1]+=scnd; + tmp[2]=(byte)0x02; tmp[3]=(byte)0x14; + tmp[3]+=frst; + System.arraycopy(sig, 0, tmp, 4+frst, 20); + tmp[4+tmp[3]]=(byte)0x02; tmp[5+tmp[3]]=(byte)0x14; + tmp[5+tmp[3]]+=scnd; + System.arraycopy(sig, 20, tmp, 6+tmp[3]+scnd, 20); + sig=tmp; + +/* + tmp=new byte[sig.length+6]; + tmp[0]=(byte)0x30; tmp[1]=(byte)0x2c; + tmp[2]=(byte)0x02; tmp[3]=(byte)0x14; + System.arraycopy(sig, 0, tmp, 4, 20); + tmp[24]=(byte)0x02; tmp[25]=(byte)0x14; + System.arraycopy(sig, 20, tmp, 26, 20); sig=tmp; +*/ + return signature.verify(sig); + } +} diff --git a/java/com/jcraft/jsch/jce/SignatureRSA.java b/java/com/jcraft/jsch/jce/SignatureRSA.java new file mode 100644 index 00000000..50d8b5a8 --- /dev/null +++ b/java/com/jcraft/jsch/jce/SignatureRSA.java @@ -0,0 +1,83 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; + +public class SignatureRSA implements com.jcraft.jsch.SignatureRSA{ + + java.security.Signature signature; + KeyFactory keyFactory; + + public void init() throws Exception{ + signature=java.security.Signature.getInstance("SHA1withRSA"); + keyFactory=KeyFactory.getInstance("RSA"); + } + public void setPubKey(byte[] e, byte[] n) throws Exception{ + RSAPublicKeySpec rsaPubKeySpec = + new RSAPublicKeySpec(new BigInteger(n), + new BigInteger(e)); + PublicKey pubKey=keyFactory.generatePublic(rsaPubKeySpec); + signature.initVerify(pubKey); + } + public void setPrvKey(byte[] d, byte[] n) throws Exception{ + RSAPrivateKeySpec rsaPrivKeySpec = + new RSAPrivateKeySpec(new BigInteger(n), + new BigInteger(d)); + PrivateKey prvKey = keyFactory.generatePrivate(rsaPrivKeySpec); + signature.initSign(prvKey); + } + public byte[] sign() throws Exception{ + byte[] sig=signature.sign(); + return sig; + } + public void update(byte[] foo) throws Exception{ + signature.update(foo); + } + public boolean verify(byte[] sig) throws Exception{ + int i=0; + int j=0; + byte[] tmp; + + if(sig[0]==0 && sig[1]==0 && sig[2]==0){ + j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)| + ((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff); + i+=j; + j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)| + ((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff); + tmp=new byte[j]; + System.arraycopy(sig, i, tmp, 0, j); sig=tmp; + } +//System.err.println("j="+j+" "+Integer.toHexString(sig[0]&0xff)); + return signature.verify(sig); + } +} diff --git a/java/com/jcraft/jsch/jce/TripleDESCBC.java b/java/com/jcraft/jsch/jce/TripleDESCBC.java new file mode 100644 index 00000000..2cbf9b75 --- /dev/null +++ b/java/com/jcraft/jsch/jce/TripleDESCBC.java @@ -0,0 +1,84 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class TripleDESCBC implements Cipher{ + private static final int ivsize=8; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + //if(padding) pad="PKCS5Padding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("DESede/CBC/"+pad); +/* + // The following code does not work on IBM's JDK 1.4.1 + SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); +*/ + DESedeKeySpec keyspec=new DESedeKeySpec(key); + SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede"); + SecretKey _key=keyfactory.generateSecret(keyspec); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/java/com/jcraft/jsch/jce/TripleDESCTR.java b/java/com/jcraft/jsch/jce/TripleDESCTR.java new file mode 100644 index 00000000..899f03bd --- /dev/null +++ b/java/com/jcraft/jsch/jce/TripleDESCTR.java @@ -0,0 +1,84 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class TripleDESCTR implements Cipher{ + private static final int ivsize=8; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + //if(padding) pad="PKCS5Padding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("DESede/CTR/"+pad); +/* + // The following code does not work on IBM's JDK 1.4.1 + SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); +*/ + DESedeKeySpec keyspec=new DESedeKeySpec(key); + SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede"); + SecretKey _key=keyfactory.generateSecret(keyspec); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/java/com/jcraft/jsch/jcraft/Compression.java b/java/com/jcraft/jsch/jcraft/Compression.java new file mode 100644 index 00000000..dddcaed4 --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/Compression.java @@ -0,0 +1,140 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; +import com.jcraft.jzlib.*; +import com.jcraft.jsch.*; + +public class Compression implements com.jcraft.jsch.Compression { + static private final int BUF_SIZE=4096; + private final int buffer_margin=32+20; // AES256 + HMACSHA1 + private int type; + private ZStream stream; + private byte[] tmpbuf=new byte[BUF_SIZE]; + + public Compression(){ + stream=new ZStream(); + } + + public void init(int type, int level){ + if(type==DEFLATER){ + stream.deflateInit(level); + this.type=DEFLATER; + } + else if(type==INFLATER){ + stream.inflateInit(); + inflated_buf=new byte[BUF_SIZE]; + this.type=INFLATER; + } + } + + private byte[] inflated_buf; + + public byte[] compress(byte[] buf, int start, int[] len){ + stream.next_in=buf; + stream.next_in_index=start; + stream.avail_in=len[0]-start; + int status; + int outputlen=start; + byte[] outputbuf=buf; + int tmp=0; + + do{ + stream.next_out=tmpbuf; + stream.next_out_index=0; + stream.avail_out=BUF_SIZE; + status=stream.deflate(JZlib.Z_PARTIAL_FLUSH); + switch(status){ + case JZlib.Z_OK: + tmp=BUF_SIZE-stream.avail_out; + if(outputbuf.length<outputlen+tmp+buffer_margin){ + byte[] foo=new byte[(outputlen+tmp+buffer_margin)*2]; + System.arraycopy(outputbuf, 0, foo, 0, outputbuf.length); + outputbuf=foo; + } + System.arraycopy(tmpbuf, 0, outputbuf, outputlen, tmp); + outputlen+=tmp; + break; + default: + System.err.println("compress: deflate returnd "+status); + } + } + while(stream.avail_out==0); + + len[0]=outputlen; + return outputbuf; + } + + public byte[] uncompress(byte[] buffer, int start, int[] length){ + int inflated_end=0; + + stream.next_in=buffer; + stream.next_in_index=start; + stream.avail_in=length[0]; + + while(true){ + stream.next_out=tmpbuf; + stream.next_out_index=0; + stream.avail_out=BUF_SIZE; + int status=stream.inflate(JZlib.Z_PARTIAL_FLUSH); + switch(status){ + case JZlib.Z_OK: + if(inflated_buf.length<inflated_end+BUF_SIZE-stream.avail_out){ + int len=inflated_buf.length*2; + if(len<inflated_end+BUF_SIZE-stream.avail_out) + len=inflated_end+BUF_SIZE-stream.avail_out; + byte[] foo=new byte[len]; + System.arraycopy(inflated_buf, 0, foo, 0, inflated_end); + inflated_buf=foo; + } + System.arraycopy(tmpbuf, 0, + inflated_buf, inflated_end, + BUF_SIZE-stream.avail_out); + inflated_end+=(BUF_SIZE-stream.avail_out); + length[0]=inflated_end; + break; + case JZlib.Z_BUF_ERROR: + if(inflated_end>buffer.length-start){ + byte[] foo=new byte[inflated_end+start]; + System.arraycopy(buffer, 0, foo, 0, start); + System.arraycopy(inflated_buf, 0, foo, start, inflated_end); + buffer=foo; + } + else{ + System.arraycopy(inflated_buf, 0, buffer, start, inflated_end); + } + length[0]=inflated_end; + return buffer; + default: + System.err.println("uncompress: inflate returnd "+status); + return null; + } + } + } +} diff --git a/java/com/jcraft/jsch/jcraft/HMAC.java b/java/com/jcraft/jsch/jcraft/HMAC.java new file mode 100644 index 00000000..9aedc27f --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/HMAC.java @@ -0,0 +1,107 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import java.security.*; + +class HMAC{ + + /* + * Refer to RFC2104. + * + * H(K XOR opad, H(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + private static final int B=64; + private byte[] k_ipad=null; + private byte[] k_opad=null; + + private MessageDigest md=null; + + private int bsize=0; + + protected void setH(MessageDigest md){ + this.md=md; + bsize=md.getDigestLength(); + } + + public int getBlockSize(){return bsize;}; + public void init(byte[] key) throws Exception{ + if(key.length>bsize){ + byte[] tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, bsize); + key=tmp; + } + + /* if key is longer than B bytes reset it to key=MD5(key) */ + if(key.length>B){ + md.update(key, 0, key.length); + key=md.digest(); + } + + k_ipad=new byte[B]; + System.arraycopy(key, 0, k_ipad, 0, key.length); + k_opad=new byte[B]; + System.arraycopy(key, 0, k_opad, 0, key.length); + + /* XOR key with ipad and opad values */ + for(int i=0; i<B; i++) { + k_ipad[i]^=(byte)0x36; + k_opad[i]^=(byte)0x5c; + } + + md.update(k_ipad, 0, B); + } + + private final byte[] tmp=new byte[4]; + public void update(int i){ + tmp[0]=(byte)(i>>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + + public void update(byte foo[], int s, int l){ + md.update(foo, s, l); + } + + public void doFinal(byte[] buf, int offset){ + byte[] result=md.digest(); + md.update(k_opad, 0, B); + md.update(result, 0, bsize); + try{md.digest(buf, offset, bsize);}catch(Exception e){} + md.update(k_ipad, 0, B); + } +} diff --git a/java/com/jcraft/jsch/jcraft/HMACMD5.java b/java/com/jcraft/jsch/jcraft/HMACMD5.java new file mode 100644 index 00000000..90960113 --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/HMACMD5.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; +import java.security.*; + +public class HMACMD5 extends HMAC implements MAC{ + private static final String name="hmac-md5"; + + public HMACMD5(){ + super(); + MessageDigest md=null; + try{ md=MessageDigest.getInstance("MD5"); } + catch(Exception e){ + System.err.println(e); + } + setH(md); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jcraft/HMACMD596.java b/java/com/jcraft/jsch/jcraft/HMACMD596.java new file mode 100644 index 00000000..95c6f60d --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/HMACMD596.java @@ -0,0 +1,50 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; + +public class HMACMD596 extends HMACMD5{ + + private static final String name="hmac-md5-96"; + private static final int BSIZE=12; + + public int getBlockSize(){return BSIZE;}; + + private final byte[] _buf16=new byte[16]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, BSIZE); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jcraft/HMACSHA1.java b/java/com/jcraft/jsch/jcraft/HMACSHA1.java new file mode 100644 index 00000000..ea9eccf1 --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/HMACSHA1.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; +import java.security.*; + +public class HMACSHA1 extends HMAC implements MAC{ + private static final String name="hmac-sha1"; + + public HMACSHA1(){ + super(); + MessageDigest md=null; + try{ md=MessageDigest.getInstance("SHA-1"); } + catch(Exception e){ + System.err.println(e); + } + setH(md); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jcraft/HMACSHA196.java b/java/com/jcraft/jsch/jcraft/HMACSHA196.java new file mode 100644 index 00000000..86a81b5b --- /dev/null +++ b/java/com/jcraft/jsch/jcraft/HMACSHA196.java @@ -0,0 +1,50 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; + +public class HMACSHA196 extends HMACSHA1{ + + private static final String name="hmac-sha1-96"; + private static final int BSIZE=12; + + public int getBlockSize(){return BSIZE;}; + + private final byte[] _buf16=new byte[20]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, BSIZE); + } + + public String getName(){ + return name; + } +} diff --git a/java/com/jcraft/jsch/jgss/GSSContextKrb5.java b/java/com/jcraft/jsch/jgss/GSSContextKrb5.java new file mode 100644 index 00000000..9ee1560e --- /dev/null +++ b/java/com/jcraft/jsch/jgss/GSSContextKrb5.java @@ -0,0 +1,177 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jgss; + +import com.jcraft.jsch.JSchException; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.MessageProp; +import org.ietf.jgss.Oid; + +public class GSSContextKrb5 implements com.jcraft.jsch.GSSContext{ + + private static final String pUseSubjectCredsOnly = + "javax.security.auth.useSubjectCredsOnly"; + private static String useSubjectCredsOnly = + getSystemProperty(pUseSubjectCredsOnly); + + private GSSContext context=null; + public void create(String user, String host) throws JSchException{ + try{ + // RFC 1964 + Oid krb5=new Oid("1.2.840.113554.1.2.2"); + // Kerberos Principal Name Form + Oid principalName=new Oid("1.2.840.113554.1.2.2.1"); + + GSSManager mgr=GSSManager.getInstance(); + + GSSCredential crd=null; + /* + try{ + GSSName _user=mgr.createName(user, principalName); + crd=mgr.createCredential(_user, + GSSCredential.DEFAULT_LIFETIME, + krb5, + GSSCredential.INITIATE_ONLY); + } + catch(GSSException crdex){ + } + */ + + String cname=host; + try{ + cname=InetAddress.getByName(cname).getCanonicalHostName(); + } + catch(UnknownHostException e){ + } + GSSName _host=mgr.createName("host/"+cname, principalName); + + context=mgr.createContext(_host, + krb5, + crd, + GSSContext.DEFAULT_LIFETIME); + + // RFC4462 3.4. GSS-API Session + // + // When calling GSS_Init_sec_context(), the client MUST set + // integ_req_flag to "true" to request that per-message integrity + // protection be supported for this context. In addition, + // deleg_req_flag MAY be set to "true" to request access delegation, if + // requested by the user. + // + // Since the user authentication process by its nature authenticates + // only the client, the setting of mutual_req_flag is not needed for + // this process. This flag SHOULD be set to "false". + + // TODO: OpenSSH's sshd does accepts 'false' for mutual_req_flag + //context.requestMutualAuth(false); + context.requestMutualAuth(true); + context.requestConf(true); + context.requestInteg(true); // for MIC + context.requestCredDeleg(true); + context.requestAnonymity(false); + + return; + } + catch(GSSException ex){ + throw new JSchException(ex.toString()); + } + } + + public boolean isEstablished(){ + return context.isEstablished(); + } + + public byte[] init(byte[] token, int s, int l) throws JSchException { + try{ + // Without setting "javax.security.auth.useSubjectCredsOnly" to "false", + // Sun's JVM for Un*x will show messages to stderr in + // processing context.initSecContext(). + // This hack is not thread safe ;-<. + // If that property is explicitly given as "true" or "false", + // this hack must not be invoked. + if(useSubjectCredsOnly==null){ + setSystemProperty(pUseSubjectCredsOnly, "false"); + } + return context.initSecContext(token, 0, l); + } + catch(GSSException ex){ + throw new JSchException(ex.toString()); + } + catch(java.lang.SecurityException ex){ + throw new JSchException(ex.toString()); + } + finally{ + if(useSubjectCredsOnly==null){ + // By the default, it must be "true". + setSystemProperty(pUseSubjectCredsOnly, "true"); + } + } + } + + public byte[] getMIC(byte[] message, int s, int l){ + try{ + MessageProp prop = new MessageProp(0, true); + return context.getMIC(message, s, l, prop); + } + catch(GSSException ex){ + return null; + } + } + + public void dispose(){ + try{ + context.dispose(); + } + catch(GSSException ex){ + } + } + + private static String getSystemProperty(String key){ + try{ return System.getProperty(key); } + catch(Exception e){ + // We are not allowed to get the System properties. + return null; + } + } + + private static void setSystemProperty(String key, String value){ + try{ System.setProperty(key, value); } + catch(Exception e){ + // We are not allowed to set the System properties. + } + } +} |